1 /*
2 * Copyright (c) 2012-2014, Bruno Levy
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * * Neither the name of the ALICE Project-Team nor the names of its
14 * contributors may be used to endorse or promote products derived from this
15 * software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 *
29 * If you modify this software, you should include a notice giving the
30 * name of the person performing the modification, the date of modification,
31 * and the reason for such modification.
32 *
33 * Contact: Bruno Levy
34 *
35 * Bruno.Levy@inria.fr
36 * http://www.loria.fr/~levy
37 *
38 * ALICE Project
39 * LORIA, INRIA Lorraine,
40 * Campus Scientifique, BP 239
41 * 54506 VANDOEUVRE LES NANCY CEDEX
42 * FRANCE
43 *
44 */
45
46 #include <geogram_gfx/gui/simple_application.h>
47 #include <geogram/delaunay/delaunay.h>
48 #include <geogram/numerics/predicates.h>
49
50 namespace {
51 using namespace GEO;
52
53 typedef vector<vec2> Polygon;
54
55 /*******************************************************************/
56
57 // http://astronomy.swin.edu.au/~pbourke/geometry/polyarea/
signed_area(const Polygon & P)58 double signed_area(const Polygon& P) {
59 double result = 0 ;
60 for(unsigned int i=0; i<P.size(); i++) {
61 unsigned int j = (i+1) % P.size() ;
62 const vec2& t1 = P[i] ;
63 const vec2& t2 = P[j] ;
64 result += t1.x * t2.y - t2.x * t1.y ;
65 }
66 result /= 2.0 ;
67 return result ;
68 }
69
70 // http://astronomy.swin.edu.au/~pbourke/geometry/polyarea/
centroid(const Polygon & P)71 vec2 centroid(const Polygon& P) {
72 geo_assert(P.size() > 0) ;
73
74 double A = signed_area(P) ;
75
76 if(::fabs(A) < 1e-30) {
77 return P[0] ;
78 }
79
80 double x = 0.0 ;
81 double y = 0.0 ;
82 for(unsigned int i=0; i<P.size(); i++) {
83 unsigned int j = (i+1) % P.size() ;
84 const vec2& t1 = P[i] ;
85 const vec2& t2 = P[j] ;
86 double d = (t1.x * t2.y - t2.x * t1.y) ;
87 x += (t1.x + t2.x) * d ;
88 y += (t1.y + t2.y) * d ;
89 }
90
91 return vec2(
92 x / (6.0 * A),
93 y / (6.0 * A)
94 ) ;
95 }
96
point_is_in_half_plane(const vec2 & p,const vec2 & q1,const vec2 & q2)97 static inline Sign point_is_in_half_plane(
98 const vec2& p, const vec2& q1, const vec2& q2
99 ) {
100 return PCK::orient_2d(q1, q2, p);
101 }
102
intersect_segments(const vec2 & p1,const vec2 & p2,const vec2 & q1,const vec2 & q2,vec2 & result)103 static inline bool intersect_segments(
104 const vec2& p1, const vec2& p2,
105 const vec2& q1, const vec2& q2,
106 vec2& result
107 ) {
108
109 vec2 Vp = p2 - p1;
110 vec2 Vq = q2 - q1;
111 vec2 pq = q1 - p1;
112
113 double a = Vp.x;
114 double b = -Vq.x;
115 double c = Vp.y;
116 double d = -Vq.y;
117
118 double delta = a*d-b*c;
119 if(delta == 0.0) {
120 return false ;
121 }
122
123 double tp = (d * pq.x -b * pq.y) / delta;
124
125 result = vec2(
126 (1.0 - tp) * p1.x + tp * p2.x,
127 (1.0 - tp) * p1.y + tp * p2.y
128 );
129
130 return true;
131 }
132
clip_polygon_by_half_plane(const Polygon & P,const vec2 & q1,const vec2 & q2,Polygon & result)133 void clip_polygon_by_half_plane(
134 const Polygon& P,
135 const vec2& q1,
136 const vec2& q2,
137 Polygon& result
138 ) {
139 result.clear() ;
140
141 if(P.size() == 0) {
142 return ;
143 }
144
145 if(P.size() == 1) {
146 if(point_is_in_half_plane(P[0], q1, q2)) {
147 result.push_back(P[0]) ;
148 }
149 return ;
150 }
151
152 vec2 prev_p = P[P.size() - 1] ;
153 Sign prev_status = point_is_in_half_plane(
154 prev_p, q1, q2
155 );
156
157 for(unsigned int i=0; i<P.size(); i++) {
158 vec2 p = P[i] ;
159 Sign status = point_is_in_half_plane(
160 p, q1, q2
161 );
162 if(
163 status != prev_status &&
164 status != ZERO &&
165 prev_status != ZERO
166 ) {
167 vec2 intersect ;
168 if(intersect_segments(prev_p, p, q1, q2, intersect)) {
169 result.push_back(intersect) ;
170 }
171 }
172
173 switch(status) {
174 case NEGATIVE:
175 break ;
176 case ZERO:
177 result.push_back(p) ;
178 break ;
179 case POSITIVE:
180 result.push_back(p) ;
181 break ;
182 }
183
184 prev_p = p ;
185 prev_status = status ;
186 }
187 }
188
convex_clip_polygon(const Polygon & P,const Polygon & clip,Polygon & result)189 void convex_clip_polygon(
190 const Polygon& P, const Polygon& clip, Polygon& result
191 ) {
192 Polygon tmp1 = P ;
193 Polygon tmp2 ;
194 Polygon* src = &tmp1 ;
195 Polygon* dst = &tmp2 ;
196 for(unsigned int i=0; i<clip.size(); i++) {
197 unsigned int j = ((i+1) % clip.size()) ;
198 const vec2& p1 = clip[i] ;
199 const vec2& p2 = clip[j] ;
200 clip_polygon_by_half_plane(*src, p1, p2, *dst);
201 std::swap(src, dst) ;
202 }
203 result = *src ;
204 }
205
206 /*******************************************************************/
207
208 #define c1 0.35
209 #define c2 0.5
210 #define c3 1.0
211
212 double color_table[12][3] = {
213 {c3, c2, c2},
214 {c2, c3, c2},
215 {c2, c2, c3},
216 {c2, c3, c3},
217 {c3, c2, c3},
218 {c3, c3, c2},
219
220 {c1, c2, c2},
221 {c2, c1, c2},
222 {c2, c2, c1},
223 {c2, c1, c1},
224 {c1, c2, c1},
225 {c1, c1, c2}
226 };
227
228 int random_color_index_ = 0 ;
229
230
231 float white[4] = {
232 0.8f, 0.8f, 0.3f, 1.0f
233 };
234
glup_random_color()235 void glup_random_color() {
236 glupColor3d(
237 color_table[random_color_index_][0],
238 color_table[random_color_index_][1],
239 color_table[random_color_index_][2]
240 );
241 random_color_index_ = (random_color_index_ + 1) % 12 ;
242 }
243
glup_randomize_colors(int index)244 void glup_randomize_colors(int index) {
245 random_color_index_ = (index % 12) ;
246 }
247
glup_random_color_from_index(int index)248 void glup_random_color_from_index(int index) {
249 if(index >= 0) {
250 glup_randomize_colors(index) ;
251 glup_random_color() ;
252 } else {
253 glupColor4fv(white) ;
254 }
255 }
256
257 /*******************************************************************/
258
259 class Delaunay2dApplication : public SimpleApplication {
260 public:
Delaunay2dApplication()261 Delaunay2dApplication() : SimpleApplication("Delaunay2d") {
262 console_visible_ = false;
263 viewer_properties_visible_ = false;
264 delaunay_ = Delaunay::create(2,"BDEL2d");
265 border_shape_ = index_t(-1);
266 set_border_shape(0);
267 create_random_points(3);
268 point_size_ = 20;
269 edit_ = true;
270 show_Voronoi_cells_ = true;
271 show_Delaunay_triangles_ = true;
272 show_Voronoi_edges_ = true;
273 show_points_ = true;
274 show_border_ = true;
275 picked_point_ = index_t(-1);
276 last_button_ = index_t(-1);
277 add_key_toggle("F10", &edit_);
278 set_2d();
279 start_animation();
280 }
281
282 protected:
283 /**
284 * \brief Updates the Delaunay triangulation with the current
285 * vector of points.
286 */
update_Delaunay()287 void update_Delaunay() {
288 delaunay_->set_vertices(points_.size(), &points_.data()->x);
289 }
290
291 /**
292 * \brief Creates random points.
293 * \details Points are created uniformly in the [0,1]x[0,1]
294 * square
295 * \param[in] nb the number of points to create.
296 */
create_random_points(index_t nb)297 void create_random_points(index_t nb) {
298 for(index_t i=0; i<nb; ++i) {
299 points_.push_back(
300 vec2(0.25, 0.25) +
301 vec2(
302 Numeric::random_float64()*0.5,
303 Numeric::random_float64()*0.5
304 )
305 );
306 }
307 update_Delaunay();
308 }
309
310 /**
311 * \brief Gets the index of the point that matches a given
312 * point.
313 * \details The current GLUP point size is taken into account
314 * to determine the tolerance.
315 * \param[in] p a const reference coordinate to a world-space
316 * point.
317 * \param[in] get_nearest if true, gets the nearest point, else
318 * only gets the point that matches p (up to current value of
319 * point size when reprojected on the screen)
320 * \return the index of the point that corresponds to p if it
321 * exists, or index_t(-1) if no such point exists.
322 */
get_picked_point(const vec2 & p,bool get_nearest=false)323 index_t get_picked_point(const vec2& p, bool get_nearest = false) {
324 if(points_.size() == 0) {
325 return index_t(-1);
326 }
327 double dist_so_far = Numeric::max_float64();
328 index_t nearest = index_t(-1);
329 for(index_t i=0; i<points_.size(); ++i) {
330 double dist = distance2(p, points_[i]);
331 if(dist < dist_so_far) {
332 nearest = i;
333 dist_so_far = dist;
334 }
335 }
336 if(!get_nearest) {
337 vec3 q(points_[nearest].x, points_[nearest].y, 0.0);
338 vec3 p_scr = project(vec3(p.x, p.y, 0.0));
339 vec3 q_scr = project(vec3(q.x, q.y, 0.0));
340 if(distance2(p_scr,q_scr) > 2.0 * double(glupGetPointSize())) {
341 nearest = index_t(-1);
342 }
343 }
344 return nearest;
345 }
346
set_border_as_polygon(index_t nb_sides)347 void set_border_as_polygon(index_t nb_sides) {
348 border_.clear();
349 for(index_t i=0; i<nb_sides; ++i) {
350 double alpha = double(i) * 2.0 * M_PI / double(nb_sides);
351 double s = sin(alpha);
352 double c = cos(alpha);
353 border_.push_back( vec2(0.5*(c + 1.0), 0.5*(s + 1.0)) );
354 }
355 }
356
set_border_shape(index_t shape)357 void set_border_shape(index_t shape) {
358 if(border_shape_ == shape) {
359 return;
360 }
361 border_shape_ = shape;
362 switch(shape) {
363 case 1:
364 set_border_as_polygon(3);
365 break;
366 case 2:
367 set_border_as_polygon(5);
368 break;
369 case 3:
370 set_border_as_polygon(100);
371 break;
372 default:
373 border_.clear();
374 border_.push_back(vec2(0.0, 0.0));
375 border_.push_back(vec2(1.0, 0.0));
376 border_.push_back(vec2(1.0, 1.0));
377 border_.push_back(vec2(0.0, 1.0));
378 break;
379 }
380 }
381
382
383 /**
384 * \brief Draws the border of the domain.
385 */
draw_border()386 void draw_border() {
387 glupSetColor3f(GLUP_FRONT_AND_BACK_COLOR, 0.0, 0.0, 0.0);
388 glupSetMeshWidth(4);
389 glupBegin(GLUP_LINES);
390 for(index_t i=0; i<border_.size(); ++i) {
391 glupVertex(border_[i]);
392 glupVertex(border_[(i+1)%border_.size()]);
393 }
394 glupEnd();
395 }
396
397 /**
398 * \brief Draws the points.
399 */
draw_points()400 void draw_points() {
401 glupEnable(GLUP_LIGHTING);
402 glupSetPointSize(GLfloat(point_size_));
403 glupDisable(GLUP_VERTEX_COLORS);
404 glupSetColor3f(GLUP_FRONT_AND_BACK_COLOR, 0.0f, 1.0f, 1.0f);
405 glupBegin(GLUP_POINTS);
406 for(index_t i=0; i<points_.size(); ++i) {
407 glupVertex(points_[i]);
408 }
409 glupEnd();
410 glupDisable(GLUP_LIGHTING);
411 }
412
413 /**
414 * \brief Draws the Delaunay triangles.
415 */
draw_Delaunay_triangles()416 void draw_Delaunay_triangles() {
417 glupSetColor3f(GLUP_FRONT_AND_BACK_COLOR, 0.7f, 0.7f, 0.7f);
418 glupSetMeshWidth(1);
419 glupBegin(GLUP_LINES);
420 for(index_t c=0; c<delaunay_->nb_cells(); ++c) {
421 const signed_index_t* cell = delaunay_->cell_to_v() + 3*c;
422 for(index_t e=0; e<3; ++e) {
423 signed_index_t v1 = cell[e];
424 signed_index_t v2 = cell[(e+1)%3];
425 glupVertex2dv(delaunay_->vertex_ptr(index_t(v1)));
426 glupVertex2dv(delaunay_->vertex_ptr(index_t(v2)));
427 }
428 }
429 glupEnd();
430 }
431
432
433
434 /**
435 * \brief Gets the circumcenter of a triangle.
436 * \param[in] t the index of the triangle, in 0..delaunay->nb_cells()-1
437 * \return the circumcenter of triangle \p t
438 */
circumcenter(index_t t)439 vec2 circumcenter(index_t t) {
440 signed_index_t v1 = delaunay_->cell_to_v()[3*t];
441 signed_index_t v2 = delaunay_->cell_to_v()[3*t+1];
442 signed_index_t v3 = delaunay_->cell_to_v()[3*t+2];
443 vec2 p1(delaunay_->vertex_ptr(index_t(v1)));
444 vec2 p2(delaunay_->vertex_ptr(index_t(v2)));
445 vec2 p3(delaunay_->vertex_ptr(index_t(v3)));
446 return Geom::triangle_circumcenter(p1,p2,p3);
447 }
448
449 /**
450 * \brief Gets an infinite vertex in the direction normal to an
451 * edge on the boundary of the convex hull.
452 * \param[in] t the index of the triangle, in 0..delaunay->nb_cells()-1
453 * \param[in] e the local index of the edge, in {0,1,2}
454 * \return a point located far away along the direction normal to the
455 * edge \p e of triangle \p t
456 */
infinite_vertex(index_t t,index_t e)457 vec2 infinite_vertex(index_t t, index_t e) {
458 index_t lv1 = (e+1)%3;
459 index_t lv2 = (e+2)%3;
460 index_t v1 = index_t(delaunay_->cell_to_v()[3*t+lv1]);
461 index_t v2 = index_t(delaunay_->cell_to_v()[3*t+lv2]);
462 vec2 p1(delaunay_->vertex_ptr(v1));
463 vec2 p2(delaunay_->vertex_ptr(v2));
464 vec2 n = normalize(p2-p1);
465 n = vec2(n.y, -n.x);
466 return 0.5*(p1+p2)+100000.0*n;
467 }
468
469 /**
470 * \brief Draw the Voronoi edges.
471 */
draw_Voronoi_edges()472 void draw_Voronoi_edges() {
473 glupSetColor3f(GLUP_FRONT_AND_BACK_COLOR, 0.3f, 0.3f, 0.3f);
474 glupSetMeshWidth(2);
475 glupBegin(GLUP_LINES);
476 for(index_t t=0; t<delaunay_->nb_cells(); ++t) {
477 vec2 cc = circumcenter(t);
478 for(index_t e=0; e<3; ++e) {
479 signed_index_t t2 = delaunay_->cell_to_cell()[3*t+e];
480 if(t2 == -1) {
481 glupVertex(cc);
482 glupVertex(infinite_vertex(t,e));
483 } else if(t2 >signed_index_t(t)) {
484 glupVertex(cc);
485 glupVertex(circumcenter(index_t(t2)));
486 }
487 }
488 }
489 glupEnd();
490 }
491
492 /**
493 * \brief Finds the local index of a vertex in a triangle.
494 * \details Throws an assertion failure if the triangle \p t is
495 * not incident to vertex \p v
496 * \param[in] t the triangle, in 0..delaunay->nb_cells()-1
497 * \param[in] v the vertex, in 0..delaunay->nb_vertices()-1
498 * \return the local index of v in t, in {0,1,2}
499 */
find_vertex(index_t t,index_t v)500 index_t find_vertex(index_t t, index_t v) {
501 for(index_t lv=0; lv<3; ++lv) {
502 if(index_t(delaunay_->cell_to_v()[3*t+lv]) == v) {
503 return lv;
504 }
505 }
506 geo_assert_not_reached;
507 }
508
509
510 /**
511 * \brief Gets a Voronoi cell of a vertex
512 * \details The vertex is specified by a triangle and a local index in
513 * the triangle
514 * \param[in] t0 the triangle
515 * \param[in] lv the local index of the vertex in triangle \p t0
516 * \param[out] cell a reference to the Voronoi cell
517 */
get_Voronoi_cell(index_t t0,index_t lv,Polygon & cell)518 void get_Voronoi_cell(index_t t0, index_t lv, Polygon& cell) {
519 cell.resize(0);
520 index_t v = index_t(delaunay_->cell_to_v()[3*t0+lv]);
521 bool on_border = false;
522 index_t t = t0;
523
524 // First, we turn around the vertex v. To do that, we compute
525 // lv, the local index of v in the current triangle. Following
526 // the standard numerotation of a triangle, edge number lv is
527 // not incident to vertex v. The two other edges (lv+1)%3 and
528 // (lv+2)%3 of the triangle are indicent to vertex v. By always
529 // traversing (lv+1)%3, we turn around vertex v.
530 do {
531 index_t e = (lv+1)%3;
532 signed_index_t neigh_t = delaunay_->cell_to_cell()[3*t+e];
533 if(neigh_t == -1) {
534 on_border = true;
535 break;
536 }
537 cell.push_back(circumcenter(t));
538 t = index_t(neigh_t);
539 lv = find_vertex(t,v);
540 } while(t != t0);
541
542
543 // If one traversed edge is on the border of the convex hull, then
544 // we empty the cell, and start turing around the vertex in
545 // the other direction, i.e. by traversing this time
546 // edge (lv+2)%3 until we reach the other edge on the border of
547 // the convex hull that is incident to v.
548 if(on_border) {
549 cell.resize(0);
550 cell.push_back(infinite_vertex(t,(lv + 1)%3));
551 for(;;) {
552 cell.push_back(circumcenter(t));
553 index_t e = (lv+2)%3;
554 signed_index_t neigh_t = delaunay_->cell_to_cell()[3*t+e];
555 if(neigh_t == -1) {
556 cell.push_back(infinite_vertex(t, e));
557 break;
558 }
559 t = index_t(neigh_t);
560 lv = find_vertex(t,v);
561 }
562 }
563
564 Polygon clipped;
565 convex_clip_polygon(cell, border_, clipped);
566 cell.swap(clipped);
567 }
568
569 /**
570 * \brief Draws the Voronoi cells as filled polygons with
571 * random colors.
572 */
draw_Voronoi_cells()573 void draw_Voronoi_cells() {
574 v_visited_.assign(points_.size(), false);
575 Polygon cell;
576 glupEnable(GLUP_VERTEX_COLORS);
577 glupBegin(GLUP_TRIANGLES);
578 for(index_t t=0; t<delaunay_->nb_cells(); ++t) {
579 for(index_t lv=0; lv<3; ++lv) {
580 index_t v = index_t(delaunay_->cell_to_v()[3*t+lv]);
581 if(!v_visited_[v]) {
582 glup_random_color_from_index(int(v));
583 v_visited_[v] = true;
584 get_Voronoi_cell(t,lv,cell);
585 for(index_t i=1; i+1<cell.size(); ++i) {
586 glupVertex(cell[0]);
587 glupVertex(cell[i]);
588 glupVertex(cell[i+1]);
589 }
590 }
591 }
592 }
593 glupEnd();
594 glupDisable(GLUP_VERTEX_COLORS);
595 }
596
597 /**
598 * \brief Draws all the elements of the Delaunay triangulation /
599 * Voronoi diagram.
600 * \details Specified as glup_viewer_set_display_func() callback.
601 */
draw_scene()602 void draw_scene() override {
603 glupDisable(GLUP_LIGHTING);
604 if(show_Voronoi_cells_) {
605 draw_Voronoi_cells();
606 }
607 if(show_Delaunay_triangles_) {
608 draw_Delaunay_triangles();
609 }
610 if(show_Voronoi_edges_) {
611 draw_Voronoi_edges();
612 }
613 if(show_border_) {
614 draw_border();
615 }
616 glupEnable(GLUP_LIGHTING);
617 if(show_points_) {
618 draw_points();
619 }
620 if(animate()) {
621 Lloyd_relaxation();
622 }
623 }
624
Lloyd_relaxation()625 void Lloyd_relaxation() {
626 v_visited_.assign(delaunay_->nb_vertices(),false);
627 new_points_.resize(points_.size());
628 Polygon cell;
629 for(index_t t=0; t<delaunay_->nb_cells(); ++t) {
630 for(index_t lv=0; lv<3; ++lv) {
631 index_t v = index_t(delaunay_->cell_to_v()[3*t+lv]);
632 if(!v_visited_[v]) {
633 v_visited_[v] = true;
634 get_Voronoi_cell(t,lv,cell);
635 if(cell.size() > 0) {
636 new_points_[v] = centroid(cell);
637 } else {
638 new_points_[v] = points_[v];
639 }
640 }
641 }
642 }
643 std::swap(points_, new_points_);
644 update_Delaunay();
645 }
646
draw_object_properties()647 void draw_object_properties() override {
648 SimpleApplication::draw_object_properties();
649 if(ImGui::Button(
650 (icon_UTF8("home") + " Home [H]").c_str(), ImVec2(-1.0, 0.0))
651 ) {
652 home();
653 }
654 ImGui::Checkbox("Edit", &edit_);
655
656 ImGui::Separator();
657 if(ImGui::Button("Relax.")) {
658 Lloyd_relaxation();
659 }
660 ImGui::SameLine();
661 ImGui::Checkbox("animate",animate_ptr());
662 if(ImGui::Button("reset points", ImVec2(-1.0f,0.0f))) {
663 points_.clear();
664 create_random_points(3);
665 update_Delaunay();
666 }
667 ImGui::Text("Bndry");
668 ImGui::SameLine();
669 int new_border_shape = int(border_shape_);
670 ImGui::Combo(
671 "", &new_border_shape,
672 "square\0triangle\0pentagon\0circle\0\0"
673 );
674 set_border_shape(index_t(new_border_shape));
675
676 ImGui::Separator();
677 ImGui::Checkbox("cells", &show_Voronoi_cells_);
678 ImGui::SameLine();
679 ImGui::Checkbox("edges", &show_Voronoi_edges_);
680
681 ImGui::Checkbox("trgls", &show_Delaunay_triangles_);
682 ImGui::SameLine();
683 ImGui::Checkbox("points", &show_points_);
684 }
685
mouse_button_callback(int button,int action,int mods,int source)686 void mouse_button_callback(
687 int button, int action, int mods, int source
688 ) override {
689 geo_argused(source);
690
691 // Hide object and viewer properties if in phone
692 // mode and user clicked elsewhere.
693 if(phone_screen_ &&
694 !ImGui::GetIO().WantCaptureMouse &&
695 get_height() >= get_width()
696 ) {
697 if(!props_pinned_) {
698 object_properties_visible_ = false;
699 viewer_properties_visible_ = false;
700 }
701 }
702
703 if(!edit_) {
704 SimpleApplication::mouse_button_callback(
705 button, action, mods, source
706 );
707 return;
708 }
709
710 if(action != EVENT_ACTION_DOWN) {
711 last_button_ = index_t(-1);
712 return;
713 }
714
715 last_button_ = index_t(button);
716
717 if(animate()) {
718 switch(button) {
719 case 0: {
720 points_.push_back(mouse_point_);
721 picked_point_ = points_.size() - 1;
722 } break;
723 case 1: {
724 picked_point_ = get_picked_point(mouse_point_,true);
725 if(points_.size() > 3) {
726 points_.erase(points_.begin() + int(picked_point_));
727 }
728 } break;
729 }
730 } else {
731 picked_point_ = get_picked_point(mouse_point_);
732 switch(button) {
733 case 0: {
734 if(picked_point_ == index_t(-1)) {
735 points_.push_back(mouse_point_);
736 picked_point_ = points_.size() - 1;
737 }
738 } break;
739 case 1: {
740 if(points_.size() > 3) {
741 if(picked_point_ != index_t(-1)) {
742 points_.erase(
743 points_.begin() + int(picked_point_)
744 );
745 }
746 }
747 picked_point_ = index_t(-1);
748 } break;
749 }
750 }
751 update_Delaunay();
752 }
753
cursor_pos_callback(double x,double y,int source)754 void cursor_pos_callback(double x, double y, int source) override {
755 geo_argused(source);
756 if(!edit_) {
757 SimpleApplication::cursor_pos_callback(x,y,source);
758 return;
759 }
760
761 // For retina displays: x and y are in 'window pixels',
762 // and GLUP project / unproject expect 'framebuffer pixels'.
763 double sx =
764 double(get_frame_buffer_width()) / double(get_width());
765
766 double sy =
767 double(get_frame_buffer_height()) / double(get_height());
768
769 mouse_point_ = unproject_2d(
770 vec2(sx*x, sy*(double(get_height()) - y))
771 );
772 if(animate()) {
773 if(last_button_ == 0) {
774 points_.push_back(mouse_point_);
775 picked_point_ = points_.size() - 1;
776 update_Delaunay();
777 } else if(last_button_ == 1) {
778 picked_point_ = get_picked_point(mouse_point_,true);
779 if(points_.size() > 3) {
780 points_.erase(points_.begin() + int(picked_point_));
781 }
782 update_Delaunay();
783 }
784 } else {
785 if(picked_point_ != index_t(-1) && (last_button_ == 0)) {
786 points_[picked_point_] = mouse_point_;
787 update_Delaunay();
788 }
789 }
790 }
791
792 private:
793 vector<vec2> points_;
794 vector<vec2> new_points_;
795 vector<bool> v_visited_;
796 Delaunay_var delaunay_;
797 Polygon border_;
798 index_t border_shape_;
799 GLint point_size_; /**< The size of all displayed points. */
800
801 bool edit_;
802 bool show_Voronoi_cells_;
803 bool show_Delaunay_triangles_;
804 bool show_Voronoi_edges_;
805 bool show_points_;
806 bool show_border_;
807
808 index_t picked_point_;
809 index_t last_button_;
810 vec2 mouse_point_;
811 };
812
813 }
814
main(int argc,char ** argv)815 int main(int argc, char** argv) {
816 Delaunay2dApplication app;
817 app.start(argc, argv);
818 return 0;
819 }
820