view org/test-creature.org @ 125:3d65633dd736

organizing priorities for winston
author Robert McIntyre <rlm@mit.edu>
date Tue, 24 Jan 2012 18:20:57 -0700
parents 90154bd674e9
children 0efe6f04bc26
line wrap: on
line source
1 #+title: First attempt at a creature!
2 #+author: Robert McIntyre
3 #+email: rlm@mit.edu
4 #+description:
5 #+keywords: simulation, jMonkeyEngine3, clojure
6 #+SETUPFILE: ../../aurellem/org/setup.org
7 #+INCLUDE: ../../aurellem/org/level-0.org
9 * ideas
11 ** have to get done before winston
12 - [ ] write an explination for why greyscale bitmaps for senses is appropiate
13 - [ ] muscle control
14 - [ ] proprioception sensor map in the style of the other senses
15 - [ ] enable greyscale bitmaps for touch
16 - [ ] refactor integration code to distribute to each of the senses
17 - [ ] create video showing all the senses for Winston
18 - [ ] write summary for Winston
19 - [ ] project proposals for Winston
20 - [ ] send winston package
22 ** would be cool to get done before winston
23 - [ ] use sawfish to auto-tile sense windows
24 - [ ] sawfish keybinding to automatically delete all sense windows
25 - [ ] proof of concept C sense manipulation
26 - [ ] proof of concept GPU sense manipulation
27 - [ ] fourier view of sound
28 - [ ] directly change the UV-pixels to show sensor activation
30 ** don't have to get done before winston
31 - [ ] write tests for integration
32 - [ ] usertime/gametime clock HUD display
33 - [ ] find papers for each of the senses justifying my own representation
34 - [ ] show sensor maps in HUD display?
37 * Intro
38 So far, I've made the following senses --
39 - Vision
40 - Hearing
41 - Touch
42 - Proprioception
44 And one effector:
45 - Movement
47 However, the code so far has only enabled these senses, but has not
48 actually implemented them. For example, there is still a lot of work
49 to be done for vision. I need to be able to create an /eyeball/ in
50 simulation that can be moved around and see the world from different
51 angles. I also need to determine weather to use log-polar or cartesian
52 for the visual input, and I need to determine how/wether to
53 disceritise the visual input.
55 I also want to be able to visualize both the sensors and the
56 effectors in pretty pictures. This semi-retarted creature will be my
57 first attempt at bringing everything together.
59 * The creature's body
61 Still going to do an eve-like body in blender, but due to problems
62 importing the joints, etc into jMonkeyEngine3, I'm going to do all
63 the connecting here in clojure code, using the names of the individual
64 components and trial and error. Later, I'll maybe make some sort of
65 creature-building modifications to blender that support whatever
66 discreitized senses I'm going to make.
68 #+name: body-1
69 #+begin_src clojure
70 (ns cortex.silly
71 "let's play!"
72 {:author "Robert McIntyre"})
74 ;; TODO remove this!
75 (require 'cortex.import)
76 (cortex.import/mega-import-jme3)
77 (use '(cortex world util body hearing touch vision))
79 (rlm.rlm-commands/help)
80 (import java.awt.image.BufferedImage)
81 (import javax.swing.JPanel)
82 (import javax.swing.SwingUtilities)
83 (import java.awt.Dimension)
84 (import javax.swing.JFrame)
85 (import java.awt.Dimension)
86 (import com.aurellem.capture.RatchetTimer)
87 (declare joint-create)
88 (use 'clojure.contrib.def)
90 (defn points->image
91 "Take a sparse collection of points and visuliaze it as a
92 BufferedImage."
94 ;; TODO maybe parallelize this since it's easy
96 [points]
97 (if (empty? points)
98 (BufferedImage. 1 1 BufferedImage/TYPE_BYTE_BINARY)
99 (let [xs (vec (map first points))
100 ys (vec (map second points))
101 x0 (apply min xs)
102 y0 (apply min ys)
103 width (- (apply max xs) x0)
104 height (- (apply max ys) y0)
105 image (BufferedImage. (inc width) (inc height)
106 BufferedImage/TYPE_INT_RGB)]
107 (dorun
108 (for [x (range (.getWidth image))
109 y (range (.getHeight image))]
110 (.setRGB image x y 0xFF0000)))
111 (dorun
112 (for [index (range (count points))]
113 (.setRGB image (- (xs index) x0) (- (ys index) y0) -1)))
115 image)))
117 (defn average [coll]
118 (/ (reduce + coll) (count coll)))
120 (defn collapse-1d
121 "One dimensional analogue of collapse"
122 [center line]
123 (let [length (count line)
124 num-above (count (filter (partial < center) line))
125 num-below (- length num-above)]
126 (range (- center num-below)
127 (+ center num-above))))
129 (defn collapse
130 "Take a set of pairs of integers and collapse them into a
131 contigous bitmap."
132 [points]
133 (if (empty? points) []
134 (let
135 [num-points (count points)
136 center (vector
137 (int (average (map first points)))
138 (int (average (map first points))))
139 flattened
140 (reduce
141 concat
142 (map
143 (fn [column]
144 (map vector
145 (map first column)
146 (collapse-1d (second center)
147 (map second column))))
148 (partition-by first (sort-by first points))))
149 squeezed
150 (reduce
151 concat
152 (map
153 (fn [row]
154 (map vector
155 (collapse-1d (first center)
156 (map first row))
157 (map second row)))
158 (partition-by second (sort-by second flattened))))
159 relocate
160 (let [min-x (apply min (map first squeezed))
161 min-y (apply min (map second squeezed))]
162 (map (fn [[x y]]
163 [(- x min-x)
164 (- y min-y)])
165 squeezed))]
166 relocate)))
168 (defn load-bullet []
169 (let [sim (world (Node.) {} no-op no-op)]
170 (doto sim
171 (.enqueue
172 (fn []
173 (.stop sim)))
174 (.start))))
176 (defn load-blender-model
177 "Load a .blend file using an asset folder relative path."
178 [^String model]
179 (.loadModel
180 (doto (asset-manager)
181 (.registerLoader BlenderModelLoader (into-array String ["blend"])))
182 model))
184 (defn meta-data [blender-node key]
185 (if-let [data (.getUserData blender-node "properties")]
186 (.findValue data key)
187 nil))
189 (defn blender-to-jme
190 "Convert from Blender coordinates to JME coordinates"
191 [#^Vector3f in]
192 (Vector3f. (.getX in)
193 (.getZ in)
194 (- (.getY in))))
196 (defn jme-to-blender
197 "Convert from JME coordinates to Blender coordinates"
198 [#^Vector3f in]
199 (Vector3f. (.getX in)
200 (- (.getZ in))
201 (.getY in)))
203 (defn joint-targets
204 "Return the two closest two objects to the joint object, ordered
205 from bottom to top according to the joint's rotation."
206 [#^Node parts #^Node joint]
207 (loop [radius (float 0.01)]
208 (let [results (CollisionResults.)]
209 (.collideWith
210 parts
211 (BoundingBox. (.getWorldTranslation joint)
212 radius radius radius)
213 results)
214 (let [targets
215 (distinct
216 (map #(.getGeometry %) results))]
217 (if (>= (count targets) 2)
218 (sort-by
219 #(let [v
220 (jme-to-blender
221 (.mult
222 (.inverse (.getWorldRotation joint))
223 (.subtract (.getWorldTranslation %)
224 (.getWorldTranslation joint))))]
225 (println-repl (.getName %) ":" v)
226 (.dot (Vector3f. 1 1 1)
227 v))
228 (take 2 targets))
229 (recur (float (* radius 2))))))))
231 (defn world-to-local
232 "Convert the world coordinates into coordinates relative to the
233 object (i.e. local coordinates), taking into account the rotation
234 of object."
235 [#^Spatial object world-coordinate]
236 (let [out (Vector3f.)]
237 (.worldToLocal object world-coordinate out) out))
239 (defn local-to-world
240 "Convert the local coordinates into coordinates into world relative
241 coordinates"
242 [#^Spatial object local-coordinate]
243 (let [world-coordinate (Vector3f.)]
244 (.localToWorld object local-coordinate world-coordinate)
245 world-coordinate))
247 (defmulti joint-dispatch
248 "Translate blender pseudo-joints into real JME joints."
249 (fn [constraints & _]
250 (:type constraints)))
252 (defmethod joint-dispatch :point
253 [constraints control-a control-b pivot-a pivot-b rotation]
254 (println-repl "creating POINT2POINT joint")
255 (Point2PointJoint.
256 control-a
257 control-b
258 pivot-a
259 pivot-b))
261 (defmethod joint-dispatch :hinge
262 [constraints control-a control-b pivot-a pivot-b rotation]
263 (println-repl "creating HINGE joint")
264 (let [axis
265 (if-let
266 [axis (:axis constraints)]
267 axis
268 Vector3f/UNIT_X)
269 [limit-1 limit-2] (:limit constraints)
270 hinge-axis
271 (.mult
272 rotation
273 (blender-to-jme axis))]
274 (doto
275 (HingeJoint.
276 control-a
277 control-b
278 pivot-a
279 pivot-b
280 hinge-axis
281 hinge-axis)
282 (.setLimit limit-1 limit-2))))
284 (defmethod joint-dispatch :cone
285 [constraints control-a control-b pivot-a pivot-b rotation]
286 (let [limit-xz (:limit-xz constraints)
287 limit-xy (:limit-xy constraints)
288 twist (:twist constraints)]
290 (println-repl "creating CONE joint")
291 (println-repl rotation)
292 (println-repl
293 "UNIT_X --> " (.mult rotation (Vector3f. 1 0 0)))
294 (println-repl
295 "UNIT_Y --> " (.mult rotation (Vector3f. 0 1 0)))
296 (println-repl
297 "UNIT_Z --> " (.mult rotation (Vector3f. 0 0 1)))
298 (doto
299 (ConeJoint.
300 control-a
301 control-b
302 pivot-a
303 pivot-b
304 rotation
305 rotation)
306 (.setLimit (float limit-xz)
307 (float limit-xy)
308 (float twist)))))
310 (defn connect
311 "here are some examples:
312 {:type :point}
313 {:type :hinge :limit [0 (/ Math/PI 2)] :axis (Vector3f. 0 1 0)}
314 (:axis defaults to (Vector3f. 1 0 0) if not provided for hinge joints)
316 {:type :cone :limit-xz 0]
317 :limit-xy 0]
318 :twist 0]} (use XZY rotation mode in blender!)"
319 [#^Node obj-a #^Node obj-b #^Node joint]
320 (let [control-a (.getControl obj-a RigidBodyControl)
321 control-b (.getControl obj-b RigidBodyControl)
322 joint-center (.getWorldTranslation joint)
323 joint-rotation (.toRotationMatrix (.getWorldRotation joint))
324 pivot-a (world-to-local obj-a joint-center)
325 pivot-b (world-to-local obj-b joint-center)]
327 (if-let [constraints
328 (map-vals
329 eval
330 (read-string
331 (meta-data joint "joint")))]
332 ;; A side-effect of creating a joint registers
333 ;; it with both physics objects which in turn
334 ;; will register the joint with the physics system
335 ;; when the simulation is started.
336 (do
337 (println-repl "creating joint between"
338 (.getName obj-a) "and" (.getName obj-b))
339 (joint-dispatch constraints
340 control-a control-b
341 pivot-a pivot-b
342 joint-rotation))
343 (println-repl "could not find joint meta-data!"))))
345 (defn assemble-creature [#^Node pieces joints]
346 (dorun
347 (map
348 (fn [geom]
349 (let [physics-control
350 (RigidBodyControl.
351 (HullCollisionShape.
352 (.getMesh geom))
353 (if-let [mass (meta-data geom "mass")]
354 (do
355 (println-repl
356 "setting" (.getName geom) "mass to" (float mass))
357 (float mass))
358 (float 1)))]
360 (.addControl geom physics-control)))
361 (filter #(isa? (class %) Geometry )
362 (node-seq pieces))))
363 (dorun
364 (map
365 (fn [joint]
366 (let [[obj-a obj-b]
367 (joint-targets pieces joint)]
368 (connect obj-a obj-b joint)))
369 joints))
370 pieces)
372 (declare blender-creature)
374 (def hand "Models/creature1/one.blend")
376 (def worm "Models/creature1/try-again.blend")
378 (def touch "Models/creature1/touch.blend")
380 (defn worm-model [] (load-blender-model worm))
382 (defn x-ray [#^ColorRGBA color]
383 (doto (Material. (asset-manager)
384 "Common/MatDefs/Misc/Unshaded.j3md")
385 (.setColor "Color" color)
386 (-> (.getAdditionalRenderState)
387 (.setDepthTest false))))
389 (defn colorful []
390 (.getChild (worm-model) "worm-21"))
392 (import jme3tools.converters.ImageToAwt)
394 (import ij.ImagePlus)
396 ;; Every Mesh has many triangles, each with its own index.
397 ;; Every vertex has its own index as well.
399 (defn tactile-sensor-image
400 "Return the touch-sensor distribution image in BufferedImage format,
401 or nil if it does not exist."
402 [#^Geometry obj]
403 (if-let [image-path (meta-data obj "touch")]
404 (ImageToAwt/convert
405 (.getImage
406 (.loadTexture
407 (asset-manager)
408 image-path))
409 false false 0)))
411 (import ij.process.ImageProcessor)
412 (import java.awt.image.BufferedImage)
414 (def white -1)
416 (defn filter-pixels
417 "List the coordinates of all pixels matching pred, within the bounds
418 provided. Bounds -> [x0 y0 width height]"
419 {:author "Dylan Holmes"}
420 ([pred #^BufferedImage image]
421 (filter-pixels pred image [0 0 (.getWidth image) (.getHeight image)]))
422 ([pred #^BufferedImage image [x0 y0 width height]]
423 ((fn accumulate [x y matches]
424 (cond
425 (>= y (+ height y0)) matches
426 (>= x (+ width x0)) (recur 0 (inc y) matches)
427 (pred (.getRGB image x y))
428 (recur (inc x) y (conj matches [x y]))
429 :else (recur (inc x) y matches)))
430 x0 y0 [])))
432 (defn white-coordinates
433 "Coordinates of all the white pixels in a subset of the image."
434 ([#^BufferedImage image bounds]
435 (filter-pixels #(= % white) image bounds))
436 ([#^BufferedImage image]
437 (filter-pixels #(= % white) image)))
439 (defn triangle
440 "Get the triangle specified by triangle-index from the mesh within
441 bounds."
442 [#^Mesh mesh triangle-index]
443 (let [scratch (Triangle.)]
444 (.getTriangle mesh triangle-index scratch)
445 scratch))
447 (defn triangle-vertex-indices
448 "Get the triangle vertex indices of a given triangle from a given
449 mesh."
450 [#^Mesh mesh triangle-index]
451 (let [indices (int-array 3)]
452 (.getTriangle mesh triangle-index indices)
453 (vec indices)))
455 (defn vertex-UV-coord
456 "Get the uv-coordinates of the vertex named by vertex-index"
457 [#^Mesh mesh vertex-index]
458 (let [UV-buffer
459 (.getData
460 (.getBuffer
461 mesh
462 VertexBuffer$Type/TexCoord))]
463 [(.get UV-buffer (* vertex-index 2))
464 (.get UV-buffer (+ 1 (* vertex-index 2)))]))
466 (defn triangle-UV-coord
467 "Get the uv-cooridnates of the triangle's verticies."
468 [#^Mesh mesh width height triangle-index]
469 (map (fn [[u v]] (vector (* width u) (* height v)))
470 (map (partial vertex-UV-coord mesh)
471 (triangle-vertex-indices mesh triangle-index))))
473 (defn same-side?
474 "Given the points p1 and p2 and the reference point ref, is point p
475 on the same side of the line that goes through p1 and p2 as ref is?"
476 [p1 p2 ref p]
477 (<=
478 0
479 (.dot
480 (.cross (.subtract p2 p1) (.subtract p p1))
481 (.cross (.subtract p2 p1) (.subtract ref p1)))))
483 (defn triangle-seq [#^Triangle tri]
484 [(.get1 tri) (.get2 tri) (.get3 tri)])
486 (defn vector3f-seq [#^Vector3f v]
487 [(.getX v) (.getY v) (.getZ v)])
489 (defn inside-triangle?
490 "Is the point inside the triangle?"
491 {:author "Dylan Holmes"}
492 [#^Triangle tri #^Vector3f p]
493 (let [[vert-1 vert-2 vert-3] (triangle-seq tri)]
494 (and
495 (same-side? vert-1 vert-2 vert-3 p)
496 (same-side? vert-2 vert-3 vert-1 p)
497 (same-side? vert-3 vert-1 vert-2 p))))
499 (defn triangle->matrix4f
500 "Converts the triangle into a 4x4 matrix: The first three columns
501 contain the vertices of the triangle; the last contains the unit
502 normal of the triangle. The bottom row is filled with 1s."
503 [#^Triangle t]
504 (let [mat (Matrix4f.)
505 [vert-1 vert-2 vert-3]
506 ((comp vec map) #(.get t %) (range 3))
507 unit-normal (do (.calculateNormal t)(.getNormal t))
508 vertices [vert-1 vert-2 vert-3 unit-normal]]
509 (dorun
510 (for [row (range 4) col (range 3)]
511 (do
512 (.set mat col row (.get (vertices row)col))
513 (.set mat 3 row 1))))
514 mat))
516 (defn triangle-transformation
517 "Returns the affine transformation that converts each vertex in the
518 first triangle into the corresponding vertex in the second
519 triangle."
520 [#^Triangle tri-1 #^Triangle tri-2]
521 (.mult
522 (triangle->matrix4f tri-2)
523 (.invert (triangle->matrix4f tri-1))))
525 (defn point->vector2f [[u v]]
526 (Vector2f. u v))
528 (defn vector2f->vector3f [v]
529 (Vector3f. (.getX v) (.getY v) 0))
531 (defn map-triangle [f #^Triangle tri]
532 (Triangle.
533 (f 0 (.get1 tri))
534 (f 1 (.get2 tri))
535 (f 2 (.get3 tri))))
537 (defn points->triangle
538 "Convert a list of points into a triangle."
539 [points]
540 (apply #(Triangle. %1 %2 %3)
541 (map (fn [point]
542 (let [point (vec point)]
543 (Vector3f. (get point 0 0)
544 (get point 1 0)
545 (get point 2 0))))
546 (take 3 points))))
548 (defn convex-bounds
549 "Dimensions of the smallest integer bounding square of the list of
550 2D verticies in the form: [x y width height]."
551 [uv-verts]
552 (let [xs (map first uv-verts)
553 ys (map second uv-verts)
554 x0 (Math/floor (apply min xs))
555 y0 (Math/floor (apply min ys))
556 x1 (Math/ceil (apply max xs))
557 y1 (Math/ceil (apply max ys))]
558 [x0 y0 (- x1 x0) (- y1 y0)]))
560 (defn sensors-in-triangle
561 "Find the locations of the touch sensors within a triangle in both
562 UV and gemoetry relative coordinates."
563 [image mesh tri-index]
564 (let [width (.getWidth image)
565 height (.getHeight image)
566 UV-vertex-coords (triangle-UV-coord mesh width height tri-index)
567 bounds (convex-bounds UV-vertex-coords)
569 cutout-triangle (points->triangle UV-vertex-coords)
570 UV-sensor-coords
571 (filter (comp (partial inside-triangle? cutout-triangle)
572 (fn [[u v]] (Vector3f. u v 0)))
573 (white-coordinates image bounds))
574 UV->geometry (triangle-transformation
575 cutout-triangle
576 (triangle mesh tri-index))
577 geometry-sensor-coords
578 (map (fn [[u v]] (.mult UV->geometry (Vector3f. u v 0)))
579 UV-sensor-coords)]
580 {:UV UV-sensor-coords :geometry geometry-sensor-coords}))
582 (defn-memo locate-feelers
583 "Search the geometry's tactile UV image for touch sensors, returning
584 their positions in geometry-relative coordinates."
585 [#^Geometry geo]
586 (let [mesh (.getMesh geo)
587 num-triangles (.getTriangleCount mesh)]
588 (if-let [image (tactile-sensor-image geo)]
589 (map
590 (partial sensors-in-triangle image mesh)
591 (range num-triangles))
592 (repeat (.getTriangleCount mesh) {:UV nil :geometry nil}))))
594 (use 'clojure.contrib.def)
596 (defn-memo touch-topology [#^Gemoetry geo]
597 (vec (collapse (reduce concat (map :UV (locate-feelers geo))))))
599 (defn-memo feeler-coordinates [#^Geometry geo]
600 (vec (map :geometry (locate-feelers geo))))
602 (defn enable-touch [#^Geometry geo]
603 (let [feeler-coords (feeler-coordinates geo)
604 tris (triangles geo)
605 limit 0.1
606 ;;results (CollisionResults.)
607 ]
608 (if (empty? (touch-topology geo))
609 nil
610 (fn [node]
611 (let [sensor-origins
612 (map
613 #(map (partial local-to-world geo) %)
614 feeler-coords)
615 triangle-normals
616 (map (partial get-ray-direction geo)
617 tris)
618 rays
619 (flatten
620 (map (fn [origins norm]
621 (map #(doto (Ray. % norm)
622 (.setLimit limit)) origins))
623 sensor-origins triangle-normals))]
624 (vector
625 (touch-topology geo)
626 (vec
627 (for [ray rays]
628 (do
629 (let [results (CollisionResults.)]
630 (.collideWith node ray results)
631 (let [touch-objects
632 (set
633 (filter #(not (= geo %))
634 (map #(.getGeometry %) results)))]
635 (if (> (count touch-objects) 0)
636 1 0))))))))))))
638 (defn touch [#^Node pieces]
639 (filter (comp not nil?)
640 (map enable-touch
641 (filter #(isa? (class %) Geometry)
642 (node-seq pieces)))))
645 ;; human eye transmits 62kb/s to brain Bandwidth is 8.75 Mb/s
646 ;; http://en.wikipedia.org/wiki/Retina
648 (defn test-eye []
649 (.getChild
650 (.getChild (worm-model) "eyes")
651 "eye"))
654 (defn retina-sensor-image
655 "Return a map of pixel selection functions to BufferedImages
656 describing the distribution of light-sensitive components on this
657 geometry's surface. Each function creates an integer from the rgb
658 values found in the pixel. :red, :green, :blue, :gray are already
659 defined as extracting the red green blue and average components
660 respectively."
661 [#^Spatial eye]
662 (if-let [eye-map (meta-data eye "eye")]
663 (map-vals
664 #(ImageToAwt/convert
665 (.getImage (.loadTexture (asset-manager) %))
666 false false 0)
667 (eval (read-string eye-map)))))
669 (defn eye-dimensions
670 "returns the width and height specified in the metadata of the eye"
671 [#^Spatial eye]
672 (let [dimensions
673 (map #(vector (.getWidth %) (.getHeight %))
674 (vals (retina-sensor-image eye)))]
675 [(apply max (map first dimensions))
676 (apply max (map second dimensions))]))
678 (defn creature-eyes
679 "The eye nodes which are children of the \"eyes\" node in the
680 creature."
681 [#^Node creature]
682 (if-let [eye-node (.getChild creature "eyes")]
683 (seq (.getChildren eye-node))
684 (do (println-repl "could not find eyes node") [])))
686 ;; Here's how vision will work.
688 ;; Make the continuation in scene-processor take FrameBuffer,
689 ;; byte-buffer, BufferedImage already sized to the correct
690 ;; dimensions. the continuation will decide wether to "mix" them
691 ;; into the BufferedImage, lazily ignore them, or mix them halfway
692 ;; and call c/graphics card routines.
694 ;; (vision creature) will take an optional :skip argument which will
695 ;; inform the continuations in scene processor to skip the given
696 ;; number of cycles; 0 means that no cycles will be skipped.
698 ;; (vision creature) will return [init-functions sensor-functions].
699 ;; The init-functions are each single-arg functions that take the
700 ;; world and register the cameras and must each be called before the
701 ;; corresponding sensor-functions. Each init-function returns the
702 ;; viewport for that eye which can be manipulated, saved, etc. Each
703 ;; sensor-function is a thunk and will return data in the same
704 ;; format as the tactile-sensor functions; the structure is
705 ;; [topology, sensor-data]. Internally, these sensor-functions
706 ;; maintain a reference to sensor-data which is periodically updated
707 ;; by the continuation function established by its init-function.
708 ;; They can be queried every cycle, but their information may not
709 ;; necessairly be different every cycle.
711 ;; Each eye in the creature in blender will work the same way as
712 ;; joints -- a zero dimensional object with no geometry whose local
713 ;; coordinate system determines the orientation of the resulting
714 ;; eye. All eyes will have a parent named "eyes" just as all joints
715 ;; have a parent named "joints". The resulting camera will be a
716 ;; ChaseCamera or a CameraNode bound to the geo that is closest to
717 ;; the eye marker. The eye marker will contain the metadata for the
718 ;; eye, and will be moved by it's bound geometry. The dimensions of
719 ;; the eye's camera are equal to the dimensions of the eye's "UV"
720 ;; map.
723 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
725 ;; Ears work the same way as vision.
727 ;; (hearing creature) will return [init-functions
728 ;; sensor-functions]. The init functions each take the world and
729 ;; register a SoundProcessor that does foureier transforms on the
730 ;; incommong sound data, making it available to each sensor function.
732 (defn creature-ears
733 "The ear nodes which are children of the \"ears\" node in the
734 creature."
735 [#^Node creature]
736 (if-let [ear-node (.getChild creature "ears")]
737 (seq (.getChildren ear-node))
738 (do (println-repl "could not find ears node") [])))
740 (defn closest-node
741 "The closest object in creature to the given node."
742 [#^Node creature #^Node eye]
743 (loop [radius (float 0.01)]
744 (let [results (CollisionResults.)]
745 (.collideWith
746 creature
747 (BoundingBox. (.getWorldTranslation eye)
748 radius radius radius)
749 results)
750 (if-let [target (first results)]
751 (.getGeometry target)
752 (recur (float (* 2 radius)))))))
754 (defn bind-sense
755 "Bind the sense to the Spatial such that it will maintain its
756 current position relative to the Spatial no matter how the spatial
757 moves. 'sense can be either a Camera or Listener object."
758 [#^Spatial obj sense]
759 (let [sense-offset (.subtract (.getLocation sense)
760 (.getWorldTranslation obj))
761 initial-sense-rotation (Quaternion. (.getRotation sense))
762 base-anti-rotation (.inverse (.getWorldRotation obj))]
763 (.addControl
764 obj
765 (proxy [AbstractControl] []
766 (controlUpdate [tpf]
767 (let [total-rotation
768 (.mult base-anti-rotation (.getWorldRotation obj))]
769 (.setLocation sense
770 (.add
771 (.mult total-rotation sense-offset)
772 (.getWorldTranslation obj)))
773 (.setRotation sense
774 (.mult total-rotation initial-sense-rotation))))
775 (controlRender [_ _])))))
778 (defn update-listener-velocity
779 "Update the listener's velocity every update loop."
780 [#^Spatial obj #^Listener lis]
781 (let [old-position (atom (.getLocation lis))]
782 (.addControl
783 obj
784 (proxy [AbstractControl] []
785 (controlUpdate [tpf]
786 (let [new-position (.getLocation lis)]
787 (.setVelocity
788 lis
789 (.mult (.subtract new-position @old-position)
790 (float (/ tpf))))
791 (reset! old-position new-position)))
792 (controlRender [_ _])))))
794 (import com.aurellem.capture.audio.AudioSendRenderer)
796 (defn attach-ear
797 [#^Application world #^Node creature #^Spatial ear continuation]
798 (let [target (closest-node creature ear)
799 lis (Listener.)
800 audio-renderer (.getAudioRenderer world)
801 sp (sound-processor continuation)]
802 (.setLocation lis (.getWorldTranslation ear))
803 (.setRotation lis (.getWorldRotation ear))
804 (bind-sense target lis)
805 (update-listener-velocity target lis)
806 (.addListener audio-renderer lis)
807 (.registerSoundProcessor audio-renderer lis sp)))
809 (defn enable-hearing
810 [#^Node creature #^Spatial ear]
811 (let [hearing-data (atom [])]
812 [(fn [world]
813 (attach-ear world creature ear
814 (fn [data]
815 (reset! hearing-data (vec data)))))
816 [(fn []
817 (let [data @hearing-data
818 topology
819 (vec (map #(vector % 0) (range 0 (count data))))
820 scaled-data
821 (vec
822 (map
823 #(rem (int (* 255 (/ (+ 1 %) 2))) 256)
824 data))]
825 [topology scaled-data]))
826 ]]))
828 (defn hearing
829 [#^Node creature]
830 (reduce
831 (fn [[init-a senses-a]
832 [init-b senses-b]]
833 [(conj init-a init-b)
834 (into senses-a senses-b)])
835 [[][]]
836 (for [ear (creature-ears creature)]
837 (enable-hearing creature ear))))
839 (defn attach-eye
840 "Attach a Camera to the appropiate area and return the Camera."
841 [#^Node creature #^Spatial eye]
842 (let [target (closest-node creature eye)
843 [cam-width cam-height] (eye-dimensions eye)
844 cam (Camera. cam-width cam-height)]
845 (.setLocation cam (.getWorldTranslation eye))
846 (.setRotation cam (.getWorldRotation eye))
847 (.setFrustumPerspective
848 cam 45 (/ (.getWidth cam) (.getHeight cam))
849 1 1000)
850 (bind-sense target cam)
851 cam))
853 (def presets
854 {:all 0xFFFFFF
855 :red 0xFF0000
856 :blue 0x0000FF
857 :green 0x00FF00})
859 (defn enable-vision
860 "return [init-function sensor-functions] for a particular eye"
861 [#^Node creature #^Spatial eye & {skip :skip :or {skip 0}}]
862 (let [retinal-map (retina-sensor-image eye)
863 camera (attach-eye creature eye)
864 vision-image
865 (atom
866 (BufferedImage. (.getWidth camera)
867 (.getHeight camera)
868 BufferedImage/TYPE_BYTE_BINARY))]
869 [(fn [world]
870 (add-eye
871 world camera
872 (let [counter (atom 0)]
873 (fn [r fb bb bi]
874 (if (zero? (rem (swap! counter inc) (inc skip)))
875 (reset! vision-image (BufferedImage! r fb bb bi)))))))
876 (vec
877 (map
878 (fn [[key image]]
879 (let [whites (white-coordinates image)
880 topology (vec (collapse whites))
881 mask (presets key)]
882 (fn []
883 (vector
884 topology
885 (vec
886 (for [[x y] whites]
887 (bit-and
888 mask (.getRGB @vision-image x y))))))))
889 retinal-map))]))
891 (defn vision
892 [#^Node creature & {skip :skip :or {skip 0}}]
893 (reduce
894 (fn [[init-a senses-a]
895 [init-b senses-b]]
896 [(conj init-a init-b)
897 (into senses-a senses-b)])
898 [[][]]
899 (for [eye (creature-eyes creature)]
900 (enable-vision creature eye))))
903 (defn blender-creature
904 "Return a creature with all joints in place."
905 [blender-path]
906 (let [model (load-blender-model blender-path)
907 joints
908 (if-let [joint-node (.getChild model "joints")]
909 (seq (.getChildren joint-node))
910 (do (println-repl "could not find joints node") []))]
911 (assemble-creature model joints)))
913 (defn debug-window
914 "creates function that offers a debug view of sensor data"
915 []
916 (let [vi (view-image)]
917 (fn
918 [[coords sensor-data]]
919 (let [image (points->image coords)]
920 (dorun
921 (for [i (range (count coords))]
922 (.setRGB image ((coords i) 0) ((coords i) 1)
923 ({0 0x000000
924 1 0xFFFFFF} (sensor-data i)))))
925 (vi image)))))
927 (defn debug-vision-window
928 "creates function that offers a debug view of sensor data"
929 []
930 (let [vi (view-image)]
931 (fn
932 [[coords sensor-data]]
933 (let [image (points->image coords)]
934 (dorun
935 (for [i (range (count coords))]
936 (.setRGB image ((coords i) 0) ((coords i) 1)
937 (sensor-data i))))
938 (vi image)))))
940 (defn debug-hearing-window
941 "view audio data"
942 [height]
943 (let [vi (view-image)]
944 (fn [[coords sensor-data]]
945 (let [image (BufferedImage. (count coords) height
946 BufferedImage/TYPE_INT_RGB)]
947 (dorun
948 (for [x (range (count coords))]
949 (dorun
950 (for [y (range height)]
951 (let [raw-sensor (sensor-data x)]
952 (.setRGB image x y
953 (+ raw-sensor
954 (bit-shift-left raw-sensor 8)
955 (bit-shift-left raw-sensor 16))))))))
956 (vi image)))))
960 ;;(defn test-touch [world creature]
967 (defn test-creature [thing]
968 (let [x-axis
969 (box 1 0.01 0.01 :physical? false :color ColorRGBA/Red)
970 y-axis
971 (box 0.01 1 0.01 :physical? false :color ColorRGBA/Green)
972 z-axis
973 (box 0.01 0.01 1 :physical? false :color ColorRGBA/Blue)
974 creature (blender-creature thing)
975 touch-nerves (touch creature)
976 touch-debug-windows (map (fn [_] (debug-window)) touch-nerves)
977 [init-vision-fns vision-data] (vision creature)
978 vision-debug (map (fn [_] (debug-vision-window)) vision-data)
979 me (sphere 0.5 :color ColorRGBA/Blue :physical? false)
980 [init-hearing-fns hearing-senses] (hearing creature)
981 hearing-windows (map (fn [_] (debug-hearing-window 50))
982 hearing-senses)
983 bell (AudioNode. (asset-manager)
984 "Sounds/ear-and-eye.wav" false)
985 ;; dream
987 ]
988 (world
989 (nodify [creature
990 (box 10 2 10 :position (Vector3f. 0 -9 0)
991 :color ColorRGBA/Gray :mass 0)
992 x-axis y-axis z-axis
993 me
994 ])
995 (merge standard-debug-controls
996 {"key-return"
997 (fn [_ value]
998 (if value
999 (do
1000 (println-repl "play-sound")
1001 (.play bell))))})
1002 (fn [world]
1003 (light-up-everything world)
1004 (enable-debug world)
1005 (dorun (map #(% world) init-vision-fns))
1006 (dorun (map #(% world) init-hearing-fns))
1008 (add-eye world
1009 (attach-eye creature (test-eye))
1010 (comp (view-image) BufferedImage!))
1012 (add-eye world (.getCamera world) no-op)
1014 ;;(com.aurellem.capture.Capture/captureVideo
1015 ;; world (file-str "/home/r/proj/ai-videos/hand"))
1016 ;;(.setTimer world (RatchetTimer. 60))
1017 (speed-up world)
1018 ;;(set-gravity world (Vector3f. 0 0 0))
1020 (fn [world tpf]
1021 ;;(dorun
1022 ;; (map #(%1 %2) touch-nerves (repeat (.getRootNode world))))
1026 (dorun
1027 (map #(%1 (%2 (.getRootNode world)))
1028 touch-debug-windows touch-nerves))
1030 (dorun
1031 (map #(%1 (%2))
1032 vision-debug vision-data))
1033 (dorun
1034 (map #(%1 (%2)) hearing-windows hearing-senses))
1037 ;;(println-repl (vision-data))
1038 (.setLocalTranslation me (.getLocation (.getCamera world)))
1042 ;;(let [timer (atom 0)]
1043 ;; (fn [_ _]
1044 ;; (swap! timer inc)
1045 ;; (if (= (rem @timer 60) 0)
1046 ;; (println-repl (float (/ @timer 60))))))
1047 )))
1057 ;;; experiments in collisions
1061 (defn collision-test []
1062 (let [b-radius 1
1063 b-position (Vector3f. 0 0 0)
1064 obj-b (box 1 1 1 :color ColorRGBA/Blue
1065 :position b-position
1066 :mass 0)
1067 node (nodify [obj-b])
1068 bounds-b
1069 (doto (Picture.)
1070 (.setHeight 50)
1071 (.setWidth 50)
1072 (.setImage (asset-manager)
1073 "Models/creature1/hand.png"
1074 false
1075 ))
1077 ;;(Ray. (Vector3f. 0 -5 0) (.normalize (Vector3f. 0 1 0)))
1079 collisions
1080 (let [cr (CollisionResults.)]
1081 (.collideWith node bounds-b cr)
1082 (println (map #(.getContactPoint %) cr))
1083 cr)
1085 ;;collision-points
1086 ;;(map #(sphere 0.1 :position (.getContactPoint %))
1087 ;; collisions)
1089 ;;node (nodify (conj collision-points obj-b))
1091 sim
1092 (world node
1093 {"key-space"
1094 (fn [_ value]
1095 (if value
1096 (let [cr (CollisionResults.)]
1097 (.collideWith node bounds-b cr)
1098 (println-repl (map #(.getContactPoint %) cr))
1099 cr)))}
1100 no-op
1101 no-op)
1104 sim
1106 ))
1109 ;; the camera will stay in its initial position/rotation with relation
1110 ;; to the spatial.
1113 (defn follow-test
1114 "show a camera that stays in the same relative position to a blue cube."
1115 []
1116 (let [camera-pos (Vector3f. 0 30 0)
1117 rock (box 1 1 1 :color ColorRGBA/Blue
1118 :position (Vector3f. 0 10 0)
1119 :mass 30
1121 rot (.getWorldRotation rock)
1123 table (box 3 1 10 :color ColorRGBA/Gray :mass 0
1124 :position (Vector3f. 0 -3 0))]
1126 (world
1127 (nodify [rock table])
1128 standard-debug-controls
1129 (fn [world]
1130 (let
1131 [cam (doto (.clone (.getCamera world))
1132 (.setLocation camera-pos)
1133 (.lookAt Vector3f/ZERO
1134 Vector3f/UNIT_X))]
1135 (bind-sense rock cam)
1137 (.setTimer world (RatchetTimer. 60))
1138 (add-eye world cam (comp (view-image) BufferedImage!))
1139 (add-eye world (.getCamera world) no-op))
1141 (fn [_ _] (println-repl rot)))))
1145 #+end_src
1147 #+results: body-1
1148 : #'cortex.silly/test-creature
1151 * COMMENT purgatory
1152 #+begin_src clojure
1153 (defn bullet-trans []
1154 (let [obj-a (sphere 0.5 :color ColorRGBA/Red
1155 :position (Vector3f. -10 5 0))
1156 obj-b (sphere 0.5 :color ColorRGBA/Blue
1157 :position (Vector3f. -10 -5 0)
1158 :mass 0)
1159 control-a (.getControl obj-a RigidBodyControl)
1160 control-b (.getControl obj-b RigidBodyControl)
1161 swivel
1162 (.toRotationMatrix
1163 (doto (Quaternion.)
1164 (.fromAngleAxis (/ Math/PI 2)
1165 Vector3f/UNIT_X)))]
1166 (doto
1167 (ConeJoint.
1168 control-a control-b
1169 (Vector3f. 0 5 0)
1170 (Vector3f. 0 -5 0)
1171 swivel swivel)
1172 (.setLimit (* 0.6 (/ Math/PI 4))
1173 (/ Math/PI 4)
1174 (* Math/PI 0.8)))
1175 (world (nodify
1176 [obj-a obj-b])
1177 standard-debug-controls
1178 enable-debug
1179 no-op)))
1182 (defn bullet-trans* []
1183 (let [obj-a (box 1.5 0.5 0.5 :color ColorRGBA/Red
1184 :position (Vector3f. 5 0 0)
1185 :mass 90)
1186 obj-b (sphere 0.5 :color ColorRGBA/Blue
1187 :position (Vector3f. -5 0 0)
1188 :mass 0)
1189 control-a (.getControl obj-a RigidBodyControl)
1190 control-b (.getControl obj-b RigidBodyControl)
1191 move-up? (atom nil)
1192 move-down? (atom nil)
1193 move-left? (atom nil)
1194 move-right? (atom nil)
1195 roll-left? (atom nil)
1196 roll-right? (atom nil)
1197 force 100
1198 swivel
1199 (.toRotationMatrix
1200 (doto (Quaternion.)
1201 (.fromAngleAxis (/ Math/PI 2)
1202 Vector3f/UNIT_X)))
1203 x-move
1204 (doto (Matrix3f.)
1205 (.fromStartEndVectors Vector3f/UNIT_X
1206 (.normalize (Vector3f. 1 1 0))))
1208 timer (atom 0)]
1209 (doto
1210 (ConeJoint.
1211 control-a control-b
1212 (Vector3f. -8 0 0)
1213 (Vector3f. 2 0 0)
1214 ;;swivel swivel
1215 ;;Matrix3f/IDENTITY Matrix3f/IDENTITY
1216 x-move Matrix3f/IDENTITY
1218 (.setCollisionBetweenLinkedBodys false)
1219 (.setLimit (* 1 (/ Math/PI 4)) ;; twist
1220 (* 1 (/ Math/PI 4)) ;; swing span in X-Y plane
1221 (* 0 (/ Math/PI 4)))) ;; swing span in Y-Z plane
1222 (world (nodify
1223 [obj-a obj-b])
1224 (merge standard-debug-controls
1225 {"key-r" (fn [_ pressed?] (reset! move-up? pressed?))
1226 "key-t" (fn [_ pressed?] (reset! move-down? pressed?))
1227 "key-f" (fn [_ pressed?] (reset! move-left? pressed?))
1228 "key-g" (fn [_ pressed?] (reset! move-right? pressed?))
1229 "key-v" (fn [_ pressed?] (reset! roll-left? pressed?))
1230 "key-b" (fn [_ pressed?] (reset! roll-right? pressed?))})
1232 (fn [world]
1233 (enable-debug world)
1234 (set-gravity world Vector3f/ZERO)
1237 (fn [world _]
1239 (if @move-up?
1240 (.applyForce control-a
1241 (Vector3f. force 0 0)
1242 (Vector3f. 0 0 0)))
1243 (if @move-down?
1244 (.applyForce control-a
1245 (Vector3f. (- force) 0 0)
1246 (Vector3f. 0 0 0)))
1247 (if @move-left?
1248 (.applyForce control-a
1249 (Vector3f. 0 force 0)
1250 (Vector3f. 0 0 0)))
1251 (if @move-right?
1252 (.applyForce control-a
1253 (Vector3f. 0 (- force) 0)
1254 (Vector3f. 0 0 0)))
1256 (if @roll-left?
1257 (.applyForce control-a
1258 (Vector3f. 0 0 force)
1259 (Vector3f. 0 0 0)))
1260 (if @roll-right?
1261 (.applyForce control-a
1262 (Vector3f. 0 0 (- force))
1263 (Vector3f. 0 0 0)))
1265 (if (zero? (rem (swap! timer inc) 100))
1266 (.attachChild
1267 (.getRootNode world)
1268 (sphere 0.05 :color ColorRGBA/Yellow
1269 :physical? false :position
1270 (.getWorldTranslation obj-a)))))
1272 ))
1274 (defn transform-trianglesdsd
1275 "Transform that converts each vertex in the first triangle
1276 into the corresponding vertex in the second triangle."
1277 [#^Triangle tri-1 #^Triangle tri-2]
1278 (let [in [(.get1 tri-1)
1279 (.get2 tri-1)
1280 (.get3 tri-1)]
1281 out [(.get1 tri-2)
1282 (.get2 tri-2)
1283 (.get3 tri-2)]]
1284 (let [translate (doto (Matrix4f.) (.setTranslation (.negate (in 0))))
1285 in* [(.mult translate (in 0))
1286 (.mult translate (in 1))
1287 (.mult translate (in 2))]
1288 final-translation
1289 (doto (Matrix4f.)
1290 (.setTranslation (out 1)))
1292 rotate-1
1293 (doto (Matrix3f.)
1294 (.fromStartEndVectors
1295 (.normalize
1296 (.subtract
1297 (in* 1) (in* 0)))
1298 (.normalize
1299 (.subtract
1300 (out 1) (out 0)))))
1301 in** [(.mult rotate-1 (in* 0))
1302 (.mult rotate-1 (in* 1))
1303 (.mult rotate-1 (in* 2))]
1304 scale-factor-1
1305 (.mult
1306 (.normalize
1307 (.subtract
1308 (out 1)
1309 (out 0)))
1310 (/ (.length
1311 (.subtract (out 1)
1312 (out 0)))
1313 (.length
1314 (.subtract (in** 1)
1315 (in** 0)))))
1316 scale-1 (doto (Matrix4f.) (.setScale scale-factor-1))
1317 in*** [(.mult scale-1 (in** 0))
1318 (.mult scale-1 (in** 1))
1319 (.mult scale-1 (in** 2))]
1327 (dorun (map println in))
1328 (println)
1329 (dorun (map println in*))
1330 (println)
1331 (dorun (map println in**))
1332 (println)
1333 (dorun (map println in***))
1334 (println)
1336 ))))
1339 (defn world-setup [joint]
1340 (let [joint-position (Vector3f. 0 0 0)
1341 joint-rotation
1342 (.toRotationMatrix
1343 (.mult
1344 (doto (Quaternion.)
1345 (.fromAngleAxis
1346 (* 1 (/ Math/PI 4))
1347 (Vector3f. -1 0 0)))
1348 (doto (Quaternion.)
1349 (.fromAngleAxis
1350 (* 1 (/ Math/PI 2))
1351 (Vector3f. 0 0 1)))))
1352 top-position (.mult joint-rotation (Vector3f. 8 0 0))
1354 origin (doto
1355 (sphere 0.1 :physical? false :color ColorRGBA/Cyan
1356 :position top-position))
1357 top (doto
1358 (sphere 0.1 :physical? false :color ColorRGBA/Yellow
1359 :position top-position)
1361 (.addControl
1362 (RigidBodyControl.
1363 (CapsuleCollisionShape. 0.5 1.5 1) (float 20))))
1364 bottom (doto
1365 (sphere 0.1 :physical? false :color ColorRGBA/DarkGray
1366 :position (Vector3f. 0 0 0))
1367 (.addControl
1368 (RigidBodyControl.
1369 (CapsuleCollisionShape. 0.5 1.5 1) (float 0))))
1370 table (box 10 2 10 :position (Vector3f. 0 -20 0)
1371 :color ColorRGBA/Gray :mass 0)
1372 a (.getControl top RigidBodyControl)
1373 b (.getControl bottom RigidBodyControl)]
1375 (cond
1376 (= joint :cone)
1378 (doto (ConeJoint.
1379 a b
1380 (world-to-local top joint-position)
1381 (world-to-local bottom joint-position)
1382 joint-rotation
1383 joint-rotation
1387 (.setLimit (* (/ 10) Math/PI)
1388 (* (/ 4) Math/PI)
1389 0)))
1390 [origin top bottom table]))
1392 (defn test-joint [joint]
1393 (let [[origin top bottom floor] (world-setup joint)
1394 control (.getControl top RigidBodyControl)
1395 move-up? (atom false)
1396 move-down? (atom false)
1397 move-left? (atom false)
1398 move-right? (atom false)
1399 roll-left? (atom false)
1400 roll-right? (atom false)
1401 timer (atom 0)]
1403 (world
1404 (nodify [top bottom floor origin])
1405 (merge standard-debug-controls
1406 {"key-r" (fn [_ pressed?] (reset! move-up? pressed?))
1407 "key-t" (fn [_ pressed?] (reset! move-down? pressed?))
1408 "key-f" (fn [_ pressed?] (reset! move-left? pressed?))
1409 "key-g" (fn [_ pressed?] (reset! move-right? pressed?))
1410 "key-v" (fn [_ pressed?] (reset! roll-left? pressed?))
1411 "key-b" (fn [_ pressed?] (reset! roll-right? pressed?))})
1413 (fn [world]
1414 (light-up-everything world)
1415 (enable-debug world)
1416 (set-gravity world (Vector3f. 0 0 0))
1419 (fn [world _]
1420 (if (zero? (rem (swap! timer inc) 100))
1421 (do
1422 ;; (println-repl @timer)
1423 (.attachChild (.getRootNode world)
1424 (sphere 0.05 :color ColorRGBA/Yellow
1425 :position (.getWorldTranslation top)
1426 :physical? false))
1427 (.attachChild (.getRootNode world)
1428 (sphere 0.05 :color ColorRGBA/LightGray
1429 :position (.getWorldTranslation bottom)
1430 :physical? false))))
1432 (if @move-up?
1433 (.applyTorque control
1434 (.mult (.getPhysicsRotation control)
1435 (Vector3f. 0 0 10))))
1436 (if @move-down?
1437 (.applyTorque control
1438 (.mult (.getPhysicsRotation control)
1439 (Vector3f. 0 0 -10))))
1440 (if @move-left?
1441 (.applyTorque control
1442 (.mult (.getPhysicsRotation control)
1443 (Vector3f. 0 10 0))))
1444 (if @move-right?
1445 (.applyTorque control
1446 (.mult (.getPhysicsRotation control)
1447 (Vector3f. 0 -10 0))))
1448 (if @roll-left?
1449 (.applyTorque control
1450 (.mult (.getPhysicsRotation control)
1451 (Vector3f. -1 0 0))))
1452 (if @roll-right?
1453 (.applyTorque control
1454 (.mult (.getPhysicsRotation control)
1455 (Vector3f. 1 0 0))))))))
1459 (defprotocol Frame
1460 (frame [this]))
1462 (extend-type BufferedImage
1463 Frame
1464 (frame [image]
1465 (merge
1466 (apply
1467 hash-map
1468 (interleave
1469 (doall (for [x (range (.getWidth image)) y (range (.getHeight image))]
1470 (vector x y)))
1471 (doall (for [x (range (.getWidth image)) y (range (.getHeight image))]
1472 (let [data (.getRGB image x y)]
1473 (hash-map :r (bit-shift-right (bit-and 0xff0000 data) 16)
1474 :g (bit-shift-right (bit-and 0x00ff00 data) 8)
1475 :b (bit-and 0x0000ff data)))))))
1476 {:width (.getWidth image) :height (.getHeight image)})))
1479 (extend-type ImagePlus
1480 Frame
1481 (frame [image+]
1482 (frame (.getBufferedImage image+))))
1485 #+end_src
1488 * COMMENT generate source
1489 #+begin_src clojure :tangle ../src/cortex/silly.clj
1490 <<body-1>>
1491 #+end_src