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