rlm@306
|
1 #+title: Integration
|
rlm@73
|
2 #+author: Robert McIntyre
|
rlm@73
|
3 #+email: rlm@mit.edu
|
rlm@73
|
4 #+description:
|
rlm@73
|
5 #+keywords: simulation, jMonkeyEngine3, clojure
|
rlm@73
|
6 #+SETUPFILE: ../../aurellem/org/setup.org
|
rlm@73
|
7 #+INCLUDE: ../../aurellem/org/level-0.org
|
rlm@73
|
8
|
rlm@302
|
9 * Integration
|
rlm@129
|
10
|
rlm@281
|
11 This is the ultimate test which features all of the senses that I've
|
rlm@281
|
12 made so far. The blender file for the creature serves as an example of
|
rlm@281
|
13 a fully equipped creature in terms of senses. You can find it [[../assets/Models/test-creature/hand.blend][here]].
|
rlm@73
|
14
|
rlm@300
|
15
|
rlm@300
|
16 #+begin_html
|
rlm@300
|
17 <div class="figure">
|
rlm@300
|
18 <video controls="controls" width="755">
|
rlm@300
|
19 <source src="../video/hand.ogg" type="video/ogg"
|
rlm@300
|
20 preload="none" poster="../images/aurellem-1280x480.png" />
|
rlm@300
|
21 </video>
|
rlm@309
|
22 <br> <a href="http://youtu.be/ZgkWFIoHNuU"> YouTube </a>
|
rlm@300
|
23 <p>Simulated Senses in a Virtual Environment</p>
|
rlm@300
|
24 </div>
|
rlm@300
|
25 #+end_html
|
rlm@300
|
26
|
rlm@302
|
27 * Generating the Video
|
rlm@300
|
28
|
rlm@192
|
29 #+name: integration
|
rlm@73
|
30 #+begin_src clojure
|
rlm@300
|
31 (ns cortex.test.integration
|
rlm@73
|
32 "let's play!"
|
rlm@192
|
33 {:author "Robert McIntyre"}
|
rlm@314
|
34 (:use (cortex world util import body sense
|
rlm@281
|
35 hearing touch vision proprioception movement))
|
rlm@192
|
36 (:import (com.jme3.math ColorRGBA Vector3f))
|
rlm@281
|
37 (:import java.io.File)
|
rlm@192
|
38 (:import com.jme3.audio.AudioNode)
|
rlm@192
|
39 (:import com.aurellem.capture.RatchetTimer))
|
rlm@73
|
40
|
rlm@281
|
41 (dorun (cortex.import/mega-import-jme3))
|
rlm@314
|
42 ;;(rlm.rlm-commands/help)
|
rlm@74
|
43
|
rlm@281
|
44 (def hand "Models/test-creature/hand.blend")
|
rlm@78
|
45
|
rlm@281
|
46 (def output-base (File. "/home/r/proj/cortex/render/hand"))
|
rlm@300
|
47 #+end_src
|
rlm@189
|
48
|
rlm@300
|
49 For this demonstration I have to manually drive the muscles of the
|
rlm@300
|
50 hand. I do this by creating a little mini-language to describe
|
rlm@388
|
51 simulated muscle contraction.
|
rlm@316
|
52
|
rlm@300
|
53 #+name: integration-2
|
rlm@300
|
54 #+begin_src clojure
|
rlm@296
|
55 (defn motor-control-program
|
rlm@296
|
56 "Create a function which will execute the motor script"
|
rlm@296
|
57 [muscle-positions
|
rlm@296
|
58 script]
|
rlm@296
|
59 (let [current-frame (atom -1)
|
rlm@296
|
60 keyed-script (group-by first script)
|
rlm@296
|
61 current-forces (atom {}) ]
|
rlm@296
|
62 (fn [effectors]
|
rlm@296
|
63 (let [indexed-effectors (vec effectors)]
|
rlm@296
|
64 (dorun
|
rlm@296
|
65 (for [[_ part force] (keyed-script (swap! current-frame inc))]
|
rlm@296
|
66 (swap! current-forces (fn [m] (assoc m part force)))))
|
rlm@296
|
67 (doall (map (fn [effector power]
|
rlm@296
|
68 (effector (int power)))
|
rlm@296
|
69 effectors
|
rlm@296
|
70 (map #(@current-forces % 0) muscle-positions)))))))
|
rlm@296
|
71
|
rlm@296
|
72 (def muscle-positions
|
rlm@296
|
73 [:pointer-2-e
|
rlm@296
|
74 :pointer-2-f
|
rlm@296
|
75 :thumb-1
|
rlm@296
|
76 :thumb-1
|
rlm@296
|
77 :pointer-1-e
|
rlm@296
|
78 :pointer-1-f
|
rlm@296
|
79 :thumb-2-e
|
rlm@296
|
80 :thumb-2-f
|
rlm@296
|
81 :middle-1-e
|
rlm@296
|
82 :middle-1-f
|
rlm@296
|
83 :pointer-3-f
|
rlm@296
|
84 :pointer-3-e
|
rlm@296
|
85 :middle-2-e
|
rlm@296
|
86 :middle-2-f
|
rlm@296
|
87 :middle-3-f
|
rlm@296
|
88 :middle-3-e
|
rlm@296
|
89 :pinky-2-e
|
rlm@296
|
90 :pinky-2-f
|
rlm@296
|
91 :pinky-3-f
|
rlm@296
|
92 :pinky-3-e
|
rlm@296
|
93 :ring-3-e
|
rlm@296
|
94 :ring-3-f
|
rlm@296
|
95 :ring-2-f
|
rlm@296
|
96 :ring-2-e
|
rlm@296
|
97 :ring-1-e
|
rlm@296
|
98 :ring-1-f
|
rlm@296
|
99 :thumb-1-e
|
rlm@296
|
100 :thumb-1-f
|
rlm@296
|
101 :pinky-1-f
|
rlm@296
|
102 :pinky-1-e])
|
rlm@296
|
103
|
rlm@296
|
104 (def full 9001)
|
rlm@300
|
105
|
rlm@300
|
106
|
rlm@306
|
107 ;; Choreography:
|
rlm@300
|
108
|
rlm@300
|
109 ;; Let the hand fall palm-up
|
rlm@300
|
110
|
rlm@300
|
111 ;; it curls its phalanges, starting with the pinky.
|
rlm@300
|
112
|
rlm@300
|
113 ;; it lets its phalanges fall back down.
|
rlm@300
|
114
|
rlm@300
|
115 ;; block falls down onto the hand, accompanied by a sound. The block
|
rlm@300
|
116 ;; can be seen by the hand's eye.
|
rlm@300
|
117
|
rlm@300
|
118 ;; hand FORCEFULLY catapults the block so that it hits the camera.
|
rlm@300
|
119
|
rlm@300
|
120
|
rlm@306
|
121 ;; the syntax here is [keyframe body-part force]
|
rlm@300
|
122 (def move-fingers
|
rlm@298
|
123 [[300 :pinky-3-f 50]
|
rlm@298
|
124 [320 :pinky-2-f 80]
|
rlm@298
|
125 [340 :pinky-1-f 100]
|
rlm@297
|
126
|
rlm@298
|
127 [310 :ring-3-f 100]
|
rlm@298
|
128 [330 :ring-2-f 120]
|
rlm@298
|
129 [350 :ring-1-f 140]
|
rlm@296
|
130
|
rlm@298
|
131 [330 :middle-3-f 120]
|
rlm@298
|
132 [340 :middle-2-f 120]
|
rlm@298
|
133 [360 :middle-1-f 30]
|
rlm@296
|
134
|
rlm@298
|
135 [350 :pointer-3-f 120]
|
rlm@298
|
136 [360 :pointer-2-f 120]
|
rlm@298
|
137 [380 :pointer-1-f 30]
|
rlm@296
|
138
|
rlm@298
|
139 [800 :pinky-3-f 0]
|
rlm@298
|
140 [800 :pinky-2-f 0]
|
rlm@298
|
141 [800 :pinky-1-f 0]
|
rlm@296
|
142
|
rlm@298
|
143 [800 :ring-3-f 0]
|
rlm@298
|
144 [800 :ring-2-f 0]
|
rlm@298
|
145 [800 :ring-1-f 0]
|
rlm@296
|
146
|
rlm@298
|
147 [800 :middle-3-f 0]
|
rlm@298
|
148 [800 :middle-2-f 0]
|
rlm@298
|
149 [800 :middle-1-f 0]
|
rlm@296
|
150
|
rlm@298
|
151 [800 :pointer-3-f 0]
|
rlm@298
|
152 [800 :pointer-2-f 0]
|
rlm@298
|
153 [800 :pointer-1-f 0]
|
rlm@297
|
154
|
rlm@297
|
155
|
rlm@298
|
156 [800 :pinky-3-e 50]
|
rlm@298
|
157 [800 :pinky-2-e 80]
|
rlm@298
|
158 [800 :pinky-1-e 100]
|
rlm@297
|
159
|
rlm@298
|
160 [800 :ring-3-e 100]
|
rlm@298
|
161 [800 :ring-2-e 120]
|
rlm@298
|
162 [800 :ring-1-e 140]
|
rlm@298
|
163
|
rlm@298
|
164 [800 :middle-3-e 120]
|
rlm@298
|
165 [800 :middle-2-e 120]
|
rlm@298
|
166 [800 :middle-1-e 30]
|
rlm@298
|
167
|
rlm@298
|
168 [800 :pointer-3-e 120]
|
rlm@298
|
169 [800 :pointer-2-e 120]
|
rlm@298
|
170 [800 :pointer-1-e 30]
|
rlm@298
|
171
|
rlm@298
|
172 [870 :pinky-3-e 0]
|
rlm@298
|
173 [870 :pinky-2-e 0]
|
rlm@298
|
174 [870 :pinky-1-e 0]
|
rlm@298
|
175
|
rlm@298
|
176 [870 :ring-3-e 0]
|
rlm@298
|
177 [870 :ring-2-e 0]
|
rlm@298
|
178 [870 :ring-1-e 0]
|
rlm@298
|
179
|
rlm@298
|
180 [870 :middle-3-e 0]
|
rlm@298
|
181 [870 :middle-2-e 0]
|
rlm@298
|
182 [870 :middle-1-e 0]
|
rlm@298
|
183
|
rlm@298
|
184 [870 :pointer-3-e 0]
|
rlm@298
|
185 [870 :pointer-2-e 0]
|
rlm@298
|
186 [870 :pointer-1-e 0]
|
rlm@298
|
187
|
rlm@298
|
188 [1500 :pointer-1-f full]
|
rlm@298
|
189 [1500 :pointer-2-f full]
|
rlm@298
|
190 [1500 :pointer-3-f full]
|
rlm@298
|
191
|
rlm@298
|
192 [1500 :middle-1-f full]
|
rlm@298
|
193 [1500 :middle-2-f full]
|
rlm@298
|
194 [1500 :middle-3-f full]
|
rlm@298
|
195
|
rlm@298
|
196 [1510 :pointer-1-f 0]
|
rlm@298
|
197 [1510 :pointer-2-f 0]
|
rlm@298
|
198 [1510 :pointer-3-f 0]
|
rlm@298
|
199
|
rlm@298
|
200 [1510 :middle-1-f 0]
|
rlm@298
|
201 [1510 :middle-2-f 0]
|
rlm@298
|
202 [1510 :middle-3-f 0]
|
rlm@297
|
203 ])
|
rlm@297
|
204
|
rlm@298
|
205 (defn gen-summon-ball [debug?]
|
rlm@298
|
206 (let [wait (atom 1100)]
|
rlm@297
|
207 (fn [world]
|
rlm@297
|
208 (if (= 0 (swap! wait dec))
|
rlm@297
|
209 (let [brick
|
rlm@297
|
210 (box 0.8 0.8 0.8 :mass 0.05
|
rlm@298
|
211 :position (Vector3f. -0.5 0 0.5)
|
rlm@298
|
212 :color (ColorRGBA/Red))
|
rlm@298
|
213 bell (AudioNode. (asset-manager)
|
rlm@298
|
214 "Sounds/pure.wav" false)]
|
rlm@298
|
215 (.play bell)
|
rlm@298
|
216 (if debug?
|
rlm@298
|
217 (.addControl
|
rlm@298
|
218 brick
|
rlm@298
|
219 (proxy [AbstractControl] []
|
rlm@298
|
220 (controlUpdate [tpf]
|
rlm@298
|
221 (println-repl (.getWorldTranslation brick)))
|
rlm@298
|
222 (controlRender [_ _]))))
|
rlm@297
|
223 (add-element world brick))))))
|
rlm@295
|
224
|
rlm@298
|
225 (import com.aurellem.capture.Capture)
|
rlm@298
|
226
|
rlm@388
|
227 (defn test-integration
|
rlm@457
|
228 "Testing Everything!
|
rlm@457
|
229
|
rlm@457
|
230 You will see an articulated hand fall onto the table. It has all
|
rlm@457
|
231 senses including:
|
rlm@457
|
232 - Vision, 4 channels
|
rlm@457
|
233 - Hearing
|
rlm@457
|
234 - Touch
|
rlm@457
|
235 - Proprioceptoin
|
rlm@457
|
236 - Muscle Tension
|
rlm@457
|
237
|
rlm@457
|
238 Keys:
|
rlm@457
|
239 <space> : fire ball"
|
rlm@388
|
240 ([] (test-integration false))
|
rlm@281
|
241 ([record?]
|
rlm@281
|
242 (let [me (sphere 0.5 :color ColorRGBA/Blue :physical? false)
|
rlm@281
|
243
|
rlm@298
|
244 base (File. "/home/r/proj/cortex/render/hand")
|
rlm@298
|
245
|
rlm@298
|
246
|
rlm@281
|
247 creature (doto (load-blender-model hand)
|
rlm@281
|
248 (body!))
|
rlm@297
|
249
|
rlm@298
|
250 summon-ball (gen-summon-ball false)
|
rlm@192
|
251 ;;;;;;;;;;;; Sensors/Effectors ;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
rlm@298
|
252 touch (touch! creature)
|
rlm@298
|
253 touch-display (view-touch)
|
rlm@189
|
254
|
rlm@298
|
255 vision (vision! creature)
|
rlm@298
|
256 vision-display (view-vision)
|
rlm@189
|
257
|
rlm@298
|
258 hearing (hearing! creature)
|
rlm@298
|
259 hearing-display (view-hearing)
|
rlm@189
|
260
|
rlm@173
|
261 prop (proprioception! creature)
|
rlm@190
|
262 prop-display (view-proprioception)
|
rlm@148
|
263
|
rlm@296
|
264 control-script (motor-control-program
|
rlm@300
|
265 muscle-positions move-fingers)
|
rlm@191
|
266 muscles (movement! creature)
|
rlm@281
|
267 muscle-display (view-movement)
|
rlm@281
|
268 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
rlm@189
|
269
|
rlm@281
|
270 fix-display (gen-fix-display)]
|
rlm@285
|
271 (world
|
rlm@285
|
272 (nodify [creature
|
rlm@285
|
273 (box 10 2 10 :position (Vector3f. 0 -9 0)
|
rlm@285
|
274 :color ColorRGBA/Gray :mass 0)
|
rlm@285
|
275 me])
|
rlm@298
|
276 standard-debug-controls
|
rlm@298
|
277
|
rlm@285
|
278 (fn [world]
|
rlm@285
|
279 (.setTimer world (RatchetTimer. 60))
|
rlm@297
|
280 (position-camera
|
rlm@298
|
281 world (Vector3f. -0.13217318, 5.816415, -5.3089414)
|
rlm@298
|
282 (Quaternion. 0.55685693, 0.0042774677, -0.0028673497, 0.83059245))
|
rlm@298
|
283
|
rlm@285
|
284 (light-up-everything world)
|
rlm@285
|
285 (enable-debug world)
|
rlm@285
|
286 (add-camera! world
|
rlm@285
|
287 (add-eye! creature
|
rlm@285
|
288 (.getChild
|
rlm@285
|
289 (.getChild creature "eyes") "eye"))
|
rlm@285
|
290 (comp (view-image) BufferedImage!))
|
rlm@298
|
291
|
rlm@298
|
292 (if record?
|
rlm@298
|
293 (Capture/captureVideo
|
rlm@298
|
294 world (File. base "main")))
|
rlm@298
|
295 (if record?
|
rlm@298
|
296 (Capture/captureAudio
|
rlm@298
|
297 world (File. base "main.wav"))))
|
rlm@285
|
298 (fn [world tpf]
|
rlm@298
|
299 (prop-display
|
rlm@298
|
300 (prop)
|
rlm@298
|
301 (if record? (File. base "proprio")))
|
rlm@298
|
302 (touch-display
|
rlm@298
|
303 (map #(% (.getRootNode world)) touch)
|
rlm@298
|
304 (if record? (File. base "touch")))
|
rlm@298
|
305 (vision-display
|
rlm@298
|
306 (map #(% world) vision)
|
rlm@298
|
307 (if record? (File. base "vision")))
|
rlm@298
|
308 (hearing-display
|
rlm@298
|
309 (map #(% world) hearing)
|
rlm@298
|
310 (if record? (File. base "hearing")))
|
rlm@298
|
311 (muscle-display
|
rlm@298
|
312 (control-script muscles)
|
rlm@298
|
313 (if record? (File. base "muscle")))
|
rlm@298
|
314
|
rlm@297
|
315 (summon-ball world)
|
rlm@298
|
316
|
rlm@285
|
317 (.setLocalTranslation me (.getLocation (.getCamera world)))
|
rlm@285
|
318 (fix-display world))))))
|
rlm@300
|
319 #+end_src
|
rlm@298
|
320
|
rlm@302
|
321 ** ImageMagick / ffmpeg
|
rlm@300
|
322
|
rlm@300
|
323 Just a bunch of calls to imagemagick to arrange the data that
|
rlm@300
|
324 =test-everything!= produces.
|
rlm@300
|
325
|
rlm@300
|
326 #+name: magick-8
|
rlm@300
|
327 #+begin_src clojure
|
rlm@300
|
328 (ns cortex.video.magick8
|
rlm@300
|
329 (:import java.io.File)
|
rlm@316
|
330 (:use clojure.java.shell))
|
rlm@298
|
331
|
rlm@298
|
332 (comment
|
rlm@298
|
333 ;; list of touch targets
|
rlm@298
|
334 0 middle-11
|
rlm@298
|
335 1 middle-21
|
rlm@298
|
336 2 middle-31
|
rlm@298
|
337 3 pinky-11
|
rlm@298
|
338 4 pinky-21
|
rlm@298
|
339 5 pinky-31
|
rlm@298
|
340 6 pointer-11
|
rlm@298
|
341 7 pointer-21
|
rlm@298
|
342 8 pointer-31
|
rlm@298
|
343 9 ring-11
|
rlm@298
|
344 10 ring-21
|
rlm@298
|
345 11 ring-31
|
rlm@298
|
346 12 thumb-11
|
rlm@298
|
347 13 thumb-2.0011
|
rlm@298
|
348
|
rlm@298
|
349
|
rlm@298
|
350 ;; list of vision targets
|
rlm@298
|
351 0 :all
|
rlm@298
|
352 1 :green
|
rlm@298
|
353 2 :blue
|
rlm@298
|
354 3 :red
|
rlm@298
|
355
|
rlm@298
|
356 ;; list of proprio targets
|
rlm@298
|
357 0 middle-11 -> middle-21
|
rlm@298
|
358 1 middle-21 -> middle-31
|
rlm@298
|
359 2 thumb-11 -> thumb-2.0011
|
rlm@298
|
360 3 pointer-11 -> pointer-21
|
rlm@298
|
361 4 pointer-21 -> pointer-31
|
rlm@298
|
362 5 ring-21 -> ring-31
|
rlm@298
|
363 6 ring-11 -> ring-21
|
rlm@298
|
364 7 pinky-21 -> pinky-31
|
rlm@298
|
365 8 pinky-11 -> pinky-21
|
rlm@298
|
366 9 middle-11 -> palm1
|
rlm@298
|
367 10 pinky-11 -> palm1
|
rlm@298
|
368 11 palm1 -> pointer-11
|
rlm@298
|
369 12 palm1 -> ring-11
|
rlm@298
|
370 13 palm1 -> thumb-11
|
rlm@298
|
371
|
rlm@298
|
372
|
rlm@298
|
373 ;; list of muscle targets
|
rlm@298
|
374 0 :pointer-2-e
|
rlm@298
|
375 1 :pointer-2-f
|
rlm@298
|
376 2 :thumb-1
|
rlm@298
|
377 3 :thumb-1
|
rlm@298
|
378 4 :pointer-1-e
|
rlm@298
|
379 5 :pointer-1-f
|
rlm@298
|
380 6 :thumb-2-e
|
rlm@298
|
381 7 :thumb-2-f
|
rlm@298
|
382 8 :middle-1-e
|
rlm@298
|
383 9 :middle-1-f
|
rlm@298
|
384 10 :pointer-3-f
|
rlm@298
|
385 11 :pointer-3-e
|
rlm@298
|
386 12 :middle-2-e
|
rlm@298
|
387 13 :middle-2-f
|
rlm@298
|
388 14 :middle-3-f
|
rlm@298
|
389 15 :middle-3-e
|
rlm@298
|
390 16 :pinky-2-e
|
rlm@298
|
391 17 :pinky-2-f
|
rlm@298
|
392 18 :pinky-3-f
|
rlm@298
|
393 19 :pinky-3-e
|
rlm@298
|
394 20 :ring-3-e
|
rlm@298
|
395 21 :ring-3-f
|
rlm@298
|
396 22 :ring-2-f
|
rlm@298
|
397 23 :ring-2-e
|
rlm@298
|
398 24 :ring-1-e
|
rlm@298
|
399 25 :ring-1-f
|
rlm@298
|
400 26 :thumb-1-e
|
rlm@298
|
401 27 :thumb-1-f
|
rlm@298
|
402 28 :pinky-1-f
|
rlm@298
|
403 29 :pinky-1-e
|
rlm@298
|
404 )
|
rlm@298
|
405
|
rlm@298
|
406 (def base (File. "/home/r/proj/cortex/render/hand"))
|
rlm@298
|
407
|
rlm@298
|
408 (defn prepare-muscle [muscle]
|
rlm@298
|
409 ["(" muscle "-rotate" "90" "-scale" "15x60!" ")"])
|
rlm@298
|
410
|
rlm@298
|
411 (defn prepare-touch [touch]
|
rlm@298
|
412 ["(" touch "-rotate" "180" ")"])
|
rlm@298
|
413
|
rlm@298
|
414 (defn generate-top-finger [tip-flexor tip-extensor tip
|
rlm@298
|
415 joint-2-3
|
rlm@298
|
416 mid-flexor mid-extensor mid
|
rlm@298
|
417 joint-1-2]
|
rlm@298
|
418 ["("
|
rlm@298
|
419 "-size" "113x357" "xc:transparent"
|
rlm@298
|
420 (prepare-muscle tip-flexor) "-geometry" "+0+7" "-composite"
|
rlm@298
|
421 (prepare-muscle tip-extensor) "-geometry" "+98+7" "-composite"
|
rlm@298
|
422 (prepare-touch tip) "-geometry" "+18+0" "-composite"
|
rlm@298
|
423
|
rlm@298
|
424 joint-2-3 "-geometry" "+32+79" "-composite"
|
rlm@298
|
425
|
rlm@298
|
426 (prepare-muscle mid-flexor) "-geometry" "+19+131" "-composite"
|
rlm@298
|
427 (prepare-muscle mid-extensor) "-geometry" "+80+131" "-composite"
|
rlm@298
|
428 (prepare-touch mid) "-geometry" "+39+133" "-composite"
|
rlm@298
|
429
|
rlm@298
|
430 joint-1-2 "-geometry" "+32+193" "-composite"
|
rlm@298
|
431 ")"])
|
rlm@298
|
432
|
rlm@299
|
433 (defn file-names [#^File dir]
|
rlm@299
|
434 (map #(.getCanonicalPath %) (next (sort (file-seq dir)))))
|
rlm@299
|
435
|
rlm@299
|
436 (defn file-groups [& paths]
|
rlm@299
|
437 (apply (partial map list )
|
rlm@299
|
438 (map (comp file-names #(File. base %))
|
rlm@299
|
439 paths)))
|
rlm@299
|
440
|
rlm@299
|
441 (defn pinky []
|
rlm@299
|
442 (file-groups
|
rlm@299
|
443 "muscle/18"
|
rlm@299
|
444 "muscle/19"
|
rlm@299
|
445 "touch/5"
|
rlm@299
|
446 "proprio/7"
|
rlm@299
|
447
|
rlm@299
|
448 "muscle/17"
|
rlm@299
|
449 "muscle/16"
|
rlm@299
|
450 "touch/4"
|
rlm@299
|
451 "proprio/8"
|
rlm@299
|
452
|
rlm@299
|
453 "muscle/28"
|
rlm@299
|
454 "muscle/29"
|
rlm@299
|
455 "touch/3"
|
rlm@299
|
456 "proprio/10"))
|
rlm@299
|
457
|
rlm@299
|
458 (defn ring []
|
rlm@299
|
459 (file-groups
|
rlm@299
|
460 "muscle/21"
|
rlm@299
|
461 "muscle/20"
|
rlm@299
|
462 "touch/11"
|
rlm@299
|
463 "proprio/5"
|
rlm@299
|
464
|
rlm@299
|
465 "muscle/22"
|
rlm@299
|
466 "muscle/23"
|
rlm@299
|
467 "touch/10"
|
rlm@299
|
468 "proprio/6"
|
rlm@299
|
469
|
rlm@299
|
470 "muscle/25"
|
rlm@299
|
471 "muscle/24"
|
rlm@299
|
472 "touch/9"
|
rlm@299
|
473 "proprio/12"))
|
rlm@299
|
474
|
rlm@299
|
475 (defn middle []
|
rlm@299
|
476 (file-groups
|
rlm@299
|
477 "muscle/14"
|
rlm@299
|
478 "muscle/15"
|
rlm@299
|
479 "touch/2"
|
rlm@299
|
480 "proprio/1"
|
rlm@299
|
481
|
rlm@300
|
482 "muscle/13"
|
rlm@299
|
483 "muscle/12"
|
rlm@299
|
484 "touch/1"
|
rlm@299
|
485 "proprio/0"
|
rlm@299
|
486
|
rlm@300
|
487 "muscle/9"
|
rlm@299
|
488 "muscle/8"
|
rlm@299
|
489 "touch/0"
|
rlm@299
|
490 "proprio/9"))
|
rlm@299
|
491
|
rlm@299
|
492 (defn pointer []
|
rlm@299
|
493 (file-groups
|
rlm@299
|
494 "muscle/10"
|
rlm@299
|
495 "muscle/11"
|
rlm@299
|
496 "touch/8"
|
rlm@299
|
497 "proprio/4"
|
rlm@299
|
498
|
rlm@299
|
499 "muscle/1"
|
rlm@299
|
500 "muscle/0"
|
rlm@299
|
501 "touch/7"
|
rlm@299
|
502 "proprio/3"
|
rlm@299
|
503
|
rlm@299
|
504 "muscle/5"
|
rlm@299
|
505 "muscle/4"
|
rlm@299
|
506 "touch/6"
|
rlm@299
|
507 "proprio/11"))
|
rlm@299
|
508
|
rlm@299
|
509 (defn thumb []
|
rlm@299
|
510 (file-groups
|
rlm@299
|
511 "muscle/7"
|
rlm@299
|
512 "muscle/6"
|
rlm@299
|
513 "touch/13"
|
rlm@299
|
514 "proprio/2"
|
rlm@299
|
515
|
rlm@299
|
516 "muscle/27"
|
rlm@299
|
517 "muscle/26"
|
rlm@299
|
518 "muscle/3"
|
rlm@299
|
519 "muscle/2"
|
rlm@299
|
520 "touch/12"
|
rlm@299
|
521 "proprio/13"))
|
rlm@299
|
522
|
rlm@298
|
523 (defn generate-finger
|
rlm@298
|
524 [tip-flexor tip-extensor tip
|
rlm@298
|
525 joint-2-3
|
rlm@298
|
526 mid-flexor mid-extensor mid
|
rlm@298
|
527 joint-1-2
|
rlm@298
|
528 base-flexor base-extensor base
|
rlm@298
|
529 joint-palm-1]
|
rlm@298
|
530 ["("
|
rlm@298
|
531 "-size" "113x357" "xc:transparent"
|
rlm@298
|
532 (generate-top-finger
|
rlm@298
|
533 tip-flexor tip-extensor tip
|
rlm@298
|
534 joint-2-3
|
rlm@298
|
535 mid-flexor mid-extensor mid
|
rlm@298
|
536 joint-1-2) "-geometry" "+0+0" "-composite"
|
rlm@298
|
537 (prepare-muscle base-flexor) "-geometry" "+19+245" "-composite"
|
rlm@298
|
538 (prepare-muscle base-extensor) "-geometry" "+80+245" "-composite"
|
rlm@298
|
539 (prepare-touch base) "-geometry" "+39+247" "-composite"
|
rlm@298
|
540 joint-palm-1 "-geometry" "+32+307" "-composite"
|
rlm@298
|
541 ")"])
|
rlm@298
|
542
|
rlm@298
|
543 (defn generate-thumb
|
rlm@298
|
544 [tip-flexor tip-extensor tip
|
rlm@298
|
545 joint-1-2
|
rlm@298
|
546 mid-flexor mid-extensor mid-flexor-2 mid-extensor-2 mid
|
rlm@298
|
547 joint-palm-1]
|
rlm@298
|
548 ["("
|
rlm@298
|
549 "-size" "113x357" "xc:transparent"
|
rlm@298
|
550 (generate-top-finger
|
rlm@298
|
551 tip-flexor tip-extensor tip
|
rlm@298
|
552 joint-1-2
|
rlm@298
|
553 mid-flexor mid-extensor mid
|
rlm@298
|
554 joint-palm-1) "-geometry" "+0+0" "-composite"
|
rlm@298
|
555 (prepare-muscle mid-flexor-2) "-geometry" "+2+131" "-composite"
|
rlm@298
|
556 (prepare-muscle mid-extensor-2) "-geometry" "+100+131" "-composite"
|
rlm@298
|
557 ")"])
|
rlm@298
|
558
|
rlm@298
|
559 (defn generate-hand
|
rlm@298
|
560 [pinky-pieces
|
rlm@298
|
561 ring-pieces
|
rlm@298
|
562 middle-pieces
|
rlm@298
|
563 pointer-pieces
|
rlm@298
|
564 thumb-pieces]
|
rlm@298
|
565 ["("
|
rlm@298
|
566 "-size" "688x769" "xc:transparent"
|
rlm@298
|
567 (apply generate-finger pinky-pieces)
|
rlm@298
|
568 "-geometry" "+0+195" "-composite"
|
rlm@298
|
569 (apply generate-finger ring-pieces)
|
rlm@298
|
570 "-geometry" "+111+100" "-composite"
|
rlm@298
|
571 (apply generate-finger middle-pieces)
|
rlm@298
|
572 "-geometry" "+228+0" "-composite"
|
rlm@298
|
573 "(" (apply generate-thumb thumb-pieces) "-background" "#00000000"
|
rlm@298
|
574 "-rotate" "45" ")"
|
rlm@298
|
575 "-geometry" "+300+420" "-composite"
|
rlm@298
|
576 (apply generate-finger pointer-pieces)
|
rlm@298
|
577 "-geometry" "+350+96" "-composite"
|
rlm@298
|
578 ")"])
|
rlm@298
|
579
|
rlm@298
|
580 (defn generate-vision
|
rlm@298
|
581 [all green blue red]
|
rlm@298
|
582 ["("
|
rlm@298
|
583 "-size" "204x192" "xc:transparent"
|
rlm@298
|
584 all "-geometry" "+0+0" "-composite"
|
rlm@298
|
585 green "-geometry" "+113+0" "-composite"
|
rlm@298
|
586 blue "-geometry" "+0+105" "-composite"
|
rlm@298
|
587 red "-geometry" "+113+105" "-composite"
|
rlm@298
|
588 ")"])
|
rlm@298
|
589
|
rlm@298
|
590 (def test-muscle (File. base "muscle/0/0000000.png"))
|
rlm@298
|
591 (def test-proprio (File. base "proprio/0/0000000.png"))
|
rlm@298
|
592 (def test-tip (File. base "touch/2/0000000.png"))
|
rlm@298
|
593 (def test-mid (File. base "touch/0/0000000.png"))
|
rlm@298
|
594 (def test-vision (File. base "vision/0/0000000.png"))
|
rlm@298
|
595 (def test-hearing (File. base "hearing/0/0000000.png"))
|
rlm@298
|
596 (def test-main (File. base "main/0000000.png"))
|
rlm@298
|
597
|
rlm@298
|
598 (def test-target (File. base "output.png"))
|
rlm@298
|
599
|
rlm@298
|
600 (def background (File. base "background.png"))
|
rlm@298
|
601
|
rlm@316
|
602 (use 'clojure.java.shell)
|
rlm@299
|
603
|
rlm@299
|
604 (defn vision []
|
rlm@299
|
605 (file-groups
|
rlm@299
|
606 "vision/0"
|
rlm@299
|
607 "vision/1"
|
rlm@299
|
608 "vision/2"
|
rlm@299
|
609 "vision/3"))
|
rlm@299
|
610
|
rlm@299
|
611 (defn hearing []
|
rlm@299
|
612 (file-names (File. base "hearing/0")))
|
rlm@299
|
613
|
rlm@299
|
614 (defn main []
|
rlm@299
|
615 (file-names (File. base "main")))
|
rlm@299
|
616
|
rlm@300
|
617 (defn targets [dest max]
|
rlm@299
|
618 (map
|
rlm@299
|
619 (comp #(.getCanonicalPath %)
|
rlm@300
|
620 #(File. (str base dest (format "%07d.png" %))))
|
rlm@300
|
621 (range max)))
|
rlm@299
|
622
|
rlm@299
|
623
|
rlm@299
|
624 (defn final-image [main [all red green blue] hearing
|
rlm@299
|
625 pinky ring middle pointer thumb target]
|
rlm@299
|
626 (println target)
|
rlm@299
|
627 (apply
|
rlm@298
|
628 sh
|
rlm@298
|
629 (flatten
|
rlm@298
|
630 ["convert"
|
rlm@298
|
631 (.getCanonicalPath background)
|
rlm@299
|
632
|
rlm@299
|
633 (generate-hand pinky ring middle pointer thumb)
|
rlm@298
|
634 "-geometry" "+809+22" "-composite"
|
rlm@298
|
635
|
rlm@299
|
636 (generate-vision all red green blue)
|
rlm@298
|
637 "-geometry" "+974+599" "-composite"
|
rlm@298
|
638
|
rlm@298
|
639 hearing
|
rlm@298
|
640 "-geometry" "+784+819" "-composite"
|
rlm@298
|
641
|
rlm@298
|
642 main
|
rlm@298
|
643 "-geometry" "+78+202" "-composite"
|
rlm@298
|
644
|
rlm@299
|
645 target])))
|
rlm@298
|
646
|
rlm@300
|
647 (defn combine-files []
|
rlm@299
|
648 (dorun
|
rlm@299
|
649 (pmap final-image
|
rlm@299
|
650 (main)
|
rlm@299
|
651 (vision)
|
rlm@299
|
652 (hearing)
|
rlm@299
|
653 (pinky)
|
rlm@299
|
654 (ring)
|
rlm@299
|
655 (middle)
|
rlm@299
|
656 (pointer)
|
rlm@299
|
657 (thumb)
|
rlm@300
|
658 (targets "/out/" (count (main))))))
|
rlm@300
|
659
|
rlm@300
|
660 (defn subtitles []
|
rlm@300
|
661 (file-names (File. base "subs")))
|
rlm@300
|
662
|
rlm@300
|
663 (defn outs []
|
rlm@300
|
664 (file-names (File. base "out")))
|
rlm@300
|
665
|
rlm@300
|
666
|
rlm@300
|
667 (defn mix-subtitles []
|
rlm@300
|
668 (let [subs (subtitles)
|
rlm@300
|
669 targets (targets "/out-subs/" (count subs))
|
rlm@300
|
670 overlay (.getCanonicalPath (File. base "output.png"))]
|
rlm@300
|
671 (dorun
|
rlm@300
|
672 (pmap
|
rlm@300
|
673 (fn [sub target]
|
rlm@300
|
674 (sh
|
rlm@300
|
675 "convert"
|
rlm@300
|
676 overlay
|
rlm@300
|
677 sub "-geometry" "+0+0" "-composite"
|
rlm@300
|
678 target))
|
rlm@300
|
679 subs targets))))
|
rlm@300
|
680
|
rlm@300
|
681 (defn out-subtitles []
|
rlm@300
|
682 (file-names (File. base "out-subs")))
|
rlm@300
|
683
|
rlm@300
|
684
|
rlm@300
|
685 (defn insert-subtitles []
|
rlm@300
|
686 (let [subtitles (out-subtitles)
|
rlm@300
|
687 outs (outs)
|
rlm@300
|
688 targets (targets
|
rlm@300
|
689 "/final/"
|
rlm@300
|
690 (+ (count outs) (count subtitles)))]
|
rlm@300
|
691 (dorun
|
rlm@300
|
692 (pmap
|
rlm@300
|
693 #(sh "cp" %1 %2)
|
rlm@300
|
694 (concat subtitles outs) targets))))
|
rlm@300
|
695
|
rlm@300
|
696 (defn generate-final []
|
rlm@300
|
697 (combine-files)
|
rlm@300
|
698 (mix-subtitles)
|
rlm@300
|
699 (insert-subtitles))
|
rlm@299
|
700 #+end_src
|
rlm@298
|
701
|
rlm@300
|
702 #+begin_src sh :results silent
|
rlm@299
|
703 cd /home/r/proj/cortex/render/hand
|
rlm@298
|
704
|
rlm@300
|
705 sox --ignore-length main.wav main-delayed.wav delay 24
|
rlm@300
|
706
|
rlm@300
|
707 mogrify -resize 755x final/*
|
rlm@312
|
708 #+end_src
|
rlm@312
|
709
|
rlm@312
|
710 #+begin_src sh :results silent
|
rlm@312
|
711 cd /home/r/proj/cortex/render/hand
|
rlm@300
|
712
|
rlm@300
|
713 ffmpeg -r 60 -i final/%07d.png -i main-delayed.wav -b:a 128k \
|
rlm@299
|
714 -b:v 9000k -c:a libvorbis -c:v libtheora hand.ogg
|
rlm@87
|
715 #+end_src
|
rlm@83
|
716
|
rlm@301
|
717 * Source Listing
|
rlm@301
|
718 - [[../src/cortex/test/integration.clj][cortex.test.integration]]
|
rlm@302
|
719 - [[../src/cortex/video/magick8.clj][cortex.video.magick8]]
|
rlm@301
|
720 - [[../assets/Models/subtitles/hand.blend][subtitles/hand.blend]]
|
rlm@301
|
721 #+html: <ul> <li> <a href="../org/integration.org">This org file</a> </li> </ul>
|
rlm@301
|
722 - [[http://hg.bortreb.com ][source-repository]]
|
rlm@301
|
723
|
rlm@301
|
724
|
rlm@78
|
725 * COMMENT purgatory
|
rlm@78
|
726 #+begin_src clojure
|
rlm@77
|
727 (defn bullet-trans* []
|
rlm@77
|
728 (let [obj-a (box 1.5 0.5 0.5 :color ColorRGBA/Red
|
rlm@77
|
729 :position (Vector3f. 5 0 0)
|
rlm@77
|
730 :mass 90)
|
rlm@77
|
731 obj-b (sphere 0.5 :color ColorRGBA/Blue
|
rlm@77
|
732 :position (Vector3f. -5 0 0)
|
rlm@77
|
733 :mass 0)
|
rlm@77
|
734 control-a (.getControl obj-a RigidBodyControl)
|
rlm@77
|
735 control-b (.getControl obj-b RigidBodyControl)
|
rlm@77
|
736 move-up? (atom nil)
|
rlm@77
|
737 move-down? (atom nil)
|
rlm@77
|
738 move-left? (atom nil)
|
rlm@77
|
739 move-right? (atom nil)
|
rlm@77
|
740 roll-left? (atom nil)
|
rlm@77
|
741 roll-right? (atom nil)
|
rlm@77
|
742 force 100
|
rlm@77
|
743 swivel
|
rlm@77
|
744 (.toRotationMatrix
|
rlm@77
|
745 (doto (Quaternion.)
|
rlm@77
|
746 (.fromAngleAxis (/ Math/PI 2)
|
rlm@77
|
747 Vector3f/UNIT_X)))
|
rlm@77
|
748 x-move
|
rlm@77
|
749 (doto (Matrix3f.)
|
rlm@77
|
750 (.fromStartEndVectors Vector3f/UNIT_X
|
rlm@77
|
751 (.normalize (Vector3f. 1 1 0))))
|
rlm@77
|
752
|
rlm@77
|
753 timer (atom 0)]
|
rlm@77
|
754 (doto
|
rlm@77
|
755 (ConeJoint.
|
rlm@77
|
756 control-a control-b
|
rlm@77
|
757 (Vector3f. -8 0 0)
|
rlm@77
|
758 (Vector3f. 2 0 0)
|
rlm@77
|
759 ;;swivel swivel
|
rlm@77
|
760 ;;Matrix3f/IDENTITY Matrix3f/IDENTITY
|
rlm@77
|
761 x-move Matrix3f/IDENTITY
|
rlm@77
|
762 )
|
rlm@77
|
763 (.setCollisionBetweenLinkedBodys false)
|
rlm@77
|
764 (.setLimit (* 1 (/ Math/PI 4)) ;; twist
|
rlm@77
|
765 (* 1 (/ Math/PI 4)) ;; swing span in X-Y plane
|
rlm@77
|
766 (* 0 (/ Math/PI 4)))) ;; swing span in Y-Z plane
|
rlm@77
|
767 (world (nodify
|
rlm@77
|
768 [obj-a obj-b])
|
rlm@77
|
769 (merge standard-debug-controls
|
rlm@77
|
770 {"key-r" (fn [_ pressed?] (reset! move-up? pressed?))
|
rlm@77
|
771 "key-t" (fn [_ pressed?] (reset! move-down? pressed?))
|
rlm@77
|
772 "key-f" (fn [_ pressed?] (reset! move-left? pressed?))
|
rlm@77
|
773 "key-g" (fn [_ pressed?] (reset! move-right? pressed?))
|
rlm@77
|
774 "key-v" (fn [_ pressed?] (reset! roll-left? pressed?))
|
rlm@77
|
775 "key-b" (fn [_ pressed?] (reset! roll-right? pressed?))})
|
rlm@77
|
776
|
rlm@77
|
777 (fn [world]
|
rlm@77
|
778 (enable-debug world)
|
rlm@77
|
779 (set-gravity world Vector3f/ZERO)
|
rlm@77
|
780 )
|
rlm@77
|
781
|
rlm@77
|
782 (fn [world _]
|
rlm@77
|
783
|
rlm@77
|
784 (if @move-up?
|
rlm@77
|
785 (.applyForce control-a
|
rlm@77
|
786 (Vector3f. force 0 0)
|
rlm@77
|
787 (Vector3f. 0 0 0)))
|
rlm@77
|
788 (if @move-down?
|
rlm@77
|
789 (.applyForce control-a
|
rlm@77
|
790 (Vector3f. (- force) 0 0)
|
rlm@77
|
791 (Vector3f. 0 0 0)))
|
rlm@77
|
792 (if @move-left?
|
rlm@77
|
793 (.applyForce control-a
|
rlm@77
|
794 (Vector3f. 0 force 0)
|
rlm@77
|
795 (Vector3f. 0 0 0)))
|
rlm@77
|
796 (if @move-right?
|
rlm@77
|
797 (.applyForce control-a
|
rlm@77
|
798 (Vector3f. 0 (- force) 0)
|
rlm@77
|
799 (Vector3f. 0 0 0)))
|
rlm@77
|
800
|
rlm@77
|
801 (if @roll-left?
|
rlm@77
|
802 (.applyForce control-a
|
rlm@77
|
803 (Vector3f. 0 0 force)
|
rlm@77
|
804 (Vector3f. 0 0 0)))
|
rlm@77
|
805 (if @roll-right?
|
rlm@77
|
806 (.applyForce control-a
|
rlm@77
|
807 (Vector3f. 0 0 (- force))
|
rlm@77
|
808 (Vector3f. 0 0 0)))
|
rlm@77
|
809
|
rlm@77
|
810 (if (zero? (rem (swap! timer inc) 100))
|
rlm@77
|
811 (.attachChild
|
rlm@77
|
812 (.getRootNode world)
|
rlm@77
|
813 (sphere 0.05 :color ColorRGBA/Yellow
|
rlm@77
|
814 :physical? false :position
|
rlm@77
|
815 (.getWorldTranslation obj-a)))))
|
rlm@77
|
816 )
|
rlm@77
|
817 ))
|
rlm@77
|
818
|
rlm@106
|
819 (defn test-joint [joint]
|
rlm@106
|
820 (let [[origin top bottom floor] (world-setup joint)
|
rlm@106
|
821 control (.getControl top RigidBodyControl)
|
rlm@106
|
822 move-up? (atom false)
|
rlm@106
|
823 move-down? (atom false)
|
rlm@106
|
824 move-left? (atom false)
|
rlm@106
|
825 move-right? (atom false)
|
rlm@106
|
826 roll-left? (atom false)
|
rlm@106
|
827 roll-right? (atom false)
|
rlm@106
|
828 timer (atom 0)]
|
rlm@106
|
829
|
rlm@106
|
830 (world
|
rlm@106
|
831 (nodify [top bottom floor origin])
|
rlm@106
|
832 (merge standard-debug-controls
|
rlm@106
|
833 {"key-r" (fn [_ pressed?] (reset! move-up? pressed?))
|
rlm@106
|
834 "key-t" (fn [_ pressed?] (reset! move-down? pressed?))
|
rlm@106
|
835 "key-f" (fn [_ pressed?] (reset! move-left? pressed?))
|
rlm@106
|
836 "key-g" (fn [_ pressed?] (reset! move-right? pressed?))
|
rlm@106
|
837 "key-v" (fn [_ pressed?] (reset! roll-left? pressed?))
|
rlm@106
|
838 "key-b" (fn [_ pressed?] (reset! roll-right? pressed?))})
|
rlm@106
|
839
|
rlm@106
|
840 (fn [world]
|
rlm@106
|
841 (light-up-everything world)
|
rlm@106
|
842 (enable-debug world)
|
rlm@106
|
843 (set-gravity world (Vector3f. 0 0 0))
|
rlm@106
|
844 )
|
rlm@106
|
845
|
rlm@106
|
846 (fn [world _]
|
rlm@106
|
847 (if (zero? (rem (swap! timer inc) 100))
|
rlm@106
|
848 (do
|
rlm@106
|
849 ;; (println-repl @timer)
|
rlm@106
|
850 (.attachChild (.getRootNode world)
|
rlm@106
|
851 (sphere 0.05 :color ColorRGBA/Yellow
|
rlm@106
|
852 :position (.getWorldTranslation top)
|
rlm@106
|
853 :physical? false))
|
rlm@106
|
854 (.attachChild (.getRootNode world)
|
rlm@106
|
855 (sphere 0.05 :color ColorRGBA/LightGray
|
rlm@106
|
856 :position (.getWorldTranslation bottom)
|
rlm@106
|
857 :physical? false))))
|
rlm@106
|
858
|
rlm@106
|
859 (if @move-up?
|
rlm@106
|
860 (.applyTorque control
|
rlm@106
|
861 (.mult (.getPhysicsRotation control)
|
rlm@106
|
862 (Vector3f. 0 0 10))))
|
rlm@106
|
863 (if @move-down?
|
rlm@106
|
864 (.applyTorque control
|
rlm@106
|
865 (.mult (.getPhysicsRotation control)
|
rlm@106
|
866 (Vector3f. 0 0 -10))))
|
rlm@106
|
867 (if @move-left?
|
rlm@106
|
868 (.applyTorque control
|
rlm@106
|
869 (.mult (.getPhysicsRotation control)
|
rlm@106
|
870 (Vector3f. 0 10 0))))
|
rlm@106
|
871 (if @move-right?
|
rlm@106
|
872 (.applyTorque control
|
rlm@106
|
873 (.mult (.getPhysicsRotation control)
|
rlm@106
|
874 (Vector3f. 0 -10 0))))
|
rlm@106
|
875 (if @roll-left?
|
rlm@106
|
876 (.applyTorque control
|
rlm@106
|
877 (.mult (.getPhysicsRotation control)
|
rlm@106
|
878 (Vector3f. -1 0 0))))
|
rlm@106
|
879 (if @roll-right?
|
rlm@106
|
880 (.applyTorque control
|
rlm@106
|
881 (.mult (.getPhysicsRotation control)
|
rlm@106
|
882 (Vector3f. 1 0 0))))))))
|
rlm@99
|
883 #+end_src
|
rlm@192
|
884
|
rlm@99
|
885
|
rlm@306
|
886
|
rlm@99
|
887 * COMMENT generate source
|
rlm@300
|
888 #+begin_src clojure :tangle ../src/cortex/test/integration.clj
|
rlm@192
|
889 <<integration>>
|
rlm@300
|
890 <<integration-2>>
|
rlm@300
|
891 #+end_src
|
rlm@300
|
892
|
rlm@300
|
893 #+begin_src clojure :tangle ../src/cortex/video/magick8.clj
|
rlm@300
|
894 <<magick-8>>
|
rlm@99
|
895 #+end_src
|