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