action.c: UpdatePackage() reloads from disk
[geda-pcb/whiteaudio.git] / gts / surface.c
blob34c5cbe7f39ffeaf991e0447dc43d5fe741d045c
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.
20 #include <stdlib.h>
21 #include <math.h>
22 #include <string.h>
23 #include "gts.h"
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 ?
56 "GtsVertexBinary" :
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))
72 return -1;
73 if (GPOINTER_TO_UINT (a) > GPOINTER_TO_UINT (b))
74 return 1;
75 return 0;
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;
92 /**
93 * gts_surface_class:
95 * Returns: the #GtsSurfaceClass.
97 GtsSurfaceClass * gts_surface_class (void)
99 static GtsSurfaceClass * klass = NULL;
101 if (klass == NULL) {
102 GtsObjectClassInfo surface_info = {
103 "GtsSurface",
104 sizeof (GtsSurface),
105 sizeof (GtsSurfaceClass),
106 (GtsObjectClassInitFunc) surface_class_init,
107 (GtsObjectInitFunc) surface_init,
108 (GtsArgSetFunc) NULL,
109 (GtsArgGetFunc) NULL
111 klass = gts_object_class_new (gts_object_class (), &surface_info);
114 return klass;
118 * gts_surface_new:
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)
131 GtsSurface * s;
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;
138 return s;
142 * gts_surface_add_face:
143 * @s: a #GtsSurface.
144 * @f: a #GtsFace.
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:
173 * @s: a #GtsSurface.
174 * @f: a #GtsFace.
176 * Removes face @f from surface @s.
178 void gts_surface_remove_face (GtsSurface * s,
179 GtsFace * f)
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 &&
199 f->surfaces == NULL)
200 gts_object_destroy (GTS_OBJECT (f));
204 * gts_surface_read:
205 * @surface: a #GtsSurface.
206 * @f: a #GtsFile.
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;
219 GtsEdge ** edges;
220 guint n, nv, ne, nf;
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)");
227 return f->line;
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)");
234 return f->line;
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)");
241 return f->line;
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)");
249 return f->line;
251 gts_file_next_token (f);
252 if (f->type != GTS_STRING) {
253 gts_file_error (f, "expecting a string (GtsFaceClass)");
254 return f->line;
256 gts_file_next_token (f);
257 if (f->type != GTS_STRING) {
258 gts_file_error (f, "expecting a string (GtsEdgeClass)");
259 return f->line;
261 gts_file_next_token (f);
262 if (f->type != GTS_STRING) {
263 gts_file_error (f, "expecting a string (GtsVertexClass)");
264 return f->line;
266 if (!strcmp (f->token->str, "GtsVertexBinary"))
267 GTS_POINT_CLASS (surface->vertex_class)->binary = TRUE;
268 else {
269 GTS_POINT_CLASS (surface->vertex_class)->binary = FALSE;
270 gts_file_first_token_after (f, '\n');
273 else
274 gts_file_first_token_after (f, '\n');
276 if (nf <= 0)
277 return 0;
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 *));
283 n = 0;
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);
294 else
295 gts_object_destroy (new_vertex);
297 if (f->type == GTS_ERROR)
298 nv = n;
299 if (GTS_POINT_CLASS (surface->vertex_class)->binary)
300 gts_file_first_token_after (f, '\n');
302 n = 0;
303 while (n < ne && f->type != GTS_ERROR) {
304 guint p1, p2;
306 if (f->type != GTS_INT)
307 gts_file_error (f, "expecting an integer (first vertex index)");
308 else {
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]'",
312 p1, nv);
313 else {
314 gts_file_next_token (f);
315 if (f->type != GTS_INT)
316 gts_file_error (f, "expecting an integer (second vertex index)");
317 else {
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]'",
321 p2, nv);
322 else {
323 GtsEdge * new_edge =
324 gts_edge_new (surface->edge_class,
325 vertices[p1 - 1], vertices[p2 - 1]);
327 gts_file_next_token (f);
328 if (f->type != '\n')
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)
340 ne = n;
342 n = 0;
343 while (n < nf && f->type != GTS_ERROR) {
344 guint s1, s2, s3;
346 if (f->type != GTS_INT)
347 gts_file_error (f, "expecting an integer (first edge index)");
348 else {
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]'",
352 s1, ne);
353 else {
354 gts_file_next_token (f);
355 if (f->type != GTS_INT)
356 gts_file_error (f, "expecting an integer (second edge index)");
357 else {
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]'",
361 s2, ne);
362 else {
363 gts_file_next_token (f);
364 if (f->type != GTS_INT)
365 gts_file_error (f, "expecting an integer (third edge index)");
366 else {
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]'",
370 s3, ne);
371 else {
372 GtsFace * new_face = gts_face_new (surface->face_class,
373 edges[s1 - 1],
374 edges[s2 - 1],
375 edges[s3 - 1]);
377 gts_file_next_token (f);
378 if (f->type != '\n')
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);
384 n++;
393 if (f->type == GTS_ERROR) {
394 gts_allow_floating_vertices = TRUE;
395 while (nv)
396 gts_object_destroy (GTS_OBJECT (vertices[nv-- - 1]));
397 gts_allow_floating_vertices = FALSE;
400 g_free (vertices);
401 g_free (edges);
403 if (f->type == GTS_ERROR)
404 return f->line;
405 return 0;
408 static void sum_area (GtsFace * f, gdouble * area) {
409 *area += gts_triangle_area (GTS_TRIANGLE (f));
413 * gts_surface_area:
414 * @s: a #GtsSurface.
416 * Returns: the area of @s obtained as the sum of the signed areas of its
417 * faces.
419 gdouble gts_surface_area (GtsSurface * s)
421 gdouble area = 0.0;
422 gts_surface_foreach_face (s, (GtsFunc)sum_area, &area);
423 return area;
427 * gts_range_init:
428 * @r: a #GtsRange.
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;
439 r->n = 0;
443 * gts_range_reset:
444 * @r: a #GtsRange.
446 * Sets all the fields of @r to 0.
448 void gts_range_reset (GtsRange * r)
450 g_return_if_fail (r != NULL);
452 r->max = 0.0;
453 r->min = 0.0;
454 r->sum = r->sum2 = 0.0;
455 r->n = 0;
459 * gts_range_add_value:
460 * @r: a #GtsRange.
461 * @val: a value to add to @r.
463 * Adds @val 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;
471 r->sum += val;
472 r->sum2 += val*val;
473 r->n++;
477 * gts_range_update:
478 * @r: a #GtsRange.
480 * Updates the fields of @r.
482 void gts_range_update (GtsRange * r)
484 g_return_if_fail (r != NULL);
486 if (r->n > 0) {
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)
489 /(gdouble) r->n);
490 else
491 r->stddev = 0.;
492 r->mean = r->sum/(gdouble) r->n;
494 else
495 r->min = r->max = r->mean = r->stddev = 0.;
499 * gts_range_print:
500 * @r: a #GtsRange.
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;
516 guint nedges = 0;
518 while (i) {
519 if (GTS_IS_EDGE (i->data) &&
520 gts_edge_has_parent_surface (i->data, stats->parent))
521 nedges++;
522 i = i->next;
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++;
533 if (nt == 1)
534 stats->n_boundary_edges++;
535 else if (nt > 2)
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++;
546 stats->n_faces++;
550 * gts_surface_stats:
551 * @s: a #GtsSurface.
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);
561 stats->parent = s;
562 stats->n_faces = 0;
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),
586 GTS_POINT (s->v2)));
587 while (i) {
588 GSList * j = i->next;
589 while (j) {
590 gts_range_add_value (&stats->edge_angle,
591 fabs (gts_triangles_angle (i->data, j->data)));
592 j = j->next;
594 i = i->next;
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:
607 * @s: a #GtsSurface.
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);
617 stats->parent = s;
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:
634 * @s: a #GtsSurface.
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);
650 fprintf (fptr,
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,
660 stats.n_faces,
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);
676 fputc ('\n', 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]);
712 * gts_surface_write:
713 * @s: a #GtsSurface.
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
728 * each face.
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)
740 guint n;
741 gpointer data[4];
742 GHashTable * vindex, * eindex;
743 GtsSurfaceStats stats;
745 g_return_if_fail (s != NULL);
746 g_return_if_fail (fptr != NULL);
748 data[0] = fptr;
749 data[1] = &n;
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,
757 stats.n_faces);
758 if (GTS_OBJECT (s)->klass->write)
759 (*GTS_OBJECT (s)->klass->write) (GTS_OBJECT (s), fptr);
760 fputc ('\n', fptr);
761 n = 0;
762 gts_surface_foreach_vertex (s, (GtsFunc) write_vertex, data);
763 n = 0;
764 if (GTS_POINT_CLASS (s->vertex_class)->binary)
765 fputc ('\n', fptr);
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)
774 FILE * fp = data[0];
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);
781 else
782 fputc ('\n', fp);
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);
798 else
799 fputc ('\n', fp);
803 * gts_surface_write_oogl:
804 * @s: a #GtsSurface.
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)
811 guint n = 0;
812 gpointer data[2];
813 GtsSurfaceStats stats;
815 g_return_if_fail (s != NULL);
816 g_return_if_fail (fptr != NULL);
818 data[0] = fptr;
819 data[1] = &n;
821 gts_surface_stats (s, &stats);
822 if (GTS_OBJECT_CLASS (s->vertex_class)->color)
823 fputs ("COFF ", fptr);
824 else
825 fputs ("OFF ", fptr);
826 fprintf (fptr, "%u %u %u\n",
827 stats.edges_per_vertex.n,
828 stats.n_faces,
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)
837 FILE * fp = data[0];
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:
855 * @s: a #GtsSurface.
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)
862 guint n = 0;
863 gpointer data[2];
864 GtsSurfaceStats stats;
866 g_return_if_fail (s != NULL);
867 g_return_if_fail (fptr != NULL);
869 data[0] = fptr;
870 data[1] = &n;
872 gts_surface_stats (s, &stats);
873 fprintf (fptr,
874 "# vtk DataFile Version 2.0\n"
875 "Generated by GTS\n"
876 "ASCII\n"
877 "DATASET POLYDATA\n"
878 "POINTS %u float\n",
879 stats.edges_per_vertex.n);
880 gts_surface_foreach_vertex (s, (GtsFunc) write_vertex_vtk, data);
881 fprintf (fptr,
882 "POLYGONS %u %u\n",
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]))
891 return;
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,
898 c.r, c.g, c.b);
900 else
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:
908 * @s: a #GtsSurface.
909 * @fptr: a file pointer.
911 * Writes in the file @fptr an OOGL (Geomview) representation of the
912 * boundary of @s.
914 void gts_surface_write_oogl_boundary (GtsSurface * s, FILE * fptr)
916 gpointer data[2];
918 g_return_if_fail (s != NULL);
919 g_return_if_fail (fptr != NULL);
921 data[0] = fptr;
922 data[1] = s;
923 fputs ("LIST {\n", fptr);
924 gts_surface_foreach_edge (s, (GtsFunc) write_edge_oogl_boundary, data);
925 fputs ("}\n", fptr);
928 #ifdef USE_SURFACE_BTREE
929 static gint vertex_foreach_face (GtsTriangle * t,
930 gpointer t_data,
931 gpointer * info)
932 #else /* not USE_SURFACE_BTREE */
933 static void vertex_foreach_face (GtsTriangle * t,
934 gpointer t_data,
935 gpointer * info)
936 #endif /* not USE_SURFACE_BTREE */
938 GHashTable * hash = info[0];
939 gpointer data = info[1];
940 GtsFunc func = (GtsFunc) info[2];
941 GtsSegment
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
958 return FALSE;
959 #endif /* USE_SURFACE_BTREE */
963 * gts_surface_foreach_vertex:
964 * @s: a #GtsSurface.
965 * @func: a #GtsFunc.
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)
972 gpointer info[3];
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);
980 info[1] = data;
981 info[2] = func;
982 #ifdef USE_SURFACE_BTREE
983 g_tree_traverse (s->faces, (GTraverseFunc) vertex_foreach_face, G_IN_ORDER,
984 info);
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,
995 gpointer t_data,
996 gpointer * info)
997 #else /* not USE_SURFACE_BTREE */
998 static void edge_foreach_face (GtsTriangle * t,
999 gpointer t_data,
1000 gpointer * info)
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
1020 return FALSE;
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)
1034 gpointer info[3];
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);
1042 info[1] = data;
1043 info[2] = func;
1044 #ifdef USE_SURFACE_BTREE
1045 g_tree_traverse (s->faces, (GTraverseFunc) edge_foreach_face, G_IN_ORDER,
1046 info);
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,
1057 gpointer t_data,
1058 gpointer * info)
1059 #else /* not USE_SURFACE_BTREE */
1060 static void foreach_face (GtsFace * f,
1061 gpointer t_data,
1062 gpointer * info)
1063 #endif /* not USE_SURFACE_BTREE */
1065 (*((GtsFunc) info[0])) (f, info[1]);
1066 #ifdef USE_SURFACE_BTREE
1067 return FALSE;
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,
1080 GtsFunc func,
1081 gpointer data)
1083 gpointer info[2];
1085 g_return_if_fail (s != NULL);
1086 g_return_if_fail (func != NULL);
1088 /* forbid removal of faces */
1089 s->keep_faces = TRUE;
1090 info[0] = func;
1091 info[1] = data;
1092 #ifdef USE_SURFACE_BTREE
1093 g_tree_traverse (s->faces, (GTraverseFunc) foreach_face, G_IN_ORDER,
1094 info);
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,
1104 gpointer t_data,
1105 gpointer * info)
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);
1121 (*n)++;
1123 return FALSE;
1125 #else /* not USE_SURFACE_BTREE */
1126 static gboolean foreach_face_remove (GtsFace * f,
1127 gpointer t_data,
1128 gpointer * info)
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);
1142 return TRUE;
1144 return FALSE;
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
1157 * %FALSE).
1159 * Returns: the number of faces removed from @s.
1161 guint gts_surface_foreach_face_remove (GtsSurface * s,
1162 GtsFunc func,
1163 gpointer data)
1165 gpointer info[4];
1166 guint n = 0;
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;
1173 info[0] = func;
1174 info[1] = data;
1175 info[2] = s;
1176 #ifdef USE_SURFACE_BTREE
1177 info[3] = &n;
1178 g_tree_traverse (s->faces, (GTraverseFunc) foreach_face_remove, G_PRE_ORDER,
1179 info);
1180 #else /* not USE_SURFACE_BTREE */
1181 n = g_hash_table_foreach_remove (s->faces,
1182 (GHRFunc) foreach_face_remove,
1183 info);
1184 #endif /* not USE_SURFACE_BTREE */
1185 /* allow removal of faces */
1186 s->keep_faces = FALSE;
1188 return n;
1191 static void midvertex_insertion (GtsEdge * e,
1192 GtsSurface * surface,
1193 GtsEHeap * heap,
1194 GtsRefineFunc refine_func,
1195 gpointer refine_data,
1196 GtsVertexClass * vertex_class,
1197 GtsEdgeClass * edge_class)
1199 GtsVertex * midvertex;
1200 GtsEdge * e1, * e2;
1201 GSList * i;
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 */
1210 i = e->triangles;
1211 while (i) {
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));
1228 i = i->next;
1230 /* destroys edge */
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,
1269 gpointer cost_data,
1270 GtsRefineFunc refine_func,
1271 gpointer refine_data,
1272 GtsStopFunc stop_func,
1273 gpointer stop_data)
1275 GtsEHeap * heap;
1276 GtsEdge * e;
1277 gdouble top_cost;
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,
1295 stop_data))
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;
1306 while (i) {
1307 GtsTriangle * t = i->data;
1308 if (t->e1 == e || t->e2 == e || t->e3 == e) {
1309 GtsEdge * e2;
1310 GSList * j;
1311 if (t->e1 == e) {
1312 if (t->e2 == e1)
1313 e2 = t->e3;
1314 else
1315 e2 = t->e2;
1317 else if (t->e2 == e) {
1318 if (t->e3 == e1)
1319 e2 = t->e1;
1320 else
1321 e2 = t->e3;
1323 else {
1324 if (t->e2 == e1)
1325 e2 = t->e1;
1326 else
1327 e2 = t->e2;
1329 j = e2->triangles;
1330 while (j) {
1331 GtsTriangle * t = j->data;
1332 if (t->e1 != e && t->e2 != e && t->e3 != e)
1333 triangles = g_slist_prepend (triangles, t);
1334 j = j->next;
1337 else
1338 triangles = g_slist_prepend (triangles, t);
1339 i = i->next;
1341 return triangles;
1344 static void replace_vertex (GSList * i, GtsVertex * v1, GtsVertex * v)
1346 while (i) {
1347 GtsSegment * s = i->data;
1348 if (s->v1 == v1)
1349 s->v1 = v;
1350 else
1351 s->v2 = v;
1352 i = i->next;
1357 * gts_edge_collapse_creates_fold:
1358 * @e: a #GtsEdge.
1359 * @v: a #GtsVertex.
1360 * @max: the maximum value of the square of the cosine of the angle between
1361 * two triangles.
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,
1365 * %FALSE otherwise.
1367 gboolean gts_edge_collapse_creates_fold (GtsEdge * e,
1368 GtsVertex * v,
1369 gdouble max)
1371 GtsVertex * v1, * v2;
1372 GtsSegment * s;
1373 GSList * i;
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);
1380 v1 = s->v1;
1381 v2 = s->v2;
1382 replace_vertex (v1->segments, v1, v);
1383 replace_vertex (v2->segments, v2, v);
1385 i = v1->segments;
1386 while (i && !folded) {
1387 GtsSegment * s = i->data;
1388 if (GTS_IS_EDGE (s)) {
1389 GtsEdge * e1 = GTS_EDGE (s);
1390 if (e1 != e) {
1391 GSList * triangles = edge_triangles (e1, e);
1392 folded = gts_triangles_are_folded (triangles, s->v1, s->v2, max);
1393 g_slist_free (triangles);
1396 i = i->next;
1399 i = v2->segments;
1400 while (i && !folded) {
1401 GtsSegment * s = i->data;
1402 if (GTS_IS_EDGE (s)) {
1403 GtsEdge * e1 = GTS_EDGE (s);
1404 if (e1 != e) {
1405 GSList * triangles = edge_triangles (e1, e);
1406 folded = gts_triangles_are_folded (triangles, s->v1, s->v2, max);
1407 g_slist_free (triangles);
1410 i = i->next;
1412 #if 1
1413 if (!folded) {
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);
1420 g_assert (e1);
1421 folded = gts_triangles_are_folded (e1->triangles,
1422 GTS_SEGMENT (e1)->v1,
1423 GTS_SEGMENT (e1)->v2,
1424 max);
1426 i = i->next;
1428 g_slist_free (triangles);
1430 #endif
1431 replace_vertex (v1->segments, v, v1);
1432 replace_vertex (v2->segments, v, v2);
1433 return folded;
1437 * gts_edge_collapse_is_valid:
1438 * @e: a #GtsEdge.
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)
1448 GSList * i;
1450 g_return_val_if_fail (e != NULL, FALSE);
1452 i = GTS_SEGMENT (e)->v1->segments;
1453 while (i) {
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;
1459 while (j && !e2) {
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))
1464 e2 = e1;
1465 j = j->next;
1467 if (e2 && !gts_triangle_use_edges (e, e1, e2))
1468 return FALSE;
1470 i = i->next;
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))
1478 return FALSE;
1480 else {
1481 if (gts_vertex_is_boundary (GTS_SEGMENT (e)->v1, NULL) &&
1482 gts_vertex_is_boundary (GTS_SEGMENT (e)->v2, NULL))
1483 return FALSE;
1484 if (gts_edge_belongs_to_tetrahedron (e))
1485 return FALSE;
1488 return TRUE;
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,
1496 GtsEHeap * heap,
1497 GtsCoarsenFunc coarsen_func,
1498 gpointer coarsen_data,
1499 GtsVertexClass * klass,
1500 gdouble maxcosine2)
1502 GSList * i;
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 */
1506 if (v1 == v2) {
1507 gts_object_destroy (GTS_OBJECT (e));
1508 return NULL;
1511 if (!gts_edge_collapse_is_valid (e)) {
1512 GTS_OBJECT (e)->reserved =
1513 gts_eheap_insert_with_key (heap, e, G_MAXDOUBLE);
1514 return NULL;
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));
1523 return NULL;
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 */
1534 i = mid->segments;
1535 while (i) {
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));
1543 i = i->next;
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 */
1553 mid = NULL;
1557 return mid;
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;
1569 while (i) {
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));
1575 i = i->next;
1578 #endif
1580 static void update_2nd_closest_neighbors (GtsVertex * v, GtsEHeap * heap)
1582 GSList * i = v->segments;
1583 GSList * list = NULL;
1585 while (i) {
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;
1590 while (j) {
1591 GtsSegment * s1 = j->data;
1592 if (GTS_IS_EDGE (s1) && !g_slist_find (list, s1))
1593 list = g_slist_prepend (list, s1);
1594 j = j->next;
1597 i = i->next;
1600 i = list;
1601 while (i) {
1602 GtsEdge * e = i->data;
1603 HEAP_REMOVE_EDGE (heap, e);
1604 HEAP_INSERT_EDGE (heap, e);
1605 i = i->next;
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,
1648 gpointer cost_data,
1649 GtsCoarsenFunc coarsen_func,
1650 gpointer coarsen_data,
1651 GtsStopFunc stop_func,
1652 gpointer stop_data,
1653 gdouble minangle)
1655 GtsEHeap * heap;
1656 GtsEdge * e;
1657 gdouble top_cost;
1658 gdouble maxcosine2;
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);
1683 if (v != NULL)
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,
1709 guint nedge,
1710 guint * min_number)
1712 g_return_val_if_fail (min_number != NULL, TRUE);
1714 if (nedge < *min_number)
1715 return TRUE;
1716 return FALSE;
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,
1732 guint nedge,
1733 gdouble * max_cost)
1735 g_return_val_if_fail (max_cost != NULL, TRUE);
1737 if (cost > *max_cost)
1738 return TRUE;
1739 return FALSE;
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));
1827 return 0;
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;
1843 norm = sqrt (norm);
1845 x /= norm; y /= norm; z /= norm;
1847 return gts_vertex_new (vertex_class, x, y, z);
1850 static void tessellate_face (GtsFace * f,
1851 GtsSurface * s,
1852 GtsRefineFunc refine_func,
1853 gpointer refine_data,
1854 GtsVertexClass * vertex_class,
1855 GtsEdgeClass * edge_class)
1857 GtsTriangle * t;
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 */
1863 GSList * dum;
1864 GtsEdge * edum;
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;
1889 else {
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);
1900 e24 = dum->data;
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;
1907 else {
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);
1917 e35 = dum->data;
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;
1924 else {
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);
1934 e16 = dum->data;
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;
1941 else {
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));
1954 e1 = NULL;
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));
1960 e2 = NULL;
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));
1966 e3 = NULL;
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)
2006 GPtrArray * array;
2007 guint i;
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
2033 * Returns: @s.
2035 GtsSurface * gts_surface_generate_sphere (GtsSurface * s,
2036 guint geodesation_order)
2038 guint cgo;
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);
2048 return s;
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,
2064 GtsSurface * s)
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));
2073 * gts_surface_copy:
2074 * @s1: a #GtsSurface.
2075 * @s2: a #GtsSurface.
2077 * Add a copy of all the faces, edges and vertices of @s2 to @s1.
2079 * Returns: @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,
2087 s1->vertex_class);
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);
2094 return s1;
2097 static void merge_foreach_face (GtsFace * f,
2098 GtsSurface * s)
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
2109 * 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];
2123 if (*is_manifold) {
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;
2138 gpointer data[2];
2140 g_return_val_if_fail (s != NULL, FALSE);
2142 data[0] = &is_manifold;
2143 data[1] = s;
2144 gts_surface_foreach_edge (s, (GtsFunc) manifold_foreach_edge, data);
2145 return is_manifold;
2148 static void closed_foreach_edge (GtsEdge * e, gpointer * data)
2150 gboolean * is_closed = data[0];
2152 if (*is_closed) {
2153 if (gts_edge_face_number (e, data[1]) != 2)
2154 *is_closed = FALSE;
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;
2168 gpointer data[2];
2170 g_return_val_if_fail (s != NULL, FALSE);
2172 data[0] = &is_closed;
2173 data[1] = s;
2174 gts_surface_foreach_edge (s, (GtsFunc) closed_foreach_edge, data);
2175 return is_closed;
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)) {
2189 if (!f1) f1 = f;
2190 else if (!f2) f2 = f;
2191 else *is_orientable = FALSE;
2193 i = i->next;
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;
2212 gpointer data[2];
2214 g_return_val_if_fail (s != NULL, FALSE);
2216 data[0] = &is_orientable;
2217 data[1] = s;
2218 gts_surface_foreach_edge (s, (GtsFunc) orientable_foreach_edge, data);
2219 return is_orientable;
2222 static void volume_foreach_face (GtsTriangle * t,
2223 gdouble * volume)
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);
2253 return volume/6.;
2256 static void center_of_mass_foreach_face (GtsTriangle * t,
2257 gpointer * data)
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);
2270 x1 = p2->x - p1->x;
2271 y1 = p2->y - p1->y;
2272 z1 = p2->z - p1->z;
2274 x2 = p3->x - p1->x;
2275 y2 = p3->y - p1->y;
2276 z2 = p3->z - p1->z;
2278 nx = y1*z2 - z1*y2;
2279 ny = z1*x2 - x1*z2;
2280 nz = x1*y2 - y1*x2;
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,
2303 GtsVector cm)
2305 gdouble volume = 0.;
2306 gpointer data[2];
2308 g_return_val_if_fail (s != NULL, 0.0);
2310 data[0] = &volume;
2311 data[1] = &(cm[0]);
2312 cm[0] = cm[1] = cm[2] = 0.;
2313 gts_surface_foreach_face (s, (GtsFunc) center_of_mass_foreach_face, data);
2315 if (volume != 0.) {
2316 cm[0] /= 4.*volume;
2317 cm[1] /= 4.*volume;
2318 cm[2] /= 4.*volume;
2321 return volume/6.;
2324 static void center_of_area_foreach_face (GtsTriangle * t,
2325 gpointer * data)
2327 GtsVertex * v1, * v2, * v3;
2328 GtsPoint * p1, * p2, * p3;
2329 gdouble a;
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);
2342 *area += a;
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,
2356 GtsVector cm)
2358 gdouble area = 0.;
2359 gpointer data[2];
2361 g_return_val_if_fail (s != NULL, 0.0);
2363 data[0] = &area;
2364 data[1] = &(cm[0]);
2365 cm[0] = cm[1] = cm[2] = 0.;
2366 gts_surface_foreach_face (s, (GtsFunc) center_of_area_foreach_face, data);
2368 if (area != 0.) {
2369 cm[0] /= 3.*area;
2370 cm[1] /= 3.*area;
2371 cm[2] /= 3.*area;
2374 return area;
2377 static void number_foreach (gpointer data, guint * n)
2379 (*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)
2390 guint n = 0;
2392 g_return_val_if_fail (s != NULL, 0);
2394 gts_surface_foreach_vertex (s, (GtsFunc) number_foreach, &n);
2396 return 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)
2407 guint n = 0;
2409 g_return_val_if_fail (s != NULL, 0);
2411 gts_surface_foreach_edge (s, (GtsFunc) number_foreach, &n);
2413 return 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 (),
2442 GTS_SEGMENT (e)));
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;
2464 GSList * bboxes;
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);
2472 bboxes = 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,
2480 delta, face_range);
2481 gts_bb_tree_destroy (face_tree, TRUE);
2483 bboxes = NULL;
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,
2490 s1,
2491 (GtsBBoxDistFunc) gts_point_segment_distance,
2492 delta, boundary_range);
2493 gts_bb_tree_destroy (boundary_tree, TRUE);
2495 else
2496 gts_range_reset (boundary_range);
2498 else {
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;
2521 gpointer data[2];
2523 g_return_val_if_fail (surface != NULL, NULL);
2525 data[0] = &list;
2526 data[1] = surface;
2527 gts_surface_foreach_edge (surface, (GtsFunc) surface_boundary, data);
2529 return list;
2532 struct _GtsSurfaceTraverse {
2533 GtsFifo * q;
2534 GtsSurface * s;
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,
2546 GtsFace * f)
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 ();
2556 t->s = s;
2557 GTS_OBJECT (f)->reserved = GUINT_TO_POINTER (1);
2558 gts_fifo_push (t->q, f);
2559 return t;
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,
2582 guint * level)
2584 GtsFace * u;
2586 g_return_val_if_fail (t != NULL, NULL);
2588 u = gts_fifo_pop (t->q);
2589 if (u) {
2590 gpointer data[2];
2592 if (level)
2593 *level = GPOINTER_TO_UINT (GTS_OBJECT (u)->reserved);
2594 data[0] = t->q;
2595 data[1] = u;
2596 gts_face_foreach_neighbor (u, t->s, (GtsFunc) push_neighbor, data);
2598 return u;
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);
2613 g_free (t);
2616 static void traverse_manifold (GtsTriangle * t, GtsSurface * s)
2618 if (g_slist_length (GTS_FACE (t)->surfaces) > 1)
2619 return;
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);
2625 else
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);
2631 else
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);
2637 else
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;
2650 while (i) {
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);
2654 i = i->next;
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) {
2666 GtsSurface * s =
2667 gts_surface_new (GTS_SURFACE_CLASS (GTS_OBJECT (orig)->klass),
2668 orig->face_class,
2669 orig->edge_class,
2670 orig->vertex_class);
2671 GSList * non_manifold = NULL, * i;
2672 gpointer data[2];
2674 *components = g_slist_prepend (*components, s);
2675 data[0] = s;
2676 data[1] = &non_manifold;
2677 traverse_manifold (GTS_TRIANGLE (f), s);
2679 gts_surface_foreach_edge (s, (GtsFunc) non_manifold_edges, data);
2680 i = non_manifold;
2681 while (i) {
2682 gts_surface_remove_face (s, i->data);
2683 i = i->next;
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) {
2695 GtsSurface * s =
2696 gts_surface_new (GTS_SURFACE_CLASS (GTS_OBJECT (orig)->klass),
2697 orig->face_class,
2698 orig->edge_class,
2699 orig->vertex_class);
2700 GSList * non_manifold = NULL, * i;
2701 gpointer data[2];
2703 *components = g_slist_prepend (*components, s);
2704 data[0] = s;
2705 data[1] = &non_manifold;
2706 traverse_manifold (GTS_TRIANGLE (f), s);
2708 gts_surface_foreach_edge (s, (GtsFunc) non_manifold_edges, data);
2709 i = non_manifold;
2710 while (i) {
2711 gts_surface_remove_face (s, i->data);
2712 i = i->next;
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)
2728 gpointer data[2];
2729 GSList * components = NULL;
2731 g_return_val_if_fail (s != NULL, NULL);
2733 data[0] = s;
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);
2742 return components;