Update.
[universe.git] / universe.cc
1
2 // Written and (C) by Francois Fleuret
3 // Contact <francois.fleuret@idiap.ch> for comments & bug reports
4
5 #include <string.h>
6
7 #include "universe.h"
8
9 Universe::Universe(int nb_max_polygons,
10                    scalar_t width, scalar_t height) : _width(width),
11                                                       _height(height),
12                                                       _nb_max_polygons(nb_max_polygons),
13                                                       _nb_polygons(0) {
14   _polygons = new Polygon *[_nb_max_polygons];
15   for(int n = 0; n < _nb_max_polygons; n++) _polygons[n] = 0;
16 }
17
18 Universe::~Universe() {
19   clear();
20   delete[] _polygons;
21 }
22
23 void Universe::initialize_polygon(Polygon *p) {
24   p->initialize(_nb_max_polygons);
25 }
26
27 void Universe::clear() {
28   for(int n = 0; n < _nb_polygons; n++) if(_polygons[n]) delete _polygons[n];
29   _nb_polygons = 0;
30 }
31
32 void Universe::add_polygon(Polygon *p) {
33   if(_nb_polygons < _nb_max_polygons) {
34     if(!p->_initialized) {
35       cerr << "You can not add a non-initialized polygon." << endl;
36       abort();
37     }
38     _polygons[_nb_polygons++] = p;
39   } else {
40     cerr << "Too many polygons!" << endl;
41     exit(1);
42   }
43 }
44
45 bool Universe::collide(Polygon *p) {
46   for(int n = 0; n < _nb_polygons; n++)
47     if(_polygons[n] && _polygons[n]->collide(p)) return true;
48
49   return false;
50 }
51
52 void Universe::compute_pseudo_collisions(int nb_axis, int *nb_colliding_axis) {
53   Couple couples[_nb_polygons * 2];
54   int in[_nb_polygons];
55   memset((void *) nb_colliding_axis, 0, _nb_polygons * _nb_polygons * sizeof(int));
56
57   for(int a = 0; a < nb_axis; a++) {
58     scalar_t alpha = M_PI * scalar_t(a) / scalar_t(nb_axis);
59     scalar_t vx = cos(alpha), vy = sin(alpha);
60
61     for(int n = 0; n < _nb_polygons; n++) {
62       scalar_t *x = _polygons[n]->_x, *y = _polygons[n]->_y;
63       scalar_t min = x[0] * vx + y[0] * vy, max = min;
64
65       for(int v = 1; v < _polygons[n]->_nb_vertices; v++) {
66         scalar_t s = x[v] * vx + y[v] * vy;
67         if(s > max) max = s;
68         if(s < min) min = s;
69       }
70
71       couples[2 * n + 0].value = min;
72       couples[2 * n + 0].index = n;
73       couples[2 * n + 1].value = max;
74       couples[2 * n + 1].index = n;
75     }
76
77     qsort((void *) couples, 2 * _nb_polygons, sizeof(Couple), compare_couple);
78
79     int nb_in = 0;
80     memset((void *) in, 0, _nb_polygons * sizeof(int));
81     for(int k = 0; k < 2 * _nb_polygons; k++) {
82       int i = couples[k].index;
83       in[i] = !in[i];
84       if(in[i]) {
85         nb_in++;
86         if(nb_in > 1) {
87           for(int j = 0; j < i; j++)
88             if(j != i && in[j]) nb_colliding_axis[j + i * _nb_polygons]++;
89           for(int j = i+1; j < _nb_polygons; j++)
90             if(j != i && in[j]) nb_colliding_axis[i + j * _nb_polygons]++;
91         }
92       } else nb_in--;
93     }
94   }
95
96   for(int i = 0; i < _nb_polygons; i++) {
97     for(int j = 0; j < i; j++) {
98       if(nb_colliding_axis[j + i * _nb_polygons] > nb_colliding_axis[i + i * _nb_polygons])
99         nb_colliding_axis[i + i * _nb_polygons] = nb_colliding_axis[j + i * _nb_polygons];
100       nb_colliding_axis[i + j * _nb_polygons] = nb_colliding_axis[j + i * _nb_polygons];
101     }
102   }
103 }
104
105 bool Universe::update(scalar_t dt) {
106   bool result = false;
107   apply_collision_forces(dt);
108   for(int n = 0; n < _nb_polygons; n++) if(_polygons[n]) {
109     _polygons[n]->apply_border_forces(dt, _width, _height);
110     result |= _polygons[n]->update(dt);
111   }
112   return result;
113 }
114
115 Polygon *Universe::pick_polygon(scalar_t x, scalar_t y) {
116   for(int n = 0; n < _nb_polygons; n++)
117     if(_polygons[n] && _polygons[n]->contain(x, y)) return _polygons[n];
118   return 0;
119 }
120
121 #ifdef XFIG_SUPPORT
122 void Universe::print_xfig(XFigTracer *tracer) {
123   for(int n = 0; n < _nb_polygons; n++) {
124     if(_polygons[n]) {
125       _polygons[n]->color_xfig(tracer);
126     }
127   }
128   for(int n = 0; n < _nb_polygons; n++) {
129     if(_polygons[n]) {
130       _polygons[n]->print_xfig(tracer);
131     }
132   }
133 }
134 #endif
135
136 #ifdef X11_SUPPORT
137 void Universe::draw(SimpleWindow *window) {
138   for(int n = 0; n < _nb_polygons; n++) {
139     if(_polygons[n]) {
140       _polygons[n]->draw(window);
141     }
142   }
143
144   for(int n = 0; n < _nb_polygons; n++) {
145     if(_polygons[n]) {
146       _polygons[n]->draw_contours(window);
147     }
148   }
149 }
150 #endif
151
152 void Universe::draw(Canvas *canvas) {
153   for(int n = 0; n < _nb_polygons; n++) {
154     if(_polygons[n]) {
155       _polygons[n]->draw(canvas);
156     }
157   }
158
159   for(int n = 0; n < _nb_polygons; n++) {
160     if(_polygons[n]) {
161       _polygons[n]->draw_contours(canvas);
162     }
163   }
164 }
165
166 void Universe::apply_collision_forces(scalar_t dt) {
167   const int nb_axis = 2;
168   int nb_collision[_nb_polygons * _nb_polygons];
169
170   compute_pseudo_collisions(nb_axis, nb_collision);
171
172   for(int n = 0; n < _nb_polygons; n++) if(_polygons[n])
173     for(int m = 0; m < _nb_polygons; m++)
174       if(m != n && _polygons[m] && nb_collision[n + _nb_polygons * m] == nb_axis)
175         _polygons[n]->apply_collision_forces(dt, m, _polygons[m]);
176 }