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.
25 #include "gts-private.h"
27 static void destroy_foreach_face (GtsFace
* f
, GtsSurface
* s
)
29 f
->surfaces
= g_slist_remove (f
->surfaces
, s
);
30 if (!GTS_OBJECT_DESTROYED (f
) &&
31 !gts_allow_floating_faces
&& f
->surfaces
== NULL
)
32 gts_object_destroy (GTS_OBJECT (f
));
35 static void surface_destroy (GtsObject
* object
)
37 GtsSurface
* surface
= GTS_SURFACE (object
);
39 gts_surface_foreach_face (surface
, (GtsFunc
) destroy_foreach_face
, surface
);
40 #ifdef USE_SURFACE_BTREE
41 g_tree_destroy (surface
->faces
);
42 #else /* not USE_SURFACE_BTREE */
43 g_hash_table_destroy (surface
->faces
);
44 #endif /* not USE_SURFACE_BTREE */
46 (* GTS_OBJECT_CLASS (gts_surface_class ())->parent_class
->destroy
) (object
);
49 static void surface_write (GtsObject
* object
, FILE * fptr
)
51 fprintf (fptr
, " %s %s %s %s",
52 object
->klass
->info
.name
,
53 GTS_OBJECT_CLASS (GTS_SURFACE (object
)->face_class
)->info
.name
,
54 GTS_OBJECT_CLASS (GTS_SURFACE (object
)->edge_class
)->info
.name
,
55 GTS_POINT_CLASS (GTS_SURFACE (object
)->vertex_class
)->binary
?
57 GTS_OBJECT_CLASS (GTS_SURFACE (object
)->vertex_class
)->info
.name
);
60 static void surface_class_init (GtsSurfaceClass
* klass
)
62 GTS_OBJECT_CLASS (klass
)->destroy
= surface_destroy
;
63 GTS_OBJECT_CLASS (klass
)->write
= surface_write
;
64 klass
->add_face
= NULL
;
65 klass
->remove_face
= NULL
;
68 #ifdef USE_SURFACE_BTREE
69 static gint
compare_pointers (gconstpointer a
, gconstpointer b
)
71 if (GPOINTER_TO_UINT (a
) < GPOINTER_TO_UINT (b
))
73 if (GPOINTER_TO_UINT (a
) > GPOINTER_TO_UINT (b
))
77 #endif /* USE_SURFACE_BTREE */
79 static void surface_init (GtsSurface
* surface
)
81 #ifdef USE_SURFACE_BTREE
82 surface
->faces
= g_tree_new (compare_pointers
);
83 #else /* not USE_SURFACE_BTREE */
84 surface
->faces
= g_hash_table_new (NULL
, NULL
);
85 #endif /* not USE_SURFACE_BTREE */
86 surface
->vertex_class
= gts_vertex_class ();
87 surface
->edge_class
= gts_edge_class ();
88 surface
->face_class
= gts_face_class ();
89 surface
->keep_faces
= FALSE
;
95 * Returns: the #GtsSurfaceClass.
97 GtsSurfaceClass
* gts_surface_class (void)
99 static GtsSurfaceClass
* klass
= NULL
;
102 GtsObjectClassInfo surface_info
= {
105 sizeof (GtsSurfaceClass
),
106 (GtsObjectClassInitFunc
) surface_class_init
,
107 (GtsObjectInitFunc
) surface_init
,
108 (GtsArgSetFunc
) NULL
,
111 klass
= gts_object_class_new (gts_object_class (), &surface_info
);
119 * @klass: a #GtsSurfaceClass.
120 * @face_class: a #GtsFaceClass.
121 * @edge_class: a #GtsEdgeClass.
122 * @vertex_class: a #GtsVertexClass.
124 * Returns: a new empty #GtsSurface.
126 GtsSurface
* gts_surface_new (GtsSurfaceClass
* klass
,
127 GtsFaceClass
* face_class
,
128 GtsEdgeClass
* edge_class
,
129 GtsVertexClass
* vertex_class
)
133 s
= GTS_SURFACE (gts_object_new (GTS_OBJECT_CLASS (klass
)));
134 s
->vertex_class
= vertex_class
;
135 s
->edge_class
= edge_class
;
136 s
->face_class
= face_class
;
142 * gts_surface_add_face:
146 * Adds face @f to surface @s.
148 void gts_surface_add_face (GtsSurface
* s
, GtsFace
* f
)
150 g_return_if_fail (s
!= NULL
);
151 g_return_if_fail (f
!= NULL
);
153 g_assert (s
->keep_faces
== FALSE
);
155 #ifdef USE_SURFACE_BTREE
156 if (!g_tree_lookup (s
->faces
, f
)) {
157 f
->surfaces
= g_slist_prepend (f
->surfaces
, s
);
158 g_tree_insert (s
->faces
, f
, f
);
160 #else /* not USE_SURFACE_BTREE */
161 if (!g_hash_table_lookup (s
->faces
, f
)) {
162 f
->surfaces
= g_slist_prepend (f
->surfaces
, s
);
163 g_hash_table_insert (s
->faces
, f
, f
);
165 #endif /* not USE_SURFACE_BTREE */
167 if (GTS_SURFACE_CLASS (GTS_OBJECT (s
)->klass
)->add_face
)
168 (* GTS_SURFACE_CLASS (GTS_OBJECT (s
)->klass
)->add_face
) (s
, f
);
172 * gts_surface_remove_face:
176 * Removes face @f from surface @s.
178 void gts_surface_remove_face (GtsSurface
* s
,
181 g_return_if_fail (s
!= NULL
);
182 g_return_if_fail (f
!= NULL
);
184 g_assert (s
->keep_faces
== FALSE
);
186 #ifdef USE_SURFACE_BTREE
187 g_tree_remove (s
->faces
, f
);
188 #else /* not USE_SURFACE_BTREE */
189 g_hash_table_remove (s
->faces
, f
);
190 #endif /* not USE_SURFACE_BTREE */
192 f
->surfaces
= g_slist_remove (f
->surfaces
, s
);
194 if (GTS_SURFACE_CLASS (GTS_OBJECT (s
)->klass
)->remove_face
)
195 (* GTS_SURFACE_CLASS (GTS_OBJECT (s
)->klass
)->remove_face
) (s
, f
);
197 if (!GTS_OBJECT_DESTROYED (f
) &&
198 !gts_allow_floating_faces
&&
200 gts_object_destroy (GTS_OBJECT (f
));
205 * @surface: a #GtsSurface.
208 * Add to @surface the data read from @f. The format of the file pointed to
209 * by @f is as described in gts_surface_write().
211 * Returns: 0 if successful or the line number at which the parsing
212 * stopped in case of error (in which case the @error field of @f is
213 * set to a description of the error which occured).
215 /* Update split.c/surface_read() if modifying this function */
216 guint
gts_surface_read (GtsSurface
* surface
, GtsFile
* f
)
218 GtsVertex
** vertices
;
222 g_return_val_if_fail (surface
!= NULL
, 1);
223 g_return_val_if_fail (f
!= NULL
, 1);
225 if (f
->type
!= GTS_INT
) {
226 gts_file_error (f
, "expecting an integer (number of vertices)");
229 nv
= atoi (f
->token
->str
);
231 gts_file_next_token (f
);
232 if (f
->type
!= GTS_INT
) {
233 gts_file_error (f
, "expecting an integer (number of edges)");
236 ne
= atoi (f
->token
->str
);
238 gts_file_next_token (f
);
239 if (f
->type
!= GTS_INT
) {
240 gts_file_error (f
, "expecting an integer (number of faces)");
243 nf
= atoi (f
->token
->str
);
245 gts_file_next_token (f
);
246 if (f
->type
== GTS_STRING
) {
247 if (f
->type
!= GTS_STRING
) {
248 gts_file_error (f
, "expecting a string (GtsSurfaceClass)");
251 gts_file_next_token (f
);
252 if (f
->type
!= GTS_STRING
) {
253 gts_file_error (f
, "expecting a string (GtsFaceClass)");
256 gts_file_next_token (f
);
257 if (f
->type
!= GTS_STRING
) {
258 gts_file_error (f
, "expecting a string (GtsEdgeClass)");
261 gts_file_next_token (f
);
262 if (f
->type
!= GTS_STRING
) {
263 gts_file_error (f
, "expecting a string (GtsVertexClass)");
266 if (!strcmp (f
->token
->str
, "GtsVertexBinary"))
267 GTS_POINT_CLASS (surface
->vertex_class
)->binary
= TRUE
;
269 GTS_POINT_CLASS (surface
->vertex_class
)->binary
= FALSE
;
270 gts_file_first_token_after (f
, '\n');
274 gts_file_first_token_after (f
, '\n');
279 /* allocate nv + 1 just in case nv == 0 */
280 vertices
= g_malloc ((nv
+ 1)*sizeof (GtsVertex
*));
281 edges
= g_malloc ((ne
+ 1)*sizeof (GtsEdge
*));
284 while (n
< nv
&& f
->type
!= GTS_ERROR
) {
285 GtsObject
* new_vertex
=
286 gts_object_new (GTS_OBJECT_CLASS (surface
->vertex_class
));
288 (* GTS_OBJECT_CLASS (surface
->vertex_class
)->read
) (&new_vertex
, f
);
289 if (f
->type
!= GTS_ERROR
) {
290 if (!GTS_POINT_CLASS (surface
->vertex_class
)->binary
)
291 gts_file_first_token_after (f
, '\n');
292 vertices
[n
++] = GTS_VERTEX (new_vertex
);
295 gts_object_destroy (new_vertex
);
297 if (f
->type
== GTS_ERROR
)
299 if (GTS_POINT_CLASS (surface
->vertex_class
)->binary
)
300 gts_file_first_token_after (f
, '\n');
303 while (n
< ne
&& f
->type
!= GTS_ERROR
) {
306 if (f
->type
!= GTS_INT
)
307 gts_file_error (f
, "expecting an integer (first vertex index)");
309 p1
= atoi (f
->token
->str
);
310 if (p1
== 0 || p1
> nv
)
311 gts_file_error (f
, "vertex index `%d' is out of range `[1,%d]'",
314 gts_file_next_token (f
);
315 if (f
->type
!= GTS_INT
)
316 gts_file_error (f
, "expecting an integer (second vertex index)");
318 p2
= atoi (f
->token
->str
);
319 if (p2
== 0 || p2
> nv
)
320 gts_file_error (f
, "vertex index `%d' is out of range `[1,%d]'",
324 gts_edge_new (surface
->edge_class
,
325 vertices
[p1
- 1], vertices
[p2
- 1]);
327 gts_file_next_token (f
);
329 if (GTS_OBJECT_CLASS (surface
->edge_class
)->read
)
330 (*GTS_OBJECT_CLASS (surface
->edge_class
)->read
)
331 ((GtsObject
**) &new_edge
, f
);
332 gts_file_first_token_after (f
, '\n');
333 edges
[n
++] = new_edge
;
339 if (f
->type
== GTS_ERROR
)
343 while (n
< nf
&& f
->type
!= GTS_ERROR
) {
346 if (f
->type
!= GTS_INT
)
347 gts_file_error (f
, "expecting an integer (first edge index)");
349 s1
= atoi (f
->token
->str
);
350 if (s1
== 0 || s1
> ne
)
351 gts_file_error (f
, "edge index `%d' is out of range `[1,%d]'",
354 gts_file_next_token (f
);
355 if (f
->type
!= GTS_INT
)
356 gts_file_error (f
, "expecting an integer (second edge index)");
358 s2
= atoi (f
->token
->str
);
359 if (s2
== 0 || s2
> ne
)
360 gts_file_error (f
, "edge index `%d' is out of range `[1,%d]'",
363 gts_file_next_token (f
);
364 if (f
->type
!= GTS_INT
)
365 gts_file_error (f
, "expecting an integer (third edge index)");
367 s3
= atoi (f
->token
->str
);
368 if (s3
== 0 || s3
> ne
)
369 gts_file_error (f
, "edge index `%d' is out of range `[1,%d]'",
372 GtsFace
* new_face
= gts_face_new (surface
->face_class
,
377 gts_file_next_token (f
);
379 if (GTS_OBJECT_CLASS (surface
->face_class
)->read
)
380 (*GTS_OBJECT_CLASS (surface
->face_class
)->read
)
381 ((GtsObject
**) &new_face
, f
);
382 gts_file_first_token_after (f
, '\n');
383 gts_surface_add_face (surface
, new_face
);
393 if (f
->type
== GTS_ERROR
) {
394 gts_allow_floating_vertices
= TRUE
;
396 gts_object_destroy (GTS_OBJECT (vertices
[nv
-- - 1]));
397 gts_allow_floating_vertices
= FALSE
;
403 if (f
->type
== GTS_ERROR
)
408 static void sum_area (GtsFace
* f
, gdouble
* area
) {
409 *area
+= gts_triangle_area (GTS_TRIANGLE (f
));
416 * Returns: the area of @s obtained as the sum of the signed areas of its
419 gdouble
gts_surface_area (GtsSurface
* s
)
422 gts_surface_foreach_face (s
, (GtsFunc
)sum_area
, &area
);
430 * Initializes a #GtsRange.
432 void gts_range_init (GtsRange
* r
)
434 g_return_if_fail (r
!= NULL
);
436 r
->max
= - G_MAXDOUBLE
;
437 r
->min
= G_MAXDOUBLE
;
438 r
->sum
= r
->sum2
= 0.0;
446 * Sets all the fields of @r to 0.
448 void gts_range_reset (GtsRange
* r
)
450 g_return_if_fail (r
!= NULL
);
454 r
->sum
= r
->sum2
= 0.0;
459 * gts_range_add_value:
461 * @val: a value to add to @r.
465 void gts_range_add_value (GtsRange
* r
, gdouble val
)
467 g_return_if_fail (r
!= NULL
);
469 if (val
< r
->min
) r
->min
= val
;
470 if (val
> r
->max
) r
->max
= val
;
480 * Updates the fields of @r.
482 void gts_range_update (GtsRange
* r
)
484 g_return_if_fail (r
!= NULL
);
487 if (r
->sum2
- r
->sum
*r
->sum
/(gdouble
) r
->n
>= 0.)
488 r
->stddev
= sqrt ((r
->sum2
- r
->sum
*r
->sum
/(gdouble
) r
->n
)
492 r
->mean
= r
->sum
/(gdouble
) r
->n
;
495 r
->min
= r
->max
= r
->mean
= r
->stddev
= 0.;
501 * @fptr: a file pointer.
503 * Writes a text representation of @r in @fptr.
505 void gts_range_print (GtsRange
* r
, FILE * fptr
)
507 g_return_if_fail (r
!= NULL
);
508 g_return_if_fail (fptr
!= NULL
);
509 fprintf (fptr
, "min: %g mean: %g | %g max: %g",
510 r
->min
, r
->mean
, r
->stddev
, r
->max
);
513 static void stats_foreach_vertex (GtsVertex
* v
, GtsSurfaceStats
* stats
)
515 GSList
* i
= v
->segments
;
519 if (GTS_IS_EDGE (i
->data
) &&
520 gts_edge_has_parent_surface (i
->data
, stats
->parent
))
524 gts_range_add_value (&stats
->edges_per_vertex
, nedges
);
527 static void stats_foreach_edge (GtsEdge
* e
, GtsSurfaceStats
* stats
)
529 guint nt
= gts_edge_face_number (e
, stats
->parent
);
531 if (gts_segment_is_duplicate (GTS_SEGMENT (e
)))
532 stats
->n_duplicate_edges
++;
534 stats
->n_boundary_edges
++;
536 stats
->n_non_manifold_edges
++;
537 gts_range_add_value (&stats
->faces_per_edge
, nt
);
540 static void stats_foreach_face (GtsTriangle
* t
, GtsSurfaceStats
* stats
)
542 if (!gts_face_is_compatible (GTS_FACE (t
), stats
->parent
))
543 stats
->n_incompatible_faces
++;
544 if (gts_triangle_is_duplicate (t
))
545 stats
->n_duplicate_faces
++;
552 * @stats: a #GtsSurfaceStats.
554 * Fills @stats with the statistics relevant to surface @s.
556 void gts_surface_stats (GtsSurface
* s
, GtsSurfaceStats
* stats
)
558 g_return_if_fail (s
!= NULL
);
559 g_return_if_fail (stats
!= NULL
);
563 stats
->n_incompatible_faces
= 0;
564 stats
->n_duplicate_faces
= 0;
565 stats
->n_duplicate_edges
= 0;
566 stats
->n_boundary_edges
= 0;
567 stats
->n_non_manifold_edges
= 0;
568 gts_range_init (&stats
->edges_per_vertex
);
569 gts_range_init (&stats
->faces_per_edge
);
571 gts_surface_foreach_vertex (s
, (GtsFunc
) stats_foreach_vertex
, stats
);
572 gts_surface_foreach_edge (s
, (GtsFunc
) stats_foreach_edge
, stats
);
573 gts_surface_foreach_face (s
, (GtsFunc
) stats_foreach_face
, stats
);
575 gts_range_update (&stats
->edges_per_vertex
);
576 gts_range_update (&stats
->faces_per_edge
);
579 static void quality_foreach_edge (GtsSegment
* s
,
580 GtsSurfaceQualityStats
* stats
)
582 GSList
* i
= GTS_EDGE (s
)->triangles
;
584 gts_range_add_value (&stats
->edge_length
,
585 gts_point_distance (GTS_POINT (s
->v1
),
588 GSList
* j
= i
->next
;
590 gts_range_add_value (&stats
->edge_angle
,
591 fabs (gts_triangles_angle (i
->data
, j
->data
)));
598 static void quality_foreach_face (GtsTriangle
* t
,
599 GtsSurfaceQualityStats
* stats
)
601 gts_range_add_value (&stats
->face_quality
, gts_triangle_quality (t
));
602 gts_range_add_value (&stats
->face_area
, gts_triangle_area (t
));
606 * gts_surface_quality_stats:
608 * @stats: a #GtsSurfaceQualityStats.
610 * Fills @stats with quality statistics relevant to surface @s.
612 void gts_surface_quality_stats (GtsSurface
* s
, GtsSurfaceQualityStats
* stats
)
614 g_return_if_fail (s
!= NULL
);
615 g_return_if_fail (stats
!= NULL
);
618 gts_range_init (&stats
->face_quality
);
619 gts_range_init (&stats
->face_area
);
620 gts_range_init (&stats
->edge_length
);
621 gts_range_init (&stats
->edge_angle
);
623 gts_surface_foreach_edge (s
, (GtsFunc
) quality_foreach_edge
, stats
);
624 gts_surface_foreach_face (s
, (GtsFunc
) quality_foreach_face
, stats
);
626 gts_range_update (&stats
->face_quality
);
627 gts_range_update (&stats
->face_area
);
628 gts_range_update (&stats
->edge_length
);
629 gts_range_update (&stats
->edge_angle
);
633 * gts_surface_print_stats:
635 * @fptr: a file pointer.
637 * Writes in the file pointed to by @fptr the statistics for surface @s.
639 void gts_surface_print_stats (GtsSurface
* s
, FILE * fptr
)
641 GtsSurfaceStats stats
;
642 GtsSurfaceQualityStats qstats
;
644 g_return_if_fail (s
!= NULL
);
645 g_return_if_fail (fptr
!= NULL
);
647 gts_surface_stats (s
, &stats
);
648 gts_surface_quality_stats (s
, &qstats
);
651 "# vertices: %u edges: %u faces: %u\n"
652 "# Connectivity statistics\n"
653 "# incompatible faces: %u\n"
654 "# duplicate faces: %u\n"
655 "# boundary edges: %u\n"
656 "# duplicate edges: %u\n"
657 "# non-manifold edges: %u\n",
658 stats
.edges_per_vertex
.n
,
659 stats
.faces_per_edge
.n
,
661 stats
.n_incompatible_faces
,
662 stats
.n_duplicate_faces
,
663 stats
.n_boundary_edges
,
664 stats
.n_duplicate_edges
,
665 stats
.n_non_manifold_edges
);
666 fputs ("# edges per vertex: ", fptr
);
667 gts_range_print (&stats
.edges_per_vertex
, fptr
);
668 fputs ("\n# faces per edge: ", fptr
);
669 gts_range_print (&stats
.faces_per_edge
, fptr
);
670 fputs ("\n# Geometric statistics\n# face quality: ", fptr
);
671 gts_range_print (&qstats
.face_quality
, fptr
);
672 fputs ("\n# face area : ", fptr
);
673 gts_range_print (&qstats
.face_area
, fptr
);
674 fputs ("\n# edge length : ", fptr
);
675 gts_range_print (&qstats
.edge_length
, fptr
);
679 static void write_vertex (GtsPoint
* p
, gpointer
* data
)
681 (*GTS_OBJECT (p
)->klass
->write
) (GTS_OBJECT (p
), (FILE *) data
[0]);
682 if (!GTS_POINT_CLASS (GTS_OBJECT (p
)->klass
)->binary
)
683 fputc ('\n', (FILE *) data
[0]);
684 g_hash_table_insert (data
[2], p
,
685 GUINT_TO_POINTER (++(*((guint
*) data
[1]))));
688 static void write_edge (GtsSegment
* s
, gpointer
* data
)
690 fprintf ((FILE *) data
[0], "%u %u",
691 GPOINTER_TO_UINT (g_hash_table_lookup (data
[2], s
->v1
)),
692 GPOINTER_TO_UINT (g_hash_table_lookup (data
[2], s
->v2
)));
693 if (GTS_OBJECT (s
)->klass
->write
)
694 (*GTS_OBJECT (s
)->klass
->write
) (GTS_OBJECT (s
), (FILE *) data
[0]);
695 fputc ('\n', (FILE *) data
[0]);
696 g_hash_table_insert (data
[3], s
,
697 GUINT_TO_POINTER (++(*((guint
*) data
[1]))));
700 static void write_face (GtsTriangle
* t
, gpointer
* data
)
702 fprintf (data
[0], "%u %u %u",
703 GPOINTER_TO_UINT (g_hash_table_lookup (data
[3], t
->e1
)),
704 GPOINTER_TO_UINT (g_hash_table_lookup (data
[3], t
->e2
)),
705 GPOINTER_TO_UINT (g_hash_table_lookup (data
[3], t
->e3
)));
706 if (GTS_OBJECT (t
)->klass
->write
)
707 (*GTS_OBJECT (t
)->klass
->write
) (GTS_OBJECT (t
), data
[0]);
708 fputc ('\n', data
[0]);
714 * @fptr: a file pointer.
716 * Writes in the file @fptr an ASCII representation of @s. The file
717 * format is as follows.
719 * All the lines beginning with #GTS_COMMENTS are ignored. The first line
720 * contains three unsigned integers separated by spaces. The first
721 * integer is the number of vertices, nv, the second is the number of
722 * edges, ne and the third is the number of faces, nf.
724 * Follows nv lines containing the x, y and z coordinates of the
725 * vertices. Follows ne lines containing the two indices (starting
726 * from one) of the vertices of each edge. Follows nf lines containing
727 * the three ordered indices (also starting from one) of the edges of
730 * The format described above is the least common denominator to all
731 * GTS files. Consistent with an object-oriented approach, the GTS
732 * file format is extensible. Each of the lines of the file can be
733 * extended with user-specific attributes accessible through the
734 * read() and write() virtual methods of each of the objects written
735 * (surface, vertices, edges or faces). When read with different
736 * object classes, these extra attributes are just ignored.
738 void gts_surface_write (GtsSurface
* s
, FILE * fptr
)
742 GHashTable
* vindex
, * eindex
;
743 GtsSurfaceStats stats
;
745 g_return_if_fail (s
!= NULL
);
746 g_return_if_fail (fptr
!= NULL
);
750 data
[2] = vindex
= g_hash_table_new (NULL
, NULL
);
751 data
[3] = eindex
= g_hash_table_new (NULL
, NULL
);
753 gts_surface_stats (s
, &stats
);
754 fprintf (fptr
, "%u %u %u",
755 stats
.edges_per_vertex
.n
,
756 stats
.faces_per_edge
.n
,
758 if (GTS_OBJECT (s
)->klass
->write
)
759 (*GTS_OBJECT (s
)->klass
->write
) (GTS_OBJECT (s
), fptr
);
762 gts_surface_foreach_vertex (s
, (GtsFunc
) write_vertex
, data
);
764 if (GTS_POINT_CLASS (s
->vertex_class
)->binary
)
766 gts_surface_foreach_edge (s
, (GtsFunc
) write_edge
, data
);
767 gts_surface_foreach_face (s
, (GtsFunc
) write_face
, data
);
768 g_hash_table_destroy (vindex
);
769 g_hash_table_destroy (eindex
);
772 static void write_vertex_oogl (GtsPoint
* p
, gpointer
* data
)
776 fprintf (fp
, "%g %g %g", p
->x
, p
->y
, p
->z
);
777 if (GTS_OBJECT (p
)->klass
->color
) {
778 GtsColor c
= (* GTS_OBJECT (p
)->klass
->color
) (GTS_OBJECT (p
));
779 fprintf (fp
, " %g %g %g 1.0\n", c
.r
, c
.g
, c
.b
);
783 GTS_OBJECT (p
)->reserved
= GUINT_TO_POINTER ((*((guint
*) data
[1]))++);
786 static void write_face_oogl (GtsTriangle
* t
, FILE * fp
)
788 GtsVertex
* v1
, * v2
, * v3
;
789 gts_triangle_vertices (t
, &v1
, &v2
, &v3
);
790 fprintf (fp
, "3 %u %u %u",
791 GPOINTER_TO_UINT (GTS_OBJECT (v1
)->reserved
),
792 GPOINTER_TO_UINT (GTS_OBJECT (v2
)->reserved
),
793 GPOINTER_TO_UINT (GTS_OBJECT (v3
)->reserved
));
794 if (GTS_OBJECT (t
)->klass
->color
) {
795 GtsColor c
= (* GTS_OBJECT (t
)->klass
->color
) (GTS_OBJECT (t
));
796 fprintf (fp
, " %g %g %g\n", c
.r
, c
.g
, c
.b
);
803 * gts_surface_write_oogl:
805 * @fptr: a file pointer.
807 * Writes in the file @fptr an OOGL (Geomview) representation of @s.
809 void gts_surface_write_oogl (GtsSurface
* s
, FILE * fptr
)
813 GtsSurfaceStats stats
;
815 g_return_if_fail (s
!= NULL
);
816 g_return_if_fail (fptr
!= NULL
);
821 gts_surface_stats (s
, &stats
);
822 if (GTS_OBJECT_CLASS (s
->vertex_class
)->color
)
823 fputs ("COFF ", fptr
);
825 fputs ("OFF ", fptr
);
826 fprintf (fptr
, "%u %u %u\n",
827 stats
.edges_per_vertex
.n
,
829 stats
.faces_per_edge
.n
);
830 gts_surface_foreach_vertex (s
, (GtsFunc
) write_vertex_oogl
, data
);
831 gts_surface_foreach_face (s
, (GtsFunc
) write_face_oogl
, fptr
);
832 gts_surface_foreach_vertex (s
, (GtsFunc
) gts_object_reset_reserved
, NULL
);
835 static void write_vertex_vtk (GtsPoint
* p
, gpointer
* data
)
839 fprintf (fp
, "%g %g %g\n", p
->x
, p
->y
, p
->z
);
840 GTS_OBJECT (p
)->reserved
= GUINT_TO_POINTER ((*((guint
*) data
[1]))++);
843 static void write_face_vtk (GtsTriangle
* t
, FILE * fp
)
845 GtsVertex
* v1
, * v2
, * v3
;
846 gts_triangle_vertices (t
, &v1
, &v2
, &v3
);
847 fprintf (fp
, "3 %u %u %u\n",
848 GPOINTER_TO_UINT (GTS_OBJECT (v1
)->reserved
),
849 GPOINTER_TO_UINT (GTS_OBJECT (v2
)->reserved
),
850 GPOINTER_TO_UINT (GTS_OBJECT (v3
)->reserved
));
854 * gts_surface_write_vtk:
856 * @fptr: a file pointer.
858 * Writes in the file @fptr a VTK representation of @s.
860 void gts_surface_write_vtk (GtsSurface
* s
, FILE * fptr
)
864 GtsSurfaceStats stats
;
866 g_return_if_fail (s
!= NULL
);
867 g_return_if_fail (fptr
!= NULL
);
872 gts_surface_stats (s
, &stats
);
874 "# vtk DataFile Version 2.0\n"
879 stats
.edges_per_vertex
.n
);
880 gts_surface_foreach_vertex (s
, (GtsFunc
) write_vertex_vtk
, data
);
883 stats
.n_faces
, stats
.n_faces
*4);
884 gts_surface_foreach_face (s
, (GtsFunc
) write_face_vtk
, fptr
);
885 gts_surface_foreach_vertex (s
, (GtsFunc
) gts_object_reset_reserved
, NULL
);
888 static void write_edge_oogl_boundary (GtsSegment
* s
, gpointer
* data
)
890 if (!gts_edge_is_boundary (GTS_EDGE (s
), data
[1]))
893 if (GTS_OBJECT (s
)->klass
->color
) {
894 GtsColor c
= (* GTS_OBJECT (s
)->klass
->color
) (GTS_OBJECT (s
));
895 fprintf (data
[0], "VECT 1 2 1 2 1 %g %g %g %g %g %g %g %g %g 1.\n",
896 GTS_POINT (s
->v1
)->x
, GTS_POINT (s
->v1
)->y
, GTS_POINT (s
->v1
)->z
,
897 GTS_POINT (s
->v2
)->x
, GTS_POINT (s
->v2
)->y
, GTS_POINT (s
->v2
)->z
,
901 fprintf (data
[0], "VECT 1 2 0 2 0 %g %g %g %g %g %g\n",
902 GTS_POINT (s
->v1
)->x
, GTS_POINT (s
->v1
)->y
, GTS_POINT (s
->v1
)->z
,
903 GTS_POINT (s
->v2
)->x
, GTS_POINT (s
->v2
)->y
, GTS_POINT (s
->v2
)->z
);
907 * gts_surface_write_oogl_boundary:
909 * @fptr: a file pointer.
911 * Writes in the file @fptr an OOGL (Geomview) representation of the
914 void gts_surface_write_oogl_boundary (GtsSurface
* s
, FILE * fptr
)
918 g_return_if_fail (s
!= NULL
);
919 g_return_if_fail (fptr
!= NULL
);
923 fputs ("LIST {\n", fptr
);
924 gts_surface_foreach_edge (s
, (GtsFunc
) write_edge_oogl_boundary
, data
);
928 #ifdef USE_SURFACE_BTREE
929 static gint
vertex_foreach_face (GtsTriangle
* t
,
932 #else /* not USE_SURFACE_BTREE */
933 static void vertex_foreach_face (GtsTriangle
* t
,
936 #endif /* not USE_SURFACE_BTREE */
938 GHashTable
* hash
= info
[0];
939 gpointer data
= info
[1];
940 GtsFunc func
= (GtsFunc
) info
[2];
942 * s1
= GTS_SEGMENT (t
->e1
);
944 if (!g_hash_table_lookup (hash
, s1
->v1
)) {
945 (*func
) (s1
->v1
, data
);
946 g_hash_table_insert (hash
, s1
->v1
, GINT_TO_POINTER (-1));
948 if (!g_hash_table_lookup (hash
, s1
->v2
)) {
949 (*func
) (s1
->v2
, data
);
950 g_hash_table_insert (hash
, s1
->v2
, GINT_TO_POINTER (-1));
952 if (!g_hash_table_lookup (hash
, gts_triangle_vertex (t
))) {
953 (*func
) (gts_triangle_vertex (t
), data
);
954 g_hash_table_insert (hash
, gts_triangle_vertex (t
),
955 GINT_TO_POINTER (-1));
957 #ifdef USE_SURFACE_BTREE
959 #endif /* USE_SURFACE_BTREE */
963 * gts_surface_foreach_vertex:
966 * @data: user data to be passed to @func.
968 * Calls @func once for each vertex of @s.
970 void gts_surface_foreach_vertex (GtsSurface
* s
, GtsFunc func
, gpointer data
)
974 g_return_if_fail (s
!= NULL
);
975 g_return_if_fail (func
!= NULL
);
977 /* forbid removal of faces */
978 s
->keep_faces
= TRUE
;
979 info
[0] = g_hash_table_new (NULL
, NULL
);
982 #ifdef USE_SURFACE_BTREE
983 g_tree_traverse (s
->faces
, (GTraverseFunc
) vertex_foreach_face
, G_IN_ORDER
,
985 #else /* not USE_SURFACE_BTREE */
986 g_hash_table_foreach (s
->faces
, (GHFunc
) vertex_foreach_face
, info
);
987 #endif /* not USE_SURFACE_BTREE */
988 g_hash_table_destroy (info
[0]);
989 /* allow removal of faces */
990 s
->keep_faces
= FALSE
;
993 #ifdef USE_SURFACE_BTREE
994 static gint
edge_foreach_face (GtsTriangle
* t
,
997 #else /* not USE_SURFACE_BTREE */
998 static void edge_foreach_face (GtsTriangle
* t
,
1001 #endif /* not USE_SURFACE_BTREE */
1003 GHashTable
* hash
= info
[0];
1004 gpointer data
= info
[1];
1005 GtsFunc func
= (GtsFunc
) info
[2];
1007 if (!g_hash_table_lookup (hash
, t
->e1
)) {
1008 (*func
) (t
->e1
, data
);
1009 g_hash_table_insert (hash
, t
->e1
, GINT_TO_POINTER (-1));
1011 if (!g_hash_table_lookup (hash
, t
->e2
)) {
1012 (*func
) (t
->e2
, data
);
1013 g_hash_table_insert (hash
, t
->e2
, GINT_TO_POINTER (-1));
1015 if (!g_hash_table_lookup (hash
, t
->e3
)) {
1016 (*func
) (t
->e3
, data
);
1017 g_hash_table_insert (hash
, t
->e3
, GINT_TO_POINTER (-1));
1019 #ifdef USE_SURFACE_BTREE
1021 #endif /* not USE_SURFACE_BTREE */
1025 * gts_surface_foreach_edge:
1026 * @s: a #GtsSurface.
1027 * @func: a #GtsFunc.
1028 * @data: user data to be passed to @func.
1030 * Calls @func once for each edge of @s.
1032 void gts_surface_foreach_edge (GtsSurface
* s
, GtsFunc func
, gpointer data
)
1036 g_return_if_fail (s
!= NULL
);
1037 g_return_if_fail (func
!= NULL
);
1039 /* forbid removal of faces */
1040 s
->keep_faces
= TRUE
;
1041 info
[0] = g_hash_table_new (NULL
, NULL
);
1044 #ifdef USE_SURFACE_BTREE
1045 g_tree_traverse (s
->faces
, (GTraverseFunc
) edge_foreach_face
, G_IN_ORDER
,
1047 #else /* not USE_SURFACE_BTREE */
1048 g_hash_table_foreach (s
->faces
, (GHFunc
) edge_foreach_face
, info
);
1049 #endif /* not USE_SURFACE_BTREE */
1050 g_hash_table_destroy (info
[0]);
1051 /* allow removal of faces */
1052 s
->keep_faces
= FALSE
;
1055 #ifdef USE_SURFACE_BTREE
1056 static gint
foreach_face (GtsFace
* f
,
1059 #else /* not USE_SURFACE_BTREE */
1060 static void foreach_face (GtsFace
* f
,
1063 #endif /* not USE_SURFACE_BTREE */
1065 (*((GtsFunc
) info
[0])) (f
, info
[1]);
1066 #ifdef USE_SURFACE_BTREE
1068 #endif /* USE_SURFACE_BTREE */
1072 * gts_surface_foreach_face:
1073 * @s: a #GtsSurface.
1074 * @func: a #GtsFunc.
1075 * @data: user data to be passed to @func.
1077 * Calls @func once for each face of @s.
1079 void gts_surface_foreach_face (GtsSurface
* s
,
1085 g_return_if_fail (s
!= NULL
);
1086 g_return_if_fail (func
!= NULL
);
1088 /* forbid removal of faces */
1089 s
->keep_faces
= TRUE
;
1092 #ifdef USE_SURFACE_BTREE
1093 g_tree_traverse (s
->faces
, (GTraverseFunc
) foreach_face
, G_IN_ORDER
,
1095 #else /* not USE_SURFACE_BTREE */
1096 g_hash_table_foreach (s
->faces
, (GHFunc
) foreach_face
, info
);
1097 #endif /* not USE_SURFACE_BTREE */
1098 /* allow removal of faces */
1099 s
->keep_faces
= FALSE
;
1102 #ifdef USE_SURFACE_BTREE
1103 static gint
foreach_face_remove (GtsFace
* f
,
1107 if ((*((GtsFunc
) info
[0])) (f
, info
[1])) {
1108 GtsSurface
* s
= info
[2];
1109 guint
* n
= info
[3];
1111 f
->surfaces
= g_slist_remove (f
->surfaces
, s
);
1112 if (!GTS_OBJECT_DESTROYED (f
) &&
1113 !gts_allow_floating_faces
&&
1114 f
->surfaces
== NULL
)
1115 gts_object_destroy (GTS_OBJECT (f
));
1117 if (GTS_SURFACE_CLASS (GTS_OBJECT (s
)->klass
)->remove_face
)
1118 (* GTS_SURFACE_CLASS (GTS_OBJECT (s
)->klass
)->remove_face
) (s
, f
);
1120 g_tree_remove (s
->faces
, f
);
1125 #else /* not USE_SURFACE_BTREE */
1126 static gboolean
foreach_face_remove (GtsFace
* f
,
1130 if ((*((GtsFunc
) info
[0])) (f
, info
[1])) {
1131 GtsSurface
* s
= info
[2];
1133 f
->surfaces
= g_slist_remove (f
->surfaces
, s
);
1134 if (!GTS_OBJECT_DESTROYED (f
) &&
1135 !gts_allow_floating_faces
&&
1136 f
->surfaces
== NULL
)
1137 gts_object_destroy (GTS_OBJECT (f
));
1139 if (GTS_SURFACE_CLASS (GTS_OBJECT (s
)->klass
)->remove_face
)
1140 (* GTS_SURFACE_CLASS (GTS_OBJECT (s
)->klass
)->remove_face
) (s
, f
);
1146 #endif /* not USE_SURFACE_BTREE */
1149 * gts_surface_foreach_face_remove:
1150 * @s: a #GtsSurface.
1151 * @func: a #GtsFunc.
1152 * @data: user data to be passed to @func.
1154 * Calls @func once for each face of @s. If @func returns %TRUE the
1155 * corresponding face is removed from @s (and destroyed if it does not
1156 * belong to any other surface and #gts_allow_floating_faces is set to
1159 * Returns: the number of faces removed from @s.
1161 guint
gts_surface_foreach_face_remove (GtsSurface
* s
,
1168 g_return_val_if_fail (s
!= NULL
, 0);
1169 g_return_val_if_fail (func
!= NULL
, 0);
1171 /* forbid removal of faces */
1172 s
->keep_faces
= TRUE
;
1176 #ifdef USE_SURFACE_BTREE
1178 g_tree_traverse (s
->faces
, (GTraverseFunc
) foreach_face_remove
, G_PRE_ORDER
,
1180 #else /* not USE_SURFACE_BTREE */
1181 n
= g_hash_table_foreach_remove (s
->faces
,
1182 (GHRFunc
) foreach_face_remove
,
1184 #endif /* not USE_SURFACE_BTREE */
1185 /* allow removal of faces */
1186 s
->keep_faces
= FALSE
;
1191 static void midvertex_insertion (GtsEdge
* e
,
1192 GtsSurface
* surface
,
1194 GtsRefineFunc refine_func
,
1195 gpointer refine_data
,
1196 GtsVertexClass
* vertex_class
,
1197 GtsEdgeClass
* edge_class
)
1199 GtsVertex
* midvertex
;
1203 midvertex
= (*refine_func
) (e
, vertex_class
, refine_data
);
1204 e1
= gts_edge_new (edge_class
, GTS_SEGMENT (e
)->v1
, midvertex
);
1205 gts_eheap_insert (heap
, e1
);
1206 e2
= gts_edge_new (edge_class
, GTS_SEGMENT (e
)->v2
, midvertex
);
1207 gts_eheap_insert (heap
, e2
);
1209 /* creates new faces and modifies old ones */
1212 GtsTriangle
* t
= i
->data
;
1213 GtsVertex
* v1
, * v2
, * v3
;
1214 GtsEdge
* te2
, * te3
, * ne
, * tmp
;
1216 gts_triangle_vertices_edges (t
, e
, &v1
, &v2
, &v3
, &e
, &te2
, &te3
);
1217 ne
= gts_edge_new (edge_class
, midvertex
, v3
);
1218 gts_eheap_insert (heap
, ne
);
1219 if (GTS_SEGMENT (e1
)->v1
== v2
) {
1220 tmp
= e1
; e1
= e2
; e2
= tmp
;
1222 e1
->triangles
= g_slist_prepend (e1
->triangles
, t
);
1223 ne
->triangles
= g_slist_prepend (ne
->triangles
, t
);
1224 te2
->triangles
= g_slist_remove (te2
->triangles
, t
);
1225 t
->e1
= e1
; t
->e2
= ne
; t
->e3
= te3
;
1226 gts_surface_add_face (surface
,
1227 gts_face_new (surface
->face_class
, e2
, te2
, ne
));
1231 g_slist_free (e
->triangles
);
1232 e
->triangles
= NULL
;
1233 gts_object_destroy (GTS_OBJECT (e
));
1236 static gdouble
edge_length2_inverse (GtsSegment
* s
)
1238 return - gts_point_distance2 (GTS_POINT (s
->v1
), GTS_POINT (s
->v2
));
1241 static void create_heap_refine (GtsEdge
* e
, GtsEHeap
* heap
)
1243 gts_eheap_insert (heap
, e
);
1247 * gts_surface_refine:
1248 * @surface: a #GtsSurface.
1249 * @cost_func: a function returning the cost for a given edge.
1250 * @cost_data: user data to be passed to @cost_func.
1251 * @refine_func: a #GtsRefineFunc.
1252 * @refine_data: user data to be passed to @refine_func.
1253 * @stop_func: a #GtsStopFunc.
1254 * @stop_data: user data to be passed to @stop_func.
1256 * Refine @surface using a midvertex insertion technique. All the
1257 * edges of @surface are ordered according to @cost_func. The edges
1258 * are then processed in order until @stop_func returns %TRUE. Each
1259 * edge is split in two and new edges and faces are created.
1261 * If @cost_func is set to %NULL, the edges are sorted according
1262 * to their length squared (the longest is on top).
1264 * If @refine_func is set to %NULL gts_segment_midvertex() is used.
1267 void gts_surface_refine (GtsSurface
* surface
,
1268 GtsKeyFunc cost_func
,
1270 GtsRefineFunc refine_func
,
1271 gpointer refine_data
,
1272 GtsStopFunc stop_func
,
1279 g_return_if_fail (surface
!= NULL
);
1280 g_return_if_fail (stop_func
!= NULL
);
1282 if (cost_func
== NULL
)
1283 cost_func
= (GtsKeyFunc
) edge_length2_inverse
;
1284 if (refine_func
== NULL
)
1285 refine_func
= (GtsRefineFunc
) gts_segment_midvertex
;
1287 heap
= gts_eheap_new (cost_func
, cost_data
);
1288 gts_eheap_freeze (heap
);
1289 gts_surface_foreach_edge (surface
, (GtsFunc
) create_heap_refine
, heap
);
1290 gts_eheap_thaw (heap
);
1291 while ((e
= gts_eheap_remove_top (heap
, &top_cost
)) &&
1292 !(*stop_func
) (top_cost
,
1293 gts_eheap_size (heap
) +
1294 gts_edge_face_number (e
, surface
) + 2,
1296 midvertex_insertion (e
, surface
, heap
, refine_func
, refine_data
,
1297 surface
->vertex_class
, surface
->edge_class
);
1298 gts_eheap_destroy (heap
);
1301 static GSList
* edge_triangles (GtsEdge
* e1
, GtsEdge
* e
)
1303 GSList
* i
= e1
->triangles
;
1304 GSList
* triangles
= NULL
;
1307 GtsTriangle
* t
= i
->data
;
1308 if (t
->e1
== e
|| t
->e2
== e
|| t
->e3
== e
) {
1317 else if (t
->e2
== e
) {
1331 GtsTriangle
* t
= j
->data
;
1332 if (t
->e1
!= e
&& t
->e2
!= e
&& t
->e3
!= e
)
1333 triangles
= g_slist_prepend (triangles
, t
);
1338 triangles
= g_slist_prepend (triangles
, t
);
1344 static void replace_vertex (GSList
* i
, GtsVertex
* v1
, GtsVertex
* v
)
1347 GtsSegment
* s
= i
->data
;
1357 * gts_edge_collapse_creates_fold:
1360 * @max: the maximum value of the square of the cosine of the angle between
1363 * Returns: %TRUE if collapsing edge @e to vertex @v would create
1364 * faces making an angle the cosine squared of which would be larger than max,
1367 gboolean
gts_edge_collapse_creates_fold (GtsEdge
* e
,
1371 GtsVertex
* v1
, * v2
;
1374 gboolean folded
= FALSE
;
1376 g_return_val_if_fail (e
!= NULL
, TRUE
);
1377 g_return_val_if_fail (v
!= NULL
, TRUE
);
1379 s
= GTS_SEGMENT (e
);
1382 replace_vertex (v1
->segments
, v1
, v
);
1383 replace_vertex (v2
->segments
, v2
, v
);
1386 while (i
&& !folded
) {
1387 GtsSegment
* s
= i
->data
;
1388 if (GTS_IS_EDGE (s
)) {
1389 GtsEdge
* e1
= GTS_EDGE (s
);
1391 GSList
* triangles
= edge_triangles (e1
, e
);
1392 folded
= gts_triangles_are_folded (triangles
, s
->v1
, s
->v2
, max
);
1393 g_slist_free (triangles
);
1400 while (i
&& !folded
) {
1401 GtsSegment
* s
= i
->data
;
1402 if (GTS_IS_EDGE (s
)) {
1403 GtsEdge
* e1
= GTS_EDGE (s
);
1405 GSList
* triangles
= edge_triangles (e1
, e
);
1406 folded
= gts_triangles_are_folded (triangles
, s
->v1
, s
->v2
, max
);
1407 g_slist_free (triangles
);
1414 GSList
* triangles
= gts_vertex_triangles (v1
, NULL
);
1415 i
= triangles
= gts_vertex_triangles (v2
, triangles
);
1416 while (i
&& !folded
) {
1417 GtsTriangle
* t
= i
->data
;
1418 if (t
->e1
!= e
&& t
->e2
!= e
&& t
->e3
!= e
) {
1419 GtsEdge
* e1
= gts_triangle_edge_opposite (t
, v
);
1421 folded
= gts_triangles_are_folded (e1
->triangles
,
1422 GTS_SEGMENT (e1
)->v1
,
1423 GTS_SEGMENT (e1
)->v2
,
1428 g_slist_free (triangles
);
1431 replace_vertex (v1
->segments
, v
, v1
);
1432 replace_vertex (v2
->segments
, v
, v2
);
1437 * gts_edge_collapse_is_valid:
1440 * An implementation of the topological constraints described in the
1441 * "Mesh Optimization" article of Hoppe et al (1993).
1443 * Returns: %TRUE if @e can be collapsed without violation of the topological
1444 * constraints, %FALSE otherwise.
1446 gboolean
gts_edge_collapse_is_valid (GtsEdge
* e
)
1450 g_return_val_if_fail (e
!= NULL
, FALSE
);
1452 i
= GTS_SEGMENT (e
)->v1
->segments
;
1454 GtsEdge
* e1
= i
->data
;
1455 if (e1
!= e
&& GTS_IS_EDGE (e1
)) {
1456 GtsEdge
* e2
= NULL
;
1457 GSList
* j
= GTS_SEGMENT (e1
)->v1
== GTS_SEGMENT (e
)->v1
?
1458 GTS_SEGMENT (e1
)->v2
->segments
: GTS_SEGMENT (e1
)->v1
->segments
;
1460 GtsEdge
* e1
= j
->data
;
1461 if (GTS_IS_EDGE (e1
) &&
1462 (GTS_SEGMENT (e1
)->v1
== GTS_SEGMENT (e
)->v2
||
1463 GTS_SEGMENT (e1
)->v2
== GTS_SEGMENT (e
)->v2
))
1467 if (e2
&& !gts_triangle_use_edges (e
, e1
, e2
))
1473 if (gts_edge_is_boundary (e
, NULL
)) {
1474 GtsTriangle
* t
= e
->triangles
->data
;
1475 if (gts_edge_is_boundary (t
->e1
, NULL
) &&
1476 gts_edge_is_boundary (t
->e2
, NULL
) &&
1477 gts_edge_is_boundary (t
->e3
, NULL
))
1481 if (gts_vertex_is_boundary (GTS_SEGMENT (e
)->v1
, NULL
) &&
1482 gts_vertex_is_boundary (GTS_SEGMENT (e
)->v2
, NULL
))
1484 if (gts_edge_belongs_to_tetrahedron (e
))
1491 #define HEAP_INSERT_EDGE(h, e) (GTS_OBJECT (e)->reserved = gts_eheap_insert (h, e))
1492 #define HEAP_REMOVE_EDGE(h, e) (gts_eheap_remove (h, GTS_OBJECT (e)->reserved),\
1493 GTS_OBJECT (e)->reserved = NULL)
1495 static GtsVertex
* edge_collapse (GtsEdge
* e
,
1497 GtsCoarsenFunc coarsen_func
,
1498 gpointer coarsen_data
,
1499 GtsVertexClass
* klass
,
1503 GtsVertex
* v1
= GTS_SEGMENT (e
)->v1
, * v2
= GTS_SEGMENT (e
)->v2
, * mid
;
1505 /* if the edge is degenerate (i.e. v1 == v2), destroy and return */
1507 gts_object_destroy (GTS_OBJECT (e
));
1511 if (!gts_edge_collapse_is_valid (e
)) {
1512 GTS_OBJECT (e
)->reserved
=
1513 gts_eheap_insert_with_key (heap
, e
, G_MAXDOUBLE
);
1517 mid
= (*coarsen_func
) (e
, klass
, coarsen_data
);
1519 if (gts_edge_collapse_creates_fold (e
, mid
, maxcosine2
)) {
1520 GTS_OBJECT (e
)->reserved
=
1521 gts_eheap_insert_with_key (heap
, e
, G_MAXDOUBLE
);
1522 gts_object_destroy (GTS_OBJECT (mid
));
1526 gts_object_destroy (GTS_OBJECT (e
));
1528 gts_vertex_replace (v1
, mid
);
1529 gts_object_destroy (GTS_OBJECT (v1
));
1530 gts_vertex_replace (v2
, mid
);
1531 gts_object_destroy (GTS_OBJECT (v2
));
1533 /* destroy duplicate edges */
1536 GtsEdge
* e1
= i
->data
;
1537 GtsEdge
* duplicate
;
1538 while ((duplicate
= gts_edge_is_duplicate (e1
))) {
1539 gts_edge_replace (duplicate
, GTS_EDGE (e1
));
1540 HEAP_REMOVE_EDGE (heap
, duplicate
);
1541 gts_object_destroy (GTS_OBJECT (duplicate
));
1544 if (!e1
->triangles
) {
1545 /* e1 is the result of the collapse of one edge of a pair of identical
1546 faces (it should not happen unless duplicate triangles are present in
1547 the initial surface) */
1548 g_warning ("file %s: line %d (%s): probably duplicate triangle.",
1549 __FILE__
, __LINE__
, G_GNUC_PRETTY_FUNCTION
);
1550 HEAP_REMOVE_EDGE (heap
, e1
);
1551 gts_object_destroy (GTS_OBJECT (e1
));
1552 if (i
== NULL
) /* mid has been destroyed */
1561 * I don't see where this code is ever used, but keep it for a bit
1562 * in case it is needed for debugging
1564 #ifdef GTS_NEED_UPDATE_CLOSEST_NEIGHBORS
1565 static void update_closest_neighbors (GtsVertex
* v
, GtsEHeap
* heap
)
1567 GSList
* i
= v
->segments
;
1570 GtsSegment
* s
= i
->data
;
1571 if (GTS_IS_EDGE (s
)) {
1572 HEAP_REMOVE_EDGE (heap
, GTS_EDGE (s
));
1573 HEAP_INSERT_EDGE (heap
, GTS_EDGE (s
));
1580 static void update_2nd_closest_neighbors (GtsVertex
* v
, GtsEHeap
* heap
)
1582 GSList
* i
= v
->segments
;
1583 GSList
* list
= NULL
;
1586 GtsSegment
* s
= i
->data
;
1587 if (GTS_IS_EDGE (s
)) {
1588 GtsVertex
* v1
= s
->v1
== v
? s
->v2
: s
->v1
;
1589 GSList
* j
= v1
->segments
;
1591 GtsSegment
* s1
= j
->data
;
1592 if (GTS_IS_EDGE (s1
) && !g_slist_find (list
, s1
))
1593 list
= g_slist_prepend (list
, s1
);
1602 GtsEdge
* e
= i
->data
;
1603 HEAP_REMOVE_EDGE (heap
, e
);
1604 HEAP_INSERT_EDGE (heap
, e
);
1608 g_slist_free (list
);
1611 static gdouble
edge_length2 (GtsEdge
* e
)
1613 return gts_point_distance2 (GTS_POINT (GTS_SEGMENT (e
)->v1
),
1614 GTS_POINT (GTS_SEGMENT (e
)->v2
));
1617 static void create_heap_coarsen (GtsEdge
* e
, GtsEHeap
* heap
)
1619 HEAP_INSERT_EDGE (heap
, e
);
1623 * gts_surface_coarsen:
1624 * @surface: a #GtsSurface.
1625 * @cost_func: a function returning the cost for a given edge.
1626 * @cost_data: user data to be passed to @cost_func.
1627 * @coarsen_func: a #GtsCoarsenVertexFunc.
1628 * @coarsen_data: user data to be passed to @coarsen_func.
1629 * @stop_func: a #GtsStopFunc.
1630 * @stop_data: user data to be passed to @stop_func.
1631 * @minangle: minimum angle between two neighboring triangles.
1633 * The edges of @surface are sorted according to @cost_func to
1634 * create a priority heap (a #GtsEHeap). The edges are extracted in
1635 * turn from the top of the heap and collapsed (i.e. the vertices are
1636 * replaced by the vertex returned by the @coarsen_func function)
1637 * until the @stop_func functions returns %TRUE.
1639 * If @cost_func is set to %NULL, the edges are sorted according
1640 * to their length squared (the shortest is on top).
1642 * If @coarsen_func is set to %NULL gts_segment_midvertex() is used.
1644 * The minimum angle is used to avoid introducing faces which would be folded.
1646 void gts_surface_coarsen (GtsSurface
* surface
,
1647 GtsKeyFunc cost_func
,
1649 GtsCoarsenFunc coarsen_func
,
1650 gpointer coarsen_data
,
1651 GtsStopFunc stop_func
,
1660 g_return_if_fail (surface
!= NULL
);
1661 g_return_if_fail (stop_func
!= NULL
);
1663 if (cost_func
== NULL
)
1664 cost_func
= (GtsKeyFunc
) edge_length2
;
1665 if (coarsen_func
== NULL
)
1666 coarsen_func
= (GtsCoarsenFunc
) gts_segment_midvertex
;
1668 heap
= gts_eheap_new (cost_func
, cost_data
);
1669 maxcosine2
= cos (minangle
); maxcosine2
*= maxcosine2
;
1671 gts_eheap_freeze (heap
);
1672 gts_surface_foreach_edge (surface
, (GtsFunc
) create_heap_coarsen
, heap
);
1673 gts_eheap_thaw (heap
);
1674 /* we want to control edge destruction manually */
1675 gts_allow_floating_edges
= TRUE
;
1676 while ((e
= gts_eheap_remove_top (heap
, &top_cost
)) &&
1677 (top_cost
< G_MAXDOUBLE
) &&
1678 !(*stop_func
) (top_cost
, gts_eheap_size (heap
) -
1679 gts_edge_face_number (e
, surface
), stop_data
))
1681 GtsVertex
* v
= edge_collapse (e
, heap
, coarsen_func
, coarsen_data
,
1682 surface
->vertex_class
, maxcosine2
);
1684 update_2nd_closest_neighbors (v
, heap
);
1686 gts_allow_floating_edges
= FALSE
;
1688 /* set reserved field of remaining edges back to NULL */
1689 if (e
) GTS_OBJECT (e
)->reserved
= NULL
;
1690 gts_eheap_foreach (heap
, (GFunc
) gts_object_reset_reserved
, NULL
);
1692 gts_eheap_destroy (heap
);
1696 * gts_coarsen_stop_number:
1697 * @cost: the cost of the edge collapse considered.
1698 * @nedge: the current number of edges of the surface being simplified.
1699 * @min_number: a pointer to the minimum number of edges desired for the
1700 * surface being simplified.
1702 * This function is to be used as the @stop_func argument of
1703 * gts_surface_coarsen() or gts_psurface_new().
1705 * Returns: %TRUE if the edge collapse would create a surface with a smaller
1706 * number of edges than given by @min_number, %FALSE otherwise.
1708 gboolean
gts_coarsen_stop_number (gdouble cost
,
1712 g_return_val_if_fail (min_number
!= NULL
, TRUE
);
1714 if (nedge
< *min_number
)
1720 * gts_coarsen_stop_cost:
1721 * @cost: the cost of the edge collapse considered.
1722 * @nedge: the current number of edges of the surface being simplified.
1723 * @max_cost: a pointer to the maximum cost allowed for an edge collapse.
1725 * This function is to be used as the @stop_func argument of
1726 * gts_surface_coarsen() or gts_psurface_new().
1728 * Returns: %TRUE if the cost of the edge collapse considered is larger than
1729 * given by @max_cost, %FALSE otherwise.
1731 gboolean
gts_coarsen_stop_cost (gdouble cost
,
1735 g_return_val_if_fail (max_cost
!= NULL
, TRUE
);
1737 if (cost
> *max_cost
)
1742 #define GTS_M_ICOSAHEDRON_X /* sqrt(sqrt(5)+1)/sqrt(2*sqrt(5)) */ \
1743 0.850650808352039932181540497063011072240401406
1744 #define GTS_M_ICOSAHEDRON_Y /* sqrt(2)/sqrt(5+sqrt(5)) */ \
1745 0.525731112119133606025669084847876607285497935
1746 #define GTS_M_ICOSAHEDRON_Z 0.0
1748 static guint
generate_icosahedron (GtsSurface
* s
)
1750 GtsVertex
* v01
= gts_vertex_new (s
->vertex_class
,
1751 +GTS_M_ICOSAHEDRON_Z
, +GTS_M_ICOSAHEDRON_X
, -GTS_M_ICOSAHEDRON_Y
);
1752 GtsVertex
* v02
= gts_vertex_new (s
->vertex_class
,
1753 +GTS_M_ICOSAHEDRON_X
, +GTS_M_ICOSAHEDRON_Y
, +GTS_M_ICOSAHEDRON_Z
);
1754 GtsVertex
* v03
= gts_vertex_new (s
->vertex_class
,
1755 +GTS_M_ICOSAHEDRON_Y
, +GTS_M_ICOSAHEDRON_Z
, -GTS_M_ICOSAHEDRON_X
);
1756 GtsVertex
* v04
= gts_vertex_new (s
->vertex_class
,
1757 +GTS_M_ICOSAHEDRON_Y
, +GTS_M_ICOSAHEDRON_Z
, +GTS_M_ICOSAHEDRON_X
);
1758 GtsVertex
* v05
= gts_vertex_new (s
->vertex_class
,
1759 +GTS_M_ICOSAHEDRON_X
, -GTS_M_ICOSAHEDRON_Y
, +GTS_M_ICOSAHEDRON_Z
);
1760 GtsVertex
* v06
= gts_vertex_new (s
->vertex_class
,
1761 +GTS_M_ICOSAHEDRON_Z
, +GTS_M_ICOSAHEDRON_X
, +GTS_M_ICOSAHEDRON_Y
);
1762 GtsVertex
* v07
= gts_vertex_new (s
->vertex_class
,
1763 -GTS_M_ICOSAHEDRON_Y
, +GTS_M_ICOSAHEDRON_Z
, +GTS_M_ICOSAHEDRON_X
);
1764 GtsVertex
* v08
= gts_vertex_new (s
->vertex_class
,
1765 +GTS_M_ICOSAHEDRON_Z
, -GTS_M_ICOSAHEDRON_X
, -GTS_M_ICOSAHEDRON_Y
);
1766 GtsVertex
* v09
= gts_vertex_new (s
->vertex_class
,
1767 -GTS_M_ICOSAHEDRON_X
, +GTS_M_ICOSAHEDRON_Y
, +GTS_M_ICOSAHEDRON_Z
);
1768 GtsVertex
* v10
= gts_vertex_new (s
->vertex_class
,
1769 -GTS_M_ICOSAHEDRON_Y
, +GTS_M_ICOSAHEDRON_Z
, -GTS_M_ICOSAHEDRON_X
);
1770 GtsVertex
* v11
= gts_vertex_new (s
->vertex_class
,
1771 -GTS_M_ICOSAHEDRON_X
, -GTS_M_ICOSAHEDRON_Y
, +GTS_M_ICOSAHEDRON_Z
);
1772 GtsVertex
* v12
= gts_vertex_new (s
->vertex_class
,
1773 +GTS_M_ICOSAHEDRON_Z
, -GTS_M_ICOSAHEDRON_X
, +GTS_M_ICOSAHEDRON_Y
);
1775 GtsEdge
* e01
= gts_edge_new (s
->edge_class
, v01
, v02
);
1776 GtsEdge
* e02
= gts_edge_new (s
->edge_class
, v03
, v02
);
1777 GtsEdge
* e03
= gts_edge_new (s
->edge_class
, v01
, v03
);
1778 GtsEdge
* e04
= gts_edge_new (s
->edge_class
, v04
, v05
);
1779 GtsEdge
* e05
= gts_edge_new (s
->edge_class
, v02
, v05
);
1780 GtsEdge
* e06
= gts_edge_new (s
->edge_class
, v04
, v02
);
1781 GtsEdge
* e07
= gts_edge_new (s
->edge_class
, v06
, v07
);
1782 GtsEdge
* e08
= gts_edge_new (s
->edge_class
, v04
, v07
);
1783 GtsEdge
* e09
= gts_edge_new (s
->edge_class
, v06
, v04
);
1784 GtsEdge
* e10
= gts_edge_new (s
->edge_class
, v08
, v03
);
1785 GtsEdge
* e11
= gts_edge_new (s
->edge_class
, v03
, v05
);
1786 GtsEdge
* e12
= gts_edge_new (s
->edge_class
, v08
, v05
);
1787 GtsEdge
* e13
= gts_edge_new (s
->edge_class
, v06
, v09
);
1788 GtsEdge
* e14
= gts_edge_new (s
->edge_class
, v07
, v09
);
1789 GtsEdge
* e15
= gts_edge_new (s
->edge_class
, v08
, v10
);
1790 GtsEdge
* e16
= gts_edge_new (s
->edge_class
, v03
, v10
);
1791 GtsEdge
* e17
= gts_edge_new (s
->edge_class
, v06
, v01
);
1792 GtsEdge
* e18
= gts_edge_new (s
->edge_class
, v01
, v09
);
1793 GtsEdge
* e19
= gts_edge_new (s
->edge_class
, v08
, v11
);
1794 GtsEdge
* e20
= gts_edge_new (s
->edge_class
, v10
, v11
);
1795 GtsEdge
* e21
= gts_edge_new (s
->edge_class
, v06
, v02
);
1796 GtsEdge
* e22
= gts_edge_new (s
->edge_class
, v12
, v11
);
1797 GtsEdge
* e23
= gts_edge_new (s
->edge_class
, v12
, v08
);
1798 GtsEdge
* e24
= gts_edge_new (s
->edge_class
, v12
, v07
);
1799 GtsEdge
* e25
= gts_edge_new (s
->edge_class
, v07
, v11
);
1800 GtsEdge
* e26
= gts_edge_new (s
->edge_class
, v12
, v04
);
1801 GtsEdge
* e27
= gts_edge_new (s
->edge_class
, v09
, v11
);
1802 GtsEdge
* e28
= gts_edge_new (s
->edge_class
, v10
, v09
);
1803 GtsEdge
* e29
= gts_edge_new (s
->edge_class
, v12
, v05
);
1804 GtsEdge
* e30
= gts_edge_new (s
->edge_class
, v01
, v10
);
1806 gts_surface_add_face (s
, gts_face_new (s
->face_class
, e01
, e02
, e03
));
1807 gts_surface_add_face (s
, gts_face_new (s
->face_class
, e04
, e05
, e06
));
1808 gts_surface_add_face (s
, gts_face_new (s
->face_class
, e07
, e08
, e09
));
1809 gts_surface_add_face (s
, gts_face_new (s
->face_class
, e10
, e11
, e12
));
1810 gts_surface_add_face (s
, gts_face_new (s
->face_class
, e13
, e14
, e07
));
1811 gts_surface_add_face (s
, gts_face_new (s
->face_class
, e15
, e16
, e10
));
1812 gts_surface_add_face (s
, gts_face_new (s
->face_class
, e17
, e18
, e13
));
1813 gts_surface_add_face (s
, gts_face_new (s
->face_class
, e19
, e20
, e15
));
1814 gts_surface_add_face (s
, gts_face_new (s
->face_class
, e21
, e01
, e17
));
1815 gts_surface_add_face (s
, gts_face_new (s
->face_class
, e22
, e19
, e23
));
1816 gts_surface_add_face (s
, gts_face_new (s
->face_class
, e09
, e06
, e21
));
1817 gts_surface_add_face (s
, gts_face_new (s
->face_class
, e24
, e25
, e22
));
1818 gts_surface_add_face (s
, gts_face_new (s
->face_class
, e26
, e08
, e24
));
1819 gts_surface_add_face (s
, gts_face_new (s
->face_class
, e20
, e27
, e28
));
1820 gts_surface_add_face (s
, gts_face_new (s
->face_class
, e29
, e04
, e26
));
1821 gts_surface_add_face (s
, gts_face_new (s
->face_class
, e14
, e27
, e25
));
1822 gts_surface_add_face (s
, gts_face_new (s
->face_class
, e23
, e12
, e29
));
1823 gts_surface_add_face (s
, gts_face_new (s
->face_class
, e02
, e05
, e11
));
1824 gts_surface_add_face (s
, gts_face_new (s
->face_class
, e30
, e28
, e18
));
1825 gts_surface_add_face (s
, gts_face_new (s
->face_class
, e03
, e16
, e30
));
1830 static GtsVertex
* unit_sphere_arc_midvertex (GtsSegment
* s
,
1831 GtsVertexClass
* vertex_class
)
1833 GtsPoint
* p1
, * p2
;
1834 gdouble x
, y
, z
, norm
;
1836 p1
= GTS_POINT (s
->v1
); p2
= GTS_POINT (s
->v2
);
1838 x
= 0.5*(p1
->x
+ p2
->x
);
1839 y
= 0.5*(p1
->y
+ p2
->y
);
1840 z
= 0.5*(p1
->z
+ p2
->z
);
1842 norm
= x
*x
+ y
*y
+ z
*z
;
1845 x
/= norm
; y
/= norm
; z
/= norm
;
1847 return gts_vertex_new (vertex_class
, x
, y
, z
);
1850 static void tessellate_face (GtsFace
* f
,
1852 GtsRefineFunc refine_func
,
1853 gpointer refine_data
,
1854 GtsVertexClass
* vertex_class
,
1855 GtsEdgeClass
* edge_class
)
1858 GtsEdge
* e1
, * e2
, * e3
; /* former edges */
1859 GtsVertex
* v1
, * v2
, * v3
; /* initial vertices */
1860 GtsVertex
* v4
, * v5
, * v6
; /* new vertices */
1861 GtsEdge
* e56
, * e64
, * e45
; /* new inside edges */
1862 GtsEdge
* e24
, * e34
, * e35
, * e15
, * e16
, * e26
; /* new border edges */
1866 t
= GTS_TRIANGLE (f
);
1867 e1
= t
->e1
; e2
= t
->e2
; e3
= t
->e3
;
1869 if (GTS_SEGMENT (e1
)->v2
== GTS_SEGMENT (e2
)->v1
) {
1870 v1
= GTS_SEGMENT (e2
)->v2
;
1871 v2
= GTS_SEGMENT (e1
)->v1
;
1872 v3
= GTS_SEGMENT (e1
)->v2
;
1874 else if (GTS_SEGMENT (e1
)->v2
== GTS_SEGMENT (e2
)->v2
) {
1875 v1
= GTS_SEGMENT (e2
)->v1
;
1876 v2
= GTS_SEGMENT (e1
)->v1
;
1877 v3
= GTS_SEGMENT (e1
)->v2
;
1879 else if (GTS_SEGMENT (e1
)->v1
== GTS_SEGMENT (e2
)->v1
) {
1880 v1
= GTS_SEGMENT (e2
)->v2
;
1881 v2
= GTS_SEGMENT (e1
)->v2
;
1882 v3
= GTS_SEGMENT (e1
)->v1
;
1884 else if (GTS_SEGMENT (e1
)->v1
== GTS_SEGMENT (e2
)->v2
) {
1885 v1
= GTS_SEGMENT (e2
)->v1
;
1886 v2
= GTS_SEGMENT (e1
)->v2
;
1887 v3
= GTS_SEGMENT (e1
)->v1
;
1890 v1
= v2
= v3
= NULL
;
1891 g_assert_not_reached ();
1894 e1
->triangles
= g_slist_remove (e1
->triangles
, t
);
1895 e2
->triangles
= g_slist_remove (e2
->triangles
, t
);
1896 e3
->triangles
= g_slist_remove (e3
->triangles
, t
);
1898 if (GTS_OBJECT (e1
)->reserved
) {
1899 dum
= (GTS_OBJECT (e1
)->reserved
);
1901 e34
= dum
->next
->data
;
1902 v4
= GTS_SEGMENT (e24
)->v2
;
1903 if (GTS_SEGMENT (e24
)->v1
== v3
) {
1904 edum
= e34
; e34
= e24
; e24
= edum
;
1908 v4
= (*refine_func
) (e1
, vertex_class
, refine_data
);
1909 e24
= gts_edge_new (edge_class
, v2
, v4
);
1910 e34
= gts_edge_new (edge_class
, v3
, v4
);
1911 dum
= g_slist_append (NULL
, e24
);
1912 dum
= g_slist_append (dum
, e34
);
1913 GTS_OBJECT (e1
)->reserved
= dum
;
1915 if (GTS_OBJECT (e2
)->reserved
) {
1916 dum
= (GTS_OBJECT (e2
)->reserved
);
1918 e15
= dum
->next
->data
;
1919 v5
= GTS_SEGMENT (e35
)->v2
;
1920 if (GTS_SEGMENT (e35
)->v1
== v1
) {
1921 edum
= e15
; e15
= e35
; e35
= edum
;
1925 v5
= (*refine_func
) (e2
, vertex_class
, refine_data
);
1926 e35
= gts_edge_new (edge_class
, v3
, v5
);
1927 e15
= gts_edge_new (edge_class
, v1
, v5
);
1928 dum
= g_slist_append (NULL
, e35
);
1929 dum
= g_slist_append (dum
, e15
);
1930 GTS_OBJECT (e2
)->reserved
= dum
;
1932 if (GTS_OBJECT (e3
)->reserved
) {
1933 dum
= (GTS_OBJECT (e3
)->reserved
);
1935 e26
= dum
->next
->data
;
1936 v6
= GTS_SEGMENT (e16
)->v2
;
1937 if (GTS_SEGMENT (e16
)->v1
== v2
) {
1938 edum
= e16
; e16
= e26
; e26
= edum
;
1942 v6
= (*refine_func
) (e3
, vertex_class
, refine_data
);
1943 e16
= gts_edge_new (edge_class
, v1
, v6
);
1944 e26
= gts_edge_new (edge_class
, v2
, v6
);
1945 dum
= g_slist_append (NULL
, e16
);
1946 dum
= g_slist_append (dum
, e26
);
1947 GTS_OBJECT (e3
)->reserved
= dum
;
1950 if (e1
->triangles
== NULL
) {
1951 g_slist_free (GTS_OBJECT (e1
)->reserved
);
1952 GTS_OBJECT (e1
)->reserved
= NULL
;
1953 gts_object_destroy (GTS_OBJECT (e1
));
1956 if (e2
->triangles
== NULL
) {
1957 g_slist_free (GTS_OBJECT (e2
)->reserved
);
1958 GTS_OBJECT (e2
)->reserved
= NULL
;
1959 gts_object_destroy (GTS_OBJECT (e2
));
1962 if (e3
->triangles
== NULL
) {
1963 g_slist_free (GTS_OBJECT (e3
)->reserved
);
1964 GTS_OBJECT (e3
)->reserved
= NULL
;
1965 gts_object_destroy (GTS_OBJECT (e3
));
1969 e56
= gts_edge_new (edge_class
, v5
, v6
);
1970 e64
= gts_edge_new (edge_class
, v6
, v4
);
1971 e45
= gts_edge_new (edge_class
, v4
, v5
);
1972 t
->e1
= e56
; e56
->triangles
= g_slist_prepend (e56
->triangles
, t
);
1973 t
->e2
= e64
; e64
->triangles
= g_slist_prepend (e64
->triangles
, t
);
1974 t
->e3
= e45
; e45
->triangles
= g_slist_prepend (e45
->triangles
, t
);
1976 gts_surface_add_face (s
, gts_face_new (s
->face_class
, e16
, e56
, e15
));
1977 gts_surface_add_face (s
, gts_face_new (s
->face_class
, e26
, e24
, e64
));
1978 gts_surface_add_face (s
, gts_face_new (s
->face_class
, e45
, e34
, e35
));
1981 static void create_array_tessellate (GtsFace
* f
, GPtrArray
* array
)
1983 g_ptr_array_add (array
, f
);
1987 * gts_surface_tessellate:
1988 * @s: a #GtsSurface.
1989 * @refine_func: a #GtsRefineFunc.
1990 * @refine_data: user data to be passed to @refine_func.
1992 * Tessellate each triangle of @s with 4 triangles:
1993 * the number of triangles is increased by a factor of 4.
1994 * http://mathworld.wolfram.com/GeodesicDome.html
1996 * If @refine_func is set to %NULL a mid arc function is used: if
1997 * the surface is a polyhedron with the unit sphere as circum sphere,
1998 * then gts_surface_tessellate() corresponds to a geodesation step
1999 * (see gts_surface_generate_sphere()).
2002 void gts_surface_tessellate (GtsSurface
* s
,
2003 GtsRefineFunc refine_func
,
2004 gpointer refine_data
)
2009 g_return_if_fail (s
!= NULL
);
2011 if (refine_func
== NULL
) /* tessellate_surface == geodesate_surface */
2012 refine_func
= (GtsRefineFunc
) unit_sphere_arc_midvertex
;
2014 array
= g_ptr_array_new ();
2015 gts_surface_foreach_face (s
, (GtsFunc
) create_array_tessellate
, array
);
2016 for(i
= 0; i
< array
->len
; i
++)
2017 tessellate_face (g_ptr_array_index (array
, i
),
2018 s
, refine_func
, refine_data
,
2019 s
->vertex_class
, s
->edge_class
);
2020 g_ptr_array_free (array
, TRUE
);
2024 * gts_surface_generate_sphere:
2025 * @s: a #GtsSurface.
2026 * @geodesation_order: a #guint.
2028 * Add a triangulated unit sphere generated by recursive subdivision to @s.
2029 * First approximation is an isocahedron; each level of refinement
2030 * (@geodesation_order) increases the number of triangles by a factor of 4.
2031 * http://mathworld.wolfram.com/GeodesicDome.html
2035 GtsSurface
* gts_surface_generate_sphere (GtsSurface
* s
,
2036 guint geodesation_order
)
2040 g_return_val_if_fail (s
!= NULL
, NULL
);
2041 g_return_val_if_fail (geodesation_order
!= 0, NULL
);
2043 generate_icosahedron (s
);
2045 for (cgo
= 1; cgo
< geodesation_order
; cgo
++)
2046 gts_surface_tessellate (s
, NULL
, NULL
);
2051 static void foreach_vertex_copy (GtsPoint
* p
, GtsVertexClass
* klass
)
2053 GTS_OBJECT (p
)->reserved
= gts_vertex_new (klass
, p
->x
, p
->y
, p
->z
);
2056 static void foreach_edge_copy (GtsSegment
* s
, GtsEdgeClass
* klass
)
2058 GTS_OBJECT (s
)->reserved
= gts_edge_new (klass
,
2059 GTS_OBJECT (s
->v1
)->reserved
,
2060 GTS_OBJECT (s
->v2
)->reserved
);
2063 static void foreach_face_copy (GtsTriangle
* t
,
2066 gts_surface_add_face (s
, gts_face_new (s
->face_class
,
2067 GTS_OBJECT (t
->e1
)->reserved
,
2068 GTS_OBJECT (t
->e2
)->reserved
,
2069 GTS_OBJECT (t
->e3
)->reserved
));
2074 * @s1: a #GtsSurface.
2075 * @s2: a #GtsSurface.
2077 * Add a copy of all the faces, edges and vertices of @s2 to @s1.
2081 GtsSurface
* gts_surface_copy (GtsSurface
* s1
, GtsSurface
* s2
)
2083 g_return_val_if_fail (s1
!= NULL
, NULL
);
2084 g_return_val_if_fail (s2
!= NULL
, NULL
);
2086 gts_surface_foreach_vertex (s2
, (GtsFunc
) foreach_vertex_copy
,
2088 gts_surface_foreach_edge (s2
, (GtsFunc
) foreach_edge_copy
, s1
->edge_class
);
2089 gts_surface_foreach_face (s2
, (GtsFunc
) foreach_face_copy
, s1
);
2091 gts_surface_foreach_vertex (s2
, (GtsFunc
) gts_object_reset_reserved
, NULL
);
2092 gts_surface_foreach_edge (s2
, (GtsFunc
) gts_object_reset_reserved
, NULL
);
2097 static void merge_foreach_face (GtsFace
* f
,
2100 gts_surface_add_face (s
, f
);
2104 * gts_surface_merge:
2105 * @s: a #GtsSurface.
2106 * @with: another #GtsSurface.
2108 * Adds all the faces of @with which do not already belong to @s
2111 void gts_surface_merge (GtsSurface
* s
, GtsSurface
* with
)
2113 g_return_if_fail (s
!= NULL
);
2114 g_return_if_fail (with
!= NULL
);
2116 gts_surface_foreach_face (with
, (GtsFunc
) merge_foreach_face
, s
);
2119 static void manifold_foreach_edge (GtsEdge
* e
, gpointer
* data
)
2121 gboolean
* is_manifold
= data
[0];
2124 if (gts_edge_face_number (e
, data
[1]) > 2)
2125 *is_manifold
= FALSE
;
2130 * gts_surface_is_manifold:
2131 * @s: a #GtsSurface.
2133 * Returns: %TRUE if the surface is a manifold, %FALSE otherwise.
2135 gboolean
gts_surface_is_manifold (GtsSurface
* s
)
2137 gboolean is_manifold
= TRUE
;
2140 g_return_val_if_fail (s
!= NULL
, FALSE
);
2142 data
[0] = &is_manifold
;
2144 gts_surface_foreach_edge (s
, (GtsFunc
) manifold_foreach_edge
, data
);
2148 static void closed_foreach_edge (GtsEdge
* e
, gpointer
* data
)
2150 gboolean
* is_closed
= data
[0];
2153 if (gts_edge_face_number (e
, data
[1]) != 2)
2159 * gts_surface_is_closed:
2160 * @s: a #GtsSurface.
2162 * Returns: %TRUE if @s is a closed surface, %FALSE otherwise. Note that a
2163 * closed surface is also a manifold.
2165 gboolean
gts_surface_is_closed (GtsSurface
* s
)
2167 gboolean is_closed
= TRUE
;
2170 g_return_val_if_fail (s
!= NULL
, FALSE
);
2172 data
[0] = &is_closed
;
2174 gts_surface_foreach_edge (s
, (GtsFunc
) closed_foreach_edge
, data
);
2178 static void orientable_foreach_edge (GtsEdge
* e
, gpointer
* data
)
2180 gboolean
* is_orientable
= data
[0];
2182 if (*is_orientable
) {
2183 GtsSurface
* surface
= data
[1];
2184 GtsFace
* f1
= NULL
, * f2
= NULL
;
2185 GSList
* i
= e
->triangles
;
2186 while (i
&& *is_orientable
) {
2187 GtsFace
* f
= i
->data
;
2188 if (GTS_IS_FACE (f
) && gts_face_has_parent_surface (f
, surface
)) {
2190 else if (!f2
) f2
= f
;
2191 else *is_orientable
= FALSE
;
2195 if (f1
&& f2
&& !gts_triangles_are_compatible (GTS_TRIANGLE (f1
),
2196 GTS_TRIANGLE (f2
), e
))
2197 *is_orientable
= FALSE
;
2202 * gts_surface_is_orientable:
2203 * @s: a #GtsSurface.
2205 * Returns: %TRUE if all the faces of @s have compatible orientation
2206 * as checked by gts_faces_are_compatible(), %FALSE otherwise. Note that
2207 * an orientable surface is also a manifold.
2209 gboolean
gts_surface_is_orientable (GtsSurface
* s
)
2211 gboolean is_orientable
= TRUE
;
2214 g_return_val_if_fail (s
!= NULL
, FALSE
);
2216 data
[0] = &is_orientable
;
2218 gts_surface_foreach_edge (s
, (GtsFunc
) orientable_foreach_edge
, data
);
2219 return is_orientable
;
2222 static void volume_foreach_face (GtsTriangle
* t
,
2225 GtsVertex
* va
, * vb
, * vc
;
2226 GtsPoint
* pa
, * pb
, * pc
;
2228 gts_triangle_vertices (t
, &va
, &vb
, &vc
);
2229 pa
= GTS_POINT (va
);
2230 pb
= GTS_POINT (vb
);
2231 pc
= GTS_POINT (vc
);
2233 *volume
+= (pa
->x
* (pb
->y
* pc
->z
- pb
->z
* pc
->y
) +
2234 pb
->x
* (pc
->y
* pa
->z
- pc
->z
* pa
->y
) +
2235 pc
->x
* (pa
->y
* pb
->z
- pa
->z
* pb
->y
));
2239 * gts_surface_volume:
2240 * @s: a #GtsSurface.
2242 * Returns: the signed volume of the domain bounded by the surface @s. It
2243 * makes sense only if @s is a closed and orientable manifold.
2245 gdouble
gts_surface_volume (GtsSurface
* s
)
2247 gdouble volume
= 0.0;
2249 g_return_val_if_fail (s
!= NULL
, 0.0);
2251 gts_surface_foreach_face (s
, (GtsFunc
) volume_foreach_face
, &volume
);
2256 static void center_of_mass_foreach_face (GtsTriangle
* t
,
2259 GtsVertex
* v1
, * v2
, * v3
;
2260 GtsPoint
* p1
, * p2
, * p3
;
2261 gdouble x1
, y1
, z1
, x2
, y2
, z2
, nx
, ny
, nz
;
2262 gdouble
* volume
= data
[0];
2263 gdouble
* cm
= data
[1];
2265 gts_triangle_vertices (t
, &v1
, &v2
, &v3
);
2266 p1
= GTS_POINT (v1
);
2267 p2
= GTS_POINT (v2
);
2268 p3
= GTS_POINT (v3
);
2282 cm
[0] += nx
*(p1
->x
*p1
->x
+ p2
->x
*p2
->x
+ p3
->x
*p3
->x
+
2283 p1
->x
*p2
->x
+ p1
->x
*p3
->x
+ p2
->x
*p3
->x
);
2284 cm
[1] += ny
*(p1
->y
*p1
->y
+ p2
->y
*p2
->y
+ p3
->y
*p3
->y
+
2285 p1
->y
*p2
->y
+ p1
->y
*p3
->y
+ p2
->y
*p3
->y
);
2286 cm
[2] += nz
*(p1
->z
*p1
->z
+ p2
->z
*p2
->z
+ p3
->z
*p3
->z
+
2287 p1
->z
*p2
->z
+ p1
->z
*p3
->z
+ p2
->z
*p3
->z
);
2289 *volume
+= nx
*(p1
->x
+ p2
->x
+ p3
->x
);
2294 * gts_surface_center_of_mass:
2295 * @s: a #GtsSurface.
2296 * @cm: a #GtsVector.
2298 * Fills @cm with the coordinates of the center of mass of @s.
2300 * Returns: the signed volume of the domain bounded by the surface @s.
2302 gdouble
gts_surface_center_of_mass (GtsSurface
* s
,
2305 gdouble volume
= 0.;
2308 g_return_val_if_fail (s
!= NULL
, 0.0);
2312 cm
[0] = cm
[1] = cm
[2] = 0.;
2313 gts_surface_foreach_face (s
, (GtsFunc
) center_of_mass_foreach_face
, data
);
2324 static void center_of_area_foreach_face (GtsTriangle
* t
,
2327 GtsVertex
* v1
, * v2
, * v3
;
2328 GtsPoint
* p1
, * p2
, * p3
;
2330 gdouble
* area
= data
[0];
2331 gdouble
* cm
= data
[1];
2333 gts_triangle_vertices (t
, &v1
, &v2
, &v3
);
2334 p1
= GTS_POINT (v1
);
2335 p2
= GTS_POINT (v2
);
2336 p3
= GTS_POINT (v3
);
2338 a
= gts_triangle_area (t
);
2339 cm
[0] += a
*(p1
->x
+ p2
->x
+ p3
->x
);
2340 cm
[1] += a
*(p1
->y
+ p2
->y
+ p3
->y
);
2341 cm
[2] += a
*(p1
->z
+ p2
->z
+ p3
->z
);
2347 * gts_surface_center_of_area:
2348 * @s: a #GtsSurface.
2349 * @cm: a #GtsVector.
2351 * Fills @cm with the coordinates of the center of area of @s.
2353 * Returns: the area of surface @s.
2355 gdouble
gts_surface_center_of_area (GtsSurface
* s
,
2361 g_return_val_if_fail (s
!= NULL
, 0.0);
2365 cm
[0] = cm
[1] = cm
[2] = 0.;
2366 gts_surface_foreach_face (s
, (GtsFunc
) center_of_area_foreach_face
, data
);
2377 static void number_foreach (gpointer data
, guint
* n
)
2383 * gts_surface_vertex_number:
2384 * @s: a #GtsSurface.
2386 * Returns: the number of vertices of @s.
2388 guint
gts_surface_vertex_number (GtsSurface
* s
)
2392 g_return_val_if_fail (s
!= NULL
, 0);
2394 gts_surface_foreach_vertex (s
, (GtsFunc
) number_foreach
, &n
);
2400 * gts_surface_edge_number:
2401 * @s: a #GtsSurface.
2403 * Returns: the number of edges of @s.
2405 guint
gts_surface_edge_number (GtsSurface
* s
)
2409 g_return_val_if_fail (s
!= NULL
, 0);
2411 gts_surface_foreach_edge (s
, (GtsFunc
) number_foreach
, &n
);
2417 * gts_surface_face_number:
2418 * @s: a #GtsSurface.
2420 * Returns: the number of faces of @s
2422 guint
gts_surface_face_number (GtsSurface
* s
)
2424 g_return_val_if_fail (s
!= NULL
, 0);
2426 #ifdef USE_SURFACE_BTREE
2427 return g_tree_nnodes (s
->faces
);
2428 #else /* not USE_SURFACE_BTREE */
2429 return g_hash_table_size (s
->faces
);
2430 #endif /* not USE_SURFACE_BTREE */
2433 static void build_list_face (GtsTriangle
* t
, GSList
** list
)
2435 *list
= g_slist_prepend (*list
, gts_bbox_triangle (gts_bbox_class (), t
));
2438 static void build_list_boundary (GtsEdge
* e
, GSList
** list
)
2440 if (gts_edge_is_boundary (e
, NULL
))
2441 *list
= g_slist_prepend (*list
, gts_bbox_segment (gts_bbox_class (),
2446 * gts_surface_distance:
2447 * @s1: a #GtsSurface.
2448 * @s2: a #GtsSurface.
2449 * @delta: a spatial increment defined as the percentage of the diagonal
2450 * of the bounding box of @s2.
2451 * @face_range: a #GtsRange.
2452 * @boundary_range: a #GtsRange.
2454 * Using the gts_bb_tree_surface_distance() and
2455 * gts_bb_tree_surface_boundary_distance() functions fills @face_range
2456 * and @boundary_range with the min, max and average Euclidean
2457 * (minimum) distances between the faces of @s1 and the faces of @s2
2458 * and between the boundary edges of @s1 and @s2.
2460 void gts_surface_distance (GtsSurface
* s1
, GtsSurface
* s2
, gdouble delta
,
2461 GtsRange
* face_range
, GtsRange
* boundary_range
)
2463 GNode
* face_tree
, * boundary_tree
;
2466 g_return_if_fail (s1
!= NULL
);
2467 g_return_if_fail (s2
!= NULL
);
2468 g_return_if_fail (delta
> 0. && delta
< 1.);
2469 g_return_if_fail (face_range
!= NULL
);
2470 g_return_if_fail (boundary_range
!= NULL
);
2473 gts_surface_foreach_face (s2
, (GtsFunc
) build_list_face
, &bboxes
);
2474 if (bboxes
!= NULL
) {
2475 face_tree
= gts_bb_tree_new (bboxes
);
2476 g_slist_free (bboxes
);
2478 gts_bb_tree_surface_distance (face_tree
, s1
,
2479 (GtsBBoxDistFunc
) gts_point_triangle_distance
,
2481 gts_bb_tree_destroy (face_tree
, TRUE
);
2484 gts_surface_foreach_edge (s2
, (GtsFunc
) build_list_boundary
, &bboxes
);
2485 if (bboxes
!= NULL
) {
2486 boundary_tree
= gts_bb_tree_new (bboxes
);
2487 g_slist_free (bboxes
);
2489 gts_bb_tree_surface_boundary_distance (boundary_tree
,
2491 (GtsBBoxDistFunc
) gts_point_segment_distance
,
2492 delta
, boundary_range
);
2493 gts_bb_tree_destroy (boundary_tree
, TRUE
);
2496 gts_range_reset (boundary_range
);
2499 gts_range_reset (face_range
);
2500 gts_range_reset (boundary_range
);
2504 static void surface_boundary (GtsEdge
* e
, gpointer
* data
)
2506 GSList
** list
= data
[0];
2508 if (gts_edge_is_boundary (e
, data
[1]))
2509 *list
= g_slist_prepend (*list
, e
);
2513 * gts_surface_boundary:
2514 * @surface: a #GtsSurface.
2516 * Returns: a list of #GtsEdge boundary of @surface.
2518 GSList
* gts_surface_boundary (GtsSurface
* surface
)
2520 GSList
* list
= NULL
;
2523 g_return_val_if_fail (surface
!= NULL
, NULL
);
2527 gts_surface_foreach_edge (surface
, (GtsFunc
) surface_boundary
, data
);
2532 struct _GtsSurfaceTraverse
{
2538 * gts_surface_traverse_new:
2539 * @s: a #GtsSurface.
2540 * @f: a #GtsFace belonging to @s.
2542 * Returns: a new #GtsSurfaceTraverse, initialized to start traversing
2543 * from face @f of surface @s.
2545 GtsSurfaceTraverse
* gts_surface_traverse_new (GtsSurface
* s
,
2548 GtsSurfaceTraverse
* t
;
2550 g_return_val_if_fail (s
!= NULL
, NULL
);
2551 g_return_val_if_fail (f
!= NULL
, NULL
);
2552 g_return_val_if_fail (gts_face_has_parent_surface (f
, s
), NULL
);
2554 t
= g_malloc (sizeof (GtsSurfaceTraverse
));
2555 t
->q
= gts_fifo_new ();
2557 GTS_OBJECT (f
)->reserved
= GUINT_TO_POINTER (1);
2558 gts_fifo_push (t
->q
, f
);
2562 static void push_neighbor (GtsFace
* v
, gpointer
* data
)
2564 if (!GTS_OBJECT (v
)->reserved
) {
2565 GTS_OBJECT (v
)->reserved
=
2566 GUINT_TO_POINTER (GPOINTER_TO_UINT (GTS_OBJECT (data
[1])->reserved
) + 1);
2567 gts_fifo_push (data
[0], v
);
2572 * gts_surface_traverse_next:
2573 * @t: a #GtsSurfaceTraverse.
2574 * @level: a pointer to a guint or %NULL.
2576 * Returns: the next face of the traversal in breadth-first order or
2577 * %NULL if no faces are left. If @level if not %NULL, it is filled
2578 * with the level of the returned face (0 for the initial face, 1 for
2579 * its neighbors and so on).
2581 GtsFace
* gts_surface_traverse_next (GtsSurfaceTraverse
* t
,
2586 g_return_val_if_fail (t
!= NULL
, NULL
);
2588 u
= gts_fifo_pop (t
->q
);
2593 *level
= GPOINTER_TO_UINT (GTS_OBJECT (u
)->reserved
);
2596 gts_face_foreach_neighbor (u
, t
->s
, (GtsFunc
) push_neighbor
, data
);
2602 * gts_surface_traverse_destroy:
2603 * @t: a #GtsSurfaceTraverse.
2605 * Frees all the memory allocated for @t.
2607 void gts_surface_traverse_destroy (GtsSurfaceTraverse
* t
)
2609 g_return_if_fail (t
!= NULL
);
2611 gts_surface_foreach_face (t
->s
, (GtsFunc
) gts_object_reset_reserved
, NULL
);
2612 gts_fifo_destroy (t
->q
);
2616 static void traverse_manifold (GtsTriangle
* t
, GtsSurface
* s
)
2618 if (g_slist_length (GTS_FACE (t
)->surfaces
) > 1)
2621 gts_surface_add_face (s
, GTS_FACE (t
));
2622 if (g_slist_length (t
->e1
->triangles
) == 2) {
2623 if (t
->e1
->triangles
->data
!= t
)
2624 traverse_manifold (t
->e1
->triangles
->data
, s
);
2626 traverse_manifold (t
->e1
->triangles
->next
->data
, s
);
2628 if (g_slist_length (t
->e2
->triangles
) == 2) {
2629 if (t
->e2
->triangles
->data
!= t
)
2630 traverse_manifold (t
->e2
->triangles
->data
, s
);
2632 traverse_manifold (t
->e2
->triangles
->next
->data
, s
);
2634 if (g_slist_length (t
->e3
->triangles
) == 2) {
2635 if (t
->e3
->triangles
->data
!= t
)
2636 traverse_manifold (t
->e3
->triangles
->data
, s
);
2638 traverse_manifold (t
->e3
->triangles
->next
->data
, s
);
2642 static void non_manifold_edges (GtsEdge
* e
, gpointer
* data
)
2644 GtsSurface
* s
= data
[0];
2645 GSList
** non_manifold
= data
[1];
2647 if (gts_edge_face_number (e
, s
) > 2) {
2648 GSList
* i
= e
->triangles
;
2651 if (gts_face_has_parent_surface (i
->data
, s
) &&
2652 !g_slist_find (*non_manifold
, i
->data
))
2653 *non_manifold
= g_slist_prepend (*non_manifold
, i
->data
);
2659 static void traverse_boundary (GtsEdge
* e
, gpointer
* data
)
2661 GtsSurface
* orig
= data
[0];
2662 GSList
** components
= data
[1];
2663 GtsFace
* f
= gts_edge_is_boundary (e
, orig
);
2665 if (f
!= NULL
&& g_slist_length (f
->surfaces
) == 1) {
2667 gts_surface_new (GTS_SURFACE_CLASS (GTS_OBJECT (orig
)->klass
),
2670 orig
->vertex_class
);
2671 GSList
* non_manifold
= NULL
, * i
;
2674 *components
= g_slist_prepend (*components
, s
);
2676 data
[1] = &non_manifold
;
2677 traverse_manifold (GTS_TRIANGLE (f
), s
);
2679 gts_surface_foreach_edge (s
, (GtsFunc
) non_manifold_edges
, data
);
2682 gts_surface_remove_face (s
, i
->data
);
2685 g_slist_free (non_manifold
);
2689 static void traverse_remaining (GtsFace
* f
, gpointer
* data
)
2691 GtsSurface
* orig
= data
[0];
2692 GSList
** components
= data
[1];
2694 if (g_slist_length (f
->surfaces
) == 1) {
2696 gts_surface_new (GTS_SURFACE_CLASS (GTS_OBJECT (orig
)->klass
),
2699 orig
->vertex_class
);
2700 GSList
* non_manifold
= NULL
, * i
;
2703 *components
= g_slist_prepend (*components
, s
);
2705 data
[1] = &non_manifold
;
2706 traverse_manifold (GTS_TRIANGLE (f
), s
);
2708 gts_surface_foreach_edge (s
, (GtsFunc
) non_manifold_edges
, data
);
2711 gts_surface_remove_face (s
, i
->data
);
2714 g_slist_free (non_manifold
);
2719 * gts_surface_split:
2720 * @s: a #GtsSurface.
2722 * Splits a surface into connected and manifold components.
2724 * Returns: a list of new #GtsSurface.
2726 GSList
* gts_surface_split (GtsSurface
* s
)
2729 GSList
* components
= NULL
;
2731 g_return_val_if_fail (s
!= NULL
, NULL
);
2734 data
[1] = &components
;
2736 /* boundary components */
2737 gts_surface_foreach_edge (s
, (GtsFunc
) traverse_boundary
, data
);
2739 /* remaining components */
2740 gts_surface_foreach_face (s
, (GtsFunc
) traverse_remaining
, data
);