Initial commit
[universe.git] / retina.cc
1
2 ////////////////////////////////////////////////////////////////////////////////
3 // This program is free software; you can redistribute it and/or              //
4 // modify it under the terms of the GNU General Public License                //
5 // version 2 as published by the Free Software Foundation.                    //
6 //                                                                            //
7 // This program is distributed in the hope that it will be useful, but        //
8 // WITHOUT ANY WARRANTY; without even the implied warranty of                 //
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU          //
10 // General Public License for more details.                                   //
11 //                                                                            //
12 // Written and (C) by François Fleuret                                        //
13 // Contact <francois.fleuret@epfl.ch> for comments & bug reports              //
14 ////////////////////////////////////////////////////////////////////////////////
15
16 #include "retina.h"
17
18 const int ring_width[] = { 128, 64, 32, 16, 8, 4, 1, 1, 1, 1};
19
20 // const int ring_width[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
21
22 Retina::Retina(Universe *universe) : _universe(universe),
23                                      _tag_map(new Tag *[_width * _height]),
24                                      _tag_stack(new Tag[_width * _height]),
25                                      _tag_stack_top(_tag_stack),
26                                      _red_imap(new scalar_t[(_width + 1) * (_height + 1)]),
27                                      _green_imap(new scalar_t[(_width + 1) * (_height + 1)]),
28                                      _blue_imap(new scalar_t[(_width + 1) * (_height + 1)]) {
29   reset();
30
31   int np = 0;
32   int w = _width;
33   for(int k = 0; k < int(sizeof(ring_width)/sizeof(int)); k++) {
34     np += 4 * (w / ring_width[k] - 1);
35     w -= 2 * ring_width[k];
36   }
37
38   np *= 3;
39   Map::init(np);
40 }
41
42 Retina::~Retina() {
43   delete[] _tag_map;
44   delete[] _tag_stack;
45   delete[] _red_imap;
46   delete[] _green_imap;
47   delete[] _blue_imap;
48 }
49
50 void Retina::reset() {
51   for(int k = 0; k < _width * _height; k++) _tag_map[k] = 0;
52   _tag_stack_top = _tag_stack;
53   _current_polygon = 0;
54 }
55
56 void Retina::draw_polygon(Polygon *p, scalar_t delta_x, scalar_t delta_y) {
57   int nb = p->_nb_vertices;
58
59   int ix[nb], iy[nb];
60
61   for(int n = 0; n < nb; n++) {
62     ix[n] = int(p->_x[n] + delta_x);
63     iy[n] = int(p->_y[n] + delta_y);
64   }
65
66   scalar_t red = p->_red;
67   scalar_t green = p->_green;
68   scalar_t blue = p->_blue;
69
70   int direction;
71   if(iy[0] > iy[nb-1]) direction = 1;
72   else if(iy[0] < iy[nb-1]) direction = -1;
73   else direction = 0;
74
75   for(int n = 0; n < nb; n++) {
76     int m = (n+1)%nb;
77
78     if(ix[n] >= 0 && ix[n] < _width && iy[n] >= 0 && iy[n] < _height &&
79        ix[m] >= 0 && ix[m] < _width && iy[m] >= 0 && iy[m] < _height) {
80
81       if(iy[m] > iy[n]) { // RIGHT BORDERS
82
83         if(direction < 0) push_tag(1 + ix[n], iy[n], red, green, blue);
84         for(int y = iy[n] + 1; y <= iy[m]; y++)
85           push_tag(1 + ix[n] + ((ix[m] - ix[n]) * (y - iy[n]))/(iy[m] - iy[n]), y,
86                    red, green, blue);
87         direction = 1;
88
89       } else if(iy[m] < iy[n]) { // LEFT BORDERS
90
91         if(direction >= 0) push_tag(ix[n], iy[n], red, green, blue);
92         for(int y = iy[n] - 1; y >= iy[m]; y--)
93           push_tag(ix[n] + ((ix[m] - ix[n]) * (y - iy[n]))/(iy[m] - iy[n]), y,
94                    red, green, blue);
95         direction = -1;
96
97       } else {
98
99         if(direction >= 0) push_tag(ix[n]+1, iy[n], red, green, blue);
100         push_tag(ix[m], iy[m], red, green, blue);
101         direction = 0;
102
103       }
104
105     } else {
106
107       if(iy[m] > iy[n]) { // RIGHT BORDERS
108
109         if(direction < 0) protected_push_tag(1 + ix[n], iy[n], red, green, blue);
110         for(int y = iy[n] + 1; y <= iy[m]; y++)
111           protected_push_tag(1 + ix[n] + ((ix[m] - ix[n]) * (y - iy[n]))/(iy[m] - iy[n]), y,
112                              red, green, blue);
113         direction = 1;
114
115       } else if(iy[m] < iy[n]) { // LEFT BORDERS
116
117         if(direction >= 0) protected_push_tag(ix[n], iy[n], red, green, blue);
118         for(int y = iy[n] - 1; y >= iy[m]; y--)
119           protected_push_tag(ix[n] + ((ix[m] - ix[n]) * (y - iy[n]))/(iy[m] - iy[n]), y,
120                              red, green, blue);
121         direction = -1;
122
123       } else {
124
125         if(direction >= 0) protected_push_tag(ix[n]+1, iy[n], red, green, blue);
126         protected_push_tag(ix[m], iy[m], red, green, blue);
127         direction = 0;
128
129       }
130     }
131   }
132
133   _current_polygon++;
134 }
135
136 void Retina::fill() {
137   bool in[_current_polygon];
138   for(int p = 0; p < _current_polygon; p++) in[p] = false;
139
140   scalar_t red[_current_polygon], green[_current_polygon], blue[_current_polygon];
141   Tag **u = _tag_map;
142
143   int lk = 0, k = 0;
144
145   for(int x = 0; x < _width+1; x++) {
146     _red_imap[k] = 0;
147     _green_imap[k] = 0;
148     _blue_imap[k] = 0;
149     k++;
150   }
151
152   int current_index = -1;
153   for(int y = 0; y < _height; y++) {
154     scalar_t sred = 0, sgreen = 0, sblue = 0;
155     _red_imap[k] = 0;
156     _green_imap[k] = 0;
157     _blue_imap[k] = 0;
158     k++; lk++;
159
160     for(int x = 0; x < _width; x++) {
161       for(Tag *t = *u; t; t = t->next) {
162         if(in[t->index]) {
163           in[t->index] = false;
164           if(t->index == current_index) {
165             current_index--;
166             while(current_index >= 0 && !in[current_index]) current_index--;
167           }
168         } else {
169           in[t->index] = true;
170           red[t->index] = t->red;
171           green[t->index] = t->green;
172           blue[t->index] = t->blue;
173           if(t->index > current_index) current_index = t->index;
174         }
175       }
176
177       if(current_index >= 0) {
178         sred += red[current_index];
179         sgreen += green[current_index];
180         sblue += blue[current_index];
181       }
182
183       _red_imap[k] = sred + _red_imap[lk];
184       _green_imap[k] = sgreen + _green_imap[lk];
185       _blue_imap[k] = sblue + _blue_imap[lk];
186
187       k++; lk++;
188       u++;
189     }
190   }
191 }
192
193 void Retina::raw_rectangle(unsigned char *image, int xmin, int ymin, int w, int h,
194                            scalar_t red, scalar_t green, scalar_t blue) {
195   for(int y = ymin; y < ymin + h; y++) for(int x = xmin; x < xmin + w; x++) {
196     image[(y * _width + x) * 3 + 0] = (unsigned char) (255 * red);
197     image[(y * _width + x) * 3 + 1] = (unsigned char) (255 * green);
198     image[(y * _width + x) * 3 + 2] = (unsigned char) (255 * blue);
199   }
200 }
201
202 void Retina::save_as_ppm(char *filename) {
203   unsigned char *image = new unsigned char[_width * _height * 3];
204
205
206   int n = 0;
207   int w = _width;
208
209   for(int k = 0; k < int(sizeof(ring_width)/sizeof(int)); k++) {
210     int s = ring_width[k];
211     int z0 = (_width - w)/2;
212     int z3 = z0 + w;
213     int z2 = z3 - s;
214
215     for(int x = z0; x < z2; x += s) {
216       raw_rectangle(image, x, z0, s, s, parameters[n + 0], parameters[n + 1], parameters[n + 2]);
217       n += 3;
218       raw_rectangle(image, z2, x, s, s, parameters[n + 0], parameters[n + 1], parameters[n + 2]);
219       n += 3;
220       raw_rectangle(image, x + s, z2, s, s, parameters[n + 0], parameters[n + 1], parameters[n + 2]);
221       n += 3;
222       raw_rectangle(image, z0, x + s, s, s, parameters[n + 0], parameters[n + 1], parameters[n + 2]);
223       n += 3;
224     }
225     w -= 2 * s;
226   }
227
228   ofstream out(filename);
229   out << "P6" << endl;
230   out << _width << " " << _height << endl;
231   out << 255 << endl;
232   out.write((char *) image, _width * _height * 3);
233   out.flush();
234   delete[] image;
235 }
236
237 void Retina::update_map() {
238
239   reset();
240   for(int p = 0; p < _universe->_nb_polygons; p++) if(_universe->_polygons[p])
241     draw_polygon(_universe->_polygons[p], scalar_t(_width)/2 - _eye_x, scalar_t(_height)/2 - _eye_y);
242   fill();
243
244   int n = 0;
245   int w = _width;
246
247   for(int k = 0; k < int(sizeof(ring_width)/sizeof(int)); k++) {
248
249     int s = ring_width[k];
250     int z0 = (_width - w)/2;
251     int z1 = z0 + s;
252     int z3 = z0 + w;
253     int z2 = z3 - s;
254
255     for(int x = z0; x < z2; x += s) {
256       parameters[n++] = red_sum(x, z0, x + s + 0, z1 + 0) / scalar_t(s * s);
257       parameters[n++] = green_sum(x, z0, x + s + 0, z1 + 0) / scalar_t(s * s);
258       parameters[n++] = blue_sum(x, z0, x + s + 0, z1 + 0) / scalar_t(s * s);
259
260       parameters[n++] = red_sum(z2, x, z3 + 0, x + s + 0) / scalar_t(s * s);
261       parameters[n++] = green_sum(z2, x, z3 + 0, x + s + 0) / scalar_t(s * s);
262       parameters[n++] = blue_sum(z2, x, z3 + 0, x + s + 0) / scalar_t(s * s);
263
264       parameters[n++] = red_sum(x + s, z2, x + 2 * s + 0, z3 + 0) / scalar_t(s * s);
265       parameters[n++] = green_sum(x + s, z2, x + 2 * s + 0, z3 + 0) / scalar_t(s * s);
266       parameters[n++] = blue_sum(x + s, z2, x + 2 * s + 0, z3 + 0) / scalar_t(s * s);
267
268       parameters[n++] = red_sum(z0, x + s, z1 + 0, x + 2 * s + 0) / scalar_t(s * s);
269       parameters[n++] = green_sum(z0, x + s, z1 + 0, x + 2 * s + 0) / scalar_t(s * s);
270       parameters[n++] = blue_sum(z0, x + s, z1 + 0, x + 2 * s + 0) / scalar_t(s * s);
271     }
272     w -= 2 * s;
273   }
274
275 #ifdef DEBUG
276   if(w != 0) {
277     cerr << "Error in the retina ring sizes!" << endl;
278     abort();
279   }
280 #endif
281
282 }
283
284 void Retina::draw_on_universe(SimpleWindow *window) {
285   window->color(1.0, 1.0, 1.0);
286   int xc = int(_eye_x), yc = int(_eye_y);
287   window->draw_line(xc - _width/2, yc - _height/2, xc + _width/2, yc - _height/2);
288   window->draw_line(xc + _width/2, yc - _height/2, xc + _width/2, yc + _height/2);
289   window->draw_line(xc + _width/2, yc + _height/2, xc - _width/2, yc + _height/2);
290   window->draw_line(xc - _width/2, yc + _height/2, xc - _width/2, yc - _height/2);
291 }
292
293 void Retina::draw_parameters(int x0, int y0, SimpleWindow *window) {
294   int n = 0;
295   int w = _width;
296
297   for(int k = 0; k < int(sizeof(ring_width)/sizeof(int)); k++) {
298     int s = ring_width[k];
299     int z0 = (_width - w)/2;
300     int z3 = z0 + w;
301     int z2 = z3 - s;
302
303     for(int x = z0; x < z2; x += s) {
304       window->color(parameters[n + 0], parameters[n + 1], parameters[n + 2]);
305       window->fill_rectangle(x0 + x, y0 + z0, s, s);
306       n += 3;
307       window->color(parameters[n + 0], parameters[n + 1], parameters[n + 2]);
308       window->fill_rectangle(x0 + z2, y0 + x, s, s);
309       n += 3;
310       window->color(parameters[n + 0], parameters[n + 1], parameters[n + 2]);
311       window->fill_rectangle(x0 + x + s, y0 + z2, s, s);
312       n += 3;
313       window->color(parameters[n + 0], parameters[n + 1], parameters[n + 2]);
314       window->fill_rectangle(x0 + z0, y0 + x + s, s, s);
315       n += 3;
316     }
317     w -= 2 * s;
318   }
319 }