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