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