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