view org/sense.org @ 187:6142e85f5825

extracted common elements of display code
author Robert McIntyre <rlm@mit.edu>
date Sat, 04 Feb 2012 09:42:19 -0700
parents 21816b27d7c8
children 22548d48cc85
line wrap: on
line source
1 #+title: General sense/effector utilities
2 #+author: Robert McIntyre
3 #+email: rlm@mit.edu
4 #+description: sensory utilities
5 #+keywords: simulation, jMonkeyEngine3, clojure, simulated senses
6 #+SETUPFILE: ../../aurellem/org/setup.org
7 #+INCLUDE: ../../aurellem/org/level-0.org
9 #+name: sense
10 #+begin_src clojure
11 (ns cortex.sense
12 "Here are functions useful in the construction of two or more
13 sensors/effectors."
14 {:author "Robert McInytre"}
15 (:use (cortex world util))
16 (:import ij.process.ImageProcessor)
17 (:import jme3tools.converters.ImageToAwt)
18 (:import java.awt.image.BufferedImage)
19 (:import com.jme3.collision.CollisionResults)
20 (:import com.jme3.bounding.BoundingBox)
21 (:import (com.jme3.scene Node Spatial))
22 (:import com.jme3.scene.control.AbstractControl)
23 (:import (com.jme3.math Quaternion Vector3f)))
25 (defn meta-data
26 "Get the meta-data for a node created with blender."
27 [blender-node key]
28 (if-let [data (.getUserData blender-node "properties")]
29 (.findValue data key)
30 nil))
32 (defn closest-node
33 "Return the node in creature which is closest to the given node."
34 [#^Node creature #^Node eye]
35 (loop [radius (float 0.01)]
36 (let [results (CollisionResults.)]
37 (.collideWith
38 creature
39 (BoundingBox. (.getWorldTranslation eye)
40 radius radius radius)
41 results)
42 (if-let [target (first results)]
43 (.getGeometry target)
44 (recur (float (* 2 radius)))))))
46 (defn bind-sense
47 "Bind the sense to the Spatial such that it will maintain its
48 current position relative to the Spatial no matter how the spatial
49 moves. 'sense can be either a Camera or Listener object."
50 [#^Spatial obj sense]
51 (let [sense-offset (.subtract (.getLocation sense)
52 (.getWorldTranslation obj))
53 initial-sense-rotation (Quaternion. (.getRotation sense))
54 base-anti-rotation (.inverse (.getWorldRotation obj))]
55 (.addControl
56 obj
57 (proxy [AbstractControl] []
58 (controlUpdate [tpf]
59 (let [total-rotation
60 (.mult base-anti-rotation (.getWorldRotation obj))]
61 (.setLocation
62 sense
63 (.add
64 (.mult total-rotation sense-offset)
65 (.getWorldTranslation obj)))
66 (.setRotation
67 sense
68 (.mult total-rotation initial-sense-rotation))))
69 (controlRender [_ _])))))
71 (def white 0xFFFFFF)
73 (defn white? [rgb]
74 (= (bit-and white rgb) white))
76 (defn filter-pixels
77 "List the coordinates of all pixels matching pred, within the bounds
78 provided.
79 bounds -> [x0 y0 width height]"
80 {:author "Dylan Holmes"}
81 ([pred #^BufferedImage image]
82 (filter-pixels pred image [0 0 (.getWidth image) (.getHeight image)]))
83 ([pred #^BufferedImage image [x0 y0 width height]]
84 ((fn accumulate [x y matches]
85 (cond
86 (>= y (+ height y0)) matches
87 (>= x (+ width x0)) (recur 0 (inc y) matches)
88 (pred (.getRGB image x y))
89 (recur (inc x) y (conj matches [x y]))
90 :else (recur (inc x) y matches)))
91 x0 y0 [])))
93 (defn white-coordinates
94 "Coordinates of all the white pixels in a subset of the image."
95 ([#^BufferedImage image bounds]
96 (filter-pixels white? image bounds))
97 ([#^BufferedImage image]
98 (filter-pixels white? image)))
100 (defn points->image
101 "Take a sparse collection of points and visuliaze it as a
102 BufferedImage."
103 [points]
104 (if (empty? points)
105 (BufferedImage. 1 1 BufferedImage/TYPE_BYTE_BINARY)
106 (let [xs (vec (map first points))
107 ys (vec (map second points))
108 x0 (apply min xs)
109 y0 (apply min ys)
110 width (- (apply max xs) x0)
111 height (- (apply max ys) y0)
112 image (BufferedImage. (inc width) (inc height)
113 BufferedImage/TYPE_INT_RGB)]
114 (dorun
115 (for [x (range (.getWidth image))
116 y (range (.getHeight image))]
117 (.setRGB image x y 0xFF0000)))
118 (dorun
119 (for [index (range (count points))]
120 (.setRGB image (- (xs index) x0) (- (ys index) y0) -1)))
121 image)))
123 (defn average [coll]
124 (/ (reduce + coll) (count coll)))
126 (defn collapse-1d
127 "One dimensional analogue of collapse."
128 [center line]
129 (let [length (count line)
130 num-above (count (filter (partial < center) line))
131 num-below (- length num-above)]
132 (range (- center num-below)
133 (+ center num-above))))
135 (defn collapse
136 "Take a set of pairs of integers and collapse them into a
137 contigous bitmap with no \"holes\"."
138 [points]
139 (if (empty? points) []
140 (let
141 [num-points (count points)
142 center (vector
143 (int (average (map first points)))
144 (int (average (map first points))))
145 flattened
146 (reduce
147 concat
148 (map
149 (fn [column]
150 (map vector
151 (map first column)
152 (collapse-1d (second center)
153 (map second column))))
154 (partition-by first (sort-by first points))))
155 squeezed
156 (reduce
157 concat
158 (map
159 (fn [row]
160 (map vector
161 (collapse-1d (first center)
162 (map first row))
163 (map second row)))
164 (partition-by second (sort-by second flattened))))
165 relocated
166 (let [min-x (apply min (map first squeezed))
167 min-y (apply min (map second squeezed))]
168 (map (fn [[x y]]
169 [(- x min-x)
170 (- y min-y)])
171 squeezed))]
172 relocated)))
174 (defn world-to-local
175 "Convert the world coordinates into coordinates relative to the
176 object (i.e. local coordinates), taking into account the rotation
177 of object."
178 [#^Spatial object world-coordinate]
179 (.worldToLocal object world-coordinate nil))
181 (defn local-to-world
182 "Convert the local coordinates into world relative coordinates"
183 [#^Spatial object local-coordinate]
184 (.localToWorld object local-coordinate nil))
186 (defn sense-nodes
187 "For each sense there is a special blender node whose children are
188 considered markers for an instance of a that sense. This function
189 generates functions to find those children, given the name of the
190 special parent node."
191 [parent-name]
192 (fn [#^Node creature]
193 (if-let [sense-node (.getChild creature parent-name)]
194 (seq (.getChildren sense-node))
195 (do (println-repl "could not find" parent-name "node") []))))
197 (defn load-image
198 "Load an image as a BufferedImage using the asset-manager system."
199 [asset-relative-path]
200 (ImageToAwt/convert
201 (.getImage (.loadTexture (asset-manager) asset-relative-path))
202 false false 0))
204 (defn jme-to-blender
205 "Convert from JME coordinates to Blender coordinates"
206 [#^Vector3f in]
207 (Vector3f. (.getX in)
208 (- (.getZ in))
209 (.getY in)))
211 (defn blender-to-jme
212 "Convert from Blender coordinates to JME coordinates"
213 [#^Vector3f in]
214 (Vector3f. (.getX in)
215 (.getZ in)
216 (- (.getY in))))
219 (defn view-sense
220 "Take a function that produces a BufferedImage from some sense data
221 and return a function which takes a list of sense and displays
222 those images in multiple JFrames."
223 [sense-display-kernel]
224 (let
225 [windows (atom [])]
226 (fn [data]
227 (if (> (count data) (count @windows))
228 (reset! windows (map (fn [_] (view-image))
229 (range (count data)))))
230 (dorun
231 (map
232 (fn [display gen data]
233 (display (gen data)))
234 @windows sense-display-kernel data)))))
238 #+end_src
241 * COMMENT generate source
242 #+begin_src clojure :tangle ../src/cortex/sense.clj
243 <<sense>>
244 #+end_src