Update.
[flatland.git] / universe.cc
1
2 /*
3
4    flatland is a simple 2d physical simulator
5
6    Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
7    Written by Francois Fleuret <francois.fleuret@idiap.ch>
8
9    This file is part of flatland
10
11    flatland is free software: you can redistribute it and/or modify it
12    under the terms of the GNU General Public License version 3 as
13    published by the Free Software Foundation.
14
15    flatland is distributed in the hope that it will be useful, but
16    WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18    General Public License for more details.
19
20    You should have received a copy of the GNU General Public License
21    along with flatland.  If not, see <http://www.gnu.org/licenses/>.
22
23 */
24
25 #include <string.h>
26
27 #include "universe.h"
28
29 Universe::Universe(int nb_max_polygons,
30                    scalar_t width, scalar_t height) : _width(width),
31                                                       _height(height),
32                                                       _nb_max_polygons(nb_max_polygons),
33                                                       _nb_polygons(0) {
34   _polygons = new Polygon *[_nb_max_polygons];
35   for(int n = 0; n < _nb_max_polygons; n++) _polygons[n] = 0;
36 }
37
38 Universe::~Universe() {
39   clear();
40   delete[] _polygons;
41 }
42
43 void Universe::initialize_polygon(Polygon *p) {
44   p->initialize(_nb_max_polygons);
45 }
46
47 void Universe::clear() {
48   for(int n = 0; n < _nb_polygons; n++) if(_polygons[n]) delete _polygons[n];
49   _nb_polygons = 0;
50 }
51
52 void Universe::add_polygon(Polygon *p) {
53   if(_nb_polygons < _nb_max_polygons) {
54     if(!p->_initialized) {
55       cerr << "You can not add a non-initialized polygon." << endl;
56       abort();
57     }
58     _polygons[_nb_polygons++] = p;
59   } else {
60     cerr << "Too many polygons!" << endl;
61     exit(1);
62   }
63 }
64
65 bool Universe::collide_with_borders(Polygon *p, scalar_t padding) {
66   return p->collide_with_borders(padding, padding,_width - padding, _height - padding);
67
68 }
69
70 bool Universe::collide(Polygon *p) {
71   for(int n = 0; n < _nb_polygons; n++)
72     if(_polygons[n] && _polygons[n]->collide(p)) return true;
73
74   return false;
75 }
76
77 void Universe::compute_pseudo_collisions(int nb_axis, int *nb_colliding_axis) {
78   Couple couples[_nb_polygons * 2];
79   int in[_nb_polygons];
80   memset((void *) nb_colliding_axis, 0, _nb_polygons * _nb_polygons * sizeof(int));
81
82   for(int a = 0; a < nb_axis; a++) {
83     scalar_t alpha = M_PI * scalar_t(a) / scalar_t(nb_axis);
84     scalar_t vx = cos(alpha), vy = sin(alpha);
85
86     for(int n = 0; n < _nb_polygons; n++) {
87       scalar_t *x = _polygons[n]->_x, *y = _polygons[n]->_y;
88       scalar_t min = x[0] * vx + y[0] * vy, max = min;
89
90       for(int v = 1; v < _polygons[n]->_nb_vertices; v++) {
91         scalar_t s = x[v] * vx + y[v] * vy;
92         if(s > max) max = s;
93         if(s < min) min = s;
94       }
95
96       couples[2 * n + 0].value = min;
97       couples[2 * n + 0].index = n;
98       couples[2 * n + 1].value = max;
99       couples[2 * n + 1].index = n;
100     }
101
102     qsort((void *) couples, 2 * _nb_polygons, sizeof(Couple), compare_couple);
103
104     int nb_in = 0;
105     memset((void *) in, 0, _nb_polygons * sizeof(int));
106     for(int k = 0; k < 2 * _nb_polygons; k++) {
107       int i = couples[k].index;
108       in[i] = !in[i];
109       if(in[i]) {
110         nb_in++;
111         if(nb_in > 1) {
112           for(int j = 0; j < i; j++)
113             if(j != i && in[j]) nb_colliding_axis[j + i * _nb_polygons]++;
114           for(int j = i+1; j < _nb_polygons; j++)
115             if(j != i && in[j]) nb_colliding_axis[i + j * _nb_polygons]++;
116         }
117       } else nb_in--;
118     }
119   }
120
121   for(int i = 0; i < _nb_polygons; i++) {
122     for(int j = 0; j < i; j++) {
123       if(nb_colliding_axis[j + i * _nb_polygons] > nb_colliding_axis[i + i * _nb_polygons])
124         nb_colliding_axis[i + i * _nb_polygons] = nb_colliding_axis[j + i * _nb_polygons];
125       nb_colliding_axis[i + j * _nb_polygons] = nb_colliding_axis[j + i * _nb_polygons];
126     }
127   }
128 }
129
130 bool Universe::update(scalar_t dt, scalar_t padding) {
131   bool result = false;
132   apply_collision_forces(dt);
133   for(int n = 0; n < _nb_polygons; n++) if(_polygons[n]) {
134       _polygons[n]->apply_border_forces(dt,
135                                         padding, padding,
136                                         _width - padding, _height - padding);
137     result |= _polygons[n]->update(dt);
138   }
139   return result;
140 }
141
142 Polygon *Universe::pick_polygon(scalar_t x, scalar_t y) {
143   for(int n = 0; n < _nb_polygons; n++)
144     if(_polygons[n] && _polygons[n]->contain(x, y)) return _polygons[n];
145   return 0;
146 }
147
148 void Universe::draw(Canvas *canvas) {
149   for(int n = 0; n < _nb_polygons; n++) {
150     if(_polygons[n]) {
151       _polygons[n]->draw(canvas);
152     }
153   }
154
155   for(int n = 0; n < _nb_polygons; n++) {
156     if(_polygons[n]) {
157       _polygons[n]->draw_contours(canvas);
158     }
159   }
160 }
161
162 void Universe::apply_gravity(scalar_t dt, scalar_t fx, scalar_t fy) {
163   for(int n = 0; n < _nb_polygons; n++)
164     if(_polygons[n])
165       _polygons[n]->apply_force(dt,
166                                 _polygons[n]->_center_x, _polygons[n]->_center_y,
167                                 fx, fy);
168 }
169
170 void Universe::apply_collision_forces(scalar_t dt) {
171   const int nb_axis = 2;
172   int nb_collision[_nb_polygons * _nb_polygons];
173
174   compute_pseudo_collisions(nb_axis, nb_collision);
175
176   for(int n = 0; n < _nb_polygons; n++) if(_polygons[n])
177     for(int m = 0; m < _nb_polygons; m++)
178       if(m != n && _polygons[m] && nb_collision[n + _nb_polygons * m] == nb_axis)
179         _polygons[n]->apply_collision_forces(dt, m, _polygons[m]);
180 }