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.
23 gboolean gts_allow_floating_vertices
= FALSE
;
25 static void vertex_destroy (GtsObject
* object
)
27 GtsVertex
* vertex
= GTS_VERTEX (object
);
32 GTS_OBJECT_SET_FLAGS (i
->data
, GTS_DESTROYED
);
37 GSList
* next
= i
->next
;
38 gts_object_destroy (i
->data
);
41 g_assert (vertex
->segments
== NULL
);
43 (* GTS_OBJECT_CLASS (gts_vertex_class ())->parent_class
->destroy
) (object
);
46 static void vertex_clone (GtsObject
* clone
, GtsObject
* object
)
48 (* GTS_OBJECT_CLASS (gts_vertex_class ())->parent_class
->clone
) (clone
,
50 GTS_VERTEX (clone
)->segments
= NULL
;
53 static void vertex_class_init (GtsVertexClass
* klass
)
55 klass
->intersection_attributes
= NULL
;
56 GTS_OBJECT_CLASS (klass
)->clone
= vertex_clone
;
57 GTS_OBJECT_CLASS (klass
)->destroy
= vertex_destroy
;
60 static void vertex_init (GtsVertex
* vertex
)
62 vertex
->segments
= NULL
;
68 * Returns: the #GtsVertexClass.
70 GtsVertexClass
* gts_vertex_class (void)
72 static GtsVertexClass
* klass
= NULL
;
75 GtsObjectClassInfo vertex_info
= {
78 sizeof (GtsVertexClass
),
79 (GtsObjectClassInitFunc
) vertex_class_init
,
80 (GtsObjectInitFunc
) vertex_init
,
84 klass
= gts_object_class_new (GTS_OBJECT_CLASS (gts_point_class ()),
93 * @klass: a #GtsVertexClass.
94 * @x: the x-coordinate of the vertex to create.
95 * @y: the y-coordinate of the vertex to create.
96 * @z: the y-coordinate of the vertex to create.
98 * Returns: a new #GtsVertex with @x, @y and @z as coordinates.
100 GtsVertex
* gts_vertex_new (GtsVertexClass
* klass
,
101 gdouble x
, gdouble y
, gdouble z
)
105 v
= GTS_VERTEX (gts_object_new (GTS_OBJECT_CLASS (klass
)));
106 gts_point_set (GTS_POINT (v
), x
, y
, z
);
112 * gts_vertex_replace:
114 * @with: another #GtsVertex.
116 * Replaces vertex @v with vertex @with. @v and @with must be
117 * different. All the #GtsSegment which have @v has one of their
118 * vertices are updated. The segments list of vertex @v is freed and
119 * @v->segments is set to %NULL.
121 void gts_vertex_replace (GtsVertex
* v
, GtsVertex
* with
)
125 g_return_if_fail (v
!= NULL
);
126 g_return_if_fail (with
!= NULL
);
127 g_return_if_fail (v
!= with
);
131 GtsSegment
* s
= i
->data
;
132 if (s
->v1
!= with
&& s
->v2
!= with
)
133 with
->segments
= g_slist_prepend (with
->segments
, s
);
134 if (s
->v1
== v
) s
->v1
= with
;
135 if (s
->v2
== v
) s
->v2
= with
;
138 g_slist_free (v
->segments
);
143 * gts_vertex_is_unattached:
146 * Returns: %TRUE if @v is not the endpoint of any #GtsSegment,
149 gboolean
gts_vertex_is_unattached (GtsVertex
* v
)
151 g_return_val_if_fail (v
!= NULL
, FALSE
);
152 if (v
->segments
== NULL
)
158 * gts_vertices_are_connected:
160 * @v2: another #GtsVertex.
162 * Returns: if @v1 and @v2 are the vertices of the same #GtsSegment
163 * this segment else %NULL.
165 GtsSegment
* gts_vertices_are_connected (GtsVertex
* v1
, GtsVertex
* v2
)
169 g_return_val_if_fail (v1
!= NULL
, FALSE
);
170 g_return_val_if_fail (v2
!= NULL
, FALSE
);
174 GtsSegment
* s
= i
->data
;
176 if (s
->v1
== v2
|| s
->v2
== v2
)
184 * gts_vertices_from_segments:
185 * @segments: a list of #GtsSegment.
187 * Returns: a list of #GtsVertex, vertices of a #GtsSegment in @segments.
188 * Each element in the list is unique (no duplicates).
190 GSList
* gts_vertices_from_segments (GSList
* segments
)
193 GSList
* vertices
= NULL
, * i
;
195 hash
= g_hash_table_new (NULL
, NULL
);
198 GtsSegment
* s
= i
->data
;
199 if (g_hash_table_lookup (hash
, s
->v1
) == NULL
) {
200 vertices
= g_slist_prepend (vertices
, s
->v1
);
201 g_hash_table_insert (hash
, s
->v1
, s
);
203 if (g_hash_table_lookup (hash
, s
->v2
) == NULL
) {
204 vertices
= g_slist_prepend (vertices
, s
->v2
);
205 g_hash_table_insert (hash
, s
->v2
, s
);
209 g_hash_table_destroy (hash
);
214 * gts_vertex_triangles:
216 * @list: a list of #GtsTriangle.
218 * Adds all the #GtsTriangle which share @v as a vertex and do not
219 * already belong to @list.
221 * Returns: the new list of unique #GtsTriangle which share @v as a
224 GSList
* gts_vertex_triangles (GtsVertex
* v
,
229 g_return_val_if_fail (v
!= NULL
, NULL
);
233 GtsSegment
* s
= i
->data
;
234 if (GTS_IS_EDGE (s
)) {
235 GSList
* j
= GTS_EDGE (s
)->triangles
;
237 if (!g_slist_find (list
, j
->data
))
238 list
= g_slist_prepend (list
, j
->data
);
250 * @surface: a #GtsSurface or %NULL.
251 * @list: a list of #GtsFace.
253 * Adds all the #GtsFace belonging to @surface (if not %NULL) which share
254 * @v as a vertex and do not already belong to @list.
256 * Returns: the new list of unique #GtsFace belonging to @surface
257 * which share @v as a vertex.
259 GSList
* gts_vertex_faces (GtsVertex
* v
,
260 GtsSurface
* surface
,
265 g_return_val_if_fail (v
!= NULL
, NULL
);
269 GtsSegment
* s
= i
->data
;
270 if (GTS_IS_EDGE (s
)) {
271 GSList
* j
= GTS_EDGE (s
)->triangles
;
273 GtsTriangle
* t
= j
->data
;
276 (!surface
|| gts_face_has_parent_surface (GTS_FACE (t
), surface
))
278 !g_slist_find (list
, t
))
279 list
= g_slist_prepend (list
, t
);
289 * gts_vertex_neighbors:
291 * @list: a list of #GtsVertex.
292 * @surface: a #GtsSurface or %NULL.
294 * Adds to @list all the #GtsVertex connected to @v by a #GtsSegment and not
295 * already in @list. If @surface is not %NULL only the vertices connected to
296 * @v by an edge belonging to @surface are considered.
298 * Returns: the new list of unique #GtsVertex.
300 GSList
* gts_vertex_neighbors (GtsVertex
* v
,
302 GtsSurface
* surface
)
306 g_return_val_if_fail (v
!= NULL
, NULL
);
310 GtsSegment
* s
= i
->data
;
311 GtsVertex
* v1
= s
->v1
== v
? s
->v2
: s
->v1
;
315 gts_edge_has_parent_surface (GTS_EDGE (s
), surface
))) &&
316 !g_slist_find (list
, v1
))
317 list
= g_slist_prepend (list
, v1
);
324 * gts_vertex_is_boundary:
326 * @surface: a #GtsSurface or %NULL.
328 * Returns: %TRUE if @v is used by a #GtsEdge boundary of @surface as
329 * determined by gts_edge_is_boundary(), %FALSE otherwise.
331 gboolean
gts_vertex_is_boundary (GtsVertex
* v
, GtsSurface
* surface
)
335 g_return_val_if_fail (v
!= NULL
, FALSE
);
339 if (GTS_IS_EDGE (i
->data
) &&
340 gts_edge_is_boundary (i
->data
, surface
))
349 * gts_vertices_merge:
350 * @vertices: a list of #GtsVertex.
351 * @epsilon: half the size of the bounding box to consider for each vertex.
352 * @check: function called for each pair of vertices about to be merged
355 * For each vertex v in @vertices look if there are any vertex of
356 * @vertices contained in a box centered on v of size 2*@epsilon. If
357 * there are and if @check is not %NULL and returns %TRUE, replace
358 * them with v (using gts_vertex_replace()), destroy them and remove
359 * them from list. This is done efficiently using Kd-Trees.
361 * Returns: the updated list of vertices.
363 GList
* gts_vertices_merge (GList
* vertices
,
365 gboolean (* check
) (GtsVertex
*, GtsVertex
*))
371 g_return_val_if_fail (vertices
!= NULL
, 0);
373 array
= g_ptr_array_new ();
376 g_ptr_array_add (array
, i
->data
);
379 kdtree
= gts_kdtree_new (array
, NULL
);
380 g_ptr_array_free (array
, TRUE
);
384 GtsVertex
* v
= i
->data
;
385 if (!GTS_OBJECT (v
)->reserved
) { /* Do something only if v is active */
387 GSList
* selected
, * j
;
389 /* build bounding box */
390 bbox
= gts_bbox_new (gts_bbox_class (),
392 GTS_POINT (v
)->x
- epsilon
,
393 GTS_POINT (v
)->y
- epsilon
,
394 GTS_POINT (v
)->z
- epsilon
,
395 GTS_POINT (v
)->x
+ epsilon
,
396 GTS_POINT (v
)->y
+ epsilon
,
397 GTS_POINT (v
)->z
+ epsilon
);
399 /* select vertices which are inside bbox using kdtree */
400 j
= selected
= gts_kdtree_range (kdtree
, bbox
, NULL
);
402 GtsVertex
* sv
= j
->data
;
403 if (sv
!= v
&& !GTS_OBJECT (sv
)->reserved
&& (!check
|| (*check
) (sv
, v
))) {
404 /* sv is not v and is active */
405 gts_vertex_replace (sv
, v
);
406 GTS_OBJECT (sv
)->reserved
= sv
; /* mark sv as inactive */
410 g_slist_free (selected
);
411 gts_object_destroy (GTS_OBJECT (bbox
));
416 gts_kdtree_destroy (kdtree
);
418 /* destroy inactive vertices and removes them from list */
420 /* we want to control vertex destruction */
421 gts_allow_floating_vertices
= TRUE
;
425 GtsVertex
* v
= i
->data
;
426 GList
* next
= i
->next
;
427 if (GTS_OBJECT (v
)->reserved
) { /* v is inactive */
428 gts_object_destroy (GTS_OBJECT (v
));
429 vertices
= g_list_remove_link (vertices
, i
);
434 gts_allow_floating_vertices
= FALSE
;
439 /* returns the list of edges belonging to @surface turning around @v */
440 static GSList
* edge_fan_list (GtsVertex
* v
,
441 GtsSurface
* surface
,
446 GSList
* i
= e
->triangles
;
447 GtsFace
* neighbor
= NULL
;
448 GtsEdge
* next
= NULL
, * enext
= NULL
;
451 GtsFace
* f1
= i
->data
;
452 if (GTS_IS_FACE (f1
) &&
454 gts_face_has_parent_surface (f1
, surface
)) {
455 g_return_val_if_fail (neighbor
== NULL
, NULL
); /* non-manifold edge */
460 if (neighbor
== NULL
|| neighbor
== first
) /* end of fan */
463 if (GTS_TRIANGLE (neighbor
)->e1
== e
) {
464 next
= GTS_TRIANGLE (neighbor
)->e2
;
465 enext
= GTS_TRIANGLE (neighbor
)->e3
;
467 else if (GTS_TRIANGLE (neighbor
)->e2
== e
) {
468 next
= GTS_TRIANGLE (neighbor
)->e3
;
469 enext
= GTS_TRIANGLE (neighbor
)->e1
;
471 else if (GTS_TRIANGLE (neighbor
)->e3
== e
) {
472 next
= GTS_TRIANGLE (neighbor
)->e1
;
473 enext
= GTS_TRIANGLE (neighbor
)->e2
;
476 g_assert_not_reached ();
478 /* checking for correct orientation */
479 g_return_val_if_fail (GTS_SEGMENT (enext
)->v1
== v
||
480 GTS_SEGMENT (enext
)->v2
== v
, NULL
);
482 return g_slist_prepend (edge_fan_list (v
, surface
, neighbor
, enext
, first
),
487 * gts_vertex_fan_oriented:
489 * @surface: a #GtsSurface.
491 * Returns: a list of #GtsEdge describing in counterclockwise order the
492 * boundary of the fan of summit @v, the faces of the fan belonging to
495 GSList
* gts_vertex_fan_oriented (GtsVertex
* v
, GtsSurface
* surface
)
500 GtsVertex
* v1
, * v2
, * v3
;
501 GtsEdge
* e1
, * e2
, * e3
;
503 g_return_val_if_fail (v
!= NULL
, NULL
);
504 g_return_val_if_fail (surface
!= NULL
, NULL
);
508 GtsEdge
* e
= i
->data
;
509 if (GTS_IS_EDGE (e
)) {
510 GSList
* j
= e
->triangles
;
514 if (GTS_IS_FACE (j
->data
) &&
515 gts_face_has_parent_surface (j
->data
, surface
)) {
522 g_return_val_if_fail (degree
<= 2, NULL
); /* non-manifold edge */
524 gts_triangle_vertices_edges (GTS_TRIANGLE (f1
), NULL
,
525 &v1
, &v2
, &v3
, &e1
, &e2
, &e3
);
539 else if (degree
<= d
)
549 gts_triangle_vertices_edges (GTS_TRIANGLE (f
), NULL
,
550 &v1
, &v2
, &v3
, &e1
, &e2
, &e3
);
560 return g_slist_prepend (edge_fan_list (v
, surface
, f
, e3
, f
), e2
);
563 #define edge_use_vertex(e, v) (GTS_SEGMENT(e)->v1 == v ||\
564 GTS_SEGMENT(e)->v2 == v)
566 static GtsEdge
* replace_vertex (GtsTriangle
* t
,
573 if (t
->e1
!= e1
&& edge_use_vertex (t
->e1
, v
))
575 else if (t
->e2
!= e1
&& edge_use_vertex (t
->e2
, v
))
577 else if (t
->e3
!= e1
&& edge_use_vertex (t
->e3
, v
))
583 GtsSegment
* s
= GTS_SEGMENT (e
);
584 if (s
->v1
== v
) s
->v1
= with
;
585 if (s
->v2
== v
) s
->v2
= with
;
586 with
->segments
= g_slist_prepend (with
->segments
, s
);
587 v
->segments
= g_slist_remove (v
->segments
, s
);
593 static void triangle_next (GtsEdge
* e
, GtsVertex
* v
, GtsVertex
* with
)
602 GtsTriangle
* t
= i
->data
;
603 if (GTS_OBJECT (t
)->reserved
) {
604 GTS_OBJECT (t
)->reserved
= NULL
;
605 triangle_next (replace_vertex (t
, e
, v
, with
), v
, with
);
612 * gts_vertex_is_contact:
614 * @sever: if %TRUE and if @v is a contact vertex between two or more
615 * sets of connected triangles replaces it with as many vertices,
618 * Returns: the number of sets of connected triangles sharing @v as a
621 guint
gts_vertex_is_contact (GtsVertex
* v
, gboolean sever
)
623 GSList
* triangles
, * i
;
624 GtsVertex
* with
= v
;
625 guint ncomponent
= 0;
627 g_return_val_if_fail (v
!= NULL
, 0);
629 triangles
= gts_vertex_triangles (v
, NULL
);
632 GTS_OBJECT (i
->data
)->reserved
= i
;
638 GtsTriangle
* t
= i
->data
;
639 if (GTS_OBJECT (t
)->reserved
) {
641 if (ncomponent
&& sever
)
642 with
= GTS_VERTEX (gts_object_clone (GTS_OBJECT (v
)));
643 GTS_OBJECT (t
)->reserved
= NULL
;
644 e
= replace_vertex (t
, NULL
, v
, with
);
645 triangle_next (e
, v
, with
);
646 triangle_next (replace_vertex (t
, e
, v
, with
), v
, with
);
651 g_slist_free (triangles
);
656 /* GtsVertexNormal: Object */
658 static void vertex_normal_attributes (GtsVertex
* v
,
662 g_return_if_fail (GTS_IS_EDGE (e
));
663 g_return_if_fail (GTS_IS_TRIANGLE (t
));
665 if (GTS_IS_VERTEX_NORMAL (GTS_SEGMENT (e
)->v1
) &&
666 GTS_IS_VERTEX_NORMAL (GTS_SEGMENT (e
)->v2
)) {
667 GtsPoint
* p1
= GTS_POINT (GTS_SEGMENT (e
)->v1
);
668 GtsPoint
* p2
= GTS_POINT (GTS_SEGMENT (e
)->v2
);
669 GtsPoint
* p
= GTS_POINT (v
);
670 gdouble a
, b
, lambda
;
673 a
= p2
->x
- p1
->x
; b
= p
->x
- p1
->x
;
674 if (fabs (p2
->y
- p1
->y
) > fabs (a
)) {
675 a
= p2
->y
- p1
->y
; b
= p
->y
- p1
->y
;
677 if (fabs (p2
->z
- p1
->z
) > fabs (a
)) {
678 a
= p2
->z
- p1
->z
; b
= p
->z
- p1
->z
;
680 lambda
= a
!= 0. ? b
/a
: 0.;
681 for (i
= 0; i
< 3; i
++)
682 GTS_VERTEX_NORMAL (v
)->n
[i
] =
683 (1. - lambda
)*GTS_VERTEX_NORMAL (GTS_SEGMENT (e
)->v1
)->n
[i
] +
684 lambda
*GTS_VERTEX_NORMAL (GTS_SEGMENT (e
)->v2
)->n
[i
];
687 GtsVertex
* v1
, * v2
, * v3
;
689 gts_triangle_vertices (GTS_TRIANGLE (t
), &v1
, &v2
, &v3
);
690 if (GTS_IS_VERTEX_NORMAL (v1
) &&
691 GTS_IS_VERTEX_NORMAL (v2
) &&
692 GTS_IS_VERTEX_NORMAL (v3
)) {
693 GtsVector a1
, a2
, a3
, det
;
697 gts_vector_init (a1
, GTS_POINT (v1
), GTS_POINT (v
));
698 gts_vector_init (a2
, GTS_POINT (v1
), GTS_POINT (v2
));
699 gts_vector_init (a3
, GTS_POINT (v1
), GTS_POINT (v3
));
700 gts_vector_cross (det
, a2
, a3
);
701 if (fabs (det
[1]) > fabs (det
[0])) j
= 1;
702 if (fabs (det
[2]) > fabs (det
[j
])) j
= 2;
704 g_warning ("vertex_normal_attributes: det[%d] == 0.", j
);
709 l1
= (a1
[1]*a3
[2] - a1
[2]*a3
[1])/det
[0];
710 l2
= (a1
[2]*a2
[1] - a1
[1]*a2
[2])/det
[0];
713 l1
= (a1
[2]*a3
[0] - a1
[0]*a3
[2])/det
[1];
714 l2
= (a1
[0]*a2
[2] - a1
[2]*a2
[0])/det
[1];
717 l1
= (a1
[0]*a3
[1] - a1
[1]*a3
[0])/det
[2];
718 l2
= (a1
[1]*a2
[0] - a1
[0]*a2
[1])/det
[2];
723 for (i
= 0; i
< 3; i
++)
724 GTS_VERTEX_NORMAL (v
)->n
[i
] =
725 GTS_VERTEX_NORMAL (v1
)->n
[i
]*(1. - l1
- l2
) +
726 GTS_VERTEX_NORMAL (v2
)->n
[i
]*l1
+
727 GTS_VERTEX_NORMAL (v3
)->n
[i
]*l2
;
732 static void gts_vertex_normal_class_init (GtsVertexClass
* klass
)
734 klass
->intersection_attributes
= vertex_normal_attributes
;
737 GtsVertexClass
* gts_vertex_normal_class (void)
739 static GtsVertexClass
* klass
= NULL
;
742 GtsObjectClassInfo gts_vertex_normal_info
= {
744 sizeof (GtsVertexNormal
),
745 sizeof (GtsVertexClass
),
746 (GtsObjectClassInitFunc
) gts_vertex_normal_class_init
,
747 (GtsObjectInitFunc
) NULL
,
748 (GtsArgSetFunc
) NULL
,
751 klass
= gts_object_class_new (GTS_OBJECT_CLASS (gts_vertex_class ()),
752 >s_vertex_normal_info
);
758 /* GtsColorVertex: Object */
760 GtsVertexClass
* gts_color_vertex_class (void)
762 static GtsVertexClass
* klass
= NULL
;
765 GtsObjectClassInfo gts_color_vertex_info
= {
767 sizeof (GtsColorVertex
),
768 sizeof (GtsVertexClass
),
769 (GtsObjectClassInitFunc
) NULL
,
770 (GtsObjectInitFunc
) NULL
,
771 (GtsArgSetFunc
) NULL
,
774 klass
= gts_object_class_new (GTS_OBJECT_CLASS (gts_vertex_class ()),
775 >s_color_vertex_info
);