1 /* GTS - Library for the manipulation of triangulated surfaces
2 * Copyright (C) 1999 Stéphane Popinet
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
28 #ifdef USE_SURFACE_BTREE
30 static gint
find_closest (GtsTriangle
* t
, gpointer value
, gpointer
* data
)
38 gdouble
* dmin
= data
[0];
39 gpointer
* closest
= data
[1];
40 GtsPoint
* p
= data
[4];
42 if (gts_triangle_orientation (t
) > 0.) {
43 GtsPoint
* p1
= GTS_POINT (GTS_SEGMENT (t
->e1
)->v1
);
44 gdouble d
= (p
->x
- p1
->x
)*(p
->x
- p1
->x
) + (p
->y
- p1
->y
)*(p
->y
- p1
->y
);
56 /* select the face closest to @p among n^1/3 randomly picked faces
58 static GtsFace
* closest_face (GtsSurface
* s
, GtsPoint
* p
)
61 gdouble dmin
= G_MAXDOUBLE
;
62 GtsFace
* closest
= NULL
;
65 nt
= gts_surface_face_number (s
);
68 ns
= exp (log ((gdouble
) nt
)/3.);
75 g_tree_traverse (s
->faces
, (GTraverseFunc
) find_closest
, G_IN_ORDER
, data
);
80 #else /* not USE_SURFACE_BTREE */
82 typedef struct _SFindClosest SFindClosest
;
84 struct _SFindClosest
{
91 # if GLIB_CHECK_VERSION(2,4,0)
92 /* finally, with g_hash_table_find we are able to stop iteration over the hash
93 table in the middle */
95 static gboolean
find_closest (gpointer key
, gpointer value
, gpointer user_data
)
97 SFindClosest
* data
= (SFindClosest
*) user_data
;
98 GtsFace
* f
= GTS_FACE (value
);
100 if (gts_triangle_orientation (GTS_TRIANGLE (f
)) > 0.) {
101 GtsPoint
* p1
= GTS_POINT (GTS_SEGMENT (GTS_TRIANGLE (f
)->e1
)->v1
);
102 gdouble d
= ((data
->p
->x
- p1
->x
)*(data
->p
->x
- p1
->x
) +
103 (data
->p
->y
- p1
->y
)*(data
->p
->y
- p1
->y
));
105 if (d
< data
->dmin
) {
111 return !(data
->stop
> 0);
114 static GtsFace
* closest_face (GtsSurface
* s
, GtsPoint
* p
)
118 fc
.dmin
= G_MAXDOUBLE
;
121 fc
.stop
= (gint
) exp (log ((gdouble
) g_hash_table_size (s
->faces
))/3.);
122 g_hash_table_find (s
->faces
, find_closest
, &fc
);
127 # else /* VERSION < 2.4.0 */
130 find_closest (gpointer key
, gpointer value
, gpointer user_data
)
132 SFindClosest
* data
= (SFindClosest
*) user_data
;
133 GtsFace
* f
= GTS_FACE (value
);
135 if (gts_triangle_orientation (GTS_TRIANGLE (f
)) > 0.) {
136 GtsPoint
* p1
= GTS_POINT (GTS_SEGMENT (GTS_TRIANGLE (f
)->e1
)->v1
);
137 gdouble d
= ((data
->p
->x
- p1
->x
)*(data
->p
->x
- p1
->x
) +
138 (data
->p
->y
- p1
->y
)*(data
->p
->y
- p1
->y
));
140 if (d
< data
->dmin
) {
148 /* select the face closest to @p among n^1/3 randomly picked faces
150 static GtsFace
* closest_face (GtsSurface
* s
, GtsPoint
* p
)
154 if (!g_hash_table_size (s
->faces
))
157 fc
.dmin
= G_MAXDOUBLE
;
160 fc
.stop
= (gint
) exp (log ((gdouble
) g_hash_table_size (s
->faces
))/3.);
161 g_hash_table_foreach (s
->faces
, find_closest
, &fc
);
164 # endif /* VERSION < 2.4.0 */
165 #endif /* not USE_SURFACE_BTREE */
167 /* returns the face belonging to @surface and neighbor of @f via @e */
168 static GtsFace
* neighbor (GtsFace
* f
,
170 GtsSurface
* surface
)
172 GSList
* i
= e
->triangles
;
173 GtsTriangle
* t
= GTS_TRIANGLE (f
);
176 GtsTriangle
* t1
= i
->data
;
179 gts_face_has_parent_surface (GTS_FACE (t1
), surface
))
180 return GTS_FACE (t1
);
186 /* given a triangle @t and a segment s (@o -> @p).
187 @o must be in @t. Returns the
188 edge of @t which is intersected by s or %NULL if @p is also
189 contained in @t (on_summit is set to %FALSE) or if s intersects @t
190 exactly on one of its summit (on_summit is set to %TRUE). */
191 static GtsEdge
* triangle_next_edge (GtsTriangle
* t
,
192 GtsPoint
* o
, GtsPoint
* p
,
193 gboolean
* on_summit
)
195 GtsVertex
* v1
, * v2
, * v3
;
196 GtsEdge
* e1
, * e2
, * e3
;
197 gdouble orient
= 0.0;
199 gts_triangle_vertices_edges (t
, NULL
,
204 orient
= gts_point_orientation (o
, GTS_POINT (v1
), p
);
206 orient
= gts_point_orientation (o
, GTS_POINT (v2
), p
);
208 if (gts_point_orientation (GTS_POINT (v2
), GTS_POINT (v3
), p
) >= 0.0)
213 if (gts_point_orientation (GTS_POINT (v1
), GTS_POINT (v2
), p
) >= 0.0)
217 if (gts_point_orientation (GTS_POINT (v1
), GTS_POINT (v2
), p
) < 0.0)
223 orient
= gts_point_orientation (o
, GTS_POINT (v3
), p
);
225 if (gts_point_orientation (GTS_POINT (v3
), GTS_POINT (v1
), p
) >= 0.0)
230 if (gts_point_orientation (GTS_POINT (v2
), GTS_POINT (v3
), p
) >= 0.0)
234 if (gts_point_orientation (GTS_POINT (v3
), GTS_POINT (v1
), p
) < 0.0)
239 if (gts_point_orientation (GTS_POINT (v2
), GTS_POINT (v3
), p
) < 0.0)
241 if (gts_point_orientation (GTS_POINT (v1
), GTS_POINT (v2
), p
) < 0.0)
246 static void triangle_barycenter (GtsTriangle
* t
, GtsPoint
* b
)
248 GtsPoint
* p
= GTS_POINT (gts_triangle_vertex (t
));
250 GTS_POINT (GTS_SEGMENT(t
->e1
)->v1
)->x
+
251 GTS_POINT (GTS_SEGMENT(t
->e1
)->v2
)->x
)/3.;
253 GTS_POINT (GTS_SEGMENT(t
->e1
)->v1
)->y
+
254 GTS_POINT (GTS_SEGMENT(t
->e1
)->v2
)->y
)/3.;
257 static GtsFace
* point_locate (GtsPoint
* o
,
260 GtsSurface
* surface
)
264 GtsVertex
* v1
, * v2
, * v3
;
267 prev
= triangle_next_edge (GTS_TRIANGLE (f
), o
, p
, &on_summit
);
273 return f
; /* p is inside f */
275 /* s intersects f exactly on a summit: restarts from a neighbor of f */
276 if ((f1
= neighbor (f
, GTS_TRIANGLE (f
)->e1
, surface
)) ||
277 (f1
= neighbor (f
, GTS_TRIANGLE (f
)->e2
, surface
)) ||
278 (f1
= neighbor (f
, GTS_TRIANGLE (f
)->e3
, surface
))) {
279 triangle_barycenter (GTS_TRIANGLE (f1
), o
);
280 return point_locate (o
, p
, f1
, surface
);
285 f
= neighbor (f
, prev
, surface
);
287 gts_triangle_vertices_edges (GTS_TRIANGLE (f
), prev
,
288 &v1
, &v2
, &v3
, &prev
, &e2
, &e3
);
290 gdouble orient
= gts_point_orientation (o
, GTS_POINT (v3
), p
);
293 if (gts_point_orientation (GTS_POINT (v2
), GTS_POINT (v3
), p
) >= 0.0)
294 return f
; /* p is inside f */
295 f
= neighbor (f
, e2
, surface
);
299 else if (orient
> 0.0) {
300 if (gts_point_orientation (GTS_POINT (v3
), GTS_POINT (v1
), p
) >= 0.0)
301 return f
; /* p is inside f */
302 f
= neighbor (f
, e3
, surface
);
309 if (gts_point_orientation (GTS_POINT (v2
), GTS_POINT (v3
), p
) >= 0.0)
310 return f
; /* p is inside f */
312 /* s intersects f exactly on v3: restarts from a neighbor of f */
313 if ((f1
= neighbor (f
, e2
, surface
)) ||
314 (f1
= neighbor (f
, e3
, surface
))) {
315 triangle_barycenter (GTS_TRIANGLE (f1
), o
);
316 return point_locate (o
, p
, f1
, surface
);
320 /* update e2, e3, v3 for the new triangle */
322 if (prev
== GTS_TRIANGLE (f
)->e1
) {
323 e2
= GTS_TRIANGLE (f
)->e2
; e3
= GTS_TRIANGLE (f
)->e3
;
325 else if (prev
== GTS_TRIANGLE (f
)->e2
) {
326 e2
= GTS_TRIANGLE (f
)->e3
; e3
= GTS_TRIANGLE (f
)->e1
;
329 e2
= GTS_TRIANGLE (f
)->e1
; e3
= GTS_TRIANGLE (f
)->e2
;
331 if (GTS_SEGMENT (e2
)->v1
== v1
|| GTS_SEGMENT (e2
)->v1
== v2
)
332 v3
= GTS_SEGMENT (e2
)->v2
;
334 v3
= GTS_SEGMENT (e2
)->v1
;
343 * @surface: a #GtsSurface.
344 * @guess: %NULL or a face of @surface close to @p.
346 * Locates the face of the planar projection of @surface containing
347 * @p. The planar projection of @surface must define a connected set
348 * of triangles without holes and bounded by a convex boundary. The
349 * algorithm is randomized and performs in O(n^1/3) expected time
350 * where n is the number of triangles of @surface.
352 * If a good @guess is given the point location can be significantly faster.
354 * Returns: a #GtsFace of @surface containing @p or %NULL if @p is not
355 * contained within the boundary of @surface.
357 GtsFace
* gts_point_locate (GtsPoint
* p
,
358 GtsSurface
* surface
,
364 g_return_val_if_fail (p
!= NULL
, NULL
);
365 g_return_val_if_fail (surface
!= NULL
, NULL
);
366 g_return_val_if_fail (guess
== NULL
||
367 gts_face_has_parent_surface (guess
, surface
), NULL
);
370 guess
= closest_face (surface
, p
);
372 g_return_val_if_fail (gts_triangle_orientation (GTS_TRIANGLE (guess
)) > 0., NULL
);
377 o
= GTS_POINT (gts_object_new (GTS_OBJECT_CLASS (gts_point_class ())));
378 triangle_barycenter (GTS_TRIANGLE (guess
), o
);
379 fr
= point_locate (o
, p
, guess
, surface
);
380 gts_object_destroy (GTS_OBJECT (o
));
387 * gts_constraint_class:
389 * Returns: the #GtsConstraintClass.
391 GtsConstraintClass
* gts_constraint_class (void)
393 static GtsConstraintClass
* klass
= NULL
;
396 GtsObjectClassInfo constraint_info
= {
398 sizeof (GtsConstraint
),
399 sizeof (GtsConstraintClass
),
400 (GtsObjectClassInitFunc
) NULL
,
401 (GtsObjectInitFunc
) NULL
,
402 (GtsArgSetFunc
) NULL
,
405 klass
= gts_object_class_new (GTS_OBJECT_CLASS (gts_edge_class ()),
412 static void split_list (GtsListFace
* f
, GtsListFace
* f1
, GtsListFace
* f2
,
413 GtsPoint
* p1
, GtsPoint
* p2
,
414 GSList
** last1
, GSList
** last2
)
416 GSList
* i
= f
->points
, * l1
= *last1
, * l2
= *last2
;
419 GtsPoint
* p
= i
->data
;
421 if (gts_point_orientation (p1
, p2
, p
) >= 0.) {
422 if (l1
) l1
->next
= i
; else f1
->points
= i
;
426 if (l2
) l2
->next
= i
; else f2
->points
= i
;
436 /* cf. figure misc/swap.fig */
437 static void swap_if_in_circle (GtsFace
* f1
,
444 GtsSurface
* surface
)
450 if (GTS_IS_CONSTRAINT (e1
)) /* @e1 is a constraint can not swap */
453 f2
= neighbor (f1
, e1
, surface
);
454 if (f2
== NULL
) /* @e1 is a boundary of @surface */
457 if (GTS_TRIANGLE (f2
)->e1
== e1
) {
458 e4
= GTS_TRIANGLE (f2
)->e2
; e5
= GTS_TRIANGLE (f2
)->e3
;
460 else if (GTS_TRIANGLE (f2
)->e2
== e1
) {
461 e4
= GTS_TRIANGLE (f2
)->e3
; e5
= GTS_TRIANGLE (f2
)->e1
;
464 e4
= GTS_TRIANGLE (f2
)->e1
; e5
= GTS_TRIANGLE (f2
)->e2
;
466 if (GTS_SEGMENT (e4
)->v1
== GTS_SEGMENT (e1
)->v1
||
467 GTS_SEGMENT (e4
)->v1
== GTS_SEGMENT (e1
)->v2
)
468 v4
= GTS_SEGMENT (e4
)->v2
;
470 v4
= GTS_SEGMENT (e4
)->v1
;
472 if (gts_point_in_circle (GTS_POINT (v4
), GTS_POINT (v1
),
473 GTS_POINT (v2
), GTS_POINT (v3
)) > 0.0) {
475 GtsSegment
* sn
= gts_vertices_are_connected (v3
, v4
);
478 if (!GTS_IS_EDGE (sn
))
479 en
= gts_edge_new (surface
->edge_class
, v3
, v4
);
483 f3
= gts_face_new (surface
->face_class
, en
, e5
, e2
);
484 gts_object_attributes (GTS_OBJECT (f3
), GTS_OBJECT (f1
));
485 f4
= gts_face_new (surface
->face_class
, en
, e3
, e4
);
486 gts_object_attributes (GTS_OBJECT (f4
), GTS_OBJECT (f2
));
488 if (GTS_IS_LIST_FACE (f3
)) {
489 GSList
* last3
= NULL
, * last4
= NULL
;
491 if (GTS_IS_LIST_FACE (f1
))
492 split_list (GTS_LIST_FACE (f1
), GTS_LIST_FACE (f3
), GTS_LIST_FACE (f4
),
493 GTS_POINT (v3
), GTS_POINT (v4
), &last3
, &last4
);
494 if (GTS_IS_LIST_FACE (f2
))
495 split_list (GTS_LIST_FACE (f2
), GTS_LIST_FACE (f3
), GTS_LIST_FACE (f4
),
496 GTS_POINT (v3
), GTS_POINT (v4
), &last3
, &last4
);
497 if (last3
) last3
->next
= NULL
;
498 if (last4
) last4
->next
= NULL
;
501 gts_surface_remove_face (surface
, f1
);
502 gts_surface_remove_face (surface
, f2
);
503 gts_surface_add_face (surface
, f3
);
504 gts_surface_add_face (surface
, f4
);
506 swap_if_in_circle (f3
, v4
, v2
, v3
, e5
, e2
, en
, surface
);
507 swap_if_in_circle (f4
, v1
, v4
, v3
, e4
, en
, e3
, surface
);
512 * gts_delaunay_add_vertex_to_face:
513 * @surface: a #GtsSurface.
515 * @f: a #GtsFace belonging to @surface.
517 * Adds vertex @v to the face @f of the Delaunay triangulation defined
520 * Returns: %NULL is @v has been successfully added to @surface or was
521 * already contained in @surface or a #GtsVertex having the same x and
522 * y coordinates as @v.
524 GtsVertex
* gts_delaunay_add_vertex_to_face (GtsSurface
* surface
,
528 GtsEdge
* e1
, * e2
, * e3
;
529 GtsSegment
* s4
, * s5
, * s6
;
530 GtsEdge
* e4
, * e5
, * e6
;
531 GtsVertex
* v1
, * v2
, * v3
;
534 g_return_val_if_fail (surface
!= NULL
, v
);
535 g_return_val_if_fail (v
!= NULL
, v
);
536 g_return_val_if_fail (f
!= NULL
, v
);
538 gts_triangle_vertices_edges (GTS_TRIANGLE (f
), NULL
,
539 &v1
, &v2
, &v3
, &e1
, &e2
, &e3
);
540 if (v
== v1
|| v
== v2
|| v
== v3
) /* v already in @surface */
542 if (GTS_POINT (v
)->x
== GTS_POINT (v1
)->x
&&
543 GTS_POINT (v
)->y
== GTS_POINT (v1
)->y
)
545 if (GTS_POINT (v
)->x
== GTS_POINT (v2
)->x
&&
546 GTS_POINT (v
)->y
== GTS_POINT (v2
)->y
)
548 if (GTS_POINT (v
)->x
== GTS_POINT (v3
)->x
&&
549 GTS_POINT (v
)->y
== GTS_POINT (v3
)->y
)
552 s4
= gts_vertices_are_connected (v
, v1
);
553 if (!GTS_IS_EDGE (s4
))
554 e4
= gts_edge_new (surface
->edge_class
, v
, v1
);
557 s5
= gts_vertices_are_connected (v
, v2
);
558 if (!GTS_IS_EDGE (s5
))
559 e5
= gts_edge_new (surface
->edge_class
, v
, v2
);
562 s6
= gts_vertices_are_connected (v
, v3
);
563 if (!GTS_IS_EDGE (s6
))
564 e6
= gts_edge_new (surface
->edge_class
, v
, v3
);
568 /* cf. figure misc/swap.fig */
569 nf
[0] = gts_face_new (surface
->face_class
, e4
, e1
, e5
);
570 gts_object_attributes (GTS_OBJECT (nf
[0]), GTS_OBJECT (f
));
571 nf
[1] = gts_face_new (surface
->face_class
, e5
, e2
, e6
);
572 gts_object_attributes (GTS_OBJECT (nf
[1]), GTS_OBJECT (f
));
573 nf
[2] = gts_face_new (surface
->face_class
, e6
, e3
, e4
);
574 gts_object_attributes (GTS_OBJECT (nf
[2]), GTS_OBJECT (f
));
576 if (GTS_IS_LIST_FACE (f
) && GTS_IS_LIST_FACE (nf
[0])) {
577 GSList
* i
= GTS_LIST_FACE (f
)->points
, * last
[3] = { NULL
, NULL
, NULL
};
580 GtsPoint
* p
= i
->data
;
581 GSList
* next
= i
->next
;
584 if (p
!= GTS_POINT (v
)) {
585 if (gts_point_orientation (GTS_POINT (v
), GTS_POINT (v1
), p
) >= 0.) {
586 gdouble o
= gts_point_orientation (GTS_POINT (v
), GTS_POINT (v2
), p
);
591 j
= gts_point_orientation (GTS_POINT (v
), GTS_POINT (v3
), p
)
594 else if (gts_point_orientation (GTS_POINT (v
), GTS_POINT (v3
), p
) > 0.)
601 GTS_LIST_FACE (nf
[j
])->points
= i
;
608 GTS_LIST_FACE (f
)->points
= NULL
;
609 if (last
[0]) last
[0]->next
= NULL
;
610 if (last
[1]) last
[1]->next
= NULL
;
611 if (last
[2]) last
[2]->next
= NULL
;
614 gts_surface_remove_face (surface
, f
);
615 gts_surface_add_face (surface
, nf
[0]);
616 gts_surface_add_face (surface
, nf
[1]);
617 gts_surface_add_face (surface
, nf
[2]);
619 swap_if_in_circle (nf
[0], v1
, v2
, v
, e1
, e5
, e4
, surface
);
620 swap_if_in_circle (nf
[1], v2
, v3
, v
, e2
, e6
, e5
, surface
);
621 swap_if_in_circle (nf
[2], v3
, v1
, v
, e3
, e4
, e6
, surface
);
627 * gts_delaunay_add_vertex:
628 * @surface: a #GtsSurface.
630 * @guess: %NULL or a #GtsFace belonging to @surface to be used as an initial
631 * guess for point location.
633 * Adds vertex @v to the Delaunay triangulation defined by
634 * @surface. If @v is not contained in the convex hull bounding
635 * @surface, @v is not added to the triangulation.
637 * Returns: %NULL is @v has been successfully added to @surface or was
638 * already contained in @surface, @v if @v is not contained in the
639 * convex hull bounding surface or a #GtsVertex having the same x and
640 * y coordinates as @v.
642 GtsVertex
* gts_delaunay_add_vertex (GtsSurface
* surface
,
648 g_return_val_if_fail (surface
!= NULL
, v
);
649 g_return_val_if_fail (v
!= NULL
, v
);
651 if (!(f
= gts_point_locate (GTS_POINT (v
), surface
, guess
)))
653 return gts_delaunay_add_vertex_to_face (surface
, v
, f
);
656 static gboolean
polygon_in_circle (GSList
* poly
,
661 GtsVertex
* v1
= NULL
, * v2
= NULL
;
664 GtsSegment
* s
= poly
->data
;
667 if (v
!= v1
&& v
!= v2
&&
668 v
!= GTS_VERTEX (p1
) &&
669 v
!= GTS_VERTEX (p2
) &&
670 v
!= GTS_VERTEX (p3
) &&
671 gts_point_in_circle (GTS_POINT (v
), p1
, p2
, p3
) > 0.)
674 if (v
!= v1
&& v
!= v2
&&
675 v
!= GTS_VERTEX (p1
) &&
676 v
!= GTS_VERTEX (p2
) &&
677 v
!= GTS_VERTEX (p3
) &&
678 gts_point_in_circle (GTS_POINT (v
), p1
, p2
, p3
) > 0.)
687 static void triangulate_polygon (GSList
* poly
,
688 GtsSurface
* surface
,
691 GSList
* i
, * poly1
, * poly2
;
692 GtsVertex
* v1
, * v2
, * v3
= NULL
;
693 gboolean found
= FALSE
;
694 GtsSegment
* s
, * s1
, * s2
;
698 if (poly
== NULL
|| poly
->next
== NULL
) {
704 s1
= poly
->next
->data
;
705 if (s
->v1
== s1
->v1
|| s
->v1
== s1
->v2
) {
710 g_assert (s
->v2
== s1
->v1
|| s
->v2
== s1
->v2
);
717 while (i
&& !found
) {
722 g_assert (s1
->v2
== v3
);
726 gts_point_orientation (GTS_POINT (v1
),
728 GTS_POINT (v3
)) >= 0. &&
729 !polygon_in_circle (poly
,
743 s1
= gts_vertices_are_connected (v2
, v3
);
744 if (!GTS_IS_EDGE (s1
))
745 e1
= gts_edge_new (surface
->edge_class
, v2
, v3
);
748 s2
= gts_vertices_are_connected (v3
, v1
);
749 if (!GTS_IS_EDGE (s2
))
750 e2
= gts_edge_new (surface
->edge_class
, v3
, v1
);
753 f
= gts_face_new (surface
->face_class
, GTS_EDGE (s
), e1
, e2
);
754 gts_object_attributes (GTS_OBJECT (f
), GTS_OBJECT (ref
));
755 gts_surface_add_face (surface
, f
);
758 g_slist_free_1 (poly
);
759 if (i
->next
&& e2
!= i
->next
->data
)
760 poly2
= g_slist_prepend (i
->next
, e2
);
764 i
->next
= g_slist_prepend (NULL
, e1
);
768 triangulate_polygon (poly1
, surface
, ref
);
769 triangulate_polygon (poly2
, surface
, ref
);
773 * gts_delaunay_remove_vertex:
774 * @surface: a #GtsSurface.
777 * Removes @v from the Delaunay triangulation defined by @surface and
778 * restores the Delaunay property. Vertex @v must not be used by any
779 * constrained edge otherwise the triangulation is not guaranteed to
782 void gts_delaunay_remove_vertex (GtsSurface
* surface
, GtsVertex
* v
)
784 GSList
* triangles
, * i
;
785 GtsFace
* ref
= NULL
;
787 g_return_if_fail (surface
!= NULL
);
788 g_return_if_fail (v
!= NULL
);
790 i
= triangles
= gts_vertex_triangles (v
, NULL
);
792 if (GTS_IS_FACE (i
->data
) &&
793 gts_face_has_parent_surface (i
->data
, surface
))
798 g_slist_free (triangles
);
799 g_return_if_fail (ref
);
801 triangulate_polygon (gts_vertex_fan_oriented (v
, surface
), surface
, ref
);
804 if (GTS_IS_FACE (i
->data
) &&
805 gts_face_has_parent_surface (i
->data
, surface
))
806 gts_surface_remove_face (surface
, i
->data
);
809 g_slist_free (triangles
);
812 #define NEXT_CUT(edge, edge1, list) { next = neighbor (f, edge, surface);\
813 remove_triangles (e, surface);\
814 if (!constraint && !e->triangles)\
815 gts_object_destroy (GTS_OBJECT (e));\
817 *list = g_slist_prepend (*list, edge1);\
818 return g_slist_concat (constraint,\
819 remove_intersected_edge (s, edge,\
820 next, surface, left, right));\
823 static void remove_triangles (GtsEdge
* e
, GtsSurface
* s
)
825 GSList
* i
= e
->triangles
;
828 GSList
* next
= i
->next
;
830 if (GTS_IS_FACE (i
->data
) && gts_face_has_parent_surface (i
->data
, s
))
831 gts_surface_remove_face (s
, i
->data
);
837 remove_intersected_edge (GtsSegment
* s
,
840 GtsSurface
* surface
,
841 GSList
** left
, GSList
** right
)
843 GtsVertex
* v1
, * v2
, * v3
;
847 GSList
* constraint
= NULL
;
849 if (GTS_IS_CONSTRAINT (e
))
850 constraint
= g_slist_prepend (NULL
, e
);
852 gts_triangle_vertices_edges (GTS_TRIANGLE (f
), e
,
853 &v1
, &v2
, &v3
, &e
, &e1
, &e2
);
855 o1
= gts_point_orientation (GTS_POINT (v2
), GTS_POINT (v3
),
857 o2
= gts_point_orientation (GTS_POINT (v3
), GTS_POINT (v1
),
860 if (o1
== 0. && o2
== 0.) {
862 fprintf(stderr, "o1 = %f o2 = %f\n", o1, o2);
863 fprintf(stderr, "v1 = %f, %f\n", GTS_POINT(v1)->x, GTS_POINT(v1)->y);
864 fprintf(stderr, "v2 = %f, %f\n", GTS_POINT(v2)->x, GTS_POINT(v2)->y);
865 fprintf(stderr, "v3 = %f, %f\n", GTS_POINT(v3)->x, GTS_POINT(v3)->y);
866 fprintf(stderr, "s->v2 = %f, %f\n", GTS_POINT(s->v2)->x, GTS_POINT(s->v2)->y);
871 remove_triangles (e
, surface
);
872 if (!constraint
&& !e
->triangles
)
873 gts_object_destroy (GTS_OBJECT (e
));
874 *left
= g_slist_prepend (*left
, e2
);
875 *right
= g_slist_prepend (*right
, e1
);
880 NEXT_CUT (e2
, e1
, right
)
883 NEXT_CUT (e1
, e2
, left
)
885 gdouble o3
= gts_point_orientation (GTS_POINT (s
->v1
), GTS_POINT (s
->v2
),
888 NEXT_CUT (e1
, e2
, left
)
890 NEXT_CUT (e2
, e1
, right
)
896 remove_intersected_vertex (GtsSegment
* s
,
898 GtsSurface
* surface
,
903 GSList
* triangles
= gts_vertex_triangles (v
, NULL
);
908 GtsTriangle
* t
= i
->data
;
909 if (GTS_IS_FACE (t
) &&
910 gts_face_has_parent_surface (GTS_FACE (t
), surface
)) {
911 GtsVertex
* v1
, * v2
, * v3
;
914 gts_triangle_vertices (t
, &v1
, &v2
, &v3
);
926 if ((o1
= gts_point_orientation (GTS_POINT (v
), GTS_POINT (v2
),
927 GTS_POINT (s
->v2
))) >= 0. &&
928 (o2
= gts_point_orientation (GTS_POINT (v3
), GTS_POINT (v
),
929 GTS_POINT (s
->v2
))) >= 0.) {
930 gdouble o3
= gts_point_orientation (GTS_POINT (v2
), GTS_POINT (v3
),
932 GtsEdge
* e
= gts_triangle_edge_opposite (t
, v
);
934 GtsFace
* next
= neighbor (GTS_FACE (t
), e
, surface
);
937 gts_triangle_vertices_edges (t
, e
, &v2
, &v3
, &v
, &e
, &e2
, &e1
);
939 g_slist_free (triangles
);
941 if (o3
>= 0.) /* @s->v2 is inside (or on the edge) of t */
944 gts_allow_floating_faces
= TRUE
;
945 gts_surface_remove_face (surface
, GTS_FACE (t
));
946 gts_allow_floating_faces
= FALSE
;
948 *left
= g_slist_prepend (*left
, e2
);
949 *right
= g_slist_prepend (*right
, e1
);
952 return remove_intersected_edge (s
, e
, next
, surface
, left
, right
);
958 g_assert_not_reached ();
963 * gts_delaunay_add_constraint:
964 * @surface: a #GtsSurface.
965 * @c: a #GtsConstraint.
967 * Add constraint @c to the constrained Delaunay triangulation defined by
970 * Returns: a list of #GtsConstraint conflicting (i.e. intersecting) with @c
971 * which were removed from @surface (%NULL if there was none).
973 GSList
* gts_delaunay_add_constraint (GtsSurface
* surface
,
976 GSList
* constraints
;
977 GtsVertex
* v1
; //, * v2;
978 GSList
* left
= NULL
, * right
= NULL
;
979 GtsFace
* ref
= NULL
;
981 g_return_val_if_fail (surface
!= NULL
, NULL
);
982 g_return_val_if_fail (c
!= NULL
, NULL
);
983 g_return_val_if_fail (GTS_IS_CONSTRAINT (c
), NULL
);
985 v1
= GTS_SEGMENT (c
)->v1
;
986 //v2 = GTS_SEGMENT (c)->v2;
988 gts_allow_floating_edges
= TRUE
;
989 constraints
= remove_intersected_vertex (GTS_SEGMENT (c
), v1
, surface
,
990 &left
, &right
, &ref
);
991 gts_allow_floating_edges
= FALSE
;
993 triangulate_polygon (g_slist_prepend (g_slist_reverse (right
), c
),
995 triangulate_polygon (g_slist_prepend (left
, c
),
998 right
= g_slist_prepend (g_slist_reverse (right
), c
);
999 left
= g_slist_prepend (left
, c
);
1001 FILE * fp0
= fopen ("hole", "wt");
1002 FILE * fp1
= fopen ("right", "wt");
1003 FILE * fp2
= fopen ("left", "wt");
1006 gts_surface_write (surface
, fp0
);
1009 fprintf (fp2
, "LIST {\n");
1011 GtsSegment
* s
= i
->data
;
1014 "VECT 1 2 0 2 0 %g %g 0 %g %g 0\n",
1016 GTS_POINT (s
->v1
)->x
, GTS_POINT (s
->v1
)->y
,
1017 GTS_POINT (s
->v2
)->x
, GTS_POINT (s
->v2
)->y
);
1020 fprintf (fp2
, "}\n");
1021 fprintf (fp1
, "LIST {\n");
1024 GtsSegment
* s
= i
->data
;
1027 "VECT 1 2 0 2 0 %g %g 0 %g %g 0\n",
1029 GTS_POINT (s
->v1
)->x
, GTS_POINT (s
->v1
)->y
,
1030 GTS_POINT (s
->v2
)->x
, GTS_POINT (s
->v2
)->y
);
1033 fprintf (fp1
, "}\n");
1037 triangulate_polygon (right
, surface
);
1038 triangulate_polygon (left
, surface
);
1040 if (ref
&& !ref
->surfaces
) {
1041 gts_allow_floating_edges
= TRUE
;
1042 gts_object_destroy (GTS_OBJECT (ref
));
1043 gts_allow_floating_edges
= FALSE
;
1048 static void delaunay_check (GtsTriangle
* t
, gpointer
* data
)
1050 GtsSurface
* surface
= data
[0];
1051 GtsFace
** face
= data
[1];
1053 if (*face
== NULL
) {
1055 GtsVertex
* v1
, * v2
, * v3
;
1057 gts_triangle_vertices (t
, &v1
, &v2
, &v3
);
1058 list
= gts_vertex_neighbors (v1
, NULL
, surface
);
1059 list
= gts_vertex_neighbors (v2
, list
, surface
);
1060 list
= gts_vertex_neighbors (v3
, list
, surface
);
1062 while (i
&& *face
== NULL
) {
1063 GtsVertex
* v
= i
->data
;
1064 if (v
!= v1
&& v
!= v2
&& v
!= v3
&&
1065 gts_point_in_circle (GTS_POINT (v
),
1068 GTS_POINT (v3
)) > 0.)
1069 *face
= GTS_FACE (t
);
1072 g_slist_free (list
);
1077 * gts_delaunay_check:
1078 * @surface: a #GtsSurface.
1080 * Returns: %NULL if the planar projection of @surface is a Delaunay
1081 * triangulation (unconstrained), a #GtsFace violating the Delaunay
1082 * property otherwise.
1084 GtsFace
* gts_delaunay_check (GtsSurface
* surface
)
1086 GtsFace
* face
= NULL
;
1089 g_return_val_if_fail (surface
!= NULL
, FALSE
);
1093 gts_surface_foreach_face (surface
, (GtsFunc
) delaunay_check
, data
);
1099 * gts_delaunay_remove_hull:
1100 * @surface: a #GtsSurface.
1102 * Removes all the edges of the boundary of @surface which are not
1105 void gts_delaunay_remove_hull (GtsSurface
* surface
)
1109 g_return_if_fail (surface
!= NULL
);
1111 boundary
= gts_surface_boundary (surface
);
1112 gts_allow_floating_edges
= TRUE
;
1114 GSList
* i
= boundary
;
1115 GtsEdge
* e
= i
->data
;
1119 if (!GTS_IS_CONSTRAINT (e
)) {
1120 GtsTriangle
* t
= GTS_TRIANGLE (gts_edge_is_boundary (e
, surface
));
1123 if (t
->e1
!= e
&& !GTS_IS_CONSTRAINT (t
->e1
) &&
1124 !gts_edge_is_boundary (t
->e1
, surface
))
1125 boundary
= g_slist_prepend (boundary
, t
->e1
);
1126 if (t
->e2
!= e
&& !GTS_IS_CONSTRAINT (t
->e2
) &&
1127 !gts_edge_is_boundary (t
->e2
, surface
))
1128 boundary
= g_slist_prepend (boundary
, t
->e2
);
1129 if (t
->e3
!= e
&& !GTS_IS_CONSTRAINT (t
->e3
) &&
1130 !gts_edge_is_boundary (t
->e3
, surface
))
1131 boundary
= g_slist_prepend (boundary
, t
->e3
);
1132 gts_surface_remove_face (surface
, GTS_FACE (t
));
1135 gts_object_destroy (GTS_OBJECT (e
));
1138 gts_allow_floating_edges
= FALSE
;
1141 /* GtsListFace: Object */
1143 static void gts_list_face_destroy (GtsObject
* object
)
1145 g_slist_free (GTS_LIST_FACE (object
)->points
);
1147 (* GTS_OBJECT_CLASS (gts_list_face_class ())->parent_class
->destroy
)
1151 static void gts_list_face_class_init (GtsFaceClass
* klass
)
1153 GTS_OBJECT_CLASS (klass
)->destroy
= gts_list_face_destroy
;
1156 GtsFaceClass
* gts_list_face_class (void)
1158 static GtsFaceClass
* klass
= NULL
;
1160 if (klass
== NULL
) {
1161 GtsObjectClassInfo gts_list_face_info
= {
1163 sizeof (GtsListFace
),
1164 sizeof (GtsFaceClass
),
1165 (GtsObjectClassInitFunc
) gts_list_face_class_init
,
1166 (GtsObjectInitFunc
) NULL
,
1167 (GtsArgSetFunc
) NULL
,
1168 (GtsArgGetFunc
) NULL
1170 klass
= gts_object_class_new (GTS_OBJECT_CLASS (gts_face_class ()),
1171 >s_list_face_info
);