Add patches
[clnl] / src / main / interface.lisp
1 (in-package #:clnl-interface)
2
3 (defvar *patch-size* 13d0)
4
5 (defvar *turtle-list* nil)
6 (defvar *patch-list* nil)
7
8 ; It may be useful to keep windows around
9 (defvar *glut-window-opened* nil)
10 (defvar *dimensions* nil)
11
12 (defvar *colors*
13  '((140 140 140) ; gray       (5)
14    (215 48 39) ; red       (15)
15    (241 105 19) ; orange    (25)
16    (156 109 70) ; brown     (35)
17    (237 237 47) ; yellow    (45)
18    (87 176 58) ; green     (55)
19    (42 209 57) ; lime      (65)
20    (27 158 119) ; turquoise (75)
21    (82 196 196) ; cyan      (85)
22    (43 140 190) ; sky       (95)
23    (50 92 168) ; blue     (105)
24    (123 78 163) ; violet   (115)
25    (166 25 105) ; magenta  (125)
26    (224 126 149) ; pink     (135)
27    (0 0 0) ; black
28    (255 255 255))) ; white
29
30 (defun nl-color->rgb (color)
31  (let*
32   ((step (+ (/ (- (mod (floor (* color 10)) 100) 50) 50.48) 0.012)))
33   (mapcar
34    (lambda (x) (/ (+ x (floor (* (if (< step 0d0) x (- 255 x)) step))) 255))
35    (nth (floor color 10) *colors*))))
36
37 (defun render-scene ()
38  (gl:clear :color-buffer-bit :depth-buffer-bit)
39  (gl:matrix-mode :projection)
40  (gl:load-identity)
41  (gl:ortho -71 71 -71 71 1 5000)
42  (gl:matrix-mode :modelview)
43  (gl:load-identity)
44  (destructuring-bind (turtles patches) (clnl-nvm:current-state)
45   (mapcar
46    (lambda (patch)
47     (let
48      ((color (nl-color->rgb (getf patch :color))))
49      (gl:color (car color) (cadr color) (caddr color)))
50     (gl:with-pushed-matrix
51      (gl:translate (* (getf patch :xcor) *patch-size*) (* (getf patch :ycor) *patch-size*) 0)
52      (gl:translate (floor (* -.5d0 *patch-size*)) (floor (* -.5d0 *patch-size*)) 0)
53      (gl:scale *patch-size* *patch-size* 1)
54      (gl:call-list *patch-list*)))
55    patches)
56   (mapcar
57    (lambda (turtle)
58     (let
59      ((color (nl-color->rgb (getf turtle :color))))
60      (gl:color (car color) (cadr color) (caddr color)))
61     (mapcar
62      (lambda (x-modification y-modification)
63       (gl:with-pushed-matrix
64        (gl:translate (* (getf turtle :xcor) *patch-size*) (* (getf turtle :ycor) *patch-size*) 0)
65        (gl:translate x-modification y-modification 0)
66        (gl:rotate (getf turtle :heading) 0 0 -1)
67        (gl:scale *patch-size* *patch-size* 1)
68        (gl:call-list *turtle-list*)))
69      (list 0 (1- (world-width-in-pixels)) (- (1- (world-width-in-pixels))) 0 0)
70      (list 0 0 0 (1- (world-height-in-pixels)) (- (1- (world-height-in-pixels))))))
71    turtles))
72  (gl:flush))
73
74 (defun display ()
75  (render-scene)
76  (cl-glut:swap-buffers))
77
78 (defun idle ()
79  (cl-glut:post-redisplay))
80
81 (defun close-func ()
82  (sb-ext:exit :abort t))
83
84 (defun reshape (width height)
85  (when (and (/= 0 width) (/= 0 height))
86   (gl:viewport 0 0 width height)))
87
88 (cffi:defcallback display :void () (display))
89 (cffi:defcallback idle :void () (idle))
90 (cffi:defcallback close-func :void () (close-func))
91 (cffi:defcallback reshape :void ((width :int) (height :int)) (reshape width height))
92
93 (defun set-turtle-list ()
94  (setf *turtle-list* (gl:gen-lists 1))
95  (gl:with-new-list (*turtle-list* :compile)
96   (gl:rotate 180 0 0 -1)
97   (gl:scale (/ 1d0 300d0) (/ 1d0 300d0) 1)
98   (gl:translate -150 -150 -4.0)
99   (gl:begin :polygon)
100   (gl:vertex 150 5 0)
101   (gl:vertex 40 250 0)
102   (gl:vertex 150 205 0)
103   (gl:vertex 260 250 0)
104   (gl:end)))
105
106 (defun set-patch-list ()
107  (setf *patch-list* (gl:gen-lists 1))
108  (gl:with-new-list (*patch-list* :compile)
109   (gl:translate 0d0 0d0 -4.0)
110   (gl:begin :polygon)
111   (gl:vertex 0 0 0)
112   (gl:vertex 0 1 0)
113   (gl:vertex 1 1 0)
114   (gl:vertex 1 0 0)
115   (gl:end)))
116
117 (defun initialize (&key dims)
118  "INITIALIZE &key DIMS => RESULT
119
120   DIMS: (:xmin XMIN :xmax XMAX :ymin YMIN :ymax YMAX)
121
122 ARGUMENTS AND VALUES:
123
124   RESULT: undefined
125   XMIN: An integer representing the minimum patch coord in X
126   XMAX: An integer representing the maximum patch coord in X
127   YMIN: An integer representing the minimum patch coord in Y
128   YMAX: An integer representing the maximum patch coord in Y
129
130 DESCRIPTION:
131
132   This is where the initialization of the interface that sits behind
133   the interface lives.  From here, one can go into headless or running
134   mode, but for certain things this interface will still need to act,
135   and also allows for bringing up and taking down of visual elements."
136  (setf *dimensions* dims))
137
138 (defun run ()
139  "RUN => RESULT
140
141 ARGUMENTS AND VALUES:
142
143   RESULT: undefined, should never get here
144
145 DESCRIPTION:
146
147   RUN runs the view in an external window.
148
149   This should be run inside another thread as it starts the glut main-loop.
150   Closing this window will then cause the entire program to terminate."
151  ; I do this because I don't know who or what in the many layers
152  ; is causing the floating point errors, but I definitely don't
153  ; want to investigate until simply ignoring them becomes a problem.
154  (sb-int:with-float-traps-masked (:invalid)
155   (cl-glut:init)
156   (cl-glut:init-window-size
157    (world-width-in-pixels)
158    (world-height-in-pixels))
159   (cl-glut:init-display-mode :double :rgba)
160   (cl-glut:create-window "CLNL Test Window")
161   (setf *glut-window-opened* t)
162   (gl:clear-color 0 0 0 1)
163   (cl-glut:display-func (cffi:get-callback 'display))
164   (glut:reshape-func (cffi:callback reshape))
165   (cl-glut:idle-func (cffi:get-callback 'idle))
166   (cl-glut:close-func (cffi:get-callback 'close-func))
167   (set-turtle-list)
168   (set-patch-list)
169   (cl-glut:main-loop)))
170
171 (defun world-width-in-pixels ()
172  (floor (* *patch-size* (1+ (- (getf *dimensions* :xmax) (getf *dimensions* :xmin))))))
173
174 (defun world-height-in-pixels ()
175  (floor (* *patch-size* (1+ (- (getf *dimensions* :ymax) (getf *dimensions* :ymin))))))
176
177 (defun export-view ()
178  "EXPORT-VIEW => IMAGE-DATA
179
180 ARGUMENTS AND VALUES:
181
182   IMAGE-DATA: A vector, pixel data as returned by opengls readPixels
183
184 DESCRIPTION:
185
186   EXPORT-VIEW returns the current view in raw data of RGBA pixels.
187
188   Each pixel is made up of 4 bytes of data, which an be walked over.  The number
189   of pixels is the current width x height.  Converting to some other image format
190   is a matter of pulling that information out and putting it into whatever format
191   you like.
192
193   This requires opengl to run, but can be used with xvfb in a headless mode."
194  (sb-int:with-float-traps-masked (:invalid)
195   (when (not *glut-window-opened*)
196    (cl-glut:init)
197    (cl-glut:init-window-size 1 1)
198    (cl-glut:create-window "CLNL Test Window")
199    (gl:clear-color 0 0 0 1)
200    (set-turtle-list)
201    (set-patch-list)
202    (setf *glut-window-opened* t))
203   (let
204    ((fbo (first (gl:gen-framebuffers 1)))
205     (render-buf (first (gl:gen-renderbuffers 1)))
206    ;(width
207    ; (floor (* *patch-size* (1+ (-
208    ;                             (getf *dimensions* :ymax)
209    ;                             (getf *dimensions* :ymin))))))
210    ;(height
211    ; (floor (* *patch-size* (1+ (- (getf *world-dims* :xmax) (getf *world-dims* :xmin))))))
212    ; (floor (* *patch-size* (1+ (-
213    ;                            (getf *dimensions* :xmax)
214    ;                            (getf *dimensions* :xmin)))))
215     (width (world-width-in-pixels))  ; Hard coded for now, yay v1 (if you see this comment in a year, please cry for me)
216     (height (world-height-in-pixels)))
217    (gl:bind-framebuffer :framebuffer fbo)
218    (gl:bind-renderbuffer :renderbuffer render-buf)
219    (gl:renderbuffer-storage :renderbuffer :rgba8 width height)
220    (gl:framebuffer-renderbuffer :draw-framebuffer :color-attachment0 :renderbuffer render-buf)
221    (gl:viewport 0 0 width height)
222    (render-scene)
223    (gl:read-pixels 0 0 width height :rgba :unsigned-byte))))