# HG changeset patch
# User Dylan Holmes
# Date 1329191634 21600
# Node ID bee5145ce4630d3c9883775b4977b30077b00360
# Parent e57d8c52f12f195f9c690ad16b5212e5b0a57b49# Parent 2fdcbe8185b1a2c30fcece3697f8b629f8f3ec70
Merged Robert's proprioception with Dylan's vision.
diff -r e57d8c52f12f -r bee5145ce463 assets/Models/test-creature/basic-muscle.png
Binary file assets/Models/test-creature/basic-muscle.png has changed
diff -r e57d8c52f12f -r bee5145ce463 assets/Models/test-creature/worm.blend
Binary file assets/Models/test-creature/worm.blend has changed
diff -r e57d8c52f12f -r bee5145ce463 assets/Models/test-touch/touch-cube.blend
Binary file assets/Models/test-touch/touch-cube.blend has changed
diff -r e57d8c52f12f -r bee5145ce463 images/basic-muscle.png
Binary file images/basic-muscle.png has changed
diff -r e57d8c52f12f -r bee5145ce463 images/worm-with-muscle.png
Binary file images/worm-with-muscle.png has changed
diff -r e57d8c52f12f -r bee5145ce463 org/hearing.org
--- a/org/hearing.org Mon Feb 13 21:53:28 2012 -0600
+++ b/org/hearing.org Mon Feb 13 21:53:54 2012 -0600
@@ -950,7 +950,6 @@
#+end_src
* Testing Hearing
-
** Advanced Java Example
I wrote a test case in Java that demonstrates the use of the Java
@@ -1083,7 +1082,6 @@
sound it hears beomes fainter. This shows the 3D localization of
sound in this world.
-
#+end_html
*** Creating the Ear Video
@@ -1169,7 +1167,6 @@
#+html:
- [[http://hg.bortreb.com ][source-repository]]
-
* Next
The worm can see and hear, but it can't feel the world or
itself. Next post, I'll give the worm a [[./touch.org][sense of touch]].
diff -r e57d8c52f12f -r bee5145ce463 org/ideas.org
--- a/org/ideas.org Mon Feb 13 21:53:28 2012 -0600
+++ b/org/ideas.org Mon Feb 13 21:53:54 2012 -0600
@@ -100,6 +100,7 @@
- [ ] show sensor maps in HUD display? -- 4 days
- [ ] show sensor maps in AWT display? -- 2 days
- [ ] upgrade to clojure 1.3, replace all defvars with new def
+ - [ ] common video creation code.
diff -r e57d8c52f12f -r bee5145ce463 org/movement.org
--- a/org/movement.org Mon Feb 13 21:53:28 2012 -0600
+++ b/org/movement.org Mon Feb 13 21:53:54 2012 -0600
@@ -1,4 +1,4 @@
-#+title: Movement!
+#+title: Simulated Muscles
#+author: Robert McIntyre
#+email: rlm@mit.edu
#+description: muscles for a simulated creature
@@ -7,48 +7,61 @@
#+INCLUDE: ../../aurellem/org/level-0.org
+* Muscles
+
Surprisingly enough, terristerial creatures only move by using torque
applied about their joints. There's not a single straight line of
force in the human body at all! (A straight line of force would
-correspond to some sort of jet or rocket propulseion.)
+correspond to some sort of jet or rocket propulsion.)
+*(next paragraph is from memory and needs to be checked!)*
-Here's how motor-control/ proprioception will work: Each muscle is
-defined by a 1-D array of numbers (the "motor pool") each of which
-represent muscle fibers. A muscle also has a scalar :strength factor
-which determines how strong the muscle as a whole is. The effector
-function for a muscle takes a number < (count motor-pool) and that
-number is said to "activate" all the muscle fibers whose index is
-lower than the number. Each fiber will apply force in proportion to
-its value in the array. Lower values cause less force. The lower
-values can be put at the "beginning" of the 1-D array to simulate the
-layout of actual human muscles, which are capable of more percise
-movements when exerting less force.
+In humans, muscles are composed of millions of sarcomeres, which can
+contract to exert force. A single motor neuron might control 100-1,000
+sarcomeres. When the motor neuron is engaged by the brain, it
+activates all of the sarcomeres to which it is attached. Some motor
+neurons command many sarcomeres, and some command only a few. The
+spinal cord generally engages the motor neurons which control few
+sarcomeres before the motor neurons which control many sarcomeres.
+This recruitment stragety allows for percise movements at low
+strength. The collection of all motor neurons that control a muscle is
+called the motor pool. The brain essentially says "activate 30% of the
+motor pool" and the spinal cord recruits motor neurons untill 30% are
+activated. Since the distribution of power among motor neurons is
+unequal and recruitment goes from weakest to strongest, 30% of the
+motor pool might be 5% of the strength of the muscle.
-#+name: movement
+My simulated muscles follow a similiar design: Each muscle is defined
+by a 1-D array of numbers (the "motor pool"). Each number represents a
+motor neuron which controlls a number of sarcomeres equal to the
+number. A muscle also has a scalar :strength factor which determines
+the total force the muscle can exert when all motor neurons are
+activated. The effector function for a muscle takes a number to index
+into the motor pool, and that number "activates" all the motor neurons
+whose index is lower or equal to the number. Each motor-neuron will
+apply force in proportion to its value in the array. Lower values
+cause less force. The lower values can be put at the "beginning" of
+the 1-D array to simulate the layout of actual human muscles, which
+are capable of more percise movements when exerting less force. Or,
+the motor pool can simulate more exoitic recruitment strageties which
+do not correspond to human muscles.
+
+This 1D array is defined in an image file for ease of
+creation/visualization. Here is an example muscle profile image.
+
+#+caption: A muscle profile image that describes the strengths of each motor neuron in a muscle. White is weakest and dark red is strongest. This particular pattern has weaker motor neurons at the beginning, just like human muscle.
+[[../images/basic-muscle.png]]
+
+* Blender Meta-data
+
+In blender, each muscle is an empty node whose top level parent is
+named "muscles", just like eyes, ears, and joints.
+
+These functions define the expected meta-data for a muscle node.
+
+#+name: muscle-meta-data
#+begin_src clojure
-(ns cortex.movement
- "Give simulated creatures defined in special blender files the power
- to move around in a simulated environment."
- {:author "Robert McIntyre"}
- (:use (cortex world util sense body))
- (:use clojure.contrib.def)
- (:import java.awt.image.BufferedImage)
- (:import com.jme3.scene.Node)
- (:import com.jme3.math.Vector3f)
- (:import com.jme3.bullet.control.RigidBodyControl))
-
-(defn muscle-profile
- "Return a vector where each entry is the strength of the \"motor
- pool\" at that part in the muscle."
- [#^BufferedImage profile]
- (vec
- (let [width (.getWidth profile)]
- (for [x (range width)]
- (- 255
- (bit-and
- 0x0000FF
- (.getRGB profile x 0)))))))
+(in-ns 'cortex.movement)
(defvar
^{:arglists '([creature])}
@@ -56,30 +69,64 @@
(sense-nodes "muscles")
"Return the children of the creature's \"muscles\" node.")
-(defn movement-fn
+(defn muscle-profile-image
+ "Get the muscle-profile image from the node's blender meta-data."
+ [#^Node muscle]
+ (if-let [image (meta-data muscle "muscle")]
+ (load-image image)))
+
+(defn muscle-strength
+ "Return the strength of this muscle, or 1 if it is not defined."
+ [#^Node muscle]
+ (if-let [strength (meta-data muscle "strength")]
+ strength 1))
+
+(defn motor-pool
+ "Return a vector where each entry is the strength of the \"motor
+ neuron\" at that part in the muscle."
+ [#^Node muscle]
+ (let [profile (muscle-profile-image muscle)]
+ (vec
+ (let [width (.getWidth profile)]
+ (for [x (range width)]
+ (- 255
+ (bit-and
+ 0x0000FF
+ (.getRGB profile x 0))))))))
+#+end_src
+
+Of note here is =(motor-pool)= which interprets the muscle-profile
+image in a way that allows me to use gradients between white and red,
+instead of shades of gray as I've been using for all the other
+senses. This is purely an aesthetic touch.
+
+* Creating Muscles
+#+name: muscle-kernel
+#+begin_src clojure
+(in-ns 'cortex.movement)
+
+(defn movement-kernel
"Returns a function which when called with a integer value inside a
running simulation will cause movement in the creature according
to the muscle's position and strength profile. Each function
returns the amount of force applied / max force."
- [#^Node parts #^Node muscle]
- (let [target (closest-node parts muscle)
+ [#^Node creature #^Node muscle]
+ (let [target (closest-node creature muscle)
axis
(.mult (.getWorldRotation muscle) Vector3f/UNIT_Y)
- strength (meta-data muscle "strength")
- image-name (read-string (meta-data muscle "muscle"))
- image (load-image image-name)
- fibers (muscle-profile image)
- fiber-integral (reductions + fibers)
+ strength (muscle-strength muscle)
+
+ pool (motor-pool muscle)
+ pool-integral (reductions + pool)
force-index
- (vec (map #(float (* strength (/ % (last fiber-integral))))
- fiber-integral))
+ (vec (map #(float (* strength (/ % (last pool-integral))))
+ pool-integral))
control (.getControl target RigidBodyControl)]
(fn [n]
- (let [pool-index (max 0 (min n (dec (count fibers))))
+ (let [pool-index (max 0 (min n (dec (count pool))))
force (force-index pool-index)]
(.applyTorque control (.mult axis force))
(float (/ force strength))))))
-
(defn movement!
"Endow the creature with the power of movement. Returns a sequence
@@ -87,8 +134,24 @@
activate their corresponding muscle."
[#^Node creature]
(for [muscle (muscles creature)]
- (movement-fn creature muscle)))
+ (movement-kernel creature muscle)))
+#+end_src
+=(movement-kernel)= creates a function that will move the nearest
+physical object to the muscle node. The muscle exerts a rotational
+force dependant on it's orientation to the object in the blender
+file. The function returned by =(movement-kernel)= is also a sense
+function: it returns the percent of the total muscle strength that is
+currently being employed. This is analogous to muscle tension in
+humans and completes the sense of proprioception begun in the last
+post.
+
+* Visualizing Muscle Tension
+Muscle exertion is a percent of a total, so the visulazation is just a
+simple percent bar.
+
+#+name: visualization
+#+begin_src clojure
(defn movement-display-kernel
"Display muscle exertion data as a bar filling up with red."
[exertion]
@@ -108,14 +171,166 @@
displays each element of the list to the screen."
[]
(view-sense movement-display-kernel))
-
#+end_src
+* Adding Touch to the Worm
+#+begin_src clojure
+(defn test-movement
+ ([] (test-movement false))
+ ([record?]
+ (let [creature (doto (worm) (body!))
+ muscle-exertion (atom 0)
+ muscles (movement! creature)
+ muscle-display (view-movement)]
+ (.setMass
+ (.getControl (.getChild creature "worm-11") RigidBodyControl)
+ (float 0))
+ (world
+ (nodify [creature (floor)])
+ (merge standard-debug-controls
+ {"key-h"
+ (fn [_ value]
+ (if value
+ (swap! muscle-exertion (partial + 20))))
+ "key-n"
+ (fn [_ value]
+ (if value
+ (swap! muscle-exertion (fn [v] (- v 20)))))})
+ (fn [world]
+ (if record?
+ (Capture/captureVideo
+ world
+ (File. "/home/r/proj/cortex/render/worm-muscles/main-view")))
+ (light-up-everything world)
+ (enable-debug world)
+ (.setTimer world (RatchetTimer. 60))
+ (set-gravity world (Vector3f. 0 0 0))
+ (.setLocation (.getCamera world)
+ (Vector3f. -4.912815, 2.004171, 0.15710819))
+ (.setRotation (.getCamera world)
+ (Quaternion. 0.13828252, 0.65516764,
+ -0.12370994, 0.7323449))
+ (comment
+ (com.aurellem.capture.Capture/captureVideo
+ world (file-str "/home/r/proj/ai-videos/hand"))))
+ (fn [world tpf]
+ (muscle-display
+ (map #(% @muscle-exertion) muscles)
+ (if record?
+ (File. "/home/r/proj/cortex/render/worm-muscles/muscles"))))))))
+#+end_src
+
+* Video Demonstration
+
+#+begin_html
+
+#+end_html
+
+
+** Making the Worm Muscles Video
+#+name: magick7
+#+begin_src clojure
+(ns cortex.video.magick7
+ (:import java.io.File)
+ (:use clojure.contrib.shell-out))
+
+(defn images [path]
+ (sort (rest (file-seq (File. path)))))
+
+(def base "/home/r/proj/cortex/render/worm-muscles/")
+
+(defn pics [file]
+ (images (str base file)))
+
+(defn combine-images []
+ (let [main-view (pics "main-view")
+ muscles (pics "muscles/0")
+ targets (map
+ #(File. (str base "out/" (format "%07d.png" %)))
+ (range 0 (count main-view)))]
+ (dorun
+ (pmap
+ (comp
+ (fn [[ main-view muscles target]]
+ (println target)
+ (sh "convert"
+ main-view
+ muscles "-geometry" "+320+440" "-composite"
+ target))
+ (fn [& args] (map #(.getCanonicalPath %) args)))
+ main-view muscles targets))))
+#+end_src
+
+#+begin_src sh :results silent
+cd ~/proj/cortex/render/worm-muscles
+ffmpeg -r 60 -i out/%07d.png -b:v 9000k -c:v libtheora worm-muscles.ogg
+#+end_src
+
+* Headers
+#+name: muscle-header
+#+begin_src clojure
+(ns cortex.movement
+ "Give simulated creatures defined in special blender files the power
+ to move around in a simulated environment."
+ {:author "Robert McIntyre"}
+ (:use (cortex world util sense body))
+ (:use clojure.contrib.def)
+ (:import java.awt.image.BufferedImage)
+ (:import com.jme3.scene.Node)
+ (:import com.jme3.math.Vector3f)
+ (:import com.jme3.bullet.control.RigidBodyControl))
+#+end_src
+
+#+name: test-header
+#+begin_src clojure
+(ns cortex.test.movement
+ (:use (cortex world util sense body movement))
+ (:use cortex.test.body)
+ (:use clojure.contrib.def)
+ (:import java.io.File)
+ (:import java.awt.image.BufferedImage)
+ (:import com.jme3.scene.Node)
+ (:import com.jme3.math.Vector3f)
+ (:import (com.aurellem.capture Capture RatchetTimer))
+ (:import com.jme3.bullet.control.RigidBodyControl))
+
+(cortex.import/mega-import-jme3)
+#+end_src
+
+* Source Listing
+ - [[../src/cortex/movement.clj][cortex.movement]]
+ - [[../src/cortex/test/movement.clj][cortex.test.movement]]
+ - [[../src/cortex/video/magick7.clj][cortex.video.magick7]]
+#+html:
+ - [[http://hg.bortreb.com ][source-repository]]
* COMMENT code generation
#+begin_src clojure :tangle ../src/cortex/movement.clj
-<>
+<>
+<>
+<>
+<>
#+end_src
+
+#+begin_src clojure :tangle ../src/cortex/test/movement.clj
+<>
+<>
+#+end_src
+
+#+begin_src clojure :tangle ../src/cortex/video/magick7.clj
+<>
+#+end_src
diff -r e57d8c52f12f -r bee5145ce463 org/proprioception.org
--- a/org/proprioception.org Mon Feb 13 21:53:28 2012 -0600
+++ b/org/proprioception.org Mon Feb 13 21:53:54 2012 -0600
@@ -6,36 +6,89 @@
#+SETUPFILE: ../../aurellem/org/setup.org
#+INCLUDE: ../../aurellem/org/level-0.org
-#+name: proprioception
+* Proprioception
+
+Close your eyes, and touch your nose with your right index finger. How
+did you do it? You could not see your hand, and neither your hand nor
+your nose could use the sense of touch to guide the path of your hand.
+There are no sound cues, and Taste and Smell certainly don't provide
+any help. You know where your hand is without your other senses
+because of Proprioception.
+
+Humans can sometimes loose this sense through viral infections or
+damage to the spinal cord or brain, and when they do, they loose the
+ability to control their own bodies without looking directly at the
+parts they want to move. In [[http://en.wikipedia.org/wiki/The_Man_Who_Mistook_His_Wife_for_a_Hat][The Man Who Mistook His Wife for a Hat]],
+a woman named Christina looses this sense and has to learn how to move
+by carefully watching her arms and legs. She describes proprioception
+as the "eyes of the body, the way the body sees itself".
+
+Proprioception in humans is mediated by [[http://en.wikipedia.org/wiki/Articular_capsule][joint capsules]], [[http://en.wikipedia.org/wiki/Muscle_spindle][muscle
+spindles]], and the [[http://en.wikipedia.org/wiki/Golgi_tendon_organ][Golgi tendon organs]]. These measure the relative
+positions of each pody part by monitoring muscle strain and length.
+
+It's clear that this is a vital sense for fulid, graceful
+movement. It's also particurally easy to implement in jMonkeyEngine.
+
+My simulated proprioception calculates the relative angles of each
+joint from the rest position defined in the blender file. This
+simulates the muscle-spindles and joint capsules. I will deal with
+Golgi tendon organs, which calculate muscle strain, in the [[./movement.org][next post]].
+
+* Helper Functions
+
+=(absolute-angle)= calculates the angle between two vectors, relative to a
+third axis vector. This angle is the number of radians you have to
+move counterclockwise around the axis vector to get from the first to
+the second vector. It is not commutative like a normal dot-product
+angle is.
+
+#+name: helpers
#+begin_src clojure
-(ns cortex.proprioception
- "Simulate the sense of proprioception (ability to detect the
- relative positions of body parts with repsect to other body parts)
- in jMonkeyEngine3. Reads specially prepared blender files to
- automatically generate proprioceptive senses."
- (:use (cortex world util sense body))
- (:use clojure.contrib.def)
- (:import com.jme3.scene.Node)
- (:import java.awt.image.BufferedImage)
- (:import (com.jme3.math Vector3f Quaternion)))
+(in-ns 'cortex.proprioception)
(defn right-handed?
"true iff the three vectors form a right handed coordinate
- system. The three vectors do not have to be normalized or
- orthogonal."
+ system. The three vectors do not have to be normalized or
+ orthogonal."
[vec1 vec2 vec3]
(< 0 (.dot (.cross vec1 vec2) vec3)))
(defn absolute-angle
- "The angle between 'vec1 and 'vec2. Positive if the angle to get
- from 'vec1 to 'vec2 is counterclockwise around 'axis, and negative
- otherwise."
+ "The angle between 'vec1 and 'vec2 around 'axis. In the range
+ [0 (* 2 Math/PI)]."
[vec1 vec2 axis]
(let [angle (.angleBetween vec1 vec2)]
(if (right-handed? vec1 vec2 axis)
angle (- (* 2 Math/PI) angle))))
+#+end_src
-(defn proprioception-fn
+#+begin_src clojure :exports both
+(in-ns 'cortex.proprioception)
+(absolute-angle Vector3f/UNIT_X Vector3f/UNIT_Y Vector3f/UNIT_Z)
+#+end_src
+
+#+results:
+: 1.5707964
+
+#+begin_src clojure :exports both
+(in-ns 'cortex.proprioception)
+(absolute-angle
+ Vector3f/UNIT_X (.mult Vector3f/UNIT_Y (float -1)) Vector3f/UNIT_Z)
+#+end_src
+
+#+results:
+: 4.7123889366733
+
+* Proprioception Kernel
+
+Given a joint, =(proprioception-kernel)= produces a function that
+calculates the euler angles between the the objects the joint
+connects.
+
+#+name: proprioception
+#+begin_src clojure
+(defn proprioception-kernel
"Returns a function which returns proprioceptive sensory data when
called inside a running simulation."
[#^Node parts #^Node joint]
@@ -44,12 +97,6 @@
x0 (.mult joint-rot Vector3f/UNIT_X)
y0 (.mult joint-rot Vector3f/UNIT_Y)
z0 (.mult joint-rot Vector3f/UNIT_Z)]
- (println-repl "x:" x0)
- (println-repl "y:" y0)
- (println-repl "z:" z0)
- (println-repl "init-a:" (.getWorldRotation obj-a))
- (println-repl "init-b:" (.getWorldRotation obj-b))
-
(fn []
(let [rot-a (.clone (.getWorldRotation obj-a))
rot-b (.clone (.getWorldRotation obj-b))
@@ -80,18 +127,33 @@
that joint."
[#^Node creature]
;; extract the body's joints
- (let [senses (map (partial proprioception-fn creature)
+ (let [senses (map (partial proprioception-kernel creature)
(joints creature))]
(fn []
(map #(%) senses))))
+#+end_src
+=(proprioception!)= maps =(proprioception-kernel)= across all the
+joints of the creature. It uses the same list of joints that
+=(cortex.body/joints)= uses.
+
+* Visualizing Proprioception
+
+Proprioception has the lowest bandwidth of all the senses so far, and
+it doesn't lend itself as readily to visual representation like
+vision, hearing, or touch. This visualization code creates a "guage"
+to view each of the three relative angles along a circle.
+
+#+name: visualize
+#+begin_src clojure
+(in-ns 'cortex.proprioception)
+
(defn draw-sprite [image sprite x y color ]
(dorun
(for [[u v] sprite]
(.setRGB image (+ u x) (+ v y) color))))
-
(defn view-angle
"create a debug view of an angle"
[color]
@@ -109,7 +171,6 @@
(reset! previous position))
image))))
-
(defn proprioception-display-kernel
"Display proprioception angles in a BufferedImage"
[[h p r]]
@@ -143,14 +204,16 @@
display each element of the list to the screen as an image."
[]
(view-sense proprioception-display-kernel))
-
-
-
#+end_src
-#+name: test-body
+* Proprioception Test
+This test does not use the worm, but instead uses two bars, bound
+together by a point2point joint. One bar is fixed, and I control the
+other bar from the keyboard.
+
+#+name: test-proprioception
#+begin_src clojure
-
+(in-ns 'cortex.test.proprioception)
(defn test-proprioception
"Testing proprioception:
@@ -159,92 +222,166 @@
change only the value of pitch. key-f/key-g moves it side to side
and changes yaw. key-v/key-b will spin the blue segment clockwise
and counterclockwise, and only affect roll."
- []
- (let [hand (box 0.2 1 0.2 :position (Vector3f. 0 0 0)
- :mass 0 :color ColorRGBA/Green :name "hand")
- finger (box 0.2 1 0.2 :position (Vector3f. 0 2.4 0)
- :mass 1 :color ColorRGBA/Red :name "finger")
- joint-node (box 0.1 0.05 0.05 :color ColorRGBA/Yellow
- :position (Vector3f. 0 1.2 0)
- :rotation (doto (Quaternion.)
- (.fromAngleAxis
- (/ Math/PI 2)
- (Vector3f. 0 0 1)))
- :physical? false)
- joint (join-at-point hand finger (Vector3f. 0 1.2 0 ))
- creature (nodify [hand finger joint-node])
- finger-control (.getControl finger RigidBodyControl)
- hand-control (.getControl hand RigidBodyControl)]
-
+ ([] (test-proprioception false))
+ ([record?]
+ (let [hand (box 0.2 1 0.2 :position (Vector3f. 0 0 0)
+ :mass 0 :color ColorRGBA/Gray :name "hand")
+ finger (box 0.2 1 0.2 :position (Vector3f. 0 2.4 0)
+ :mass 1
+ :color
+ (ColorRGBA. (/ 184 255) (/ 127 255) (/ 201 255) 1)
+ :name "finger")
+ joint-node (box 0.1 0.05 0.05 :color ColorRGBA/Yellow
+ :position (Vector3f. 0 1.2 0)
+ :rotation (doto (Quaternion.)
+ (.fromAngleAxis
+ (/ Math/PI 2)
+ (Vector3f. 0 0 1)))
+ :physical? false)
+ creature (nodify [hand finger joint-node])
+ finger-control (.getControl finger RigidBodyControl)
+ hand-control (.getControl hand RigidBodyControl)
+ joint (joint-dispatch {:type :point} hand-control finger-control
+ (Vector3f. 0 1.2 0)
+ (Vector3f. 0 -1.2 0) nil)
- (let
- ;; *******************************************
-
- [floor (box 10 10 10 :position (Vector3f. 0 -15 0)
- :mass 0 :color ColorRGBA/Gray)
-
- root (nodify [creature floor])
- prop (joint-proprioception creature joint-node)
- prop-view (proprioception-debug-window)
-
- controls
- (merge standard-debug-controls
- {"key-o"
- (fn [_ _] (.setEnabled finger-control true))
- "key-p"
- (fn [_ _] (.setEnabled finger-control false))
- "key-k"
- (fn [_ _] (.setEnabled hand-control true))
- "key-l"
- (fn [_ _] (.setEnabled hand-control false))
- "key-i"
- (fn [world _] (set-gravity world (Vector3f. 0 0 0)))
- "key-period"
- (fn [world _]
- (.setEnabled finger-control false)
- (.setEnabled hand-control false)
- (.rotate creature (doto (Quaternion.)
- (.fromAngleAxis
- (float (/ Math/PI 15))
- (Vector3f. 0 0 -1))))
-
- (.setEnabled finger-control true)
- (.setEnabled hand-control true)
- (set-gravity world (Vector3f. 0 0 0))
- )
-
-
- }
- )
+ root (nodify [creature])
+ prop (proprioception-kernel creature joint-node)
+ prop-view (view-proprioception)]
+ (.setCollisionGroup
+ (.getControl hand RigidBodyControl)
+ PhysicsCollisionObject/COLLISION_GROUP_NONE)
+ (apply
+ world
+ (with-movement
+ finger
+ ["key-r" "key-t" "key-f" "key-g" "key-v" "key-b"]
+ [1 1 10 10 10 10]
+ [root
+ standard-debug-controls
+ (fn [world]
+ (if record?
+ (Capture/captureVideo
+ world
+ (File. "/home/r/proj/cortex/render/proprio/main-view")))
+ (.setTimer world (com.aurellem.capture.RatchetTimer. 60))
+ (set-gravity world (Vector3f. 0 0 0))
+ (enable-debug world)
+ (light-up-everything world))
+ (fn [_ _]
+ (prop-view
+ (list (prop))
+ (if record?
+ (File. "/home/r/proj/cortex/render/proprio/proprio"))))])))))
+#+end_src
- ]
- (comment
- (.setCollisionGroup
- (.getControl hand RigidBodyControl)
- PhysicsCollisionObject/COLLISION_GROUP_NONE)
- )
- (apply
- world
- (with-movement
- hand
- ["key-y" "key-u" "key-h" "key-j" "key-n" "key-m"]
- [10 10 10 10 1 1]
- (with-movement
- finger
- ["key-r" "key-t" "key-f" "key-g" "key-v" "key-b"]
- [1 1 10 10 10 10]
- [root
- controls
- (fn [world]
- (.setTimer world (com.aurellem.capture.RatchetTimer. 60))
- (set-gravity world (Vector3f. 0 0 0))
- (light-up-everything world))
- (fn [_ _] (prop-view (list (prop))))]))))))
+#+results: test-proprioception
+: #'cortex.test.proprioception/test-proprioception
+* Video of Proprioception
+
+#+begin_html
+
+#+end_html
+
+** Generating the Proprioception Video
+#+name: magick6
+#+begin_src clojure
+(ns cortex.video.magick6
+ (:import java.io.File)
+ (:use clojure.contrib.shell-out))
+
+(defn images [path]
+ (sort (rest (file-seq (File. path)))))
+
+(def base "/home/r/proj/cortex/render/proprio/")
+
+(defn pics [file]
+ (images (str base file)))
+
+(defn combine-images []
+ (let [main-view (pics "main-view")
+ proprioception (pics "proprio/0")
+ targets (map
+ #(File. (str base "out/" (format "%07d.png" %)))
+ (range 0 (count main-view)))]
+ (dorun
+ (pmap
+ (comp
+ (fn [[ main-view proprioception target]]
+ (println target)
+ (sh "convert"
+ main-view
+ proprioception "-geometry" "+20+20" "-composite"
+ target))
+ (fn [& args] (map #(.getCanonicalPath %) args)))
+ main-view proprioception targets))))
#+end_src
+#+begin_src sh :results silent
+cd ~/proj/cortex/render/proprio
+ffmpeg -r 60 -i out/%07d.png -b:v 9000k -c:v libtheora \
+ test-proprioception.ogg
+#+end_src
+
+* Headers
+#+name: proprioception-header
+#+begin_src clojure
+(ns cortex.proprioception
+ "Simulate the sense of proprioception (ability to detect the
+ relative positions of body parts with repsect to other body parts)
+ in jMonkeyEngine3. Reads specially prepared blender files to
+ automatically generate proprioceptive senses."
+ (:use (cortex world util sense body))
+ (:use clojure.contrib.def)
+ (:import com.jme3.scene.Node)
+ (:import java.awt.image.BufferedImage)
+ (:import (com.jme3.math Vector3f Quaternion)))
+#+end_src
+
+#+name: test-proprioception-header
+#+begin_src clojure
+(ns cortex.test.proprioception
+(:import (com.aurellem.capture Capture RatchetTimer))
+(:use (cortex util world proprioception body))
+(:import java.io.File))
+(cortex.import/mega-import-jme3)
+#+end_src
+
+* Source Listing
+ - [[../src/cortex/proprioception.clj][cortex.proprioception]]
+ - [[../src/cortex/test/touch.clj][cortex.test.proprioception]]
+ - [[../src/cortex/video/magick6.clj][cortex.video.magick6]]
+#+html:
+ - [[http://hg.bortreb.com ][source-repository]]
+
+* Next
+
+Next time, I'll give the Worm the power to [[./movement.org][move on it's own]].
+
* COMMENT generate source
#+begin_src clojure :tangle ../src/cortex/proprioception.clj
+<>
+<>
<>
+<>
#+end_src
+
+#+begin_src clojure :tangle ../src/cortex/test/proprioception.clj
+<>
+<>
+#+end_src
+
+#+begin_src clojure :tangle ../src/cortex/video/magick6.clj
+<>
+#+end_src
diff -r e57d8c52f12f -r bee5145ce463 org/touch.org
--- a/org/touch.org Mon Feb 13 21:53:28 2012 -0600
+++ b/org/touch.org Mon Feb 13 21:53:54 2012 -0600
@@ -122,7 +122,7 @@
#+end_src
It is convienent to treat a =Triangle= as a vector of vectors, and a
-=Vector2f= and =Vector3f= as vectors of floats. (->vector3f) and
+=Vector2f= or =Vector3f= as vectors of floats. (->vector3f) and
(->triangle) undo the operations of =(vector3f-seq)= and
=(triangle-seq)=. If these classes implemented =Iterable= then =(seq)=
would work on them automitacally.
@@ -497,6 +497,7 @@
I'll use a new creature --- a simple cube which has touch sensors
evenly distributed along each of its sides.
+#+name: test-touch-0
#+begin_src clojure
(in-ns 'cortex.test.touch)
@@ -504,10 +505,7 @@
(load-blender-model "Models/test-touch/touch-cube.blend"))
#+end_src
-#+begin_html
-
-#+end_html
-
+** The Touch Cube
#+begin_html