4 flatland is a simple 2d physical simulator
6 Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
7 Written by Francois Fleuret <francois.fleuret@idiap.ch>
9 This file is part of flatland
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.
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.
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/>.
32 static const scalar_t dl = 20.0;
33 static const scalar_t repulsion_constant = 0.2;
34 static const scalar_t speed_max = 1e2;
36 Polygon::Polygon(scalar_t mass,
37 scalar_t red, scalar_t green, scalar_t blue,
38 scalar_t *x, scalar_t *y,
39 int nv) : _mass(mass),
40 _moment_of_inertia(0), _radius(0),
41 _relative_x(new scalar_t[nv]), _relative_y(new scalar_t[nv]),
43 _nb_dots(new int[nv]),
45 _length(new scalar_t[nv]),
46 _triangles(new Triangle[nv-2]),
47 _initialized(false), _nailed(false),
49 _x(new scalar_t[nv]), _y(new scalar_t[nv]),
50 _red(red), _green(green), _blue(blue) {
55 if(x) for(int i = 0; i < nv; i++) _relative_x[i] = x[i];
56 if(y) for(int i = 0; i < nv; i++) _relative_y[i] = y[i];
67 delete[] _effecting_edge;
70 Polygon *Polygon::clone() {
71 return new Polygon(_mass, _red, _green, _blue, _relative_x, _relative_y, _nb_vertices);
75 void Polygon::color_xfig(XFigTracer *tracer) {
76 tracer->add_color(int(255 * _red), int(255 * _green), int(255 * _blue));
79 void Polygon::print_xfig(XFigTracer *tracer) {
80 tracer->draw_polygon(int(255 * _red), int(255 * _green), int(255 * _blue),
81 _nb_vertices, _x, _y);
86 void Polygon::draw(SimpleWindow *window) {
87 window->color(_red, _green, _blue);
88 int x[_nb_vertices], y[_nb_vertices];
89 for(int n = 0; n < _nb_vertices; n++) {
93 window->fill_polygon(_nb_vertices, x, y);
96 void Polygon::draw_contours(SimpleWindow *window) {
97 int x[_nb_vertices], y[_nb_vertices];
98 for(int n = 0; n < _nb_vertices; n++) {
102 window->color(0.0, 0.0, 0.0);
103 // window->color(1.0, 1.0, 1.0);
104 for(int n = 0; n < _nb_vertices; n++) {
105 window->draw_line(x[n], y[n], x[(n+1)%_nb_vertices], y[(n+1)%_nb_vertices]);
110 void Polygon::draw(Canvas *canvas) {
111 canvas->set_drawing_color(_red, _green, _blue);
112 canvas->draw_polygon(1, _nb_vertices, _x, _y);
115 void Polygon::draw_contours(Canvas *canvas) {
116 canvas->set_drawing_color(0.0, 0.0, 0.0);
117 canvas->draw_polygon(0, _nb_vertices, _x, _y);
120 void Polygon::set_vertex(int k, scalar_t x, scalar_t y) {
125 void Polygon::set_position(scalar_t center_x, scalar_t center_y, scalar_t theta) {
126 _center_x = center_x;
127 _center_y = center_y;
131 void Polygon::set_speed(scalar_t dcenter_x, scalar_t dcenter_y, scalar_t dtheta) {
132 _dcenter_x = dcenter_x;
133 _dcenter_y = dcenter_y;
137 bool Polygon::contain(scalar_t x, scalar_t y) {
138 for(int t = 0; t < _nb_vertices-2; t++) {
139 scalar_t xa = _x[_triangles[t].a], ya = _y[_triangles[t].a];
140 scalar_t xb = _x[_triangles[t].b], yb = _y[_triangles[t].b];
141 scalar_t xc = _x[_triangles[t].c], yc = _y[_triangles[t].c];
142 if(prod_vect(x - xa, y - ya, xb - xa, yb - ya) <= 0 &&
143 prod_vect(x - xb, y - yb, xc - xb, yc - yb) <= 0 &&
144 prod_vect(x - xc, y - yc, xa - xc, ya - yc) <= 0) return true;
149 void Polygon::triangularize(int &nt, int nb, int *index) {
152 if(nt >= _nb_vertices-2) {
153 cerr << "Error type #1 in triangularization." << endl;
157 _triangles[nt].a = index[0];
158 _triangles[nt].b = index[1];
159 _triangles[nt].c = index[2];
163 int best_m = -1, best_n = -1;
164 scalar_t best_split = -1, det, s = -1, t = -1;
166 for(int n = 0; n < nb; n++) for(int m = 0; m < n; m++) if(n > m+1 && m+nb > n+1) {
167 bool no_intersection = true;
168 for(int k = 0; no_intersection & (k < nb); k++)
169 if(k != n && k != m && (k+1)%nb != n && (k+1)%nb != m) {
170 intersection(_relative_x[index[n]], _relative_y[index[n]],
171 _relative_x[index[m]], _relative_y[index[m]],
172 _relative_x[index[k]], _relative_y[index[k]],
173 _relative_x[index[(k+1)%nb]], _relative_y[index[(k+1)%nb]], det, s, t);
174 no_intersection = det == 0 || s < 0 || s > 1 || t < 0 || t > 1;
177 if(no_intersection) {
178 scalar_t a1 = 0, a2 = 0;
179 for(int k = 0; k < nb; k++) if(k >= m && k < n)
180 a1 += prod_vect(_relative_x[index[k]] - _relative_x[index[m]],
181 _relative_y[index[k]] - _relative_y[index[m]],
182 _relative_x[index[k+1]] - _relative_x[index[m]],
183 _relative_y[index[k+1]] - _relative_y[index[m]]);
185 a2 += prod_vect(_relative_x[index[k]] - _relative_x[index[m]],
186 _relative_y[index[k]] - _relative_y[index[m]],
187 _relative_x[index[(k+1)%nb]] - _relative_x[index[m]],
188 _relative_y[index[(k+1)%nb]] - _relative_y[index[m]]);
190 if((a1 * a2 > 0 && best_split < 0) || (abs(a1 - a2) < best_split)) {
191 best_n = n; best_m = m;
192 best_split = abs(a1 - a2);
197 if(best_n >= 0 && best_m >= 0) {
198 int index_neg[nb], index_pos[nb];
199 int neg = 0, pos = 0;
200 for(int k = 0; k < nb; k++) {
201 if(k >= best_m && k <= best_n) index_pos[pos++] = index[k];
202 if(k <= best_m || k >= best_n) index_neg[neg++] = index[k];
204 if(pos < 3 || neg < 3) {
205 cerr << "Error type #2 in triangularization." << endl;
208 triangularize(nt, pos, index_pos);
209 triangularize(nt, neg, index_neg);
211 cerr << "Error type #3 in triangularization." << endl;
217 void Polygon::initialize(int nb_polygons) {
220 _nb_polygons = nb_polygons;
222 a = _relative_x[_nb_vertices - 1] * _relative_y[0]
223 - _relative_x[0] * _relative_y[_nb_vertices - 1];
225 for(int n = 0; n < _nb_vertices - 1; n++)
226 a += _relative_x[n] * _relative_y[n+1] - _relative_x[n+1] * _relative_y[n];
229 // Reorder the vertices
234 for(int n = 0; n < _nb_vertices / 2; n++) {
237 _relative_x[n] = _relative_x[_nb_vertices - 1 - n];
238 _relative_y[n] = _relative_y[_nb_vertices - 1 - n];
239 _relative_x[_nb_vertices - 1 - n] = x;
240 _relative_y[_nb_vertices - 1 - n] = y;
244 // Compute the center of mass and moment of inertia
250 for(int n = 0; n < _nb_vertices; n++) {
251 int np = (n+1)%_nb_vertices;
252 w =_relative_x[n] * _relative_y[np] - _relative_x[np] * _relative_y[n];
253 sx += (_relative_x[n] + _relative_x[np]) * w;
254 sy += (_relative_y[n] + _relative_y[np]) * w;
260 for(int n = 0; n < _nb_vertices; n++) {
261 _relative_x[n] -= sx;
262 _relative_y[n] -= sy;
263 scalar_t r = sqrt(sq(_relative_x[n]) + sq(_relative_y[n]));
264 if(r > _radius) _radius = r;
267 scalar_t num = 0, den = 0;
268 for(int n = 0; n < _nb_vertices; n++) {
269 int np = (n+1)%_nb_vertices;
270 den += abs(prod_vect(_relative_x[np], _relative_y[np], _relative_x[n], _relative_y[n]));
271 num += abs(prod_vect(_relative_x[np], _relative_y[np], _relative_x[n], _relative_y[n])) *
272 (_relative_x[np] * _relative_x[np] + _relative_y[np] * _relative_y[np] +
273 _relative_x[np] * _relative_x[n] + _relative_y[np] * _relative_y[n] +
274 _relative_x[n] * _relative_x[n] + _relative_y[n] * _relative_y[n]);
277 _moment_of_inertia = num / (6 * den);
279 scalar_t vx = cos(_theta), vy = sin(_theta);
281 for(int n = 0; n < _nb_vertices; n++) {
282 _x[n] = _center_x + _relative_x[n] * vx + _relative_y[n] * vy;
283 _y[n] = _center_y - _relative_x[n] * vy + _relative_y[n] * vx;
288 for(int n = 0; n < _nb_vertices; n++) {
289 length = sqrt(sq(_relative_x[n] - _relative_x[(n+1)%_nb_vertices]) +
290 sq(_relative_y[n] - _relative_y[(n+1)%_nb_vertices]));
292 _nb_dots[n] = int(length / dl + 1);
293 _total_nb_dots += _nb_dots[n];
296 delete[] _effecting_edge;
297 _effecting_edge = new int[_nb_polygons * _total_nb_dots];
298 for(int p = 0; p < _nb_polygons * _total_nb_dots; p++) _effecting_edge[p] = -1;
301 int index[_nb_vertices];
302 for(int n = 0; n < _nb_vertices; n++) index[n] = n;
303 triangularize(nt, _nb_vertices, index);
308 bool Polygon::update(scalar_t dt) {
309 scalar_t speed = sqrt(_dcenter_x * _dcenter_x + _dcenter_y * _dcenter_y);
310 scalar_t speed_target = speed_max - exp(-speed / speed_max) * speed_max;
312 _dcenter_x = speed_target * _dcenter_x / speed;
313 _dcenter_y = speed_target * _dcenter_y / speed;
316 _center_x += _dcenter_x * dt;
317 _center_y += _dcenter_y * dt;
318 _theta += _dtheta * dt;
321 scalar_t vx = cos(_theta), vy = sin(_theta);
323 for(int n = 0; n < _nb_vertices; n++) {
324 _x[n] = _center_x + _relative_x[n] * vx + _relative_y[n] * vy;
325 _y[n] = _center_y - _relative_x[n] * vy + _relative_y[n] * vx;
328 if(abs(_center_x - _last_center_x) +
329 abs(_center_y - _last_center_y) +
330 abs(_theta - _last_theta) * _radius > 0.1) {
331 _last_center_x = _center_x;
332 _last_center_y = _center_y;
333 _last_theta = _theta;
338 scalar_t Polygon::relative_x(scalar_t ax, scalar_t ay) {
339 return (ax - _center_x) * cos(_theta) - (ay - _center_y) * sin(_theta);
342 scalar_t Polygon::relative_y(scalar_t ax, scalar_t ay) {
343 return (ax - _center_x) * sin(_theta) + (ay - _center_y) * cos(_theta);
346 scalar_t Polygon::absolute_x(scalar_t rx, scalar_t ry) {
347 return _center_x + rx * cos(_theta) + ry * sin(_theta);
350 scalar_t Polygon::absolute_y(scalar_t rx, scalar_t ry) {
351 return _center_y - rx * sin(_theta) + ry * cos(_theta);
354 void Polygon::apply_force(scalar_t dt, scalar_t x, scalar_t y, scalar_t fx, scalar_t fy) {
355 _dcenter_x += fx / _mass * dt;
356 _dcenter_y += fy / _mass * dt;
357 _dtheta -= prod_vect(x - _center_x, y - _center_y, fx, fy) / (_mass * _moment_of_inertia) * dt;
360 void Polygon::apply_border_forces(scalar_t dt,
361 scalar_t xmin, scalar_t ymin,
362 scalar_t xmax, scalar_t ymax) {
363 for(int v = 0; v < _nb_vertices; v++) {
364 int vp = (v+1)%_nb_vertices;
365 for(int d = 0; d < _nb_dots[v]; d++) {
366 scalar_t s = scalar_t(d * dl)/_length[v];
367 scalar_t x = _x[v] * (1 - s) + _x[vp] * s;
368 scalar_t y = _y[v] * (1 - s) + _y[vp] * s;
369 scalar_t vx = 0, vy = 0;
370 if(x < xmin) vx = x - xmin;
371 else if(x > xmax) vx = x - xmax;
372 if(y < ymin) vy = y - ymin;
373 else if(y > ymax) vy = y - ymax;
374 if(vx != 0 || vy != 0) {
375 // cerr << "apply_border_forces vx = " << vx << " vy = " << vy << endl;
376 apply_force(dt, x, y,
377 - dl * vx * repulsion_constant, - dl * vy * repulsion_constant);
383 void Polygon::apply_collision_forces(scalar_t dt, int n_polygon, Polygon *p) {
384 scalar_t closest_x[_total_nb_dots], closest_y[_total_nb_dots];
385 bool inside[_total_nb_dots];
386 scalar_t distance[_total_nb_dots];
387 int _new_effecting_edge[_total_nb_dots];
391 for(int v = 0; v < _nb_vertices; v++) {
392 int vp = (v+1)%_nb_vertices;
393 scalar_t x = _x[v], y = _y[v], xp = _x[vp], yp = _y[vp];
395 for(int d = 0; d < _nb_dots[v]; d++) {
397 distance[d] = FLT_MAX;
400 // First, we tag the dots located inside the polygon p by looping
401 // through the _nb_vertices - 2 triangles from the decomposition
403 for(int t = 0; t < p->_nb_vertices - 2; t++) {
404 scalar_t min = 0, max = 1;
405 scalar_t xa = p->_x[p->_triangles[t].a], ya = p->_y[p->_triangles[t].a];
406 scalar_t xb = p->_x[p->_triangles[t].b], yb = p->_y[p->_triangles[t].b];
407 scalar_t xc = p->_x[p->_triangles[t].c], yc = p->_y[p->_triangles[t].c];
410 const scalar_t eps = 1e-6;
412 den = prod_vect(xp - x, yp - y, xb - xa, yb - ya);
413 num = prod_vect(xa - x, ya - y, xb - xa, yb - ya);
415 if(num / den < max) max = num / den;
416 } else if(den < -eps) {
417 if(num / den > min) min = num / den;
419 if(num < 0) { min = 1; max = 0; }
422 den = prod_vect(xp - x, yp - y, xc - xb, yc - yb);
423 num = prod_vect(xb - x, yb - y, xc - xb, yc - yb);
425 if(num / den < max) max = num / den;
426 } else if(den < -eps) {
427 if(num / den > min) min = num / den;
429 if(num < 0) { min = 1; max = 0; }
432 den = prod_vect(xp - x, yp - y, xa - xc, ya - yc);
433 num = prod_vect(xc - x, yc - y, xa - xc, ya - yc);
435 if(num / den < max) max = num / den;
436 } else if(den < -eps) {
437 if(num / den > min) min = num / den;
439 if(num < 0) { min = 1; max = 0; }
442 for(int d = 0; d < _nb_dots[v]; d++) {
443 scalar_t s = scalar_t(d * dl)/_length[v];
444 if(s >= min && s <= max) inside[d] = true;
448 // Then, we compute for each dot what is the closest point on
451 for(int m = 0; m < p->_nb_vertices; m++) {
452 int mp = (m+1)%p->_nb_vertices;
453 scalar_t xa = p->_x[m], ya = p->_y[m];
454 scalar_t xb = p->_x[mp], yb = p->_y[mp];
455 scalar_t gamma0 = ((x - xa) * (xb - xa) + (y - ya) * (yb - ya)) / sq(p->_length[m]);
456 scalar_t gamma1 = ((xp - x) * (xb - xa) + (yp - y) * (yb - ya)) / sq(p->_length[m]);
457 scalar_t delta0 = (prod_vect(xb - xa, yb - ya, x - xa, y - ya)) / p->_length[m];
458 scalar_t delta1 = (prod_vect(xb - xa, yb - ya, xp - x, yp - y)) / p->_length[m];
460 for(int d = 0; d < _nb_dots[v]; d++) if(inside[d]) {
461 int r = _effecting_edge[(first_dot + d) * _nb_polygons + n_polygon];
463 // If there is already a spring, we look only at the
464 // vertices next to the current one
466 if(r < 0 || m == r || m == (r+1)%p->_nb_vertices || (m+1)%p->_nb_vertices == r) {
468 scalar_t s = scalar_t(d * dl)/_length[v];
469 scalar_t delta = abs(s * delta1 + delta0);
470 if(delta < distance[d]) {
471 scalar_t gamma = s * gamma1 + gamma0;
473 scalar_t l = sqrt(sq(x * (1 - s) + xp * s - xa) + sq(y * (1 - s) + yp * s - ya));
474 if(l < distance[d]) {
478 _new_effecting_edge[first_dot + d] = m;
480 } else if(gamma > 1) {
481 scalar_t l = sqrt(sq(x * (1 - s) + xp * s - xb) + sq(y * (1 - s) + yp * s - yb));
482 if(l < distance[d]) {
486 _new_effecting_edge[first_dot + d] = m;
490 closest_x[d] = xa * (1 - gamma) + xb * gamma;
491 closest_y[d] = ya * (1 - gamma) + yb * gamma;
492 _new_effecting_edge[first_dot + d] = m;
496 } else _new_effecting_edge[first_dot + d] = -1;
501 for(int d = 0; d < _nb_dots[v]; d++) if(inside[d]) {
502 scalar_t s = scalar_t(d * dl)/_length[v];
503 scalar_t x = _x[v] * (1 - s) + _x[vp] * s;
504 scalar_t y = _y[v] * (1 - s) + _y[vp] * s;
505 scalar_t vx = x - closest_x[d];
506 scalar_t vy = y - closest_y[d];
509 closest_x[d], closest_y[d],
510 dl * vx * repulsion_constant, dl * vy * repulsion_constant);
514 - dl * vx * repulsion_constant, - dl * vy * repulsion_constant);
517 first_dot += _nb_dots[v];
520 for(int d = 0; d < _total_nb_dots; d++)
521 _effecting_edge[d * _nb_polygons + n_polygon] = _new_effecting_edge[d];
525 bool Polygon::collide_with_borders(scalar_t xmin, scalar_t ymin,
526 scalar_t xmax, scalar_t ymax) {
527 for(int n = 0; n < _nb_vertices; n++) {
528 if(_x[n] <= xmin || _x[n] >= xmax || _y[n] <= ymin || _y[n] >= ymax) return true;
533 bool Polygon::collide(Polygon *p) {
534 for(int n = 0; n < _nb_vertices; n++) {
535 int np = (n+1)%_nb_vertices;
536 for(int m = 0; m < p->_nb_vertices; m++) {
537 int mp = (m+1)%p->_nb_vertices;
538 scalar_t det, s = -1, t = -1;
539 intersection(_x[n], _y[n], _x[np], _y[np],
540 p->_x[m], p->_y[m], p->_x[mp], p->_y[mp], det, s, t);
541 if(det != 0 && s>= 0 && s <= 1&& t >= 0 && t <= 1) return true;
545 for(int n = 0; n < _nb_vertices; n++) if(p->contain(_x[n], _y[n])) return true;
546 for(int n = 0; n < p->_nb_vertices; n++) if(contain(p->_x[n], p->_y[n])) return true;