Index: trunk/doc-rnd/TODO =================================================================== --- trunk/doc-rnd/TODO (revision 1064) +++ trunk/doc-rnd/TODO (revision 1065) @@ -1,9 +1,7 @@ CLEANUP - move to plugin: - toporouter - - dbus - gpmi - - move src/3rd/ one level up - remove libstroke or move it in a plugin? - warnings in util/ Index: trunk/scconfig/hooks.c =================================================================== --- trunk/scconfig/hooks.c (revision 1064) +++ trunk/scconfig/hooks.c (revision 1065) @@ -333,7 +333,7 @@ printf("Generating Makefile.conf (%d)\n", generr |= tmpasm("..", "Makefile.conf.in", "Makefile.conf")); - printf("Generating gts/Makefile (%d)\n", generr |= tmpasm("../src/3rd/gts", "Makefile.in", "Makefile")); + printf("Generating gts/Makefile (%d)\n", generr |= tmpasm("../src_3rd/gts", "Makefile.in", "Makefile")); printf("Generating pcb/Makefile (%d)\n", generr |= tmpasm("../src", "Makefile.in", "Makefile")); /* Has to be after pcb/Makefile so that all the modules are loaded. */ Index: trunk/src/3rd/README =================================================================== --- trunk/src/3rd/README (revision 1064) +++ trunk/src/3rd/README (nonexistent) @@ -1 +0,0 @@ -3rd party software libs Index: trunk/src/3rd/gts/face.c =================================================================== --- trunk/src/3rd/gts/face.c (revision 1064) +++ trunk/src/3rd/gts/face.c (nonexistent) @@ -1,297 +0,0 @@ -/* GTS - Library for the manipulation of triangulated surfaces - * Copyright (C) 1999 Stéphane Popinet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include "gts.h" - -gboolean gts_allow_floating_faces = FALSE; - -static void face_destroy (GtsObject * object) -{ - GtsFace * face = GTS_FACE (object); - GSList * i; - - i = face->surfaces; - while (i) { - GSList * next = i->next; - gts_surface_remove_face (i->data, face); - i = next; - } - g_assert (face->surfaces == NULL); - - (* GTS_OBJECT_CLASS (gts_face_class ())->parent_class->destroy) (object); -} - -static void face_clone (GtsObject * clone, GtsObject * object) -{ - (* GTS_OBJECT_CLASS (gts_face_class ())->parent_class->clone) (clone, - object); - GTS_FACE (clone)->surfaces = NULL; -} - -static void face_class_init (GtsFaceClass * klass) -{ - GTS_OBJECT_CLASS (klass)->clone = face_clone; - GTS_OBJECT_CLASS (klass)->destroy = face_destroy; -} - -static void face_init (GtsFace * face) -{ - face->surfaces = NULL; -} - -/** - * gts_face_class: - * - * Returns: the #GtsFaceClass. - */ -GtsFaceClass * gts_face_class (void) -{ - static GtsFaceClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo face_info = { - "GtsFace", - sizeof (GtsFace), - sizeof (GtsFaceClass), - (GtsObjectClassInitFunc) face_class_init, - (GtsObjectInitFunc) face_init, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_triangle_class ()), - &face_info); - } - - return klass; -} - -/** - * gts_face_new: - * @klass: a #GtsFaceClass. - * @e1: a #GtsEdge. - * @e2: a #GtsEdge. - * @e3: a #GtsEdge. - * - * Returns: a new #GtsFace using @e1, @e2 and @e3 as edges. - */ -GtsFace * gts_face_new (GtsFaceClass * klass, - GtsEdge * e1, GtsEdge * e2, GtsEdge * e3) -{ - GtsFace * f; - - f = GTS_FACE (gts_object_new (GTS_OBJECT_CLASS (klass))); - gts_triangle_set (GTS_TRIANGLE (f), e1, e2, e3); - - return f; -} - -/** - * gts_face_has_parent_surface: - * @f: a #GtsFace. - * @s: a #GtsSurface. - * - * Returns: %TRUE if @f belongs to @s, %FALSE otherwise. - */ -gboolean gts_face_has_parent_surface (GtsFace * f, GtsSurface * s) -{ - GSList * i; - - g_return_val_if_fail (f != NULL, FALSE); - - i = f->surfaces; - while (i) { - if (i->data == s) - return TRUE; - i = i->next; - } - return FALSE; -} - -/** - * gts_faces_from_edges: - * @edges: a list of #GtsEdge. - * @s: a #GtsSurface or %NULL. - * - * Builds a list of unique faces which belong to @s and have - * one of their edges in @edges. - * - * Returns: the list of faces. - */ -GSList * gts_faces_from_edges (GSList * edges, GtsSurface * s) -{ - GHashTable * hash; - GSList * faces = NULL, * i; - - hash = g_hash_table_new (NULL, NULL); - i = edges; - while (i) { - GSList * j = GTS_EDGE (i->data)->triangles; - while (j) { - GtsTriangle * t = j->data; - if (GTS_IS_FACE (t) && - (!s || gts_face_has_parent_surface (GTS_FACE (t), s)) && - g_hash_table_lookup (hash, t) == NULL) { - faces = g_slist_prepend (faces, t); - g_hash_table_insert (hash, t, i); - } - j = j->next; - } - i = i->next; - } - g_hash_table_destroy (hash); - - return faces; -} - -/** - * gts_face_neighbor_number: - * @f: a #GtsFace. - * @s: a #GtsSurface or %NULL. - * - * Returns: the number of faces neighbors of @f and belonging to @s. - */ -guint gts_face_neighbor_number (GtsFace * f, GtsSurface * s) -{ - GSList * i; - guint nn = 0; - GtsEdge * e[4], ** e1 = e; - - g_return_val_if_fail (f != NULL, 0); - - e[0] = GTS_TRIANGLE (f)->e1; - e[1] = GTS_TRIANGLE (f)->e2; - e[2] = GTS_TRIANGLE (f)->e3; - e[3] = NULL; - while (*e1) { - i = (*e1++)->triangles; - while (i) { - GtsTriangle * t = i->data; - if (GTS_FACE (t) != f && - GTS_IS_FACE (t) && - (!s || gts_face_has_parent_surface (GTS_FACE (t), s))) - nn++; - i = i->next; - } - } - - return nn; -} - -/** - * gts_face_neighbors: - * @f: a #GtsFace. - * @s: a #GtsSurface or %NULL. - * - * Returns: a list of unique #GtsFace neighbors of @f and belonging to @s. - */ -GSList * gts_face_neighbors (GtsFace * f, GtsSurface * s) -{ - GSList * i, * list = NULL; - GtsEdge * e[4], ** e1 = e; - - g_return_val_if_fail (f != NULL, NULL); - - e[0] = GTS_TRIANGLE (f)->e1; - e[1] = GTS_TRIANGLE (f)->e2; - e[2] = GTS_TRIANGLE (f)->e3; - e[3] = NULL; - while (*e1) { - i = (*e1++)->triangles; - while (i) { - GtsTriangle * t = i->data; - if (GTS_FACE (t) != f && - GTS_IS_FACE (t) && - (!s || gts_face_has_parent_surface (GTS_FACE (t), s))) - list = g_slist_prepend (list, t); - i = i->next; - } - } - - return list; -} - -/** - * gts_face_foreach_neighbor: - * @f: a #GtsFace. - * @s: a #GtsSurface or %NULL. - * @func: a #GtsFunc. - * @data: user data to pass to @func. - * - * Calls @func for each neighbor of @f belonging to @s (if not %NULL). - */ -void gts_face_foreach_neighbor (GtsFace * f, - GtsSurface * s, - GtsFunc func, - gpointer data) -{ - GSList * i; - GtsEdge * e[4], ** e1 = e; - - g_return_if_fail (f != NULL); - g_return_if_fail (func != NULL); - - e[0] = GTS_TRIANGLE (f)->e1; - e[1] = GTS_TRIANGLE (f)->e2; - e[2] = GTS_TRIANGLE (f)->e3; - e[3] = NULL; - while (*e1) { - i = (*e1++)->triangles; - while (i) { - GtsTriangle * t = i->data; - if (GTS_FACE (t) != f && - GTS_IS_FACE (t) && - (!s || gts_face_has_parent_surface (GTS_FACE (t), s))) - (* func) (t, data); - i = i->next; - } - } -} - -static gboolean triangle_is_incompatible (GtsTriangle * t, GtsEdge * e, GtsSurface * s) -{ - GSList * i = e->triangles; - - while (i) { - if (i->data != t && - GTS_IS_FACE (i->data) && - gts_face_has_parent_surface (i->data, s) && - !gts_triangles_are_compatible (t, i->data, e)) - return TRUE; - i = i->next; - } - return FALSE; -} - -/** - * gts_face_is_compatible: - * @f: a #GtsFace. - * @s: a #GtsSurface. - * - * Returns: %TRUE if @f is compatible with all its neighbors belonging - * to @s, %FALSE otherwise. - */ -gboolean gts_face_is_compatible (GtsFace * f, GtsSurface * s) -{ - g_return_val_if_fail (f != NULL, FALSE); - g_return_val_if_fail (s != NULL, FALSE); - - return !(triangle_is_incompatible (GTS_TRIANGLE (f), GTS_TRIANGLE (f)->e1, s) || - triangle_is_incompatible (GTS_TRIANGLE (f), GTS_TRIANGLE (f)->e2, s) || - triangle_is_incompatible (GTS_TRIANGLE (f), GTS_TRIANGLE (f)->e3, s)); -} Index: trunk/src/3rd/gts/Makefile.in =================================================================== --- trunk/src/3rd/gts/Makefile.in (revision 1064) +++ trunk/src/3rd/gts/Makefile.in (nonexistent) @@ -1,63 +0,0 @@ -put /local/gts/CFLAGS [@-I. -I.. -DG_LOG_DOMAIN=\"Gts\" @libs/sul/glib/cflags@@] -put /local/gts/OBJS [@ - object.o - point.o - vertex.o - segment.o - edge.o - triangle.o - face.o - kdtree.o - bbtree.o - misc.o - predicates.o - heap.o - eheap.o - fifo.o - matrix.o - surface.o - stripe.o - vopt.o - refine.o - iso.o - isotetra.o - split.o - psurface.o - hsurface.o - cdt.o - boolean.o - named.o - oocs.o - container.o - graph.o - pgraph.o - partition.o - curvature.o - tribox3.o -@] - -put /tmpasm/OFS { } -uniq /local/gts/OBJS -put /local/gts/SRCS /local/gts/OBJS -gsub /local/gts/SRCS {.o } {.c } - -print [@ -CFLAGS = @/local/gts/CFLAGS@ -OBJS = @/local/gts/OBJS@ -CC=@cc/cc@ - -libgts.a: $(OBJS) - @fstools/ar@ rvu libgts.a $(OBJS) - -clean: - -@fstools/rm@ $(OBJS) libgts.a -@] - -# generate explicit rules for .c -> .o -put /local/comp/OBJS /local/gts/OBJS -include {../../../scconfig/Makefile.comp.inc} - -# generate deps -put /local/dep/CFLAGS /local/gts/CFLAGS -put /local/dep/SRCS /local/gts/SRCS -include {../../../scconfig/Makefile.dep.inc} Index: trunk/src/3rd/gts/segment.c =================================================================== --- trunk/src/3rd/gts/segment.c (revision 1064) +++ trunk/src/3rd/gts/segment.c (nonexistent) @@ -1,233 +0,0 @@ -/* GTS - Library for the manipulation of triangulated surfaces - * Copyright (C) 1999 Stéphane Popinet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include "gts.h" - -static void segment_destroy (GtsObject * object) -{ - GtsSegment * segment = GTS_SEGMENT (object); - GtsVertex * v1 = segment->v1; - GtsVertex * v2 = segment->v2; - - v1->segments = g_slist_remove (v1->segments, segment); - if (!GTS_OBJECT_DESTROYED (v1) && - !gts_allow_floating_vertices && v1->segments == NULL) - gts_object_destroy (GTS_OBJECT (v1)); - - v2->segments = g_slist_remove (v2->segments, segment); - if (!GTS_OBJECT_DESTROYED (v2) && - !gts_allow_floating_vertices && v2->segments == NULL) - gts_object_destroy (GTS_OBJECT (v2)); - - (* GTS_OBJECT_CLASS (gts_segment_class ())->parent_class->destroy) (object); -} - -static void segment_class_init (GtsObjectClass * klass) -{ - klass->destroy = segment_destroy; -} - -static void segment_init (GtsSegment * segment) -{ - segment->v1 = segment->v2 = NULL; -} - -/** - * gts_segment_class: - * - * Returns: the #GtsSegmentClass. - */ -GtsSegmentClass * gts_segment_class (void) -{ - static GtsSegmentClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo segment_info = { - "GtsSegment", - sizeof (GtsSegment), - sizeof (GtsSegmentClass), - (GtsObjectClassInitFunc) segment_class_init, - (GtsObjectInitFunc) segment_init, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (gts_object_class (), - &segment_info); - } - - return klass; -} - -/** - * gts_segment_new: - * @klass: a #GtsSegmentClass. - * @v1: a #GtsVertex. - * @v2: another #GtsVertex different from @v1. - * - * Returns: a new #GtsSegment linking @v1 and @v2. - */ -GtsSegment * gts_segment_new (GtsSegmentClass * klass, - GtsVertex * v1, GtsVertex * v2) -{ - GtsSegment * s; - - g_return_val_if_fail (v1 != NULL, NULL); - g_return_val_if_fail (v2 != NULL, NULL); - g_return_val_if_fail (v1 != v2, NULL); - - s = GTS_SEGMENT (gts_object_new (GTS_OBJECT_CLASS (klass))); - s->v1 = v1; - s->v2 = v2; - v1->segments = g_slist_prepend (v1->segments, s); - v2->segments = g_slist_prepend (v2->segments, s); - - return s; -} - -/** - * gts_segment_is_duplicate: - * @s: a #GtsSegment. - * - * Returns: the first #GtsSegment different from @s which shares the - * same endpoints or %NULL if there is none. - */ -GtsSegment * gts_segment_is_duplicate (GtsSegment * s) -{ - GSList * i; - GtsVertex * v2; - - g_return_val_if_fail (s != NULL, NULL); - - v2 = s->v2; - i = s->v1->segments; - if (s->v1 == v2) /* s is degenerate: special treatment */ - while (i) { - GtsSegment * s1 = i->data; - if (s1 != s && s1->v1 == v2 && s1->v2 == v2) - return s1; - i = i->next; - } - else /* s is not degenerate */ - while (i) { - GtsSegment * s1 = i->data; - if (s1 != s && (s1->v1 == v2 || s1->v2 == v2)) - return s1; - i = i->next; - } - return NULL; -} - -/** - * gts_segments_are_intersecting: - * @s1: a #GtsSegment. - * @s2: a #GtsSegment. - * - * Returns: %GTS_IN if @s1 and @s2 are intersecting, %GTS_ON if one of the - * endpoints of @s1 (resp. @s2) lies on @s2 (resp. @s1), %GTS_OUT otherwise. - */ -GtsIntersect gts_segments_are_intersecting (GtsSegment * s1, GtsSegment * s2) -{ - GtsPoint * p1, * p2, * p3, * p4; - gdouble d1, d2, d3, d4; - - g_return_val_if_fail (s1 != NULL && s2 != NULL, FALSE); - - p1 = GTS_POINT (s1->v1); p2 = GTS_POINT (s1->v2); - p3 = GTS_POINT (s2->v1); p4 = GTS_POINT (s2->v2); - d1 = gts_point_orientation (p1, p2, p3); - d2 = gts_point_orientation (p1, p2, p4); - if ((d1 > 0.0 && d2 > 0.0) || - (d1 < 0.0 && d2 < 0.0)) - return GTS_OUT; - d3 = gts_point_orientation (p3, p4, p1); - d4 = gts_point_orientation (p3, p4, p2); - if ((d3 > 0.0 && d4 > 0.0) || - (d3 < 0.0 && d4 < 0.0)) - return GTS_OUT; - if (d1 == 0.0 || d2 == 0.0 || d3 == 0.0 || d4 == 0.0) - return GTS_ON; - return GTS_IN; -} - -/** - * gts_segment_midvertex: - * @s: a #GtsSegment. - * @klass: a #GtsVertexClass to be used for the new vertex. - * - * Returns: a new #GtsVertex, midvertex of @s. - */ -GtsVertex * gts_segment_midvertex (GtsSegment * s, GtsVertexClass * klass) -{ - GtsPoint * p1, * p2; - - g_return_val_if_fail (s != NULL, NULL); - g_return_val_if_fail (klass != NULL, NULL); - - p1 = GTS_POINT (s->v1); p2 = GTS_POINT (s->v2); - return gts_vertex_new (klass, - (p1->x + p2->x)/2., - (p1->y + p2->y)/2., - (p1->z + p2->z)/2.); -} - -/** - * gts_segments_from_vertices: - * @vertices: a list of #GtsVertex. - * - * Returns: a list of unique #GtsSegment which have one of their vertices in - * @vertices. - */ -GSList * gts_segments_from_vertices (GSList * vertices) -{ - GHashTable * hash; - GSList * segments = NULL, * i; - - hash = g_hash_table_new (NULL, NULL); - i = vertices; - while (i) { - GSList * j = GTS_VERTEX (i->data)->segments; - while (j) { - GtsSegment * s = j->data; - if (g_hash_table_lookup (hash, s) == NULL) { - segments = g_slist_prepend (segments, s); - g_hash_table_insert (hash, s, i); - } - j = j->next; - } - i = i->next; - } - g_hash_table_destroy (hash); - return segments; -} - -/** - * gts_segment_is_ok: - * @s: a #GtsSegment. - * - * Returns: %TRUE if @s is not degenerate (i.e. @s->v1 != @s->v2) and not - * duplicate, %FALSE otherwise. - */ -gboolean gts_segment_is_ok (GtsSegment * s) -{ - g_return_val_if_fail (s != NULL, FALSE); - g_return_val_if_fail (s->v1 != s->v2, FALSE); - g_return_val_if_fail (!gts_segment_is_duplicate (s), FALSE); - g_return_val_if_fail (GTS_OBJECT (s)->reserved == NULL, FALSE); - return TRUE; -} Index: trunk/src/3rd/gts/oocs.c =================================================================== --- trunk/src/3rd/gts/oocs.c (revision 1064) +++ trunk/src/3rd/gts/oocs.c (nonexistent) @@ -1,387 +0,0 @@ -/* GTS - Library for the manipulation of triangulated surfaces - * Copyright (C) 1999 Stéphane Popinet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include -#include "gts.h" - -static void cluster_destroy (GtsObject * object) -{ - GtsCluster * c = GTS_CLUSTER (object); - - if (c->v && gts_vertex_is_unattached (c->v)) - gts_object_destroy (GTS_OBJECT (c->v)); - - /* do not forget to call destroy method of the parent */ - (* GTS_OBJECT_CLASS (gts_cluster_class ())->parent_class->destroy) (object); -} - -static void cluster_add (GtsCluster * c, GtsPoint * p, gpointer data) -{ - GtsPoint * cp; - - g_return_if_fail (c != NULL); - g_return_if_fail (c->v != NULL); - g_return_if_fail (p != NULL); - - cp = GTS_POINT (c->v); - - cp->x += p->x; - cp->y += p->y; - cp->z += p->z; - c->n++; -} - -static void cluster_update (GtsCluster * c) -{ - GtsPoint * p; - - g_return_if_fail (c != NULL); - g_return_if_fail (c->v != NULL); - - if (c->n > 1) { - p = GTS_POINT (c->v); - p->x /= c->n; - p->y /= c->n; - p->z /= c->n; - } -} - -static void cluster_class_init (GtsClusterClass * klass) -{ - klass->add = cluster_add; - klass->update = cluster_update; - - GTS_OBJECT_CLASS (klass)->destroy = cluster_destroy; -} - -static void cluster_init (GtsCluster * c) -{ - c->v = NULL; - c->n = 0; -} - -/** - * gts_cluster_class: - * - * Returns: the #GtsClusterClass. - */ -GtsClusterClass * gts_cluster_class (void) -{ - static GtsClusterClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo cluster_info = { - "GtsCluster", - sizeof (GtsCluster), - sizeof (GtsClusterClass), - (GtsObjectClassInitFunc) cluster_class_init, - (GtsObjectInitFunc) cluster_init, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (gts_object_class (), &cluster_info); - } - - return klass; -} - -/** - * gts_cluster_new: - * @klass: a #GtsClusterClass. - * @id: the id of the new cluster. - * @vklass: a #GtsVertexClass for the representative vertex of the cluster. - * - * Returns: a new #GtsCluster. - */ -GtsCluster * gts_cluster_new (GtsClusterClass * klass, - GtsClusterId id, - GtsVertexClass * vklass) -{ - GtsCluster * c; - - c = GTS_CLUSTER (gts_object_new (GTS_OBJECT_CLASS (klass))); - c->id = id; - c->v = gts_vertex_new (vklass, 0., 0., 0.); - - return c; -} - -/** - * gts_cluster_add: - * @c: a #GtsCluster. - * @p: a #GtsPoint. - * @data: data to pass to the add() virtual method of #GtsClusterClass. - * - * Adds point @p to cluster @c. - */ -void gts_cluster_add (GtsCluster * c, GtsPoint * p, gpointer data) -{ - g_return_if_fail (c != NULL); - g_return_if_fail (p != NULL); - - (* GTS_CLUSTER_CLASS (GTS_OBJECT (c)->klass)->add) (c, p, data); -} - -/** - * gts_cluster_update: - * @c: a #GtsCluster. - * - * Updates the position of the vertex representative of all the - * vertices added to @c. - */ -void gts_cluster_update (GtsCluster * c) -{ - g_return_if_fail (c != NULL); - - (* GTS_CLUSTER_CLASS (GTS_OBJECT (c)->klass)->update) (c); -} - -static void destroy_cluster (GtsClusterId * id, GtsObject * cluster) -{ - gts_object_destroy (cluster); -} - -static void cluster_grid_destroy (GtsObject * object) -{ - GtsClusterGrid * cluster_grid = GTS_CLUSTER_GRID (object); - - g_hash_table_foreach (cluster_grid->clusters, - (GHFunc) destroy_cluster, NULL); - g_hash_table_destroy (cluster_grid->clusters); - - (* GTS_OBJECT_CLASS (gts_cluster_grid_class ())->parent_class->destroy) - (object); -} - -static void cluster_grid_class_init (GtsClusterGridClass * klass) -{ - GTS_OBJECT_CLASS (klass)->destroy = cluster_grid_destroy; -} - -static gint cluster_id_equal (gconstpointer v1, - gconstpointer v2) -{ - const GtsClusterId * id1 = (const GtsClusterId *) v1; - const GtsClusterId * id2 = (const GtsClusterId *) v2; - return ((id1->x == id2->x) && (id1->y == id2->y) && (id1->z == id2->z)); -} - -static guint cluster_id_hash (gconstpointer key) -{ - const GtsClusterId * id = (const GtsClusterId *) key; - return id->x + id->y + id->z; -} - -static void cluster_grid_init (GtsClusterGrid * cluster_grid) -{ - cluster_grid->surface = NULL; - cluster_grid->bbox = NULL; - cluster_grid->cluster_class = gts_cluster_class (); - cluster_grid->clusters = g_hash_table_new (cluster_id_hash, - cluster_id_equal); -} - -/** - * gts_cluster_grid_class: - * - * Returns: the #GtsClusterGridClass. - */ -GtsClusterGridClass * gts_cluster_grid_class (void) -{ - static GtsClusterGridClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo cluster_grid_info = { - "GtsClusterGrid", - sizeof (GtsClusterGrid), - sizeof (GtsClusterGridClass), - (GtsObjectClassInitFunc) cluster_grid_class_init, - (GtsObjectInitFunc) cluster_grid_init, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (gts_object_class (), &cluster_grid_info); - } - - return klass; -} - -/** - * gts_cluster_grid_new: - * @klass: a #GtsClusterGridClass. - * @cluster_class: the klass to be used for the vertex clusters. - * @s: the simplified surface. - * @bbox: bounding box of the surface to be simplified. - * @delta: the size of one grid cell of the simplification grid. - * - * Returns: a new #GtsClusterGrid. - */ -GtsClusterGrid * gts_cluster_grid_new (GtsClusterGridClass * klass, - GtsClusterClass * cluster_class, - GtsSurface * s, - GtsBBox * bbox, - gdouble delta) -{ - GtsClusterGrid * cluster_grid; - GtsVector size; - - g_return_val_if_fail (klass != NULL, NULL); - g_return_val_if_fail (cluster_class != NULL, NULL); - g_return_val_if_fail (s != NULL, NULL); - g_return_val_if_fail (bbox != NULL, NULL); - g_return_val_if_fail (delta > 0., NULL); - - size[0] = ceil ((bbox->x2 - bbox->x1)/delta); - size[1] = ceil ((bbox->y2 - bbox->y1)/delta); - size[2] = ceil ((bbox->z2 - bbox->z1)/delta); - g_return_val_if_fail (size[0] <= 2.*G_MAXINT + 2. && - size[1] <= 2.*G_MAXINT + 2. && - size[2] <= 2.*G_MAXINT + 2., NULL); - cluster_grid = - GTS_CLUSTER_GRID (gts_object_new (GTS_OBJECT_CLASS (klass))); - cluster_grid->cluster_class = cluster_class; - cluster_grid->surface = s; - cluster_grid->bbox = bbox; - cluster_grid->size[0] = size[0]; - cluster_grid->size[1] = size[1]; - cluster_grid->size[2] = size[2]; - - return cluster_grid; -} - -static GtsClusterId cluster_index (GtsPoint * p, - GtsBBox * bb, - GtsVector n) -{ - GtsClusterId id = {0, 0, 0}; - - g_return_val_if_fail (p->x >= bb->x1 && p->x <= bb->x2, id); - g_return_val_if_fail (p->y >= bb->y1 && p->y <= bb->y2, id); - g_return_val_if_fail (p->z >= bb->z1 && p->z <= bb->z2, id); - - id.x = (guint) (p->x == bb->x2 ? n[0] - 1. : n[0]*(p->x - bb->x1)/(bb->x2 - bb->x1)); - id.y = (guint) (p->y == bb->y2 ? n[1] - 1. : n[1]*(p->y - bb->y1)/(bb->y2 - bb->y1)); - id.z = (guint) (p->z == bb->z2 ? n[2] - 1. : n[2]*(p->z - bb->z1)/(bb->z2 - bb->z1)); - - return id; -} - -static GtsCluster * cluster_grid_add_point (GtsClusterGrid * cluster_grid, - GtsPoint * p, - gpointer data) -{ - GtsClusterId id = cluster_index (p, - cluster_grid->bbox, - cluster_grid->size); - GtsCluster * c = g_hash_table_lookup (cluster_grid->clusters, &id); - - if (c == NULL) { - c = gts_cluster_new (cluster_grid->cluster_class, id, - cluster_grid->surface->vertex_class); - g_hash_table_insert (cluster_grid->clusters, &c->id, c); - } - - gts_cluster_add (c, p, data); - - return c; -} - -/** - * gts_cluster_grid_add_triangle: - * @cluster_grid: a #GtsClusterGrid. - * @p1: a #GtsPoint. - * @p2: a #GtsPoint. - * @p3: a #GtsPoint. - * @data: user data to pass to the cluster add() method. - * - * Adds the triangle defined by @p1, @p2 and @p3 to the respective clusters - * of @cluster_grid. - */ -void gts_cluster_grid_add_triangle (GtsClusterGrid * cluster_grid, - GtsPoint * p1, - GtsPoint * p2, - GtsPoint * p3, - gpointer data) -{ - GtsCluster * c1, * c2, * c3; - - g_return_if_fail (cluster_grid != NULL); - g_return_if_fail (p1 != NULL); - g_return_if_fail (p2 != NULL); - g_return_if_fail (p3 != NULL); - g_return_if_fail (cluster_grid->surface != NULL); - - c1 = cluster_grid_add_point (cluster_grid, p1, data); - c2 = cluster_grid_add_point (cluster_grid, p2, data); - c3 = cluster_grid_add_point (cluster_grid, p3, data); - - if (c1 != c2 && c2 != c3 && c3 != c1) { - GtsVertex * v1, * v2, * v3; - GtsEdge * e1, * e2, * e3; - gboolean new_edge = FALSE; - - v1 = c1->v; v2 = c2->v; v3 = c3->v; - - if ((e1 = GTS_EDGE (gts_vertices_are_connected (v1, v2))) == NULL) { - e1 = gts_edge_new (cluster_grid->surface->edge_class, v1, v2); - new_edge = TRUE; - } - if ((e2 = GTS_EDGE (gts_vertices_are_connected (v2, v3))) == NULL) { - e2 = gts_edge_new (cluster_grid->surface->edge_class, v2, v3); - new_edge = TRUE; - } - if ((e3 = GTS_EDGE (gts_vertices_are_connected (v3, v1))) == NULL) { - e3 = gts_edge_new (cluster_grid->surface->edge_class, v3, v1); - new_edge = TRUE; - } - if (new_edge || !gts_triangle_use_edges (e1, e2, e3)) - gts_surface_add_face (cluster_grid->surface, - gts_face_new (cluster_grid->surface->face_class, - e1, e2, e3)); - } -} - -static void update_cluster (gint * id, GtsCluster * cluster, GtsRange * stats) -{ - gts_cluster_update (cluster); - gts_range_add_value (stats, cluster->n); -} - -/** - * gts_cluster_grid_update: - * @cluster_grid: a #GtsClusterGrid. - * - * Updates the representative vertices of all the clusters of @cluster_grid. - * - * Returns: a #GtsRange describing the statistics for the number of vertices - * added to each cluster of @cluster_grid. - */ -GtsRange gts_cluster_grid_update (GtsClusterGrid * cluster_grid) -{ - GtsRange stats; - - gts_range_init (&stats); - g_return_val_if_fail (cluster_grid != NULL, stats); - - g_hash_table_foreach (cluster_grid->clusters, - (GHFunc) update_cluster, &stats); - gts_range_update (&stats); - - return stats; -} Index: trunk/src/3rd/gts/triangle.c =================================================================== --- trunk/src/3rd/gts/triangle.c (revision 1064) +++ trunk/src/3rd/gts/triangle.c (nonexistent) @@ -1,1094 +0,0 @@ -/* GTS - Library for the manipulation of triangulated surfaces - * Copyright (C) 1999 Stéphane Popinet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include -#include "gts.h" - -static void triangle_destroy (GtsObject * object) -{ - GtsTriangle * triangle = GTS_TRIANGLE (object); - GtsEdge * e1 = triangle->e1; - GtsEdge * e2 = triangle->e2; - GtsEdge * e3 = triangle->e3; - - e1->triangles = g_slist_remove (e1->triangles, triangle); - if (!GTS_OBJECT_DESTROYED (e1) && - !gts_allow_floating_edges && e1->triangles == NULL) - gts_object_destroy (GTS_OBJECT (e1)); - - e2->triangles = g_slist_remove (e2->triangles, triangle); - if (!GTS_OBJECT_DESTROYED (e2) && - !gts_allow_floating_edges && e2->triangles == NULL) - gts_object_destroy (GTS_OBJECT (e2)); - - e3->triangles = g_slist_remove (e3->triangles, triangle); - if (!GTS_OBJECT_DESTROYED (e3) && - !gts_allow_floating_edges && e3->triangles == NULL) - gts_object_destroy (GTS_OBJECT (e3)); - - (* GTS_OBJECT_CLASS (gts_triangle_class ())->parent_class->destroy) (object); -} - -static void triangle_class_init (GtsObjectClass * klass) -{ - klass->destroy = triangle_destroy; -} - -static void triangle_init (GtsTriangle * triangle) -{ - triangle->e1 = triangle->e2 = triangle->e3 = NULL; -} - -/** - * gts_triangle_class: - * - * Returns: the #GtsTriangleClass. - */ -GtsTriangleClass * gts_triangle_class (void) -{ - static GtsTriangleClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo triangle_info = { - "GtsTriangle", - sizeof (GtsTriangle), - sizeof (GtsTriangleClass), - (GtsObjectClassInitFunc) triangle_class_init, - (GtsObjectInitFunc) triangle_init, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (gts_object_class (), - &triangle_info); - } - - return klass; -} - -/** - * gts_triangle_set: - * @triangle: a #GtsTriangle. - * @e1: a #GtsEdge. - * @e2: another #GtsEdge touching @e1. - * @e3: another #GtsEdge touching both @e1 and @e2. - * - * Sets the edge of @triangle to @e1, @e2 and @e3 while checking that they - * define a valid triangle. - */ -void gts_triangle_set (GtsTriangle * triangle, - GtsEdge * e1, - GtsEdge * e2, - GtsEdge * e3) -{ - g_return_if_fail (e1 != NULL); - g_return_if_fail (e2 != NULL); - g_return_if_fail (e3 != NULL); - g_return_if_fail (e1 != e2 && e1 != e3 && e2 != e3); - - triangle->e1 = e1; - triangle->e2 = e2; - triangle->e3 = e3; - - if (GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e2)->v1) - g_return_if_fail (gts_segment_connect (GTS_SEGMENT (e3), - GTS_SEGMENT (e1)->v2, - GTS_SEGMENT (e2)->v2)); - else if (GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e2)->v1) - g_return_if_fail (gts_segment_connect (GTS_SEGMENT (e3), - GTS_SEGMENT (e1)->v1, - GTS_SEGMENT (e2)->v2)); - else if (GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e2)->v2) - g_return_if_fail (gts_segment_connect (GTS_SEGMENT (e3), - GTS_SEGMENT (e1)->v1, - GTS_SEGMENT (e2)->v1)); - else if (GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e2)->v2) - g_return_if_fail (gts_segment_connect (GTS_SEGMENT (e3), - GTS_SEGMENT (e1)->v2, - GTS_SEGMENT (e2)->v1)); - else - g_assert_not_reached (); - - e1->triangles = g_slist_prepend (e1->triangles, triangle); - e2->triangles = g_slist_prepend (e2->triangles, triangle); - e3->triangles = g_slist_prepend (e3->triangles, triangle); -} - -/** - * gts_triangle_new: - * @klass: a #GtsTriangleClass. - * @e1: a #GtsEdge. - * @e2: another #GtsEdge touching @e1. - * @e3: another #GtsEdge touching both @e1 and @e2. - * - * Returns: a new #GtsTriangle having @e1, @e2 and @e3 as edges. - */ -GtsTriangle * gts_triangle_new (GtsTriangleClass * klass, - GtsEdge * e1, - GtsEdge * e2, - GtsEdge * e3) -{ - GtsTriangle * t; - - t = GTS_TRIANGLE (gts_object_new (GTS_OBJECT_CLASS (klass))); - gts_triangle_set (t, e1, e2, e3); - - return t; -} - -/** - * gts_triangle_vertex_opposite: - * @t: a #GtsTriangle. - * @e: a #GtsEdge used by @t. - * - * This function fails if @e is not an edge of @t. - * - * Returns: a #GtsVertex, vertex of @t which does not belong to @e. - */ -GtsVertex * gts_triangle_vertex_opposite (GtsTriangle * t, GtsEdge * e) -{ - g_return_val_if_fail (t != NULL, NULL); - g_return_val_if_fail (e != NULL, NULL); - - if (t->e1 == e) { - GtsVertex * v = GTS_SEGMENT (t->e2)->v1; - if (v != GTS_SEGMENT (e)->v1 && v != GTS_SEGMENT (e)->v2) - return v; - return GTS_SEGMENT (t->e2)->v2; - } - if (t->e2 == e) { - GtsVertex * v = GTS_SEGMENT (t->e1)->v1; - if (v != GTS_SEGMENT (e)->v1 && v != GTS_SEGMENT (e)->v2) - return v; - return GTS_SEGMENT (t->e1)->v2; - } - if (t->e3 == e) { - GtsVertex * v = GTS_SEGMENT (t->e2)->v1; - if (v != GTS_SEGMENT (e)->v1 && v != GTS_SEGMENT (e)->v2) - return v; - return GTS_SEGMENT (t->e2)->v2; - } - g_assert_not_reached (); - return NULL; -} - -/** - * gts_triangle_edge_opposite: - * @t: a #GtsTriangle. - * @v: a #GtsVertex of @t. - * - * Returns: the edge of @t opposite @v or %NULL if @v is not a vertice of @t. - */ -GtsEdge * gts_triangle_edge_opposite (GtsTriangle * t, GtsVertex * v) -{ - GtsSegment * s1, * s2, * s3; - - g_return_val_if_fail (t != NULL, NULL); - g_return_val_if_fail (v != NULL, NULL); - - s1 = GTS_SEGMENT (t->e1); - s2 = GTS_SEGMENT (t->e2); - - if (s1->v1 != v && s1->v2 != v) { - if (s2->v1 != v && s2->v2 != v) - return NULL; - return t->e1; - } - if (s2->v1 != v && s2->v2 != v) - return t->e2; - s3 = GTS_SEGMENT (t->e3); - g_assert (s3->v1 != v && s3->v2 != v); - return t->e3; -} - -/** - * gts_triangles_angle: - * @t1: a #GtsTriangle. - * @t2: a #GtsTriangle. - * - * Returns: the value (in radians) of the angle between @t1 and @t2. - */ -gdouble gts_triangles_angle (GtsTriangle * t1, - GtsTriangle * t2) -{ - gdouble nx1, ny1, nz1, nx2, ny2, nz2; - gdouble pvx, pvy, pvz; - gdouble theta; - - g_return_val_if_fail (t1 != NULL && t2 != NULL, 0.0); - - gts_triangle_normal (t1, &nx1, &ny1, &nz1); - gts_triangle_normal (t2, &nx2, &ny2, &nz2); - - pvx = ny1*nz2 - nz1*ny2; - pvy = nz1*nx2 - nx1*nz2; - pvz = nx1*ny2 - ny1*nx2; - - theta = atan2 (sqrt (pvx*pvx + pvy*pvy + pvz*pvz), - nx1*nx2 + ny1*ny2 + nz1*nz2) - M_PI; - return theta < - M_PI ? theta + 2.*M_PI : theta; -} - -/** - * gts_triangles_are_compatible: - * @t1: a #GtsTriangle. - * @t2: a #GtsTriangle. - * @e: a #GtsEdge used by both @t1 and @t2. - * - * Checks if @t1 and @t2 have compatible orientations i.e. if @t1 and - * @t2 can be part of the same surface without conflict in the surface - * normal orientation. - * - * Returns: %TRUE if @t1 and @t2 are compatible, %FALSE otherwise. - */ -gboolean gts_triangles_are_compatible (GtsTriangle * t1, - GtsTriangle * t2, - GtsEdge * e) -{ - GtsEdge * e1 = NULL, * e2 = NULL; - - g_return_val_if_fail (t1 != NULL, FALSE); - g_return_val_if_fail (t2 != NULL, FALSE); - g_return_val_if_fail (e != NULL, FALSE); - - if (t1->e1 == e) e1 = t1->e2; - else if (t1->e2 == e) e1 = t1->e3; - else if (t1->e3 == e) e1 = t1->e1; - else - g_assert_not_reached (); - if (t2->e1 == e) e2 = t2->e2; - else if (t2->e2 == e) e2 = t2->e3; - else if (t2->e3 == e) e2 = t2->e1; - else - g_assert_not_reached (); - if (GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e2)->v1 || - GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e2)->v2 || - GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e2)->v1 || - GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e2)->v2) - return FALSE; - return TRUE; -} - -/** - * gts_triangle_area: - * @t: a #GtsTriangle. - * - * Returns: the area of the triangle @t. - */ -gdouble gts_triangle_area (GtsTriangle * t) -{ - gdouble x, y, z; - - g_return_val_if_fail (t != NULL, 0.0); - - gts_triangle_normal (t, &x, &y, &z); - - return sqrt (x*x + y*y + z*z)/2.; -} - -/** - * gts_triangle_perimeter: - * @t: a #GtsTriangle. - * - * Returns: the perimeter of the triangle @t. - */ -gdouble gts_triangle_perimeter (GtsTriangle * t) -{ - GtsVertex * v; - - g_return_val_if_fail (t != NULL, 0.0); - - v = gts_triangle_vertex (t); - return - gts_point_distance (GTS_POINT (GTS_SEGMENT (t->e1)->v1), - GTS_POINT (GTS_SEGMENT (t->e1)->v2)) + - gts_point_distance (GTS_POINT (GTS_SEGMENT (t->e1)->v1), - GTS_POINT (v)) + - gts_point_distance (GTS_POINT (GTS_SEGMENT (t->e1)->v2), - GTS_POINT (v)); -} - -/* perimeter of the equilateral triangle of area unity */ -#define GOLDEN_PERIMETER 4.5590141139 - -/** - * gts_triangle_quality: - * @t: a #GtsTriangle. - * - * The quality of a triangle is defined as the ratio of the square - * root of its surface area to its perimeter relative to this same - * ratio for an equilateral triangle with the same area. The quality - * is then one for an equilateral triangle and tends to zero for a - * very stretched triangle. - * - * Returns: the quality of the triangle @t. - */ -gdouble gts_triangle_quality (GtsTriangle * t) -{ - gdouble perimeter; - - g_return_val_if_fail (t != NULL, 0.0); - - perimeter = gts_triangle_perimeter (t); - return perimeter > 0.0 ? - GOLDEN_PERIMETER*sqrt (gts_triangle_area (t))/perimeter : - 0.0; -} - -/** - * gts_triangle_normal: - * @t: a #GtsTriangle. - * @x: the x coordinate of the normal. - * @y: the y coordinate of the normal. - * @z: the z coordinate of the normal. - * - * Computes the coordinates of the oriented normal of @t as the - * cross-product of two edges, using the left-hand rule. The normal is - * not normalized. If this triangle is part of a closed and oriented - * surface, the normal points to the outside of the surface. - */ -void gts_triangle_normal (GtsTriangle * t, - gdouble * x, - gdouble * y, - gdouble * z) -{ - GtsVertex * v1, * v2 = NULL, * v3 = NULL; - GtsPoint * p1, * p2, * p3; - gdouble x1, y1, z1, x2, y2, z2; - - g_return_if_fail (t != NULL); - - v1 = GTS_SEGMENT (t->e1)->v1; - if (GTS_SEGMENT (t->e1)->v1 == GTS_SEGMENT (t->e2)->v1) { - v2 = GTS_SEGMENT (t->e2)->v2; - v3 = GTS_SEGMENT (t->e1)->v2; - } - else if (GTS_SEGMENT (t->e1)->v2 == GTS_SEGMENT (t->e2)->v2) { - v2 = GTS_SEGMENT (t->e1)->v2; - v3 = GTS_SEGMENT (t->e2)->v1; - } - else if (GTS_SEGMENT (t->e1)->v1 == GTS_SEGMENT (t->e2)->v2) { - v2 = GTS_SEGMENT (t->e2)->v1; - v3 = GTS_SEGMENT (t->e1)->v2; - } - else if (GTS_SEGMENT (t->e1)->v2 == GTS_SEGMENT (t->e2)->v1) { - v2 = GTS_SEGMENT (t->e1)->v2; - v3 = GTS_SEGMENT (t->e2)->v2; - } - else { - fprintf (stderr, "t: %p t->e1: %p t->e2: %p t->e3: %p t->e1->v1: %p t->e1->v2: %p t->e2->v1: %p t->e2->v2: %p t->e3->v1: %p t->e3->v2: %p\n", - t, t->e1, t->e2, - t->e3, GTS_SEGMENT (t->e1)->v1, GTS_SEGMENT (t->e1)->v2, - GTS_SEGMENT (t->e2)->v1, GTS_SEGMENT (t->e2)->v2, - GTS_SEGMENT (t->e3)->v1, GTS_SEGMENT (t->e3)->v2); - g_assert_not_reached (); - } - - p1 = GTS_POINT (v1); - p2 = GTS_POINT (v2); - p3 = GTS_POINT (v3); - - x1 = p2->x - p1->x; - y1 = p2->y - p1->y; - z1 = p2->z - p1->z; - - x2 = p3->x - p1->x; - y2 = p3->y - p1->y; - z2 = p3->z - p1->z; - - *x = y1*z2 - z1*y2; - *y = z1*x2 - x1*z2; - *z = x1*y2 - y1*x2; -} - -/** - * gts_triangle_orientation: - * @t: a #GtsTriangle. - * - * Checks for the orientation of the plane (x,y) projection of a - * triangle. See gts_point_orientation() for details. This function - * is geometrically robust. - * - * Returns: a number depending on the orientation of the vertices of @t. - */ -gdouble gts_triangle_orientation (GtsTriangle * t) -{ - GtsVertex * v1, * v2 = NULL, * v3 = NULL; - - g_return_val_if_fail (t != NULL, 0.0); - - v1 = GTS_SEGMENT (t->e1)->v1; - if (GTS_SEGMENT (t->e1)->v1 == GTS_SEGMENT (t->e2)->v1) { - v2 = GTS_SEGMENT (t->e2)->v2; - v3 = GTS_SEGMENT (t->e1)->v2; - } - else if (GTS_SEGMENT (t->e1)->v2 == GTS_SEGMENT (t->e2)->v2) { - v2 = GTS_SEGMENT (t->e1)->v2; - v3 = GTS_SEGMENT (t->e2)->v1; - } - else if (GTS_SEGMENT (t->e1)->v1 == GTS_SEGMENT (t->e2)->v2) { - v2 = GTS_SEGMENT (t->e2)->v1; - v3 = GTS_SEGMENT (t->e1)->v2; - } - else if (GTS_SEGMENT (t->e1)->v2 == GTS_SEGMENT (t->e2)->v1) { - v2 = GTS_SEGMENT (t->e1)->v2; - v3 = GTS_SEGMENT (t->e2)->v2; - } - else - g_assert_not_reached (); - return gts_point_orientation (GTS_POINT (v1), - GTS_POINT (v2), - GTS_POINT (v3)); -} - -/** - * gts_triangle_revert: - * @t: a #GtsTriangle. - * - * Changes the orientation of triangle @t, turning it inside out. - */ -void gts_triangle_revert (GtsTriangle * t) -{ - GtsEdge * e; - - g_return_if_fail (t != NULL); - - e = t->e1; - t->e1 = t->e2; - t->e2 = e; -} - -/** - * gts_triangles_from_edges: - * @edges: a list of #GtsEdge. - * - * Builds a list of unique triangles which have one of their edges in @edges. - * - * Returns: the list of triangles. - */ -GSList * gts_triangles_from_edges (GSList * edges) -{ - GHashTable * hash; - GSList * triangles = NULL, * i; - - hash = g_hash_table_new (NULL, NULL); - i = edges; - while (i) { - GSList * j = GTS_EDGE (i->data)->triangles; - while (j) { - GtsTriangle * t = j->data; - if (g_hash_table_lookup (hash, t) == NULL) { - triangles = g_slist_prepend (triangles, t); - g_hash_table_insert (hash, t, i); - } - j = j->next; - } - i = i->next; - } - g_hash_table_destroy (hash); - - return triangles; -} - -/** - * gts_triangle_vertices_edges: - * @t: a #GtsTriangle. - * @e: a #GtsEdge belonging to the edges of @t or %NULL. - * @v1: a #GtsVertex used by @t. - * @v2: a #GtsVertex used by @t. - * @v3: a #GtsVertex used by @t. - * @e1: a #GtsEdge used by @t. - * @e2: a #GtsEdge used by @t. - * @e3: a #GtsEdge used by @t. - * - * Given @t and @e, returns @v1, @v2, @v3, @e1, @e2 and @e3. @e1 - * has @v1 and @v2 as vertices, @e2 has @v2 and @v3 as vertices - * and @e3 has @v3 and @v1 as vertices. @v1, @v2 and @v3 respects - * the orientation of @t. If @e is not NULL, @e1 and @e are - * identical. - */ -void gts_triangle_vertices_edges (GtsTriangle * t, - GtsEdge * e, - GtsVertex ** v1, - GtsVertex ** v2, - GtsVertex ** v3, - GtsEdge ** e1, - GtsEdge ** e2, - GtsEdge ** e3) -{ - GtsEdge * ee1, * ee2; - - g_return_if_fail (t != NULL); - - if (e == t->e1 || e == NULL) { - *e1 = ee1 = t->e1; *e2 = ee2 = t->e2; *e3 = t->e3; - } - else if (e == t->e2) { - *e1 = ee1 = e; *e2 = ee2 = t->e3; *e3 = t->e1; - } - else if (e == t->e3) { - *e1 = ee1 = e; *e2 = ee2 = t->e1; *e3 = t->e2; - } - else { - g_assert_not_reached (); - ee1 = ee2 = NULL; /* to avoid complaints from the compiler */ - } - if (GTS_SEGMENT (ee1)->v2 == GTS_SEGMENT (ee2)->v1) { - *v1 = GTS_SEGMENT (ee1)->v1; - *v2 = GTS_SEGMENT (ee1)->v2; - *v3 = GTS_SEGMENT (ee2)->v2; - } - else if (GTS_SEGMENT (ee1)->v2 == GTS_SEGMENT (ee2)->v2) { - *v1 = GTS_SEGMENT (ee1)->v1; - *v2 = GTS_SEGMENT (ee1)->v2; - *v3 = GTS_SEGMENT (ee2)->v1; - } - else if (GTS_SEGMENT (ee1)->v1 == GTS_SEGMENT (ee2)->v1) { - *v1 = GTS_SEGMENT (ee1)->v2; - *v2 = GTS_SEGMENT (ee1)->v1; - *v3 = GTS_SEGMENT (ee2)->v2; - } - else if (GTS_SEGMENT (ee1)->v1 == GTS_SEGMENT (ee2)->v2) { - *v1 = GTS_SEGMENT (ee1)->v2; - *v2 = GTS_SEGMENT (ee1)->v1; - *v3 = GTS_SEGMENT (ee2)->v1; - } - else - g_assert_not_reached (); -} - -/* sqrt(3) */ -#define SQRT3 1.73205080757 - -/** - * gts_triangle_enclosing: - * @klass: the class of the new triangle. - * @points: a list of #GtsPoint. - * @scale: a scaling factor (must be larger than one). - * - * Builds a new triangle (including new vertices and edges) enclosing - * the plane projection of all the points in @points. This triangle is - * equilateral and encloses a rectangle defined by the maximum and - * minimum x and y coordinates of the points. @scale is an homothetic - * scaling factor. If equal to one, the triangle encloses exactly the - * enclosing rectangle. - * - * Returns: a new #GtsTriangle. - */ -GtsTriangle * gts_triangle_enclosing (GtsTriangleClass * klass, - GSList * points, gdouble scale) -{ - gdouble xmax, xmin, ymax, ymin; - gdouble xo, yo, r; - GtsVertex * v1, * v2, * v3; - GtsEdge * e1, * e2, * e3; - - if (points == NULL) - return NULL; - - xmax = xmin = GTS_POINT (points->data)->x; - ymax = ymin = GTS_POINT (points->data)->y; - points = points->next; - while (points) { - GtsPoint * p = points->data; - if (p->x > xmax) xmax = p->x; - else if (p->x < xmin) xmin = p->x; - if (p->y > ymax) ymax = p->y; - else if (p->y < ymin) ymin = p->y; - points = points->next; - } - xo = (xmax + xmin)/2.; - yo = (ymax + ymin)/2.; - r = scale*sqrt((xmax - xo)*(xmax - xo) + (ymax - yo)*(ymax - yo)); - if (r == 0.0) r = scale; - v1 = gts_vertex_new (gts_vertex_class (), - xo + r*SQRT3, yo - r, 0.0); - v2 = gts_vertex_new (gts_vertex_class (), - xo, yo + 2.*r, 0.0); - v3 = gts_vertex_new (gts_vertex_class (), - xo - r*SQRT3, yo - r, 0.0); - e1 = gts_edge_new (gts_edge_class (), v1, v2); - e2 = gts_edge_new (gts_edge_class (), v2, v3); - e3 = gts_edge_new (gts_edge_class (), v3, v1); - return gts_triangle_new (gts_triangle_class (), e1, e2, e3); -} - -/** - * gts_triangle_neighbor_number: - * @t: a #GtsTriangle. - * - * Returns: the number of triangles neighbors of @t. - */ -guint gts_triangle_neighbor_number (GtsTriangle * t) -{ - GSList * i; - guint nn = 0; - GtsEdge * ee[4], ** e = ee; - - g_return_val_if_fail (t != NULL, 0); - - ee[0] = t->e1; ee[1] = t->e2; ee[2] = t->e3; ee[3] = NULL; - while (*e) { - i = (*e++)->triangles; - while (i) { - GtsTriangle * t1 = i->data; - if (t1 != t) - nn++; - i = i->next; - } - } - return nn; -} - -/** - * gts_triangle_neighbors: - * @t: a #GtsTriangle. - * - * Returns: a list of #GtsTriangle neighbors of @t. - */ -GSList * gts_triangle_neighbors (GtsTriangle * t) -{ - GSList * i, * list = NULL; - GtsEdge * ee[4], ** e = ee; - - g_return_val_if_fail (t != NULL, NULL); - - ee[0] = t->e1; ee[1] = t->e2; ee[2] = t->e3; ee[3] = NULL; - while (*e) { - i = (*e++)->triangles; - while (i) { - GtsTriangle * t1 = i->data; - if (t1 != t) - list = g_slist_prepend (list, t1); - i = i->next; - } - } - return list; -} - -/** - * gts_triangles_common_edge: - * @t1: a #GtsTriangle. - * @t2: a #GtsTriangle. - * - * Returns: a #GtsEdge common to both @t1 and @t2 or %NULL if @t1 and @t2 - * do not share any edge. - */ -GtsEdge * gts_triangles_common_edge (GtsTriangle * t1, - GtsTriangle * t2) -{ - g_return_val_if_fail (t1 != NULL, NULL); - g_return_val_if_fail (t2 != NULL, NULL); - - if (t1->e1 == t2->e1 || t1->e1 == t2->e2 || t1->e1 == t2->e3) - return t1->e1; - if (t1->e2 == t2->e1 || t1->e2 == t2->e2 || t1->e2 == t2->e3) - return t1->e2; - if (t1->e3 == t2->e1 || t1->e3 == t2->e2 || t1->e3 == t2->e3) - return t1->e3; - return NULL; -} - -/** - * gts_triangle_is_duplicate: - * @t: a #GtsTriangle. - * - * Returns: a #GtsTriangle different from @t but sharing all its edges - * with @t or %NULL if there is none. - */ -GtsTriangle * gts_triangle_is_duplicate (GtsTriangle * t) -{ - GSList * i; - GtsEdge * e2, * e3; - - g_return_val_if_fail (t != NULL, NULL); - - e2 = t->e2; - e3 = t->e3; - i = t->e1->triangles; - while (i) { - GtsTriangle * t1 = i->data; - if (t1 != t && - (t1->e1 == e2 || t1->e2 == e2 || t1->e3 == e2) && - (t1->e1 == e3 || t1->e2 == e3 || t1->e3 == e3)) - return t1; - i = i->next; - } - - return NULL; -} - -/** - * gts_triangle_use_edges: - * @e1: a #GtsEdge. - * @e2: a #GtsEdge. - * @e3: a #GtsEdge. - * - * Returns: a #GtsTriangle having @e1, @e2 and @e3 as edges or %NULL if @e1, - * @e2 and @e3 are not part of any triangle. - */ -GtsTriangle * gts_triangle_use_edges (GtsEdge * e1, - GtsEdge * e2, - GtsEdge * e3) -{ - GSList * i; - - g_return_val_if_fail (e1 != NULL, NULL); - g_return_val_if_fail (e2 != NULL, NULL); - g_return_val_if_fail (e3 != NULL, NULL); - - i = e1->triangles; - while (i) { - GtsTriangle * t = i->data; - if ((t->e1 == e2 && (t->e2 == e3 || t->e3 == e3)) || - (t->e2 == e2 && (t->e1 == e3 || t->e3 == e3)) || - (t->e3 == e2 && (t->e1 == e3 || t->e2 == e3))) - return t; - i = i->next; - } - - return NULL; -} - -/** - * gts_triangle_is_ok: - * @t: a #GtsTriangle. - * - * Returns: %TRUE if @t is a non-degenerate, non-duplicate triangle, - * %FALSE otherwise. - */ -gboolean gts_triangle_is_ok (GtsTriangle * t) -{ - g_return_val_if_fail (t != NULL, FALSE); - g_return_val_if_fail (t->e1 != NULL, FALSE); - g_return_val_if_fail (t->e2 != NULL, FALSE); - g_return_val_if_fail (t->e3 != NULL, FALSE); - g_return_val_if_fail (t->e1 != t->e2 && t->e1 != t->e3 && t->e2 != t->e3, - FALSE); - g_return_val_if_fail (gts_segments_touch (GTS_SEGMENT (t->e1), - GTS_SEGMENT (t->e2)), - FALSE); - g_return_val_if_fail (gts_segments_touch (GTS_SEGMENT (t->e1), - GTS_SEGMENT (t->e3)), - FALSE); - g_return_val_if_fail (gts_segments_touch (GTS_SEGMENT (t->e2), - GTS_SEGMENT (t->e3)), - FALSE); - g_return_val_if_fail (GTS_SEGMENT (t->e1)->v1 != GTS_SEGMENT (t->e1)->v2, - FALSE); - g_return_val_if_fail (GTS_SEGMENT (t->e2)->v1 != GTS_SEGMENT (t->e2)->v2, - FALSE); - g_return_val_if_fail (GTS_SEGMENT (t->e3)->v1 != GTS_SEGMENT (t->e3)->v2, - FALSE); - g_return_val_if_fail (GTS_OBJECT (t)->reserved == NULL, FALSE); - g_return_val_if_fail (!gts_triangle_is_duplicate (t), FALSE); - return TRUE; -} - -/** - * gts_triangle_vertices: - * @t: a #GtsTriangle. - * @v1: a pointer on a #GtsVertex. - * @v2: a pointer on a #GtsVertex. - * @v3: a pointer on a #GtsVertex. - * - * Fills @v1, @v2 and @v3 with the oriented set of vertices, summits of @t. - */ -void gts_triangle_vertices (GtsTriangle * t, - GtsVertex ** v1, GtsVertex ** v2, GtsVertex ** v3) -{ - GtsSegment * e1, * e2; - - g_return_if_fail (t != NULL); - g_return_if_fail (v1 != NULL && v2 != NULL && v3 != NULL); - - e1 = GTS_SEGMENT (t->e1); - e2 = GTS_SEGMENT (t->e2); - - if (GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e2)->v1) { - *v1 = GTS_SEGMENT (e1)->v1; - *v2 = GTS_SEGMENT (e1)->v2; - *v3 = GTS_SEGMENT (e2)->v2; - } - else if (GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e2)->v2) { - *v1 = GTS_SEGMENT (e1)->v1; - *v2 = GTS_SEGMENT (e1)->v2; - *v3 = GTS_SEGMENT (e2)->v1; - } - else if (GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e2)->v1) { - *v1 = GTS_SEGMENT (e1)->v2; - *v2 = GTS_SEGMENT (e1)->v1; - *v3 = GTS_SEGMENT (e2)->v2; - } - else { - *v1 = GTS_SEGMENT (e1)->v2; - *v2 = GTS_SEGMENT (e1)->v1; - *v3 = GTS_SEGMENT (e2)->v1; - } -} - -/** - * gts_triangle_circumcircle_center: - * @t: a #GtsTriangle. - * @point_class: a #GtsPointClass. - * - * Returns: a new #GtsPoint, center of the circumscribing circle of @t or - * %NULL if the circumscribing circle is not defined. - */ -GtsPoint * gts_triangle_circumcircle_center (GtsTriangle * t, - GtsPointClass * point_class) -{ - GtsVertex * v1, * v2, * v3; - gdouble xa, ya, xb, yb, xc, yc; - gdouble xd, yd, xe, ye; - gdouble xad, yad, xae, yae; - gdouble det; - - g_return_val_if_fail (t != NULL, NULL); - g_return_val_if_fail (point_class != NULL, NULL); - - gts_triangle_vertices (t, &v1, &v2, &v3); - - xa = GTS_POINT (v1)->x; ya = GTS_POINT (v1)->y; - xb = GTS_POINT (v2)->x; yb = GTS_POINT (v2)->y; - xc = GTS_POINT (v3)->x; yc = GTS_POINT (v3)->y; - xd = (xa + xb)/2.; yd = (ya + yb)/2.; - xe = (xa + xc)/2.; ye = (ya + yc)/2.; - xad = xd - xa; yad = yd - ya; - xae = xe - xa; yae = ye - ya; - det = xad*yae - xae*yad; - if (det == 0.) - return NULL; - return gts_point_new (point_class, - (yae*yad*(yd - ye) + xad*yae*xd - xae*yad*xe)/det, - -(xae*xad*(xd - xe) + yad*xae*yd - yae*xad*ye)/det, - 0.); -} - -/* square of the maximum area ratio admissible */ -#define AREA_RATIO_MAX2 1e8 - -static gboolean points_are_folded (GtsPoint * A, - GtsPoint * B, - GtsPoint * C, - GtsPoint * D, - gdouble max) -{ - GtsVector AB, AC, AD; - GtsVector n1, n2; - gdouble nn1, nn2, n1n2; - - gts_vector_init (AB, A, B); - gts_vector_init (AC, A, C); - gts_vector_init (AD, A, D); - gts_vector_cross (n1, AB, AC); - gts_vector_cross (n2, AD, AB); - - nn1 = gts_vector_scalar (n1, n1); - nn2 = gts_vector_scalar (n2, n2); - if (nn1 >= AREA_RATIO_MAX2*nn2 || nn2 >= AREA_RATIO_MAX2*nn1) - return TRUE; - n1n2 = gts_vector_scalar (n1, n2); - if (n1n2 > 0.) - return FALSE; - if (n1n2*n1n2/(nn1*nn2) > max) - return TRUE; - return FALSE; -} - -static GtsVertex * triangle_use_vertices (GtsTriangle * t, - GtsVertex * A, - GtsVertex * B) -{ - GtsVertex - * v1 = GTS_SEGMENT (t->e1)->v1, - * v2 = GTS_SEGMENT (t->e1)->v2, - * v3 = gts_triangle_vertex (t); - - if (v1 == A) { - if (v2 == B) - return v3; - g_assert (v3 == B); - return v2; - } - if (v2 == A) { - if (v1 == B) - return v3; - g_assert (v3 == B); - return v1; - } - if (v3 == A) { - if (v1 == B) - return v2; - g_assert (v2 == B); - return v1; - } - g_assert_not_reached (); - return NULL; -} - -/** - * gts_triangles_are_folded: - * @triangles: a list of #GtsTriangle. - * @A: a #GtsVertex. - * @B: another #GtsVertex. - * @max: the maximum value of the square of the cosine of the angle between - * two triangles. - * - * Given a list of triangles sharing @A and @B as vertices, checks if any - * two triangles in the list make an angle larger than a given value defined - * by @max. - * - * Returns: %TRUE if any pair of triangles in @triangles makes an angle larger - * than the maximum value, %FALSE otherwise. - */ -gboolean gts_triangles_are_folded (GSList * triangles, - GtsVertex * A, GtsVertex * B, - gdouble max) -{ - GSList * i; - - g_return_val_if_fail (A != NULL, TRUE); - g_return_val_if_fail (B != NULL, TRUE); - - i = triangles; - while (i) { - GtsVertex * C = triangle_use_vertices (i->data, A, B); - GSList * j = i->next; - while (j) { - GtsVertex * D = triangle_use_vertices (j->data, A, B); - if (points_are_folded (GTS_POINT (A), - GTS_POINT (B), - GTS_POINT (C), - GTS_POINT (D), - max)) - return TRUE; - j = j->next; - } - i = i->next; - } - return FALSE; -} - -/** - * gts_triangle_is_stabbed: - * @t: a #GtsTriangle. - * @p: a #GtsPoint. - * @orientation: a pointer or %NULL. - * - * Returns: one of the vertices of @t, one of the edges of @t or @t if - * any of these are stabbed by the ray starting at @p (included) and - * ending at (@p->x, @p->y, +infty), %NULL otherwise. If the ray is - * contained in the plane of the triangle %NULL is also returned. If - * @orientation is not %NULL, it is set to the value of the - * orientation of @p relative to @t (as given by - * gts_point_orientation_3d()). - */ -GtsObject * gts_triangle_is_stabbed (GtsTriangle * t, - GtsPoint * p, - gdouble * orientation) -{ - GtsVertex * v1, * v2, * v3, * inverted = NULL; - GtsEdge * e1, * e2, * e3, * tmp; - gdouble o, o1, o2, o3; - - g_return_val_if_fail (t != NULL, NULL); - g_return_val_if_fail (p != NULL, NULL); - - gts_triangle_vertices_edges (t, NULL, &v1, &v2, &v3, &e1, &e2, &e3); - o = gts_point_orientation (GTS_POINT (v1), GTS_POINT (v2), GTS_POINT (v3)); - if (o == 0.) - return NULL; - if (o < 0.) { - inverted = v1; - v1 = v2; - v2 = inverted; - tmp = e2; - e2 = e3; - e3 = tmp; - } - o = gts_point_orientation_3d (GTS_POINT (v1), - GTS_POINT (v2), - GTS_POINT (v3), - p); - if (o < 0.) - return NULL; - o1 = gts_point_orientation (GTS_POINT (v1), GTS_POINT (v2), p); - if (o1 < 0.) - return NULL; - o2 = gts_point_orientation (GTS_POINT (v2), GTS_POINT (v3), p); - if (o2 < 0.) - return NULL; - o3 = gts_point_orientation (GTS_POINT (v3), GTS_POINT (v1), p); - if (o3 < 0.) - return NULL; - if (orientation) *orientation = inverted ? -o : o; - if (o1 == 0.) { - if (o2 == 0.) - return GTS_OBJECT (v2); - if (o3 == 0.) - return GTS_OBJECT (v1); - return GTS_OBJECT (e1); - } - if (o2 == 0.) { - if (o3 == 0.) - return GTS_OBJECT (v3); - return GTS_OBJECT (e2); - } - if (o3 == 0.) - return GTS_OBJECT (e3); - return GTS_OBJECT (t); -} - -/** - * gts_triangle_interpolate_height: - * @t: a #GtsTriangle. - * @p: a #GtsPoint. - * - * Fills the z-coordinate of point @p belonging to the plane - * projection of triangle @t with the linearly interpolated value of - * the z-coordinates of the vertices of @t. - */ -void gts_triangle_interpolate_height (GtsTriangle * t, GtsPoint * p) -{ - GtsPoint * p1, * p2, * p3; - gdouble x1, x2, y1, y2, det; - - g_return_if_fail (t != NULL); - g_return_if_fail (p != NULL); - - p1 = GTS_POINT (GTS_SEGMENT (t->e1)->v1); - p2 = GTS_POINT (GTS_SEGMENT (t->e1)->v2); - p3 = GTS_POINT (gts_triangle_vertex (t)); - - x1 = p2->x - p1->x; - y1 = p2->y - p1->y; - x2 = p3->x - p1->x; - y2 = p3->y - p1->y; - det = x1*y2 - x2*y1; - if (det == 0.) - p->z = (p1->z + p2->z + p3->z)/3.; - else { - gdouble x = p->x - p1->x; - gdouble y = p->y - p1->y; - gdouble a = (x*y2 - y*x2)/det; - gdouble b = (y*x1 - x*y1)/det; - - p->z = (1. - a - b)*p1->z + a*p2->z + b*p3->z; - } -} Index: trunk/src/3rd/gts/stripe.c =================================================================== --- trunk/src/3rd/gts/stripe.c (revision 1064) +++ trunk/src/3rd/gts/stripe.c (nonexistent) @@ -1,766 +0,0 @@ -/* GTS - Library for the manipulation of triangulated surfaces - * Copyright (C) 1999-2003 Wagner Toledo Correa, Stéphane Popinet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include "gts.h" - -#define PRINT_HEAP_ELEMENTS 0 - -typedef struct { - GtsTriangle * t; - gboolean used; - GSList * neighbors; - GtsEHeapPair *pos; -} tri_data_t; - -typedef struct { - GHashTable * ht; -} map_t; - -typedef struct { - map_t * map; - GtsEHeap * heap; -} heap_t; - -static tri_data_t * tri_data_new (GtsTriangle * t); -static void tri_data_destroy (tri_data_t * td); -static guint tri_data_num_unused_neighbors2 (const tri_data_t * td, - const map_t * map); -static GHashTable * tri_data_unused_neighbors2 (const tri_data_t * td, - const map_t * map); - -static map_t * map_new (GtsSurface * s); -static void map_destroy (map_t * map); -static tri_data_t * map_lookup (const map_t * map, GtsTriangle * t); - - -static heap_t * heap_new (GtsSurface * s); -static void heap_destroy (heap_t * heap); -static gboolean heap_is_empty (const heap_t * heap); -static GtsTriangle * heap_top (const heap_t * heap); -static void heap_remove (heap_t * heap, GtsTriangle * t); - -/* helper functions */ - -static gboolean vertices_are_unique (GtsVertex * v1, - GtsVertex * v2, - GtsVertex * v3) -{ - g_assert (v1 && v2 && v3); - return (v1 != v2 && v1 != v3 && v2 != v3); -} - -static gboolean vertex_is_one_of (GtsVertex * v, - GtsVertex * v1, - GtsVertex * v2, - GtsVertex * v3) -{ - g_assert (v && v1 && v2 && v3); - return v == v1 || v == v2 || v == v3; -} - -static guint num_shared_vertices (GtsVertex * u1, - GtsVertex * u2, - GtsVertex * u3, - GtsVertex * v1, - GtsVertex * v2, - GtsVertex * v3) -{ - guint n = 0; - - g_assert (u1 && u2 && u3); - g_assert (v1 && v2 && v3); - g_assert (vertices_are_unique (u1, u2, u3)); - g_assert (vertices_are_unique (v1, v2, v3)); - - if (vertex_is_one_of (v1, u1, u2, u3)) - n++; - if (vertex_is_one_of (v2, u1, u2, u3)) - n++; - if (vertex_is_one_of (v3, u1, u2, u3)) - n++; - return n; -} - -static gboolean vertices_match (GtsVertex * v1, - GtsVertex * v2, - GtsVertex * v3, - GtsVertex ** v4, - GtsVertex ** v5, - GtsVertex ** v6) -{ - guint i; - - g_assert (v4 && v5 && v6); - g_assert (*v4 && *v5 && *v6); - g_assert (vertices_are_unique (*v4, *v5, *v6)); - - for (i = 0; i < 2; i++) { - if ((!v1 || (v1 == *v4)) && - (!v2 || (v2 == *v5)) && - (!v3 || (v3 == *v6))) - return TRUE; - else { - GtsVertex * v7 = * v4; - - *v4 = *v5; - *v5 = *v6; - *v6 = v7; - } - } - return ((!v1 || (v1 == *v4)) && - (!v2 || (v2 == *v5)) && - (!v3 || (v3 == *v6))); -} - -static GtsVertex * non_shared_vertex1 (GtsVertex * u1, - GtsVertex * u2, - GtsVertex * u3, - GtsVertex * v1, - GtsVertex * v2, - GtsVertex * v3) -{ - GtsVertex * u = NULL; - - g_assert (u1 && u2 && u3); - g_assert (v1 && v2 && v3); - g_assert (vertices_are_unique (u1, u2, u3)); - g_assert (vertices_are_unique (v1, v2, v3)); - g_assert (num_shared_vertices (u1, u2, u3, v1, v2, v3) == 2); - - if (!vertex_is_one_of (u1, v1, v2, v3)) { - g_assert (vertex_is_one_of (u2, v1, v2, v3)); - g_assert (vertex_is_one_of (u3, v1, v2, v3)); - u = u1; - } else if (!vertex_is_one_of (u2, v1, v2, v3)) { - g_assert (vertex_is_one_of (u1, v1, v2, v3)); - g_assert (vertex_is_one_of (u3, v1, v2, v3)); - u = u2; - } else if (!vertex_is_one_of (u3, v1, v2, v3)) { - g_assert (vertex_is_one_of (u1, v1, v2, v3)); - g_assert (vertex_is_one_of (u2, v1, v2, v3)); - u = u3; - } else - g_assert_not_reached (); - - return u; -} - -static void match_vertex (GtsVertex * v, - GtsVertex ** v1, - GtsVertex ** v2, - GtsVertex ** v3) -{ - g_assert (v && v1 && v2 && v3); - g_assert (*v1 && *v2 && *v3); - g_assert (vertex_is_one_of (v, *v1, *v2, *v3)); - while (*v1 != v) { - GtsVertex *v0 = *v1; - - *v1 = *v2; - *v2 = *v3; - *v3 = v0; - } -} - -/* tri_data_t functions */ - -static tri_data_t * tri_data_new (GtsTriangle * t) -{ - tri_data_t * td; - - td = g_malloc (sizeof (tri_data_t)); - td->t = t; - td->used = FALSE; - td->neighbors = gts_triangle_neighbors (t); - td->pos = NULL; - - return td; -} - -static void tri_data_destroy (tri_data_t * td) -{ - if (!td) - return; - g_slist_free (td->neighbors); - g_free (td); -} - -static guint tri_data_num_unused_neighbors2 (const tri_data_t * td, - const map_t * map) -{ - GHashTable *h; - guint n; - - g_assert (td); - g_assert (map); - h = tri_data_unused_neighbors2 (td, map); - n = g_hash_table_size (h); - g_hash_table_destroy (h); - return n; -} - -static void copy_key_to_array (gpointer key, - gpointer value, - gpointer user_data) -{ - GtsTriangle * t = key; - GtsTriangle *** p = user_data; - - (void) value; - g_assert (t); - g_assert (p && *p); - **p = t; - (*p)++; -} - -static gboolean are_neighbors_unique (GHashTable *h) -{ - GtsTriangle ** a; - GtsTriangle ** p; - gint i, j, n; /* guint won't work if n == 0 */ - - g_assert (h); - n = g_hash_table_size (h); -#ifdef DEBUG - if (n > 9) - g_warning ("triangle has %d 2-level neighbors", n); -#endif /* DEBUG */ - a = g_malloc(n*sizeof (GtsTriangle *)); - p = a; - g_hash_table_foreach (h, copy_key_to_array, &p); - for (i = 0; i < n - 1; i++) { - g_assert (a[i]); - for (j = i + 1; j < n; j++) { - g_assert (a[j]); - if (a[i] == a[j]) { - g_free (a); - return FALSE; - } - } - } - g_free (a); - return TRUE; -} - -static GHashTable * tri_data_unused_neighbors2 (const tri_data_t * td, - const map_t * map) -{ - GHashTable * h = g_hash_table_new (NULL, NULL); - GSList * li; - - g_assert (td); - g_assert (map); - for (li = td->neighbors; li != NULL; li = li->next) { - GtsTriangle * t2 = li->data; - tri_data_t * td2 = map_lookup (map, t2); - GSList * lj; - - g_assert (td2); - if (!td2->used) { - g_hash_table_insert (h, t2, td2); - for (lj = td2->neighbors; lj != NULL; lj = lj->next) { - GtsTriangle * t3 = lj->data; - tri_data_t * td3 = map_lookup (map, t3); - - g_assert (td3); - if (td3 != td && !td3->used) - g_hash_table_insert (h, t3, td3); - } - } - } - g_assert (are_neighbors_unique (h)); - return h; -} - -#if PRINT_HEAP_ELEMENTS -static void tri_data_print (const tri_data_t * td, FILE * fp) -{ - g_assert (td); - g_assert (fp); - fprintf(fp, "td=%p t=%p used=%d pos=%p key=%f\n", - td, td->t, td->used, td->pos, - td->pos ? td->pos->key : -1.0); -} -#endif /* PRINT_HEAP_ELEMENTS */ - -/* heap_t functions */ - -static gdouble triangle_priority (gpointer item, gpointer data) -{ - GtsTriangle * t = item; - map_t * map = data; - tri_data_t * td; - gdouble k; - - g_assert (t); - g_assert (map); - td = map_lookup (map, t); - g_assert (td); - k = tri_data_num_unused_neighbors2 (td, map); - return k; -} - -#if PRINT_HEAP_ELEMENTS -static void print_heap_element (gpointer data, gpointer user_data) -{ - GtsTriangle * t = data; - map_t * map = user_data; - tri_data_t * td; - - g_assert (t); - g_assert (map); - td = map_lookup (map, t); - g_assert (td); - g_assert (!td->used); - g_assert (td->pos); - tri_data_print (td, stderr); -} -#endif /* PRINT_HEAP_ELEMENTS */ - -static void insert_entry_into_heap (gpointer key, - gpointer value, - gpointer user_data) -{ - GtsTriangle * t = key; - tri_data_t * td = value; - GtsEHeap * heap = user_data; - - g_assert (!td->pos); - td->pos = gts_eheap_insert (heap, t); - g_assert (td->pos); -} - -static heap_t * heap_new (GtsSurface *s) -{ - heap_t * heap; - - g_assert (s); - heap = g_malloc (sizeof (heap_t)); - heap->map = map_new (s); - heap->heap = gts_eheap_new (triangle_priority, heap->map); - g_hash_table_foreach (heap->map->ht, - insert_entry_into_heap, - heap->heap); -#if PRINT_HEAP_ELEMENTS - gts_eheap_foreach (heap->heap, print_heap_element, heap->map); -#endif /* PRINT_HEAP_ELEMENTS */ - return heap; -} - -static void heap_destroy (heap_t * heap) -{ - if (!heap) - return; - map_destroy (heap->map); - gts_eheap_destroy (heap->heap); - g_free (heap); -} - -static gboolean heap_is_empty (const heap_t * heap) -{ - g_assert (heap); - g_assert (heap->heap); - return gts_eheap_size (heap->heap) == 0; -} - -typedef struct { - const heap_t * heap; - double min_key; -} min_key_t; - -static GtsTriangle * heap_top (const heap_t * heap) -{ - GtsTriangle * t; - - g_assert (heap); - g_assert (heap->heap); - t = gts_eheap_top (heap->heap, NULL); - return t; -} - -static void decrease_key (gpointer key, gpointer value, gpointer user_data) -{ - GtsTriangle * t = key; - tri_data_t * td = value; - heap_t *heap = user_data; - gdouble k; - - (void) t; - g_assert (heap); - g_assert (heap->map); - g_assert (heap->heap); - g_assert (td); - g_assert (!td->used); - g_assert (td->pos); - - k = tri_data_num_unused_neighbors2 (td, heap->map); - g_assert (k <= td->pos->key); -#ifdef DEBUG - if (k == td->pos->key) - g_warning ("same key: %f\n", k); -#endif /* DEBUG */ - if (k != td->pos->key) { - g_assert (k < td->pos->key); - g_assert (k >= 0.0); - gts_eheap_decrease_key (heap->heap, td->pos, k); - } -} - -static void heap_remove (heap_t * heap, GtsTriangle * t) -{ - tri_data_t * td; - GHashTable * h; - - g_assert (heap); - g_assert (t); - td = map_lookup (heap->map, t); - g_assert (td); - g_assert (!td->used); - g_assert (td->pos); - td->used = TRUE; - gts_eheap_remove (heap->heap, td->pos); - td->pos = NULL; - - /* fprintf(stderr, "td: %p\n", td); */ - h = tri_data_unused_neighbors2 (td, heap->map); - g_hash_table_foreach (h, decrease_key, heap); - g_hash_table_destroy (h); -} - -/* map_t functions */ - -static gint create_map_entry (gpointer item, gpointer data) -{ - GtsTriangle * t = item; - GHashTable * ht = data; - tri_data_t * td; - - g_assert (t); - g_assert (ht); - td = tri_data_new (t); - g_hash_table_insert (ht, t, td); - return 0; -} - -static void free_map_entry (gpointer key, gpointer value, gpointer user_data) -{ - GtsTriangle * t = key; - tri_data_t * td = value; - - (void) user_data; - g_assert (t); - g_assert (td); - g_assert (td->t == t); - tri_data_destroy (td); -} - -static map_t * map_new (GtsSurface * s) -{ - map_t * map; - - map = g_malloc (sizeof (map_t)); - map->ht = g_hash_table_new (NULL, NULL); - gts_surface_foreach_face (s, create_map_entry, map->ht); - return map; -} - -static void map_destroy (map_t * map) -{ - if (!map) - return; - g_hash_table_foreach (map->ht, free_map_entry, NULL); - g_hash_table_destroy (map->ht); - g_free (map); -} - -static tri_data_t * map_lookup (const map_t * map, GtsTriangle * t) -{ - tri_data_t * td; - - g_assert (map); - g_assert (map->ht); - g_assert (t); - td = g_hash_table_lookup (map->ht, t); - g_assert (td); - g_assert (td->t == t); - return td; -} - -/* other helper functions */ - -static GtsTriangle * find_min_neighbor (heap_t * heap, GtsTriangle * t) -{ - GtsTriangle * min_neighbor = NULL; - gdouble min_key = G_MAXDOUBLE; - tri_data_t * td; - GSList * li; - - g_assert (heap); - g_assert (t); - - td = map_lookup (heap->map, t); - for (li = td->neighbors; li != NULL; li = li->next) { - GtsTriangle * t2 = li->data; - tri_data_t * td2 = map_lookup (heap->map, t2); - gdouble k; - - g_assert (td2); - if (td2->used) - continue; - g_assert (td2->pos); - k = td2->pos->key; - if (k < min_key) { - min_key = k; - min_neighbor = t2; - } - } - return min_neighbor; -} - -static GtsTriangle * find_neighbor_forward (heap_t * heap, - GtsTriangle * t, - GtsVertex ** v1, - GtsVertex ** v2, - GtsVertex ** v3, - gboolean left_turn) -{ - GtsTriangle * neighbor = NULL; - tri_data_t * td; - GSList * li; - - g_assert (heap); - g_assert (t); - g_assert (v1 && v2 && v3); - g_assert (vertices_are_unique (*v1, *v2, *v3)); - - td = map_lookup (heap->map, t); - g_assert (td); - for (li = td->neighbors; li && !neighbor; li = li->next) { - GtsTriangle * t2 = li->data; - tri_data_t * td2 = map_lookup (heap->map, t2); - GtsVertex * v4, * v5, * v6; - - g_assert (td2); - if (t2 == t || td2->used) - continue; - gts_triangle_vertices (t2, &v4, &v5, &v6); - if (left_turn) { - if (!vertices_match (*v1, *v3, NULL, &v4, &v5, &v6)) - continue; - } else { - if (!vertices_match (*v3, *v2, NULL, &v4, &v5, &v6)) - continue; - } - neighbor = t2; - *v1 = v4; - *v2 = v5; - *v3 = v6; - } - return neighbor; -} - -static GtsTriangle * find_neighbor_backward (heap_t * heap, - GtsTriangle * t, - GtsVertex ** v1, - GtsVertex ** v2, - GtsVertex ** v3, - gboolean left_turn) -{ - GtsTriangle * neighbor = NULL; - tri_data_t * td; - GSList * li; - - g_assert (heap); - g_assert (t); - g_assert (v1 && v2 && v3); - g_assert (vertices_are_unique (*v1, *v2, *v3)); - - td = map_lookup (heap->map, t); - g_assert (td); - for (li = td->neighbors; li && !neighbor; li = li->next) { - GtsTriangle * t2 = li->data; - tri_data_t * td2 = map_lookup (heap->map, t2); - GtsVertex * v4, * v5, * v6; - - g_assert (td2); - if (t2 == t || td2->used) - continue; - gts_triangle_vertices (t2, &v4, &v5, &v6); - if (left_turn) { - if (!vertices_match (NULL, *v2, *v1, &v4, &v5, &v6)) - continue; - } else if (!vertices_match(*v1, NULL, *v2, &v4, &v5, &v6)) - continue; - neighbor = t2; - *v1 = v4; - *v2 = v5; - *v3 = v6; - } - return neighbor; -} - -static GSList * grow_strip_forward (heap_t * heap, - GSList * strip, - GtsTriangle * t, - GtsVertex * v1, - GtsVertex * v2, - GtsVertex * v3) -{ - gboolean left_turn; - - g_assert (heap); - g_assert (g_slist_length(strip) == 2); - g_assert (t); - g_assert (v1 && v2 && v3); - g_assert (vertices_are_unique (v1, v2, v3)); - - left_turn = TRUE; - while ((t = find_neighbor_forward (heap, t, &v1, &v2, &v3, - left_turn)) != NULL) { - heap_remove (heap, t); - strip = g_slist_prepend (strip, t); - left_turn = !left_turn; - } - return strip; -} - -static GSList * grow_strip_backward (heap_t * heap, - GSList * strip, - GtsTriangle * t, - GtsVertex * v1, - GtsVertex * v2, - GtsVertex * v3) -{ - /* we have to make sure we add an even number of triangles */ - GtsTriangle * t2; - - g_assert (heap); - g_assert (g_slist_length(strip) >= 2); - g_assert (t); - g_assert (v1 && v2 && v3); - g_assert (vertices_are_unique (v1, v2, v3)); - - while ((t2 = find_neighbor_backward (heap, t, &v1, &v2, &v3, - FALSE)) != NULL - && (t = find_neighbor_backward (heap, t2, &v1, &v2, &v3, - TRUE)) != NULL) { - heap_remove (heap, t2); - heap_remove (heap, t); - strip = g_slist_prepend (strip, t2); - strip = g_slist_prepend (strip, t); - } - return strip; -} - -static gboolean find_right_turn (GtsVertex ** v1, - GtsVertex ** v2, - GtsVertex ** v3, - GtsVertex ** v4, - GtsVertex ** v5, - GtsVertex ** v6) -{ - GtsVertex * v; - - g_assert (v1 && v2 && v3); - g_assert (v4 && v5 && v6); - g_assert (vertices_are_unique (*v1, *v2, *v3)); - g_assert (vertices_are_unique (*v4, *v5, *v6)); - g_assert (num_shared_vertices (*v1, *v2, *v3, *v4, *v5, *v6) == 2); - - v = non_shared_vertex1 (*v1, *v2, *v3, *v4, *v5, *v6); - match_vertex (v, v1, v2, v3); - match_vertex (*v3, v4, v5, v6); - - g_assert (v1 && v2 && v3); - g_assert (v4 && v5 && v6); - g_assert (*v4 == *v3); - - if (*v5 == *v2) { - g_assert (vertices_are_unique (*v1, *v2, *v3)); - g_assert (vertices_are_unique (*v4, *v5, *v6)); - g_assert (num_shared_vertices (*v1, *v2, *v3, - *v4, *v5, *v6) == 2); - return TRUE; - } else { -#ifdef DEBUG - g_warning ("couldn't find a right turn"); -#endif /* DEBUG */ - return FALSE; - } -} - -/** - * gts_surface_strip: - * @s: a #GtsSurface. - * - * Decompose @s into triangle strips for fast-rendering. - * - * Returns: a list of triangle strips containing all the triangles of @s. - * A triangle strip is itself a list of successive triangles having one edge - * in common. - */ -GSList * gts_surface_strip (GtsSurface *s) -{ - GSList * strips = NULL; - heap_t * heap; - - g_return_val_if_fail (s != NULL, NULL); - - heap = heap_new (s); - while (!heap_is_empty (heap)) { - GtsTriangle * t1, * t2; - GtsVertex * v1, * v2, * v3, * v4, * v5, * v6; - GSList * strip = NULL; - - /* remove heap top */ - t1 = heap_top (heap); - g_assert (t1); - heap_remove (heap, t1); - - /* start a new strip */ - strip = g_slist_prepend (strip, t1); - - /* find second triangle */ - t2 = find_min_neighbor (heap, t1); - if (t2) { - g_assert (t2 != t1); - - /* find right turn */ - gts_triangle_vertices (t1, &v1, &v2, &v3); - gts_triangle_vertices (t2, &v4, &v5, &v6); - if (find_right_turn (&v1, &v2, &v3, &v4, &v5, &v6)) { - heap_remove (heap, t2); - strip = g_slist_prepend (strip, t2); - - /* grow strip forward */ - strip = grow_strip_forward (heap, strip, t2, v4, v5, v6); - - strip = g_slist_reverse (strip); - - /* grow strip backward */ - strip = grow_strip_backward (heap, strip, t1, v1, v2, v3); - } - } - strips = g_slist_prepend (strips, strip); - } - strips = g_slist_reverse (strips); - heap_destroy (heap); - - return strips; -} Index: trunk/src/3rd/gts/object.c =================================================================== --- trunk/src/3rd/gts/object.c (revision 1064) +++ trunk/src/3rd/gts/object.c (nonexistent) @@ -1,345 +0,0 @@ -/* GTS - Library for the manipulation of triangulated surfaces - * Copyright (C) 1999 Stéphane Popinet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include -#include "gts.h" -#include "gts-private.h" - -static GHashTable * class_table = NULL; - -static void gts_object_class_init (GtsObjectClass * klass, - GtsObjectClass * parent_class) -{ - if (parent_class) { - gts_object_class_init (klass, parent_class->parent_class); - if (parent_class->info.class_init_func) - (*parent_class->info.class_init_func) (klass); - } -} - -/** - * gts_object_class_new: - * @parent_class: a #GtsObjectClass. - * @info: a #GtsObjectClassInfo, description of the new class to create. - * - * Returns: a new #GtsObjectClass derived from @parent_class and described by - * @info. - */ -gpointer gts_object_class_new (GtsObjectClass * parent_class, - GtsObjectClassInfo * info) -{ - GtsObjectClass * klass; - - g_return_val_if_fail (info != NULL, NULL); - g_return_val_if_fail (parent_class == NULL || - info->object_size >= parent_class->info.object_size, - NULL); - g_return_val_if_fail (parent_class == NULL || - info->class_size >= parent_class->info.class_size, - NULL); - - klass = g_malloc0 (info->class_size); - klass->info = *info; - klass->parent_class = parent_class; - gts_object_class_init (klass, klass); - - if (!class_table) - class_table = g_hash_table_new (g_str_hash, g_str_equal); - g_hash_table_insert (class_table, klass->info.name, klass); - - return klass; -} - -/** - * gts_object_class_from_name: - * @name: the name of a #GtsObjectClass. - * - * Returns: the #GtsObjectClass with name @name or %NULL if it hasn't been - * instantiated yet. - */ -GtsObjectClass * gts_object_class_from_name (const gchar * name) -{ - g_return_val_if_fail (name != NULL, NULL); - - if (!class_table) - return NULL; - return g_hash_table_lookup (class_table, name); -} - -static void object_destroy (GtsObject * object) -{ -#ifdef DEBUG_IDENTITY -#ifdef DEBUG_LEAKS - fprintf (stderr, "destroy %s %p->%d\n", - object->klass->info.name, - object, - id (object)); -#endif - id_remove (object); -#endif - object->klass = NULL; - g_free (object); -} - -static void object_clone (GtsObject * clone, GtsObject * object) -{ - memcpy (clone, object, object->klass->info.object_size); - clone->reserved = NULL; -} - -static void object_class_init (GtsObjectClass * klass) -{ - klass->clone = object_clone; - klass->destroy = object_destroy; - klass->read = NULL; - klass->write = NULL; - klass->color = NULL; - klass->attributes = NULL; -} - -static void object_init (GtsObject * object) -{ - object->reserved = NULL; - object->flags = 0; -} - -/** - * gts_object_class: - * - * Returns: the #GtsObjectClass. - */ -GtsObjectClass * gts_object_class (void) -{ - static GtsObjectClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo object_info = { - "GtsObject", - sizeof (GtsObject), - sizeof (GtsObjectClass), - (GtsObjectClassInitFunc) object_class_init, - (GtsObjectInitFunc) object_init, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (NULL, &object_info); - } - - return klass; -} - -/** - * gts_object_check_cast: - * @object: a #GtsObject. - * @klass: a #GtsObjectClass. - * - * Returns: @object while emitting warnings if @object is not of class @klass. - */ -gpointer gts_object_check_cast (gpointer object, - gpointer klass) -{ - if (!object) { - g_warning ("invalid cast from (NULL) pointer to `%s'", - GTS_OBJECT_CLASS (klass)->info.name); - return object; - } - if (!((GtsObject *) object)->klass) { - g_warning ("invalid unclassed pointer in cast to `%s'", - GTS_OBJECT_CLASS (klass)->info.name); - return object; - } - if (!gts_object_is_from_class (object, klass)) { - g_warning ("invalid cast from `%s' to `%s'", - ((GtsObject *) object)->klass->info.name, - GTS_OBJECT_CLASS (klass)->info.name); - return object; - } - return object; -} - -/** - * gts_object_class_check_cast: - * @klass: a #GtsObjectClass. - * @from: a #GtsObjectClass. - * - * Returns: @klass while emitting warnings if @klass is not derived from - * @from. - */ -gpointer gts_object_class_check_cast (gpointer klass, - gpointer from) -{ - if (!klass) { - g_warning ("invalid cast from (NULL) pointer to `%s'", - GTS_OBJECT_CLASS (from)->info.name); - return klass; - } - if (!gts_object_class_is_from_class (klass, from)) { - g_warning ("invalid cast from `%s' to `%s'", - GTS_OBJECT_CLASS (klass)->info.name, - GTS_OBJECT_CLASS (from)->info.name); - return klass; - } - return klass; -} - -/** - * gts_object_init: - * @object: a #GtsObject. - * @klass: a #GtsObjectClass. - * - * Calls the init method of @klass with @object as argument. This is done - * recursively in the correct order (from the base class to the top). You - * should rarely need this function as it is called automatically by the - * constructor for each class. - */ -void gts_object_init (GtsObject * object, GtsObjectClass * klass) -{ - GtsObjectClass * parent_class; - - g_return_if_fail (object != NULL); - g_return_if_fail (klass != NULL); - - parent_class = klass->parent_class; - if (parent_class) - gts_object_init (object, parent_class); - if (klass->info.object_init_func) - (*klass->info.object_init_func) (object); -} - -/** - * gts_object_new: - * @klass: a #GtsObjectClass. - * - * Returns: a new initialized object of class @klass. - */ -GtsObject * gts_object_new (GtsObjectClass * klass) -{ - GtsObject * object; - - g_return_val_if_fail (klass != NULL, NULL); - - object = g_malloc0 (klass->info.object_size); - object->klass = klass; - gts_object_init (object, klass); - -#ifdef DEBUG_IDENTITY - id_insert (object); -#ifdef DEBUG_LEAKS - fprintf (stderr, "new %s %p->%d\n", klass->info.name, - object, - id (object)); -#endif -#endif - - return object; -} - -/** - * gts_object_clone: - * @object: a #GtsObject. - * - * Calls the clone method of @object. The call to this function will fail - * if no clone method exists for the given object. - * - * Returns: a new object clone of @object. - */ -GtsObject * gts_object_clone (GtsObject * object) -{ - GtsObject * clone; - - g_return_val_if_fail (object != NULL, NULL); - g_return_val_if_fail (object->klass->clone, NULL); - - clone = g_malloc0 (object->klass->info.object_size); - clone->klass = object->klass; - object_init (clone); - (* object->klass->clone) (clone, object); - -#ifdef DEBUG_IDENTITY - id_insert (clone); -#ifdef DEBUG_LEAKS - fprintf (stderr, "clone %s %p->%d\n", clone->klass->info.name, - clone, - id (clone)); -#endif -#endif - - return clone; -} - -/** - * gts_object_destroy: - * @object: a #GtsObject. - * - * Calls the destroy method of @object, freeing all memory allocated for it. - */ -void gts_object_destroy (GtsObject * object) -{ - g_assert (object->klass->destroy); - GTS_OBJECT_SET_FLAGS (object, GTS_DESTROYED); - (* object->klass->destroy) (object); -} - -/** - * gts_object_reset_reserved: - * @object: a #GtsObject. - * - * Reset the reserved field of @object. - */ -void gts_object_reset_reserved (GtsObject * object) -{ - g_return_if_fail (object != NULL); - - object->reserved = NULL; -} - -/** - * gts_object_attributes: - * @object: a #GtsObject. - * @from: a #GtsObject. - * - * Calls the attributes() method of @object using @from as source. - */ -void gts_object_attributes (GtsObject * object, GtsObject * from) -{ - g_return_if_fail (object != NULL); - - if (object->klass->attributes) - (* object->klass->attributes) (object, from); -} - -static void free_class (gchar * name, GtsObjectClass * klass) -{ - g_free (klass); -} - -/** - * gts_finalize: - * - * Free all the memory allocated by the object system of GTS. No other - * GTS function can be called after this function has been called. - */ -void gts_finalize (void) -{ - if (class_table) { - g_hash_table_foreach (class_table, (GHFunc) free_class, NULL); - g_hash_table_destroy (class_table); - class_table = NULL; - } -} Index: trunk/src/3rd/gts/refine.c =================================================================== --- trunk/src/3rd/gts/refine.c (revision 1064) +++ trunk/src/3rd/gts/refine.c (nonexistent) @@ -1,418 +0,0 @@ -/* GTS - Library for the manipulation of triangulated surfaces - * Copyright (C) 1999 Stéphane Popinet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include -#include "gts.h" - -/** - * gts_vertex_encroaches_edge: - * @v: a #GtsVertex. - * @e: a #GtsEdge. - * - * Returns: %TRUE if @v is strictly contained in the diametral circle of @e, - * %FALSE otherwise. - */ -gboolean gts_vertex_encroaches_edge (GtsVertex * v, GtsEdge * e) -{ - GtsPoint * p, * p1, * p2; - - g_return_val_if_fail (v != NULL, FALSE); - g_return_val_if_fail (e != NULL, FALSE); - - p = GTS_POINT (v); - p1 = GTS_POINT (GTS_SEGMENT (e)->v1); - p2 = GTS_POINT (GTS_SEGMENT (e)->v2); - - if ((p1->x - p->x)*(p2->x - p->x) + (p1->y - p->y)*(p2->y - p->y) < 0.0) - return TRUE; - return FALSE; -} - -/** - * gts_edge_is_encroached: - * @e: a #GtsEdge. - * @s: a #GtsSurface describing a (constrained) Delaunay triangulation. - * @encroaches: a #GtsEncroachFunc. - * @data: user data to be passed to @encroaches. - * - * Returns: a #GtsVertex belonging to @s and encroaching upon @e - * (as defined by @encroaches) or %NULL if there is none. - */ -GtsVertex * gts_edge_is_encroached (GtsEdge * e, - GtsSurface * s, - GtsEncroachFunc encroaches, - gpointer data) -{ - GSList * i; - - g_return_val_if_fail (e != NULL, NULL); - g_return_val_if_fail (s != NULL, NULL); - g_return_val_if_fail (encroaches != NULL, NULL); - - i = e->triangles; - while (i) { - GtsFace * f = i->data; - if (GTS_IS_FACE (f) && gts_face_has_parent_surface (f, s)) { - GtsVertex * v = gts_triangle_vertex_opposite (GTS_TRIANGLE (f), e); - if ((* encroaches) (v, e, s, data)) - return v; - } - i = i->next; - } - - return NULL; -} - -#define ALREADY_ENCROACHED(c) (GTS_OBJECT (c)->reserved) - -static void vertex_encroaches (GtsVertex * v, - GtsSurface * surface, - GtsFifo * encroached, - GtsEncroachFunc encroaches, - gpointer data) -{ - GSList * triangles, * i; - - g_return_if_fail (v != NULL); - g_return_if_fail (surface != NULL); - g_return_if_fail (encroached != NULL); - g_return_if_fail (encroaches != NULL); - - i = triangles = gts_vertex_triangles (v, NULL); - while (i) { - GtsFace * f = i->data; - if (GTS_IS_FACE (f) && gts_face_has_parent_surface (f, surface)) { - GtsEdge * e = gts_triangle_edge_opposite (i->data, v); - if (!ALREADY_ENCROACHED (e) && - GTS_IS_CONSTRAINT (e) && - (* encroaches) (v, e, surface, data)) { - gts_fifo_push (encroached, e); - ALREADY_ENCROACHED (e) = encroached; - } - } - i = i->next; - } - g_slist_free (triangles); -} - -static void make_encroached_fifo (GtsEdge * e, gpointer * datas) -{ - GtsFifo * fifo = datas[0]; - GtsSurface * s = datas[1]; - GtsEncroachFunc encroaches = (GtsEncroachFunc) datas[2]; - gpointer data = datas[3]; - - if (GTS_IS_CONSTRAINT (e) && - gts_edge_is_encroached (e, s, encroaches, data)) { - gts_fifo_push (fifo, e); - ALREADY_ENCROACHED (e) = fifo; - } -} - -#define SQUARE_ROOT_TWO 1.41421356237309504880168872420969807856967187 -#define DISTANCE_2D(v1, v2) (sqrt ((GTS_POINT (v2)->x - GTS_POINT (v1)->x)*\ - (GTS_POINT (v2)->x - GTS_POINT (v1)->x) +\ - (GTS_POINT (v2)->y - GTS_POINT (v1)->y)*\ - (GTS_POINT (v2)->y - GTS_POINT (v1)->y))) - -/* finds where to split the given edge to avoid infinite cycles. (see - Shewchuk's thesis for details */ -static GtsVertex * split_edge (GtsEdge * e, - GtsSurface * surface) -{ - GSList * i = e->triangles; - GtsEdge * c = NULL; - - /* look for constraints touching e */ - while (i && !c) { - GtsTriangle * t = i->data; - if (GTS_IS_FACE (t) && - gts_face_has_parent_surface (GTS_FACE (t), surface)) { - GtsEdge * e1, * e2; - if (t->e1 == e) { e1 = t->e2; e2 = t->e3; } - else if (t->e2 == e) { e1 = t->e1; e2 = t->e3; } - else { e1 = t->e1; e2 = t->e2; } - if (GTS_IS_CONSTRAINT (e1) && !GTS_IS_CONSTRAINT (e2)) - c = e1; - else if (GTS_IS_CONSTRAINT (e2) && !GTS_IS_CONSTRAINT (e1)) - c = e2; - } - i = i->next; - } - if (c) { - /* use power of two concentric shells */ - GtsVertex * v1 = GTS_SEGMENT (e)->v1; - GtsVertex * v2 = GTS_SEGMENT (e)->v2; - gdouble l = DISTANCE_2D (v1, v2); - gdouble nearestpower = 1., split; - - while (l > SQUARE_ROOT_TWO*nearestpower) - nearestpower *= 2.; - while (l < SQUARE_ROOT_TWO*nearestpower/2.) - nearestpower /= 2.; - split = nearestpower/l/2.; - - if (GTS_SEGMENT (c)->v1 == v2 || GTS_SEGMENT (c)->v2 == v2) - split = 1. - split; - return gts_vertex_new (surface->vertex_class, - (1. - split)*GTS_POINT (v1)->x + - split*GTS_POINT (v2)->x, - (1. - split)*GTS_POINT (v1)->y + - split*GTS_POINT (v2)->y, - (1. - split)*GTS_POINT (v1)->z + - split*GTS_POINT (v2)->z); - } - else - return gts_segment_midvertex (GTS_SEGMENT (e), surface->vertex_class); -} - -static gint split_encroached (GtsSurface * surface, - GtsFifo * encroached, - gint steiner_max, - GtsEncroachFunc encroaches, - gpointer data) -{ - GtsSegment * s; - - while (steiner_max-- != 0 && (s = gts_fifo_pop (encroached))) { - GtsVertex * v = split_edge (GTS_EDGE (s), surface); - GtsFace * boundary = gts_edge_is_boundary (GTS_EDGE (s), surface); - GtsFace * f = boundary; -#if 1 - GtsEdge * e1 = GTS_EDGE (gts_object_clone (GTS_OBJECT (s))); - GtsEdge * e2 = GTS_EDGE (gts_object_clone (GTS_OBJECT (s))); - - GTS_SEGMENT (e1)->v1 = s->v1; - s->v1->segments = g_slist_prepend (s->v1->segments, e1); - GTS_SEGMENT (e1)->v2 = v; - v->segments = g_slist_prepend (v->segments, e1); - - GTS_SEGMENT (e2)->v1 = v; - v->segments = g_slist_prepend (v->segments, e2); - GTS_SEGMENT (e2)->v2 = s->v2; - s->v2->segments = g_slist_prepend (s->v2->segments, e2); -#else - GtsEdge * e1 = gts_edge_new (GTS_EDGE_CLASS (GTS_OBJECT (s)->klass), - s->v1, v); - GtsEdge * e2 = gts_edge_new (GTS_EDGE_CLASS (GTS_OBJECT (s)->klass), - v, s->v2); -#endif - - GTS_OBJECT (s)->klass = GTS_OBJECT_CLASS (surface->edge_class); - - if (f == NULL) - g_assert ((f = gts_edge_has_parent_surface (GTS_EDGE (s), surface))); - g_assert (gts_delaunay_add_vertex_to_face (surface, v, f) == NULL); - - if (boundary) - gts_object_destroy (GTS_OBJECT (s)); - - vertex_encroaches (v, surface, encroached, encroaches, data); - - if (gts_edge_is_encroached (e1, surface, encroaches, data)) { - gts_fifo_push (encroached, e1); - ALREADY_ENCROACHED (e1) = encroached; - } - if (gts_edge_is_encroached (e2, surface, encroaches, data)) { - gts_fifo_push (encroached, e2); - ALREADY_ENCROACHED (e2) = encroached; - } - } - - return steiner_max; -} - -/** - * gts_delaunay_conform: - * @surface: a #GtsSurface describing a constrained Delaunay triangulation. - * @steiner_max: maximum number of Steiner points. - * @encroaches: a #GtsEncroachFunc. - * @data: user-data to pass to @encroaches. - * - * Recursively split constraints of @surface which are encroached by - * vertices of @surface (see Shewchuk 96 for details). The split - * constraints are destroyed and replaced by a set of new constraints - * of the same class. If gts_vertex_encroaches_edge() is used for - * @encroaches, the resulting surface will be Delaunay conforming. - * - * If @steiner_max is positive or nul, the recursive splitting - * procedure will stop when this maximum number of Steiner points is - * reached. In that case the resulting surface will not necessarily be - * Delaunay conforming. - * - * Returns: the number of remaining encroached edges. If @steiner_max - * is set to a negative value and gts_vertex_encroaches_edge() is used - * for @encroaches this should always be zero. - */ -guint gts_delaunay_conform (GtsSurface * surface, - gint steiner_max, - GtsEncroachFunc encroaches, - gpointer data) -{ - GtsFifo * encroached; - gpointer datas[4]; - guint encroached_number; - - g_return_val_if_fail (surface != NULL, 0); - g_return_val_if_fail (surface != NULL, 0); - g_return_val_if_fail (encroaches != NULL, 0); - - datas[0] = encroached = gts_fifo_new (); - datas[1] = surface; - datas[2] = encroaches; - datas[3] = data; - gts_surface_foreach_edge (surface, (GtsFunc) make_encroached_fifo, datas); - - split_encroached (surface, - encroached, - steiner_max, - encroaches, data); - gts_fifo_foreach (encroached, (GtsFunc) gts_object_reset_reserved, NULL); - encroached_number = gts_fifo_size (encroached); - gts_fifo_destroy (encroached); - return encroached_number; -} - -#define EHEAP_PAIR(f) (GTS_OBJECT (f)->reserved) - -static void heap_surface_add_face (GtsSurface * s, GtsFace * f) -{ - GtsEHeap * heap = GTS_OBJECT (s)->reserved; - gdouble key = gts_eheap_key (heap, f); - - if (key != 0.) - EHEAP_PAIR (f) = gts_eheap_insert_with_key (heap, f, key); - - if (GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass->parent_class)->add_face) - (* GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass->parent_class)->add_face) - (s, f); -} - -static void heap_surface_remove_face (GtsSurface * s, GtsFace * f) -{ - GtsEHeap * heap = GTS_OBJECT (s)->reserved; - - if (EHEAP_PAIR (f)) - gts_eheap_remove (heap, EHEAP_PAIR (f)); - - if (GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass->parent_class)->remove_face) - (* GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass->parent_class)->remove_face) - (s, f); -} - -static void heap_surface_class_init (GtsSurfaceClass * klass) -{ - klass->add_face = heap_surface_add_face; - klass->remove_face = heap_surface_remove_face; -} - -static GtsObjectClass * heap_surface_class_new (GtsObjectClass * parent_class) -{ - GtsObjectClassInfo heap_surface_info; - - heap_surface_info = parent_class->info; - heap_surface_info.class_init_func = (GtsObjectClassInitFunc) - heap_surface_class_init; - return gts_object_class_new (parent_class, - &heap_surface_info); -} - -static void make_face_heap (GtsFace * f, GtsEHeap * heap) -{ - gdouble key = gts_eheap_key (heap, f); - - if (key != 0.) - EHEAP_PAIR (f) = gts_eheap_insert_with_key (heap, f, key); -} - -/** - * gts_delaunay_refine: - * @surface: a #GtsSurface describing a conforming Delaunay triangulation. - * @steiner_max: maximum number of Steiner points. - * @encroaches: a #GtsEncroachFunc. - * @encroach_data: user-data to pass to @encroaches. - * @cost: a #GtsKeyFunc used to sort the faces during refinement. - * @cost_data: user-data to pass to @cost. - * - * An implementation of the refinement algorithm described in Ruppert - * (1995) and Shewchuk (1996). - * - * Returns: the number of unrefined faces of @surface left. Should be zero - * if @steiner_max is set to a negative value. - */ -guint gts_delaunay_refine (GtsSurface * surface, - gint steiner_max, - GtsEncroachFunc encroaches, - gpointer encroach_data, - GtsKeyFunc cost, - gpointer cost_data) -{ - GtsObjectClass * heap_surface_class; - GtsObjectClass * original_class; - GtsEHeap * heap; - GtsFifo * encroached; - GtsFace * f; - guint unrefined_number; - - g_return_val_if_fail (surface != NULL, 0); - g_return_val_if_fail (encroaches != NULL, 0); - g_return_val_if_fail (cost != NULL, 0); - - original_class = GTS_OBJECT (surface)->klass; - heap_surface_class = heap_surface_class_new (original_class); - GTS_OBJECT (surface)->klass = heap_surface_class; - - heap = gts_eheap_new (cost, cost_data); - gts_surface_foreach_face (surface, (GtsFunc) make_face_heap, heap); - encroached = gts_fifo_new (); - - GTS_OBJECT (surface)->reserved = heap; - - while (steiner_max-- != 0 && (f = gts_eheap_remove_top (heap, NULL))) { - GtsVertex * c = - GTS_VERTEX (gts_triangle_circumcircle_center (GTS_TRIANGLE (f), - GTS_POINT_CLASS (surface->vertex_class))); - EHEAP_PAIR (f) = NULL; - g_assert (c != NULL); - g_assert (gts_delaunay_add_vertex (surface, c, f) == NULL); - - vertex_encroaches (c, surface, encroached, encroaches, encroach_data); - if (!gts_fifo_is_empty (encroached)) { - gts_delaunay_remove_vertex (surface, c); - steiner_max = split_encroached (surface, - encroached, - steiner_max, - encroaches, - encroach_data); - } - } - - unrefined_number = gts_eheap_size (heap); - gts_eheap_foreach (heap, (GFunc) gts_object_reset_reserved, NULL); - gts_eheap_destroy (heap); - - gts_fifo_foreach (encroached, (GtsFunc) gts_object_reset_reserved, NULL); - gts_fifo_destroy (encroached); - - GTS_OBJECT (surface)->klass = original_class; - GTS_OBJECT (surface)->reserved = NULL; - g_free (heap_surface_class); - - return unrefined_number; -} Index: trunk/src/3rd/gts/cdt.c =================================================================== --- trunk/src/3rd/gts/cdt.c (revision 1064) +++ trunk/src/3rd/gts/cdt.c (nonexistent) @@ -1,1173 +0,0 @@ -/* GTS - Library for the manipulation of triangulated surfaces - * Copyright (C) 1999 Stéphane Popinet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include - - -#include -#include "gts.h" - -#ifdef USE_SURFACE_BTREE - -static gint find_closest (GtsTriangle * t, gpointer value, gpointer * data) -{ - guint * ns = data[2]; - guint * n = data[3]; - - if (*n >= *ns) - return TRUE; - else { - gdouble * dmin = data[0]; - gpointer * closest = data[1]; - GtsPoint * p = data[4]; - - if (gts_triangle_orientation (t) > 0.) { - GtsPoint * p1 = GTS_POINT (GTS_SEGMENT (t->e1)->v1); - gdouble d = (p->x - p1->x)*(p->x - p1->x) + (p->y - p1->y)*(p->y - p1->y); - - if (d < *dmin) { - *dmin = d; - *closest = t; - } - (*n)++; - } - } - return FALSE; -} - -/* select the face closest to @p among n^1/3 randomly picked faces - * of @surface */ -static GtsFace * closest_face (GtsSurface * s, GtsPoint * p) -{ - guint n = 0, nt, ns; - gdouble dmin = G_MAXDOUBLE; - GtsFace * closest = NULL; - gpointer data[5]; - - nt = gts_surface_face_number (s); - if (!nt) - return NULL; - ns = exp (log ((gdouble) nt)/3.); - - data[0] = &dmin; - data[1] = &closest; - data[2] = &ns; - data[3] = &n; - data[4] = p; - g_tree_traverse (s->faces, (GTraverseFunc) find_closest, G_IN_ORDER, data); - - return closest; -} - -#else /* not USE_SURFACE_BTREE */ - -typedef struct _SFindClosest SFindClosest; - -struct _SFindClosest { - gdouble dmin; - GtsFace *closest; - GtsPoint * p; - gint stop; -}; - -# if GLIB_CHECK_VERSION(2,4,0) -/* finally, with g_hash_table_find we are able to stop iteration over the hash - table in the middle */ - -static gboolean find_closest (gpointer key, gpointer value, gpointer user_data) -{ - SFindClosest * data = (SFindClosest *) user_data; - GtsFace * f = GTS_FACE (value); - - if (gts_triangle_orientation (GTS_TRIANGLE (f)) > 0.) { - GtsPoint * p1 = GTS_POINT (GTS_SEGMENT (GTS_TRIANGLE (f)->e1)->v1); - gdouble d = ((data->p->x - p1->x)*(data->p->x - p1->x) + - (data->p->y - p1->y)*(data->p->y - p1->y)); - - if (d < data->dmin) { - data->dmin = d; - data->closest = f; - } - } - data->stop--; - return !(data->stop > 0); -} - -static GtsFace * closest_face (GtsSurface * s, GtsPoint * p) -{ - SFindClosest fc; - - fc.dmin = G_MAXDOUBLE; - fc.closest = NULL; - fc.p = p; - fc.stop = (gint) exp (log ((gdouble) g_hash_table_size (s->faces))/3.); - g_hash_table_find (s->faces, find_closest, &fc); - - return fc.closest; -} - -# else /* VERSION < 2.4.0 */ - -static void -find_closest (gpointer key, gpointer value, gpointer user_data) -{ - SFindClosest * data = (SFindClosest *) user_data; - GtsFace * f = GTS_FACE (value); - - if (gts_triangle_orientation (GTS_TRIANGLE (f)) > 0.) { - GtsPoint * p1 = GTS_POINT (GTS_SEGMENT (GTS_TRIANGLE (f)->e1)->v1); - gdouble d = ((data->p->x - p1->x)*(data->p->x - p1->x) + - (data->p->y - p1->y)*(data->p->y - p1->y)); - - if (d < data->dmin) { - data->dmin = d; - data->closest = f; - } - } - data->stop--; -} - -/* select the face closest to @p among n^1/3 randomly picked faces - * of @surface */ -static GtsFace * closest_face (GtsSurface * s, GtsPoint * p) -{ - SFindClosest fc; - - if (!g_hash_table_size (s->faces)) - return NULL; - - fc.dmin = G_MAXDOUBLE; - fc.closest = NULL; - fc.p = p; - fc.stop = (gint) exp (log ((gdouble) g_hash_table_size (s->faces))/3.); - g_hash_table_foreach (s->faces, find_closest, &fc); - return fc.closest; -} -# endif /* VERSION < 2.4.0 */ -#endif /* not USE_SURFACE_BTREE */ - -/* returns the face belonging to @surface and neighbor of @f via @e */ -static GtsFace * neighbor (GtsFace * f, - GtsEdge * e, - GtsSurface * surface) -{ - GSList * i = e->triangles; - GtsTriangle * t = GTS_TRIANGLE (f); - - while (i) { - GtsTriangle * t1 = i->data; - if (t1 != t && - GTS_IS_FACE (t1) && - gts_face_has_parent_surface (GTS_FACE (t1), surface)) - return GTS_FACE (t1); - i = i->next; - } - return NULL; -} - -/* given a triangle @t and a segment s (@o -> @p). - @o must be in @t. Returns the - edge of @t which is intersected by s or %NULL if @p is also - contained in @t (on_summit is set to %FALSE) or if s intersects @t - exactly on one of its summit (on_summit is set to %TRUE). */ -static GtsEdge * triangle_next_edge (GtsTriangle * t, - GtsPoint * o, GtsPoint * p, - gboolean * on_summit) -{ - GtsVertex * v1, * v2, * v3; - GtsEdge * e1, * e2, * e3; - gdouble orient = 0.0; - - gts_triangle_vertices_edges (t, NULL, - &v1, &v2, &v3, - &e1, &e2, &e3); - - *on_summit = FALSE; - orient = gts_point_orientation (o, GTS_POINT (v1), p); - if (orient > 0.0) { - orient = gts_point_orientation (o, GTS_POINT (v2), p); - if (orient > 0.0) { - if (gts_point_orientation (GTS_POINT (v2), GTS_POINT (v3), p) >= 0.0) - return NULL; - return e2; - } - if (orient < 0.0) { - if (gts_point_orientation (GTS_POINT (v1), GTS_POINT (v2), p) >= 0.0) - return NULL; - return e1; - } - if (gts_point_orientation (GTS_POINT (v1), GTS_POINT (v2), p) < 0.0) - *on_summit = TRUE; - return NULL; - } - - if (orient < 0.0) { - orient = gts_point_orientation (o, GTS_POINT (v3), p); - if (orient > 0.0) { - if (gts_point_orientation (GTS_POINT (v3), GTS_POINT (v1), p) >= 0.0) - return NULL; - return e3; - } - if (orient < 0.0) { - if (gts_point_orientation (GTS_POINT (v2), GTS_POINT (v3), p) >= 0.0) - return NULL; - return e2; - } - if (gts_point_orientation (GTS_POINT (v3), GTS_POINT (v1), p) < 0.0) - *on_summit = TRUE; - return NULL; - } - - if (gts_point_orientation (GTS_POINT (v2), GTS_POINT (v3), p) < 0.0) - return e2; - if (gts_point_orientation (GTS_POINT (v1), GTS_POINT (v2), p) < 0.0) - *on_summit = TRUE; - return NULL; -} - -static void triangle_barycenter (GtsTriangle * t, GtsPoint * b) -{ - GtsPoint * p = GTS_POINT (gts_triangle_vertex (t)); - b->x = (p->x + - GTS_POINT (GTS_SEGMENT(t->e1)->v1)->x + - GTS_POINT (GTS_SEGMENT(t->e1)->v2)->x)/3.; - b->y = (p->y + - GTS_POINT (GTS_SEGMENT(t->e1)->v1)->y + - GTS_POINT (GTS_SEGMENT(t->e1)->v2)->y)/3.; -} - -static GtsFace * point_locate (GtsPoint * o, - GtsPoint * p, - GtsFace * f, - GtsSurface * surface) -{ - GtsEdge * prev; - gboolean on_summit; - GtsVertex * v1, * v2, * v3; - GtsEdge * e2, * e3; - - prev = triangle_next_edge (GTS_TRIANGLE (f), o, p, &on_summit); - - if (!prev) { - GtsFace * f1; - - if (!on_summit) - return f; /* p is inside f */ - - /* s intersects f exactly on a summit: restarts from a neighbor of f */ - if ((f1 = neighbor (f, GTS_TRIANGLE (f)->e1, surface)) || - (f1 = neighbor (f, GTS_TRIANGLE (f)->e2, surface)) || - (f1 = neighbor (f, GTS_TRIANGLE (f)->e3, surface))) { - triangle_barycenter (GTS_TRIANGLE (f1), o); - return point_locate (o, p, f1, surface); - } - return NULL; - } - - f = neighbor (f, prev, surface); - if (f) - gts_triangle_vertices_edges (GTS_TRIANGLE (f), prev, - &v1, &v2, &v3, &prev, &e2, &e3); - while (f) { - gdouble orient = gts_point_orientation (o, GTS_POINT (v3), p); - - if (orient < 0.0) { - if (gts_point_orientation (GTS_POINT (v2), GTS_POINT (v3), p) >= 0.0) - return f; /* p is inside f */ - f = neighbor (f, e2, surface); - prev = e2; - v1 = v3; - } - else if (orient > 0.0) { - if (gts_point_orientation (GTS_POINT (v3), GTS_POINT (v1), p) >= 0.0) - return f; /* p is inside f */ - f = neighbor (f, e3, surface); - prev = e3; - v2 = v3; - } - else { - GtsFace * f1; - - if (gts_point_orientation (GTS_POINT (v2), GTS_POINT (v3), p) >= 0.0) - return f; /* p is inside f */ - - /* s intersects f exactly on v3: restarts from a neighbor of f */ - if ((f1 = neighbor (f, e2, surface)) || - (f1 = neighbor (f, e3, surface))) { - triangle_barycenter (GTS_TRIANGLE (f1), o); - return point_locate (o, p, f1, surface); - } - return NULL; - } - /* update e2, e3, v3 for the new triangle */ - if (f) { - if (prev == GTS_TRIANGLE (f)->e1) { - e2 = GTS_TRIANGLE (f)->e2; e3 = GTS_TRIANGLE (f)->e3; - } - else if (prev == GTS_TRIANGLE (f)->e2) { - e2 = GTS_TRIANGLE (f)->e3; e3 = GTS_TRIANGLE (f)->e1; - } - else { - e2 = GTS_TRIANGLE (f)->e1; e3 = GTS_TRIANGLE (f)->e2; - } - if (GTS_SEGMENT (e2)->v1 == v1 || GTS_SEGMENT (e2)->v1 == v2) - v3 = GTS_SEGMENT (e2)->v2; - else - v3 = GTS_SEGMENT (e2)->v1; - } - } - return NULL; -} - -/** - * gts_point_locate: - * @p: a #GtsPoint. - * @surface: a #GtsSurface. - * @guess: %NULL or a face of @surface close to @p. - * - * Locates the face of the planar projection of @surface containing - * @p. The planar projection of @surface must define a connected set - * of triangles without holes and bounded by a convex boundary. The - * algorithm is randomized and performs in O(n^1/3) expected time - * where n is the number of triangles of @surface. - * - * If a good @guess is given the point location can be significantly faster. - * - * Returns: a #GtsFace of @surface containing @p or %NULL if @p is not - * contained within the boundary of @surface. - */ -GtsFace * gts_point_locate (GtsPoint * p, - GtsSurface * surface, - GtsFace * guess) -{ - GtsFace * fr; - GtsPoint * o; - - g_return_val_if_fail (p != NULL, NULL); - g_return_val_if_fail (surface != NULL, NULL); - g_return_val_if_fail (guess == NULL || - gts_face_has_parent_surface (guess, surface), NULL); - - if (guess == NULL) - guess = closest_face (surface, p); - else - g_return_val_if_fail (gts_triangle_orientation (GTS_TRIANGLE (guess)) > 0., NULL); - - if (guess == NULL) - return NULL; - - o = GTS_POINT (gts_object_new (GTS_OBJECT_CLASS (gts_point_class ()))); - triangle_barycenter (GTS_TRIANGLE (guess), o); - fr = point_locate (o, p, guess, surface); - gts_object_destroy (GTS_OBJECT (o)); - - return fr; -} - - -/** - * gts_constraint_class: - * - * Returns: the #GtsConstraintClass. - */ -GtsConstraintClass * gts_constraint_class (void) -{ - static GtsConstraintClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo constraint_info = { - "GtsConstraint", - sizeof (GtsConstraint), - sizeof (GtsConstraintClass), - (GtsObjectClassInitFunc) NULL, - (GtsObjectInitFunc) NULL, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_edge_class ()), - &constraint_info); - } - - return klass; -} - -static void split_list (GtsListFace * f, GtsListFace * f1, GtsListFace * f2, - GtsPoint * p1, GtsPoint * p2, - GSList ** last1, GSList ** last2) -{ - GSList * i = f->points, * l1 = *last1, * l2 = *last2; - - while (i) { - GtsPoint * p = i->data; - - if (gts_point_orientation (p1, p2, p) >= 0.) { - if (l1) l1->next = i; else f1->points = i; - l1 = i; - } - else { - if (l2) l2->next = i; else f2->points = i; - l2 = i; - } - i = i->next; - } - f->points = NULL; - *last1 = l1; - *last2 = l2; -} - -/* cf. figure misc/swap.fig */ -static void swap_if_in_circle (GtsFace * f1, - GtsVertex * v1, - GtsVertex * v2, - GtsVertex * v3, - GtsEdge * e1, - GtsEdge * e2, - GtsEdge * e3, - GtsSurface * surface) -{ - GtsFace * f2; - GtsEdge * e4, *e5; - GtsVertex * v4; - - if (GTS_IS_CONSTRAINT (e1)) /* @e1 is a constraint can not swap */ - return; - - f2 = neighbor (f1, e1, surface); - if (f2 == NULL) /* @e1 is a boundary of @surface */ - return; - - if (GTS_TRIANGLE (f2)->e1 == e1) { - e4 = GTS_TRIANGLE (f2)->e2; e5 = GTS_TRIANGLE (f2)->e3; - } - else if (GTS_TRIANGLE (f2)->e2 == e1) { - e4 = GTS_TRIANGLE (f2)->e3; e5 = GTS_TRIANGLE (f2)->e1; - } - else { - e4 = GTS_TRIANGLE (f2)->e1; e5 = GTS_TRIANGLE (f2)->e2; - } - if (GTS_SEGMENT (e4)->v1 == GTS_SEGMENT (e1)->v1 || - GTS_SEGMENT (e4)->v1 == GTS_SEGMENT (e1)->v2) - v4 = GTS_SEGMENT (e4)->v2; - else - v4 = GTS_SEGMENT (e4)->v1; - - if (gts_point_in_circle (GTS_POINT (v4), GTS_POINT (v1), - GTS_POINT (v2), GTS_POINT (v3)) > 0.0) { - GtsEdge * en; - GtsSegment * sn = gts_vertices_are_connected (v3, v4); - GtsFace * f3, * f4; - - if (!GTS_IS_EDGE (sn)) - en = gts_edge_new (surface->edge_class, v3, v4); - else - en = GTS_EDGE (sn); - - f3 = gts_face_new (surface->face_class, en, e5, e2); - gts_object_attributes (GTS_OBJECT (f3), GTS_OBJECT (f1)); - f4 = gts_face_new (surface->face_class, en, e3, e4); - gts_object_attributes (GTS_OBJECT (f4), GTS_OBJECT (f2)); - - if (GTS_IS_LIST_FACE (f3)) { - GSList * last3 = NULL, * last4 = NULL; - - if (GTS_IS_LIST_FACE (f1)) - split_list (GTS_LIST_FACE (f1), GTS_LIST_FACE (f3), GTS_LIST_FACE (f4), - GTS_POINT (v3), GTS_POINT (v4), &last3, &last4); - if (GTS_IS_LIST_FACE (f2)) - split_list (GTS_LIST_FACE (f2), GTS_LIST_FACE (f3), GTS_LIST_FACE (f4), - GTS_POINT (v3), GTS_POINT (v4), &last3, &last4); - if (last3) last3->next = NULL; - if (last4) last4->next = NULL; - } - - gts_surface_remove_face (surface, f1); - gts_surface_remove_face (surface, f2); - gts_surface_add_face (surface, f3); - gts_surface_add_face (surface, f4); - - swap_if_in_circle (f3, v4, v2, v3, e5, e2, en, surface); - swap_if_in_circle (f4, v1, v4, v3, e4, en, e3, surface); - } -} - -/** - * gts_delaunay_add_vertex_to_face: - * @surface: a #GtsSurface. - * @v: a #GtsVertex. - * @f: a #GtsFace belonging to @surface. - * - * Adds vertex @v to the face @f of the Delaunay triangulation defined - * by @surface. - * - * Returns: %NULL is @v has been successfully added to @surface or was - * already contained in @surface or a #GtsVertex having the same x and - * y coordinates as @v. - */ -GtsVertex * gts_delaunay_add_vertex_to_face (GtsSurface * surface, - GtsVertex * v, - GtsFace * f) -{ - GtsEdge * e1, * e2, * e3; - GtsSegment * s4, * s5, * s6; - GtsEdge * e4, * e5, * e6; - GtsVertex * v1, * v2, * v3; - GtsFace * nf[3]; - - g_return_val_if_fail (surface != NULL, v); - g_return_val_if_fail (v != NULL, v); - g_return_val_if_fail (f != NULL, v); - - gts_triangle_vertices_edges (GTS_TRIANGLE (f), NULL, - &v1, &v2, &v3, &e1, &e2, &e3); - if (v == v1 || v == v2 || v == v3) /* v already in @surface */ - return NULL; - if (GTS_POINT (v)->x == GTS_POINT (v1)->x && - GTS_POINT (v)->y == GTS_POINT (v1)->y) - return v1; - if (GTS_POINT (v)->x == GTS_POINT (v2)->x && - GTS_POINT (v)->y == GTS_POINT (v2)->y) - return v2; - if (GTS_POINT (v)->x == GTS_POINT (v3)->x && - GTS_POINT (v)->y == GTS_POINT (v3)->y) - return v3; - - s4 = gts_vertices_are_connected (v, v1); - if (!GTS_IS_EDGE (s4)) - e4 = gts_edge_new (surface->edge_class, v, v1); - else - e4 = GTS_EDGE (s4); - s5 = gts_vertices_are_connected (v, v2); - if (!GTS_IS_EDGE (s5)) - e5 = gts_edge_new (surface->edge_class, v, v2); - else - e5 = GTS_EDGE (s5); - s6 = gts_vertices_are_connected (v, v3); - if (!GTS_IS_EDGE (s6)) - e6 = gts_edge_new (surface->edge_class, v, v3); - else - e6 = GTS_EDGE (s6); - - /* cf. figure misc/swap.fig */ - nf[0] = gts_face_new (surface->face_class, e4, e1, e5); - gts_object_attributes (GTS_OBJECT (nf[0]), GTS_OBJECT (f)); - nf[1] = gts_face_new (surface->face_class, e5, e2, e6); - gts_object_attributes (GTS_OBJECT (nf[1]), GTS_OBJECT (f)); - nf[2] = gts_face_new (surface->face_class, e6, e3, e4); - gts_object_attributes (GTS_OBJECT (nf[2]), GTS_OBJECT (f)); - - if (GTS_IS_LIST_FACE (f) && GTS_IS_LIST_FACE (nf[0])) { - GSList * i = GTS_LIST_FACE (f)->points, * last[3] = { NULL, NULL, NULL }; - - while (i) { - GtsPoint * p = i->data; - GSList * next = i->next; - guint j; - - if (p != GTS_POINT (v)) { - if (gts_point_orientation (GTS_POINT (v), GTS_POINT (v1), p) >= 0.) { - gdouble o = gts_point_orientation (GTS_POINT (v), GTS_POINT (v2), p); - - if (o != 0.) - j = o > 0. ? 1 : 0; - else - j = gts_point_orientation (GTS_POINT (v), GTS_POINT (v3), p) - > 0. ? 0 : 1; - } - else if (gts_point_orientation (GTS_POINT (v), GTS_POINT (v3), p) > 0.) - j = 2; - else - j = 1; - if (last[j]) - last[j]->next = i; - else - GTS_LIST_FACE (nf[j])->points = i; - last[j] = i; - } - else - g_slist_free_1 (i); - i = next; - } - GTS_LIST_FACE (f)->points = NULL; - if (last[0]) last[0]->next = NULL; - if (last[1]) last[1]->next = NULL; - if (last[2]) last[2]->next = NULL; - } - - gts_surface_remove_face (surface, f); - gts_surface_add_face (surface, nf[0]); - gts_surface_add_face (surface, nf[1]); - gts_surface_add_face (surface, nf[2]); - - swap_if_in_circle (nf[0], v1, v2, v, e1, e5, e4, surface); - swap_if_in_circle (nf[1], v2, v3, v, e2, e6, e5, surface); - swap_if_in_circle (nf[2], v3, v1, v, e3, e4, e6, surface); - - return NULL; -} - -/** - * gts_delaunay_add_vertex: - * @surface: a #GtsSurface. - * @v: a #GtsVertex. - * @guess: %NULL or a #GtsFace belonging to @surface to be used as an initial - * guess for point location. - * - * Adds vertex @v to the Delaunay triangulation defined by - * @surface. If @v is not contained in the convex hull bounding - * @surface, @v is not added to the triangulation. - * - * Returns: %NULL is @v has been successfully added to @surface or was - * already contained in @surface, @v if @v is not contained in the - * convex hull bounding surface or a #GtsVertex having the same x and - * y coordinates as @v. - */ -GtsVertex * gts_delaunay_add_vertex (GtsSurface * surface, - GtsVertex * v, - GtsFace * guess) -{ - GtsFace * f; - - g_return_val_if_fail (surface != NULL, v); - g_return_val_if_fail (v != NULL, v); - - if (!(f = gts_point_locate (GTS_POINT (v), surface, guess))) - return v; - return gts_delaunay_add_vertex_to_face (surface, v, f); -} - -static gboolean polygon_in_circle (GSList * poly, - GtsPoint * p1, - GtsPoint * p2, - GtsPoint * p3) -{ - GtsVertex * v1 = NULL, * v2 = NULL; - - while (poly) { - GtsSegment * s = poly->data; - GtsVertex * v; - v = s->v1; - if (v != v1 && v != v2 && - v != GTS_VERTEX (p1) && - v != GTS_VERTEX (p2) && - v != GTS_VERTEX (p3) && - gts_point_in_circle (GTS_POINT (v), p1, p2, p3) > 0.) - return TRUE; - v = s->v2; - if (v != v1 && v != v2 && - v != GTS_VERTEX (p1) && - v != GTS_VERTEX (p2) && - v != GTS_VERTEX (p3) && - gts_point_in_circle (GTS_POINT (v), p1, p2, p3) > 0.) - return TRUE; - v1 = s->v1; - v2 = s->v2; - poly = poly->next; - } - return FALSE; -} - -static void triangulate_polygon (GSList * poly, - GtsSurface * surface, - GtsFace * ref) -{ - GSList * i, * poly1, * poly2; - GtsVertex * v1, * v2, * v3 = NULL; - gboolean found = FALSE; - GtsSegment * s, * s1, * s2; - GtsEdge * e1, * e2; - GtsFace * f; - - if (poly == NULL || poly->next == NULL) { - g_slist_free (poly); - return; - } - - s = poly->data; - s1 = poly->next->data; - if (s->v1 == s1->v1 || s->v1 == s1->v2) { - v1 = s->v2; - v2 = s->v1; - } - else { - g_assert (s->v2 == s1->v1 || s->v2 == s1->v2); - v1 = s->v1; - v2 = s->v2; - } - - i = poly->next; - v3 = v2; - while (i && !found) { - s1 = i->data; - if (s1->v1 == v3) - v3 = s1->v2; - else { - g_assert (s1->v2 == v3); - v3 = s1->v1; - } - if (v3 != v1 && - gts_point_orientation (GTS_POINT (v1), - GTS_POINT (v2), - GTS_POINT (v3)) >= 0. && - !polygon_in_circle (poly, - GTS_POINT (v1), - GTS_POINT (v2), - GTS_POINT (v3))) - found = TRUE; - else - i = i->next; - } - - if (!found) { - g_slist_free (poly); - return; - } - - s1 = gts_vertices_are_connected (v2, v3); - if (!GTS_IS_EDGE (s1)) - e1 = gts_edge_new (surface->edge_class, v2, v3); - else - e1 = GTS_EDGE (s1); - s2 = gts_vertices_are_connected (v3, v1); - if (!GTS_IS_EDGE (s2)) - e2 = gts_edge_new (surface->edge_class, v3, v1); - else - e2 = GTS_EDGE (s2); - f = gts_face_new (surface->face_class, GTS_EDGE (s), e1, e2); - gts_object_attributes (GTS_OBJECT (f), GTS_OBJECT (ref)); - gts_surface_add_face (surface, f); - - poly1 = poly->next; - g_slist_free_1 (poly); - if (i->next && e2 != i->next->data) - poly2 = g_slist_prepend (i->next, e2); - else - poly2 = i->next; - if (e1 != i->data) - i->next = g_slist_prepend (NULL, e1); - else - i->next = NULL; - - triangulate_polygon (poly1, surface, ref); - triangulate_polygon (poly2, surface, ref); -} - -/** - * gts_delaunay_remove_vertex: - * @surface: a #GtsSurface. - * @v: a #GtsVertex. - * - * Removes @v from the Delaunay triangulation defined by @surface and - * restores the Delaunay property. Vertex @v must not be used by any - * constrained edge otherwise the triangulation is not guaranteed to - * be Delaunay. - */ -void gts_delaunay_remove_vertex (GtsSurface * surface, GtsVertex * v) -{ - GSList * triangles, * i; - GtsFace * ref = NULL; - - g_return_if_fail (surface != NULL); - g_return_if_fail (v != NULL); - - i = triangles = gts_vertex_triangles (v, NULL); - while (i && !ref) { - if (GTS_IS_FACE (i->data) && - gts_face_has_parent_surface (i->data, surface)) - ref = i->data; - i = i->next; - } - if (!ref) { - g_slist_free (triangles); - g_return_if_fail (ref); - } - triangulate_polygon (gts_vertex_fan_oriented (v, surface), surface, ref); - i = triangles; - while (i) { - if (GTS_IS_FACE (i->data) && - gts_face_has_parent_surface (i->data, surface)) - gts_surface_remove_face (surface, i->data); - i = i->next; - } - g_slist_free (triangles); -} - -#define NEXT_CUT(edge, edge1, list) { next = neighbor (f, edge, surface);\ - remove_triangles (e, surface);\ - if (!constraint && !e->triangles)\ - gts_object_destroy (GTS_OBJECT (e));\ - g_assert (next);\ - *list = g_slist_prepend (*list, edge1);\ - return g_slist_concat (constraint,\ - remove_intersected_edge (s, edge,\ - next, surface, left, right));\ - } - -static void remove_triangles (GtsEdge * e, GtsSurface * s) -{ - GSList * i = e->triangles; - - while (i) { - GSList * next = i->next; - - if (GTS_IS_FACE (i->data) && gts_face_has_parent_surface (i->data, s)) - gts_surface_remove_face (s, i->data); - i = next; - } -} - -static GSList * -remove_intersected_edge (GtsSegment * s, - GtsEdge * e, - GtsFace * f, - GtsSurface * surface, - GSList ** left, GSList ** right) -{ - GtsVertex * v1, * v2, * v3; - GtsEdge * e1, * e2; - gdouble o1, o2; - GtsFace * next; - GSList * constraint = NULL; - - if (GTS_IS_CONSTRAINT (e)) - constraint = g_slist_prepend (NULL, e); - - gts_triangle_vertices_edges (GTS_TRIANGLE (f), e, - &v1, &v2, &v3, &e, &e1, &e2); - - o1 = gts_point_orientation (GTS_POINT (v2), GTS_POINT (v3), - GTS_POINT (s->v2)); - o2 = gts_point_orientation (GTS_POINT (v3), GTS_POINT (v1), - GTS_POINT (s->v2)); - - if (o1 == 0. && o2 == 0.) { -/* if(o2 != 0.) { - fprintf(stderr, "o1 = %f o2 = %f\n", o1, o2); - fprintf(stderr, "v1 = %f, %f\n", GTS_POINT(v1)->x, GTS_POINT(v1)->y); - fprintf(stderr, "v2 = %f, %f\n", GTS_POINT(v2)->x, GTS_POINT(v2)->y); - fprintf(stderr, "v3 = %f, %f\n", GTS_POINT(v3)->x, GTS_POINT(v3)->y); - fprintf(stderr, "s->v2 = %f, %f\n", GTS_POINT(s->v2)->x, GTS_POINT(s->v2)->y); - - g_assert (o2 == 0.); - }*/ - // if(o2 == 0.) { - remove_triangles (e, surface); - if (!constraint && !e->triangles) - gts_object_destroy (GTS_OBJECT (e)); - *left = g_slist_prepend (*left, e2); - *right = g_slist_prepend (*right, e1); -// } - } - else if (o1 > 0.) { - g_assert (o2 <= 0.); - NEXT_CUT (e2, e1, right) - } - else if (o2 >= 0.) - NEXT_CUT (e1, e2, left) - else { - gdouble o3 = gts_point_orientation (GTS_POINT (s->v1), GTS_POINT (s->v2), - GTS_POINT (v3)); - if (o3 > 0.) - NEXT_CUT (e1, e2, left) - else - NEXT_CUT (e2, e1, right) - } - return constraint; -} - -static GSList * -remove_intersected_vertex (GtsSegment * s, - GtsVertex * v, - GtsSurface * surface, - GSList ** left, - GSList ** right, - GtsFace ** ref) -{ - GSList * triangles = gts_vertex_triangles (v, NULL); - GSList * i; - - i = triangles; - while (i) { - GtsTriangle * t = i->data; - if (GTS_IS_FACE (t) && - gts_face_has_parent_surface (GTS_FACE (t), surface)) { - GtsVertex * v1, * v2, * v3; - gdouble o1, o2; - - gts_triangle_vertices (t, &v1, &v2, &v3); - if (v == v2) { - v2 = v3; - v3 = v1; - } - else if (v == v3) { - v3 = v2; - v2 = v1; - } - else - g_assert (v == v1); - - if ((o1 = gts_point_orientation (GTS_POINT (v), GTS_POINT (v2), - GTS_POINT (s->v2))) >= 0. && - (o2 = gts_point_orientation (GTS_POINT (v3), GTS_POINT (v), - GTS_POINT (s->v2))) >= 0.) { - gdouble o3 = gts_point_orientation (GTS_POINT (v2), GTS_POINT (v3), - GTS_POINT (s->v2)); - GtsEdge * e = gts_triangle_edge_opposite (t, v); - GtsEdge * e1, * e2; - GtsFace * next = neighbor (GTS_FACE (t), e, surface); - - *ref = GTS_FACE (t); - gts_triangle_vertices_edges (t, e, &v2, &v3, &v, &e, &e2, &e1); - - g_slist_free (triangles); - - if (o3 >= 0.) /* @s->v2 is inside (or on the edge) of t */ - return NULL; - - gts_allow_floating_faces = TRUE; - gts_surface_remove_face (surface, GTS_FACE (t)); - gts_allow_floating_faces = FALSE; - - *left = g_slist_prepend (*left, e2); - *right = g_slist_prepend (*right, e1); - - g_assert (next); - return remove_intersected_edge (s, e, next, surface, left, right); - } - } - i = i->next; - } - - g_assert_not_reached (); - return NULL; -} - -/** - * gts_delaunay_add_constraint: - * @surface: a #GtsSurface. - * @c: a #GtsConstraint. - * - * Add constraint @c to the constrained Delaunay triangulation defined by - * @surface. - * - * Returns: a list of #GtsConstraint conflicting (i.e. intersecting) with @c - * which were removed from @surface (%NULL if there was none). - */ -GSList * gts_delaunay_add_constraint (GtsSurface * surface, - GtsConstraint * c) -{ - GSList * constraints; - GtsVertex * v1; //, * v2; - GSList * left = NULL, * right = NULL; - GtsFace * ref = NULL; - - g_return_val_if_fail (surface != NULL, NULL); - g_return_val_if_fail (c != NULL, NULL); - g_return_val_if_fail (GTS_IS_CONSTRAINT (c), NULL); - - v1 = GTS_SEGMENT (c)->v1; - //v2 = GTS_SEGMENT (c)->v2; - - gts_allow_floating_edges = TRUE; - constraints = remove_intersected_vertex (GTS_SEGMENT (c), v1, surface, - &left, &right, &ref); - gts_allow_floating_edges = FALSE; -#if 1 - triangulate_polygon (g_slist_prepend (g_slist_reverse (right), c), - surface, ref); - triangulate_polygon (g_slist_prepend (left, c), - surface, ref); -#else - right = g_slist_prepend (g_slist_reverse (right), c); - left = g_slist_prepend (left, c); - { - FILE * fp0 = fopen ("hole", "wt"); - FILE * fp1 = fopen ("right", "wt"); - FILE * fp2 = fopen ("left", "wt"); - GSList * i = left; - - gts_surface_write (surface, fp0); - fclose (fp0); - - fprintf (fp2, "LIST {\n"); - while (i) { - GtsSegment * s = i->data; - fprintf (fp2, - "# %p: %p->%p\n" - "VECT 1 2 0 2 0 %g %g 0 %g %g 0\n", - s, s->v1, s->v2, - GTS_POINT (s->v1)->x, GTS_POINT (s->v1)->y, - GTS_POINT (s->v2)->x, GTS_POINT (s->v2)->y); - i = i->next; - } - fprintf (fp2, "}\n"); - fprintf (fp1, "LIST {\n"); - i = right; - while (i) { - GtsSegment * s = i->data; - fprintf (fp1, - "# %p: %p->%p\n" - "VECT 1 2 0 2 0 %g %g 0 %g %g 0\n", - s, s->v1, s->v2, - GTS_POINT (s->v1)->x, GTS_POINT (s->v1)->y, - GTS_POINT (s->v2)->x, GTS_POINT (s->v2)->y); - i = i->next; - } - fprintf (fp1, "}\n"); - fclose (fp1); - fclose (fp2); - } - triangulate_polygon (right, surface); - triangulate_polygon (left, surface); -#endif - if (ref && !ref->surfaces) { - gts_allow_floating_edges = TRUE; - gts_object_destroy (GTS_OBJECT (ref)); - gts_allow_floating_edges = FALSE; - } - return constraints; -} - -static void delaunay_check (GtsTriangle * t, gpointer * data) -{ - GtsSurface * surface = data[0]; - GtsFace ** face = data[1]; - - if (*face == NULL) { - GSList * i, * list; - GtsVertex * v1, * v2, * v3; - - gts_triangle_vertices (t, &v1, &v2, &v3); - list = gts_vertex_neighbors (v1, NULL, surface); - list = gts_vertex_neighbors (v2, list, surface); - list = gts_vertex_neighbors (v3, list, surface); - i = list; - while (i && *face == NULL) { - GtsVertex * v = i->data; - if (v != v1 && v != v2 && v != v3 && - gts_point_in_circle (GTS_POINT (v), - GTS_POINT (v1), - GTS_POINT (v2), - GTS_POINT (v3)) > 0.) - *face = GTS_FACE (t); - i = i->next; - } - g_slist_free (list); - } -} - -/** - * gts_delaunay_check: - * @surface: a #GtsSurface. - * - * Returns: %NULL if the planar projection of @surface is a Delaunay - * triangulation (unconstrained), a #GtsFace violating the Delaunay - * property otherwise. - */ -GtsFace * gts_delaunay_check (GtsSurface * surface) -{ - GtsFace * face = NULL; - gpointer data[2]; - - g_return_val_if_fail (surface != NULL, FALSE); - - data[0] = surface; - data[1] = &face; - gts_surface_foreach_face (surface, (GtsFunc) delaunay_check, data); - - return face; -} - -/** - * gts_delaunay_remove_hull: - * @surface: a #GtsSurface. - * - * Removes all the edges of the boundary of @surface which are not - * constraints. - */ -void gts_delaunay_remove_hull (GtsSurface * surface) -{ - GSList * boundary; - - g_return_if_fail (surface != NULL); - - boundary = gts_surface_boundary (surface); - gts_allow_floating_edges = TRUE; - while (boundary) { - GSList * i = boundary; - GtsEdge * e = i->data; - - boundary = i->next; - g_slist_free_1 (i); - if (!GTS_IS_CONSTRAINT (e)) { - GtsTriangle * t = GTS_TRIANGLE (gts_edge_is_boundary (e, surface)); - - if (t != NULL) { - if (t->e1 != e && !GTS_IS_CONSTRAINT (t->e1) && - !gts_edge_is_boundary (t->e1, surface)) - boundary = g_slist_prepend (boundary, t->e1); - if (t->e2 != e && !GTS_IS_CONSTRAINT (t->e2) && - !gts_edge_is_boundary (t->e2, surface)) - boundary = g_slist_prepend (boundary, t->e2); - if (t->e3 != e && !GTS_IS_CONSTRAINT (t->e3) && - !gts_edge_is_boundary (t->e3, surface)) - boundary = g_slist_prepend (boundary, t->e3); - gts_surface_remove_face (surface, GTS_FACE (t)); - } - if (!e->triangles) - gts_object_destroy (GTS_OBJECT (e)); - } - } - gts_allow_floating_edges = FALSE; -} - -/* GtsListFace: Object */ - -static void gts_list_face_destroy (GtsObject * object) -{ - g_slist_free (GTS_LIST_FACE (object)->points); - - (* GTS_OBJECT_CLASS (gts_list_face_class ())->parent_class->destroy) - (object); -} - -static void gts_list_face_class_init (GtsFaceClass * klass) -{ - GTS_OBJECT_CLASS (klass)->destroy = gts_list_face_destroy; -} - -GtsFaceClass * gts_list_face_class (void) -{ - static GtsFaceClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo gts_list_face_info = { - "GtsListFace", - sizeof (GtsListFace), - sizeof (GtsFaceClass), - (GtsObjectClassInitFunc) gts_list_face_class_init, - (GtsObjectInitFunc) NULL, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_face_class ()), - >s_list_face_info); - } - - return klass; -} Index: trunk/src/3rd/gts/vertex.c =================================================================== --- trunk/src/3rd/gts/vertex.c (revision 1064) +++ trunk/src/3rd/gts/vertex.c (nonexistent) @@ -1,780 +0,0 @@ -/* GTS - Library for the manipulation of triangulated surfaces - * Copyright (C) 1999 Stéphane Popinet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include -#include "gts.h" - -gboolean gts_allow_floating_vertices = FALSE; - -static void vertex_destroy (GtsObject * object) -{ - GtsVertex * vertex = GTS_VERTEX (object); - GSList * i; - - i = vertex->segments; - while (i) { - GTS_OBJECT_SET_FLAGS (i->data, GTS_DESTROYED); - i = i->next; - } - i = vertex->segments; - while (i) { - GSList * next = i->next; - gts_object_destroy (i->data); - i = next; - } - g_assert (vertex->segments == NULL); - - (* GTS_OBJECT_CLASS (gts_vertex_class ())->parent_class->destroy) (object); -} - -static void vertex_clone (GtsObject * clone, GtsObject * object) -{ - (* GTS_OBJECT_CLASS (gts_vertex_class ())->parent_class->clone) (clone, - object); - GTS_VERTEX (clone)->segments = NULL; -} - -static void vertex_class_init (GtsVertexClass * klass) -{ - klass->intersection_attributes = NULL; - GTS_OBJECT_CLASS (klass)->clone = vertex_clone; - GTS_OBJECT_CLASS (klass)->destroy = vertex_destroy; -} - -static void vertex_init (GtsVertex * vertex) -{ - vertex->segments = NULL; -} - -/** - * gts_vertex_class: - * - * Returns: the #GtsVertexClass. - */ -GtsVertexClass * gts_vertex_class (void) -{ - static GtsVertexClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo vertex_info = { - "GtsVertex", - sizeof (GtsVertex), - sizeof (GtsVertexClass), - (GtsObjectClassInitFunc) vertex_class_init, - (GtsObjectInitFunc) vertex_init, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_point_class ()), - &vertex_info); - } - - return klass; -} - -/** - * gts_vertex_new: - * @klass: a #GtsVertexClass. - * @x: the x-coordinate of the vertex to create. - * @y: the y-coordinate of the vertex to create. - * @z: the y-coordinate of the vertex to create. - * - * Returns: a new #GtsVertex with @x, @y and @z as coordinates. - */ -GtsVertex * gts_vertex_new (GtsVertexClass * klass, - gdouble x, gdouble y, gdouble z) -{ - GtsVertex * v; - - v = GTS_VERTEX (gts_object_new (GTS_OBJECT_CLASS (klass))); - gts_point_set (GTS_POINT (v), x, y, z); - - return v; -} - -/** - * gts_vertex_replace: - * @v: a #GtsVertex. - * @with: another #GtsVertex. - * - * Replaces vertex @v with vertex @with. @v and @with must be - * different. All the #GtsSegment which have @v has one of their - * vertices are updated. The segments list of vertex @v is freed and - * @v->segments is set to %NULL. - */ -void gts_vertex_replace (GtsVertex * v, GtsVertex * with) -{ - GSList * i; - - g_return_if_fail (v != NULL); - g_return_if_fail (with != NULL); - g_return_if_fail (v != with); - - i = v->segments; - while (i) { - GtsSegment * s = i->data; - if (s->v1 != with && s->v2 != with) - with->segments = g_slist_prepend (with->segments, s); - if (s->v1 == v) s->v1 = with; - if (s->v2 == v) s->v2 = with; - i = i->next; - } - g_slist_free (v->segments); - v->segments = NULL; -} - -/** - * gts_vertex_is_unattached: - * @v: a #GtsVertex. - * - * Returns: %TRUE if @v is not the endpoint of any #GtsSegment, - * %FALSE otherwise. - */ -gboolean gts_vertex_is_unattached (GtsVertex * v) -{ - g_return_val_if_fail (v != NULL, FALSE); - if (v->segments == NULL) - return TRUE; - return FALSE; -} - -/** - * gts_vertices_are_connected: - * @v1: a #GtsVertex. - * @v2: another #GtsVertex. - * - * Returns: if @v1 and @v2 are the vertices of the same #GtsSegment - * this segment else %NULL. - */ -GtsSegment * gts_vertices_are_connected (GtsVertex * v1, GtsVertex * v2) -{ - GSList * i; - - g_return_val_if_fail (v1 != NULL, FALSE); - g_return_val_if_fail (v2 != NULL, FALSE); - - i = v1->segments; - while (i) { - GtsSegment * s = i->data; - - if (s->v1 == v2 || s->v2 == v2) - return s; - i = i->next; - } - return NULL; -} - -/** - * gts_vertices_from_segments: - * @segments: a list of #GtsSegment. - * - * Returns: a list of #GtsVertex, vertices of a #GtsSegment in @segments. - * Each element in the list is unique (no duplicates). - */ -GSList * gts_vertices_from_segments (GSList * segments) -{ - GHashTable * hash; - GSList * vertices = NULL, * i; - - hash = g_hash_table_new (NULL, NULL); - i = segments; - while (i) { - GtsSegment * s = i->data; - if (g_hash_table_lookup (hash, s->v1) == NULL) { - vertices = g_slist_prepend (vertices, s->v1); - g_hash_table_insert (hash, s->v1, s); - } - if (g_hash_table_lookup (hash, s->v2) == NULL) { - vertices = g_slist_prepend (vertices, s->v2); - g_hash_table_insert (hash, s->v2, s); - } - i = i->next; - } - g_hash_table_destroy (hash); - return vertices; -} - -/** - * gts_vertex_triangles: - * @v: a #GtsVertex. - * @list: a list of #GtsTriangle. - * - * Adds all the #GtsTriangle which share @v as a vertex and do not - * already belong to @list. - * - * Returns: the new list of unique #GtsTriangle which share @v as a - * vertex. - */ -GSList * gts_vertex_triangles (GtsVertex * v, - GSList * list) -{ - GSList * i; - - g_return_val_if_fail (v != NULL, NULL); - - i = v->segments; - while (i) { - GtsSegment * s = i->data; - if (GTS_IS_EDGE (s)) { - GSList * j = GTS_EDGE (s)->triangles; - while (j) { - if (!g_slist_find (list, j->data)) - list = g_slist_prepend (list, j->data); - j = j->next; - } - } - i = i->next; - } - return list; -} - -/** - * gts_vertex_faces: - * @v: a #GtsVertex. - * @surface: a #GtsSurface or %NULL. - * @list: a list of #GtsFace. - * - * Adds all the #GtsFace belonging to @surface (if not %NULL) which share - * @v as a vertex and do not already belong to @list. - * - * Returns: the new list of unique #GtsFace belonging to @surface - * which share @v as a vertex. - */ -GSList * gts_vertex_faces (GtsVertex * v, - GtsSurface * surface, - GSList * list) -{ - GSList * i; - - g_return_val_if_fail (v != NULL, NULL); - - i = v->segments; - while (i) { - GtsSegment * s = i->data; - if (GTS_IS_EDGE (s)) { - GSList * j = GTS_EDGE (s)->triangles; - while (j) { - GtsTriangle * t = j->data; - if (GTS_IS_FACE (t) - && - (!surface || gts_face_has_parent_surface (GTS_FACE (t), surface)) - && - !g_slist_find (list, t)) - list = g_slist_prepend (list, t); - j = j->next; - } - } - i = i->next; - } - return list; -} - -/** - * gts_vertex_neighbors: - * @v: a #GtsVertex. - * @list: a list of #GtsVertex. - * @surface: a #GtsSurface or %NULL. - * - * Adds to @list all the #GtsVertex connected to @v by a #GtsSegment and not - * already in @list. If @surface is not %NULL only the vertices connected to - * @v by an edge belonging to @surface are considered. - * - * Returns: the new list of unique #GtsVertex. - */ -GSList * gts_vertex_neighbors (GtsVertex * v, - GSList * list, - GtsSurface * surface) -{ - GSList * i; - - g_return_val_if_fail (v != NULL, NULL); - - i = v->segments; - while (i) { - GtsSegment * s = i->data; - GtsVertex * v1 = s->v1 == v ? s->v2 : s->v1; - if (v1 != v && - (!surface || - (GTS_IS_EDGE (s) && - gts_edge_has_parent_surface (GTS_EDGE (s), surface))) && - !g_slist_find (list, v1)) - list = g_slist_prepend (list, v1); - i = i->next; - } - return list; -} - -/** - * gts_vertex_is_boundary: - * @v: a #GtsVertex. - * @surface: a #GtsSurface or %NULL. - * - * Returns: %TRUE if @v is used by a #GtsEdge boundary of @surface as - * determined by gts_edge_is_boundary(), %FALSE otherwise. - */ -gboolean gts_vertex_is_boundary (GtsVertex * v, GtsSurface * surface) -{ - GSList * i; - - g_return_val_if_fail (v != NULL, FALSE); - - i = v->segments; - while (i) { - if (GTS_IS_EDGE (i->data) && - gts_edge_is_boundary (i->data, surface)) - return TRUE; - i = i->next; - } - - return FALSE; -} - -/** - * gts_vertices_merge: - * @vertices: a list of #GtsVertex. - * @epsilon: half the size of the bounding box to consider for each vertex. - * @check: function called for each pair of vertices about to be merged - * or %NULL. - * - * For each vertex v in @vertices look if there are any vertex of - * @vertices contained in a box centered on v of size 2*@epsilon. If - * there are and if @check is not %NULL and returns %TRUE, replace - * them with v (using gts_vertex_replace()), destroy them and remove - * them from list. This is done efficiently using Kd-Trees. - * - * Returns: the updated list of vertices. - */ -GList * gts_vertices_merge (GList * vertices, - gdouble epsilon, - gboolean (* check) (GtsVertex *, GtsVertex *)) -{ - GPtrArray * array; - GList * i; - GNode * kdtree; - - g_return_val_if_fail (vertices != NULL, 0); - - array = g_ptr_array_new (); - i = vertices; - while (i) { - g_ptr_array_add (array, i->data); - i = i->next; - } - kdtree = gts_kdtree_new (array, NULL); - g_ptr_array_free (array, TRUE); - - i = vertices; - while (i) { - GtsVertex * v = i->data; - if (!GTS_OBJECT (v)->reserved) { /* Do something only if v is active */ - GtsBBox * bbox; - GSList * selected, * j; - - /* build bounding box */ - bbox = gts_bbox_new (gts_bbox_class (), - v, - GTS_POINT (v)->x - epsilon, - GTS_POINT (v)->y - epsilon, - GTS_POINT (v)->z - epsilon, - GTS_POINT (v)->x + epsilon, - GTS_POINT (v)->y + epsilon, - GTS_POINT (v)->z + epsilon); - - /* select vertices which are inside bbox using kdtree */ - j = selected = gts_kdtree_range (kdtree, bbox, NULL); - while (j) { - GtsVertex * sv = j->data; - if (sv != v && !GTS_OBJECT (sv)->reserved && (!check || (*check) (sv, v))) { - /* sv is not v and is active */ - gts_vertex_replace (sv, v); - GTS_OBJECT (sv)->reserved = sv; /* mark sv as inactive */ - } - j = j->next; - } - g_slist_free (selected); - gts_object_destroy (GTS_OBJECT (bbox)); - } - i = i->next; - } - - gts_kdtree_destroy (kdtree); - - /* destroy inactive vertices and removes them from list */ - - /* we want to control vertex destruction */ - gts_allow_floating_vertices = TRUE; - - i = vertices; - while (i) { - GtsVertex * v = i->data; - GList * next = i->next; - if (GTS_OBJECT (v)->reserved) { /* v is inactive */ - gts_object_destroy (GTS_OBJECT (v)); - vertices = g_list_remove_link (vertices, i); - g_list_free_1 (i); - } - i = next; - } - gts_allow_floating_vertices = FALSE; - - return vertices; -} - -/* returns the list of edges belonging to @surface turning around @v */ -static GSList * edge_fan_list (GtsVertex * v, - GtsSurface * surface, - GtsFace * f, - GtsEdge * e, - GtsFace * first) -{ - GSList * i = e->triangles; - GtsFace * neighbor = NULL; - GtsEdge * next = NULL, * enext = NULL; - - while (i) { - GtsFace * f1 = i->data; - if (GTS_IS_FACE (f1) && - f1 != f && - gts_face_has_parent_surface (f1, surface)) { - g_return_val_if_fail (neighbor == NULL, NULL); /* non-manifold edge */ - neighbor = f1; - } - i = i->next; - } - if (neighbor == NULL || neighbor == first) /* end of fan */ - return NULL; - - if (GTS_TRIANGLE (neighbor)->e1 == e) { - next = GTS_TRIANGLE (neighbor)->e2; - enext = GTS_TRIANGLE (neighbor)->e3; - } - else if (GTS_TRIANGLE (neighbor)->e2 == e) { - next = GTS_TRIANGLE (neighbor)->e3; - enext = GTS_TRIANGLE (neighbor)->e1; - } - else if (GTS_TRIANGLE (neighbor)->e3 == e) { - next = GTS_TRIANGLE (neighbor)->e1; - enext = GTS_TRIANGLE (neighbor)->e2; - } - else - g_assert_not_reached (); - - /* checking for correct orientation */ - g_return_val_if_fail (GTS_SEGMENT (enext)->v1 == v || - GTS_SEGMENT (enext)->v2 == v, NULL); - - return g_slist_prepend (edge_fan_list (v, surface, neighbor, enext, first), - next); -} - -/** - * gts_vertex_fan_oriented: - * @v: a #GtsVertex. - * @surface: a #GtsSurface. - * - * Returns: a list of #GtsEdge describing in counterclockwise order the - * boundary of the fan of summit @v, the faces of the fan belonging to - * @surface. - */ -GSList * gts_vertex_fan_oriented (GtsVertex * v, GtsSurface * surface) -{ - GtsFace * f = NULL; - guint d = 2; - GSList * i; - GtsVertex * v1, * v2, * v3; - GtsEdge * e1, * e2, * e3; - - g_return_val_if_fail (v != NULL, NULL); - g_return_val_if_fail (surface != NULL, NULL); - - i = v->segments; - while (i) { - GtsEdge * e = i->data; - if (GTS_IS_EDGE (e)) { - GSList * j = e->triangles; - GtsFace * f1 = NULL; - guint degree = 0; - while (j) { - if (GTS_IS_FACE (j->data) && - gts_face_has_parent_surface (j->data, surface)) { - f1 = j->data; - degree++; - } - j = j->next; - } - if (f1 != NULL) { - g_return_val_if_fail (degree <= 2, NULL); /* non-manifold edge */ - if (degree == 1) { - gts_triangle_vertices_edges (GTS_TRIANGLE (f1), NULL, - &v1, &v2, &v3, &e1, &e2, &e3); - if (v == v2) { - e2 = e3; - e3 = e1; - } - else if (v == v3) { - e3 = e2; - e2 = e1; - } - if (e3 != e) { - d = 1; - f = f1; - } - } - else if (degree <= d) - f = f1; - } - } - i = i->next; - } - - if (f == NULL) - return NULL; - - gts_triangle_vertices_edges (GTS_TRIANGLE (f), NULL, - &v1, &v2, &v3, &e1, &e2, &e3); - if (v == v2) { - e2 = e3; - e3 = e1; - } - else if (v == v3) { - e3 = e2; - e2 = e1; - } - - return g_slist_prepend (edge_fan_list (v, surface, f, e3, f), e2); -} - -#define edge_use_vertex(e, v) (GTS_SEGMENT(e)->v1 == v ||\ - GTS_SEGMENT(e)->v2 == v) - -static GtsEdge * replace_vertex (GtsTriangle * t, - GtsEdge * e1, - GtsVertex * v, - GtsVertex * with) -{ - GtsEdge * e = NULL; - - if (t->e1 != e1 && edge_use_vertex (t->e1, v)) - e = t->e1; - else if (t->e2 != e1 && edge_use_vertex (t->e2, v)) - e = t->e2; - else if (t->e3 != e1 && edge_use_vertex (t->e3, v)) - e = t->e3; - else - return NULL; - - if (with != v) { - GtsSegment * s = GTS_SEGMENT (e); - if (s->v1 == v) s->v1 = with; - if (s->v2 == v) s->v2 = with; - with->segments = g_slist_prepend (with->segments, s); - v->segments = g_slist_remove (v->segments, s); - } - - return e; -} - -static void triangle_next (GtsEdge * e, GtsVertex * v, GtsVertex * with) -{ - GSList * i; - - if (e == NULL) - return; - - i = e->triangles; - while (i) { - GtsTriangle * t = i->data; - if (GTS_OBJECT (t)->reserved) { - GTS_OBJECT (t)->reserved = NULL; - triangle_next (replace_vertex (t, e, v, with), v, with); - } - i = i->next; - } -} - -/** - * gts_vertex_is_contact: - * @v: a #GtsVertex. - * @sever: if %TRUE and if @v is a contact vertex between two or more - * sets of connected triangles replaces it with as many vertices, - * clones of @v. - * - * Returns: the number of sets of connected triangles sharing @v as a - * contact vertex. - */ -guint gts_vertex_is_contact (GtsVertex * v, gboolean sever) -{ - GSList * triangles, * i; - GtsVertex * with = v; - guint ncomponent = 0; - - g_return_val_if_fail (v != NULL, 0); - - triangles = gts_vertex_triangles (v, NULL); - i = triangles; - while (i) { - GTS_OBJECT (i->data)->reserved = i; - i = i->next; - } - - i = triangles; - while (i) { - GtsTriangle * t = i->data; - if (GTS_OBJECT (t)->reserved) { - GtsEdge * e; - if (ncomponent && sever) - with = GTS_VERTEX (gts_object_clone (GTS_OBJECT (v))); - GTS_OBJECT (t)->reserved = NULL; - e = replace_vertex (t, NULL, v, with); - triangle_next (e, v, with); - triangle_next (replace_vertex (t, e, v, with), v, with); - ncomponent++; - } - i = i->next; - } - g_slist_free (triangles); - - return ncomponent; -} - -/* GtsVertexNormal: Object */ - -static void vertex_normal_attributes (GtsVertex * v, - GtsObject * e, - GtsObject * t) -{ - g_return_if_fail (GTS_IS_EDGE (e)); - g_return_if_fail (GTS_IS_TRIANGLE (t)); - - if (GTS_IS_VERTEX_NORMAL (GTS_SEGMENT (e)->v1) && - GTS_IS_VERTEX_NORMAL (GTS_SEGMENT (e)->v2)) { - GtsPoint * p1 = GTS_POINT (GTS_SEGMENT (e)->v1); - GtsPoint * p2 = GTS_POINT (GTS_SEGMENT (e)->v2); - GtsPoint * p = GTS_POINT (v); - gdouble a, b, lambda; - guint i; - - a = p2->x - p1->x; b = p->x - p1->x; - if (fabs (p2->y - p1->y) > fabs (a)) { - a = p2->y - p1->y; b = p->y - p1->y; - } - if (fabs (p2->z - p1->z) > fabs (a)) { - a = p2->z - p1->z; b = p->z - p1->z; - } - lambda = a != 0. ? b/a : 0.; - for (i = 0; i < 3; i++) - GTS_VERTEX_NORMAL (v)->n[i] = - (1. - lambda)*GTS_VERTEX_NORMAL (GTS_SEGMENT (e)->v1)->n[i] + - lambda*GTS_VERTEX_NORMAL (GTS_SEGMENT (e)->v2)->n[i]; - } - else { - GtsVertex * v1, * v2, * v3; - - gts_triangle_vertices (GTS_TRIANGLE (t), &v1, &v2, &v3); - if (GTS_IS_VERTEX_NORMAL (v1) && - GTS_IS_VERTEX_NORMAL (v2) && - GTS_IS_VERTEX_NORMAL (v3)) { - GtsVector a1, a2, a3, det; - guint i, j = 0; - gdouble l1, l2; - - gts_vector_init (a1, GTS_POINT (v1), GTS_POINT (v)); - gts_vector_init (a2, GTS_POINT (v1), GTS_POINT (v2)); - gts_vector_init (a3, GTS_POINT (v1), GTS_POINT (v3)); - gts_vector_cross (det, a2, a3); - if (fabs (det[1]) > fabs (det[0])) j = 1; - if (fabs (det[2]) > fabs (det[j])) j = 2; - if (det[j] == 0.) { - g_warning ("vertex_normal_attributes: det[%d] == 0.", j); - return; - } - switch (j) { - case 0: - l1 = (a1[1]*a3[2] - a1[2]*a3[1])/det[0]; - l2 = (a1[2]*a2[1] - a1[1]*a2[2])/det[0]; - break; - case 1: - l1 = (a1[2]*a3[0] - a1[0]*a3[2])/det[1]; - l2 = (a1[0]*a2[2] - a1[2]*a2[0])/det[1]; - break; - case 2: - l1 = (a1[0]*a3[1] - a1[1]*a3[0])/det[2]; - l2 = (a1[1]*a2[0] - a1[0]*a2[1])/det[2]; - break; - default: - l1 = l2 = 0.; - } - for (i = 0; i < 3; i++) - GTS_VERTEX_NORMAL (v)->n[i] = - GTS_VERTEX_NORMAL (v1)->n[i]*(1. - l1 - l2) + - GTS_VERTEX_NORMAL (v2)->n[i]*l1 + - GTS_VERTEX_NORMAL (v3)->n[i]*l2; - } - } -} - -static void gts_vertex_normal_class_init (GtsVertexClass * klass) -{ - klass->intersection_attributes = vertex_normal_attributes; -} - -GtsVertexClass * gts_vertex_normal_class (void) -{ - static GtsVertexClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo gts_vertex_normal_info = { - "GtsVertexNormal", - sizeof (GtsVertexNormal), - sizeof (GtsVertexClass), - (GtsObjectClassInitFunc) gts_vertex_normal_class_init, - (GtsObjectInitFunc) NULL, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_vertex_class ()), - >s_vertex_normal_info); - } - - return klass; -} - -/* GtsColorVertex: Object */ - -GtsVertexClass * gts_color_vertex_class (void) -{ - static GtsVertexClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo gts_color_vertex_info = { - "GtsColorVertex", - sizeof (GtsColorVertex), - sizeof (GtsVertexClass), - (GtsObjectClassInitFunc) NULL, - (GtsObjectInitFunc) NULL, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_vertex_class ()), - >s_color_vertex_info); - } - - return klass; -} - Index: trunk/src/3rd/gts/heap.c =================================================================== --- trunk/src/3rd/gts/heap.c (revision 1064) +++ trunk/src/3rd/gts/heap.c (nonexistent) @@ -1,258 +0,0 @@ -/* GTS - Library for the manipulation of triangulated surfaces - * Copyright (C) 1999 Stéphane Popinet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include -#include "gts.h" - -#define PARENT(i) ((i) >= 2 ? (i)/2 : 0) -#define LEFT_CHILD(i) (2*(i)) -#define RIGHT_CHILD(i) (2*(i) + 1) - -struct _GtsHeap { - GPtrArray * elts; - GCompareFunc func; - gboolean frozen; -}; - -/** - * gts_heap_new: - * @compare_func: a GCompareFunc. - * - * Returns: a new #GtsHeap using @compare_func as a sorting function. - */ -GtsHeap * gts_heap_new (GCompareFunc compare_func) -{ - GtsHeap * heap; - - g_return_val_if_fail (compare_func != NULL, NULL); - - heap = g_malloc (sizeof(GtsHeap)); - heap->elts = g_ptr_array_new (); - heap->func = compare_func; - heap->frozen = FALSE; - return heap; -} - -static void sift_up (GtsHeap * heap, guint i) -{ - gpointer parent, child; - guint p; - gpointer * pdata = heap->elts->pdata; - GCompareFunc func = heap->func; - - child = pdata[i - 1]; - while ((p = PARENT (i))) { - parent = pdata[p - 1]; - if ((*func) (parent, child) > 0) { - pdata[p - 1] = child; - pdata[i - 1] = parent; - i = p; - } - else - i = 0; - } -} - -/** - * gts_heap_insert: - * @heap: a #GtsHeap. - * @p: a pointer to add to the heap. - * - * Inserts a new element @p in the heap. - */ -void gts_heap_insert (GtsHeap * heap, gpointer p) -{ - g_return_if_fail (heap != NULL); - - g_ptr_array_add (heap->elts, p); - if (!heap->frozen) - sift_up (heap, heap->elts->len); -} - -static void sift_down (GtsHeap * heap, guint i) -{ - gpointer left_child, right_child, child, parent; - guint lc, rc, c; - gpointer * pdata = heap->elts->pdata; - guint len = heap->elts->len; - GCompareFunc func = heap->func; - - lc = LEFT_CHILD (i); - rc = RIGHT_CHILD (i); - left_child = lc <= len ? pdata[lc - 1] : NULL; - right_child = rc <= len ? pdata[rc - 1] : NULL; - - parent = pdata[i - 1]; - while (left_child != NULL) { - if (right_child == NULL || - (*func) (left_child, right_child) < 0) { - child = left_child; - c = lc; - } - else { - child = right_child; - c = rc; - } - if ((*func) (parent, child) > 0) { - pdata[i - 1] = child; - pdata[c - 1] = parent; - i = c; - lc = LEFT_CHILD (i); - rc = RIGHT_CHILD (i); - left_child = lc <= len ? pdata[lc - 1] : NULL; - right_child = rc <= len ? pdata[rc - 1] : NULL; - } - else - left_child = NULL; - } -} - -/** - * gts_heap_remove_top: - * @heap: a #GtsHeap. - * - * Removes the element at the top of the heap. - * - * Returns: the element at the top of the heap. - */ -gpointer gts_heap_remove_top (GtsHeap * heap) -{ - gpointer root; - GPtrArray * elts; - guint len; - - g_return_val_if_fail (heap != NULL, NULL); - - elts = heap->elts; len = elts->len; - - if (len == 0) - return NULL; - if (len == 1) - return g_ptr_array_remove_index (elts, 0); - - root = elts->pdata[0]; - elts->pdata[0] = g_ptr_array_remove_index (elts, len - 1); - sift_down (heap, 1); - return root; -} - -/** - * gts_heap_top: - * @heap: a #GtsHeap. - * - * Returns: the element at the top of the heap. - */ -gpointer gts_heap_top (GtsHeap * heap) -{ - GPtrArray * elts; - guint len; - - g_return_val_if_fail (heap != NULL, NULL); - - elts = heap->elts; - len = elts->len; - if (len == 0) - return NULL; - return elts->pdata[0]; -} - -/** - * gts_heap_destroy: - * @heap: a #GtsHeap. - * - * Free all the memory allocated for @heap. - */ -void gts_heap_destroy (GtsHeap * heap) -{ - g_return_if_fail (heap != NULL); - - g_ptr_array_free (heap->elts, TRUE); - g_free (heap); -} - -/** - * gts_heap_thaw: - * @heap: a #GtsHeap. - * - * If @heap has been frozen previously using gts_heap_freeze(), reorder it - * in O(n) time and unfreeze it. - */ -void gts_heap_thaw (GtsHeap * heap) -{ - guint i; - - g_return_if_fail (heap != NULL); - - if (!heap->frozen) - return; - - for (i = heap->elts->len/2; i > 0; i--) - sift_down (heap, i); - - heap->frozen = FALSE; -} - -/** - * gts_heap_foreach: - * @heap: a #GtsHeap. - * @func: the function to call for each element in the heap. - * @user_data: to pass to @func. - */ -void gts_heap_foreach (GtsHeap * heap, - GFunc func, - gpointer user_data) -{ - guint i; - GPtrArray * elts; - - g_return_if_fail (heap != NULL); - g_return_if_fail (func != NULL); - - elts = heap->elts; - for (i = 0; i < elts->len; i++) - (*func) (elts->pdata[i], user_data); -} - -/** - * gts_heap_freeze: - * @heap: a #GtsHeap. - * - * Freezes the heap. Any subsequent operation will not preserve the heap - * property. Used in conjunction with gts_heap_insert() and gts_heap_thaw() - * to create a heap in O(n) time. - */ -void gts_heap_freeze (GtsHeap * heap) -{ - g_return_if_fail (heap != NULL); - - heap->frozen = TRUE; -} - -/** - * gts_heap_size: - * @heap: a #GtsHeap. - * - * Returns: the number of items in @heap. - */ -guint gts_heap_size (GtsHeap * heap) -{ - g_return_val_if_fail (heap != NULL, 0); - - return heap->elts->len; -} Index: trunk/src/3rd/gts/kdtree.c =================================================================== --- trunk/src/3rd/gts/kdtree.c (revision 1064) +++ trunk/src/3rd/gts/kdtree.c (nonexistent) @@ -1,152 +0,0 @@ -/* GTS - Library for the manipulation of triangulated surfaces - * Copyright (C) 1999 Stéphane Popinet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include -#include "gts.h" - - -static int compare_x (const void * p1, const void * p2) { - GtsPoint - * pp1 = *((gpointer *) p1), - * pp2 = *((gpointer *) p2); - if (pp1->x > pp2->x) - return 1; - return -1; -} - -static int compare_y (const void * p1, const void * p2) { - GtsPoint - * pp1 = *((gpointer *) p1), - * pp2 = *((gpointer *) p2); - if (pp1->y > pp2->y) - return 1; - return -1; -} - -static int compare_z (const void * p1, const void * p2) { - GtsPoint - * pp1 = *((gpointer *) p1), - * pp2 = *((gpointer *) p2); - if (pp1->z > pp2->z) - return 1; - return -1; -} - -/** - * gts_kdtree_new: - * @points: an array of #GtsPoint. - * @compare: always %NULL. - * - * Note that the order of the points in array @points is modified by this - * function. - * - * Returns: a new 3D tree for @points. - */ -GNode * gts_kdtree_new (GPtrArray * points, - int (*compare) (const void *, const void *)) -{ - guint middle; - GPtrArray array; - GNode * node; - GtsPoint * point; - - g_return_val_if_fail (points != NULL, NULL); - g_return_val_if_fail (points->len > 0, NULL); - - /* sort the points */ - if (compare == compare_x) compare = compare_y; - else if (compare == compare_y) compare = compare_z; - else compare = compare_x; - qsort (points->pdata, points->len, sizeof (gpointer), compare); - - middle = (points->len - 1)/2; - point = points->pdata[middle]; - node = g_node_new (point); - - if (points->len > 1) { - array.len = middle; - if (array.len > 0) { - array.pdata = points->pdata; - g_node_prepend (node, gts_kdtree_new (&array, compare)); - } - else - g_node_prepend (node, g_node_new (NULL)); - - array.len = points->len - middle - 1; - if (array.len > 0) { - array.pdata = &(points->pdata[middle + 1]); - g_node_prepend (node, gts_kdtree_new (&array, compare)); - } - else - g_node_prepend (node, g_node_new (NULL)); - } - - return node; -} - -/** - * gts_kdtree_range: - * @tree: a 3D tree. - * @bbox: a #GtsBBox. - * @compare: always %NULL. - * - * Returns: a list of #GtsPoint belonging to @tree which are inside @bbox. - */ -GSList * gts_kdtree_range (GNode * tree_3d, - GtsBBox * bbox, - int (*compare) (const void *, const void *)) -{ - GSList * list = NULL; - GtsPoint * p; - gdouble left, right, v; - GNode * node; - - g_return_val_if_fail (tree_3d != NULL, NULL); - g_return_val_if_fail (bbox != NULL, NULL); - - p = tree_3d->data; - if (p == NULL) - return NULL; - - if (gts_bbox_point_is_inside (bbox, p)) - list = g_slist_prepend (list, p); - - if (compare == compare_x) { - left = bbox->y1; right = bbox->y2; v = p->y; - compare = compare_y; - } - else if (compare == compare_y) { - left = bbox->z1; right = bbox->z2; v = p->z; - compare = compare_z; - } - else { - left = bbox->x1; right = bbox->x2; v = p->x; - compare = compare_x; - } - - if ((node = tree_3d->children)) { - if (right >= v) - list = g_slist_concat (list, gts_kdtree_range (node, bbox, compare)); - node = node->next; - if (left <= v) - list = g_slist_concat (list, gts_kdtree_range (node, bbox, compare)); - } - return list; -} - Index: trunk/src/3rd/gts/curvature.c =================================================================== --- trunk/src/3rd/gts/curvature.c (revision 1064) +++ trunk/src/3rd/gts/curvature.c (nonexistent) @@ -1,621 +0,0 @@ -/* GTS - Library for the manipulation of triangulated surfaces - * Copyright (C) 1999-2002 Ray Jones, Stéphane Popinet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include -#include "gts.h" - -static gboolean angle_obtuse (GtsVertex * v, GtsFace * f) -{ - GtsEdge * e = gts_triangle_edge_opposite (GTS_TRIANGLE (f), v); - GtsVector vec1, vec2; - - gts_vector_init (vec1, GTS_POINT (v), GTS_POINT (GTS_SEGMENT (e)->v1)); - gts_vector_init (vec2, GTS_POINT (v), GTS_POINT (GTS_SEGMENT (e)->v2)); - - return (gts_vector_scalar (vec1, vec2) < 0.0); -} - -static gboolean triangle_obtuse (GtsVertex * v, GtsFace * f) -{ - GtsEdge * e = gts_triangle_edge_opposite (GTS_TRIANGLE (f), v); - - return (angle_obtuse (v, f) || - angle_obtuse (GTS_SEGMENT (e)->v1, f) || - angle_obtuse (GTS_SEGMENT (e)->v2, f)); -} - -static gdouble cotan (GtsVertex * vo, GtsVertex * v1, GtsVertex * v2) -{ - /* cf. Appendix B of [Meyer et al 2002] */ - GtsVector u, v; - gdouble udotv, denom; - - gts_vector_init (u, GTS_POINT (vo), GTS_POINT (v1)); - gts_vector_init (v, GTS_POINT (vo), GTS_POINT (v2)); - - udotv = gts_vector_scalar (u, v); - denom = sqrt (gts_vector_scalar (u,u)*gts_vector_scalar (v,v) - - udotv*udotv); - - - /* denom can be zero if u==v. Returning 0 is acceptable, based on - * the callers of this function below. */ - if (denom == 0.0) return (0.0); - - return (udotv/denom); -} - -static gdouble angle_from_cotan (GtsVertex * vo, - GtsVertex * v1, GtsVertex * v2) -{ - /* cf. Appendix B and the caption of Table 1 from [Meyer et al 2002] */ - GtsVector u, v; - gdouble udotv, denom; - - gts_vector_init (u, GTS_POINT (vo), GTS_POINT (v1)); - gts_vector_init (v, GTS_POINT (vo), GTS_POINT (v2)); - - udotv = gts_vector_scalar (u, v); - denom = sqrt (gts_vector_scalar (u,u)*gts_vector_scalar (v,v) - - udotv*udotv); - - /* Note: I assume this is what they mean by using atan2 (). -Ray Jones */ - - /* tan = denom/udotv = y/x (see man page for atan2) */ - return (fabs (atan2 (denom, udotv))); -} - -static gdouble region_area (GtsVertex * v, GtsFace * f) -{ - /* cf. Section 3.3 of [Meyer et al 2002] */ - - if (gts_triangle_area (GTS_TRIANGLE (f)) == 0.0) return (0.0); - - if (triangle_obtuse (v, f)) { - if (angle_obtuse (v, f)) - return (gts_triangle_area (GTS_TRIANGLE (f))/2.0); - else - return (gts_triangle_area (GTS_TRIANGLE (f))/4.0); - } else { - GtsEdge * e = gts_triangle_edge_opposite (GTS_TRIANGLE (f), v); - - return ((cotan (GTS_SEGMENT (e)->v1, v, GTS_SEGMENT (e)->v2)* - gts_point_distance2 (GTS_POINT (v), - GTS_POINT (GTS_SEGMENT (e)->v2)) + - cotan (GTS_SEGMENT (e)->v2, v, GTS_SEGMENT (e)->v1)* - gts_point_distance2 (GTS_POINT (v), - GTS_POINT (GTS_SEGMENT (e)->v1))) - /8.0); - } -} - -/** - * gts_vertex_mean_curvature_normal: - * @v: a #GtsVertex. - * @s: a #GtsSurface. - * @Kh: the Mean Curvature Normal at @v. - * - * Computes the Discrete Mean Curvature Normal approximation at @v. - * The mean curvature at @v is half the magnitude of the vector @Kh. - * - * Note: the normal computed is not unit length, and may point either - * into or out of the surface, depending on the curvature at @v. It - * is the responsibility of the caller of the function to use the mean - * curvature normal appropriately. - * - * This approximation is from the paper: - * Discrete Differential-Geometry Operators for Triangulated 2-Manifolds - * Mark Meyer, Mathieu Desbrun, Peter Schroder, Alan H. Barr - * VisMath '02, Berlin (Germany) - * http://www-grail.usc.edu/pubs.html - * - * Returns: %TRUE if the operator could be evaluated, %FALSE if the - * evaluation failed for some reason (@v is boundary or is the - * endpoint of a non-manifold edge.) - */ -gboolean gts_vertex_mean_curvature_normal (GtsVertex * v, GtsSurface * s, - GtsVector Kh) -{ - GSList * faces, * edges, * i; - gdouble area = 0.0; - - g_return_val_if_fail (v != NULL, FALSE); - g_return_val_if_fail (s != NULL, FALSE); - - /* this operator is not defined for boundary edges */ - if (gts_vertex_is_boundary (v, s)) return (FALSE); - - faces = gts_vertex_faces (v, s, NULL); - g_return_val_if_fail (faces != NULL, FALSE); - - edges = gts_vertex_fan_oriented (v, s); - if (edges == NULL) { - g_slist_free (faces); - return (FALSE); - } - - i = faces; - while (i) { - GtsFace * f = i->data; - - area += region_area (v, f); - i = i->next; - } - g_slist_free (faces); - - Kh[0] = Kh[1] = Kh[2] = 0.0; - - i = edges; - while (i) { - GtsEdge * e = i->data; - GtsVertex * v1 = GTS_SEGMENT (e)->v1; - GtsVertex * v2 = GTS_SEGMENT (e)->v2; - gdouble temp; - - temp = cotan (v1, v, v2); - Kh[0] += temp*(GTS_POINT (v2)->x - GTS_POINT (v)->x); - Kh[1] += temp*(GTS_POINT (v2)->y - GTS_POINT (v)->y); - Kh[2] += temp*(GTS_POINT (v2)->z - GTS_POINT (v)->z); - - temp = cotan (v2, v, v1); - Kh[0] += temp*(GTS_POINT (v1)->x - GTS_POINT (v)->x); - Kh[1] += temp*(GTS_POINT (v1)->y - GTS_POINT (v)->y); - Kh[2] += temp*(GTS_POINT (v1)->z - GTS_POINT (v)->z); - - i = i->next; - } - g_slist_free (edges); - - if (area > 0.0) { - Kh[0] /= 2*area; - Kh[1] /= 2*area; - Kh[2] /= 2*area; - } else { - return (FALSE); - } - - return TRUE; -} - -/** - * gts_vertex_gaussian_curvature: - * @v: a #GtsVertex. - * @s: a #GtsSurface. - * @Kg: the Discrete Gaussian Curvature approximation at @v. - * - * Computes the Discrete Gaussian Curvature approximation at @v. - * - * This approximation is from the paper: - * Discrete Differential-Geometry Operators for Triangulated 2-Manifolds - * Mark Meyer, Mathieu Desbrun, Peter Schroder, Alan H. Barr - * VisMath '02, Berlin (Germany) - * http://www-grail.usc.edu/pubs.html - * - * Returns: %TRUE if the operator could be evaluated, %FALSE if the - * evaluation failed for some reason (@v is boundary or is the - * endpoint of a non-manifold edge.) - */ -gboolean gts_vertex_gaussian_curvature (GtsVertex * v, GtsSurface * s, - gdouble * Kg) -{ - GSList * faces, * edges, * i; - gdouble area = 0.0; - gdouble angle_sum = 0.0; - - g_return_val_if_fail (v != NULL, FALSE); - g_return_val_if_fail (s != NULL, FALSE); - g_return_val_if_fail (Kg != NULL, FALSE); - - /* this operator is not defined for boundary edges */ - if (gts_vertex_is_boundary (v, s)) return (FALSE); - - faces = gts_vertex_faces (v, s, NULL); - g_return_val_if_fail (faces != NULL, FALSE); - - edges = gts_vertex_fan_oriented (v, s); - if (edges == NULL) { - g_slist_free (faces); - return (FALSE); - } - - i = faces; - while (i) { - GtsFace * f = i->data; - - area += region_area (v, f); - i = i->next; - } - g_slist_free (faces); - - i = edges; - while (i) { - GtsEdge * e = i->data; - GtsVertex * v1 = GTS_SEGMENT (e)->v1; - GtsVertex * v2 = GTS_SEGMENT (e)->v2; - - angle_sum += angle_from_cotan (v, v1, v2); - i = i->next; - } - g_slist_free (edges); - - *Kg = (2.0*M_PI - angle_sum)/area; - - return TRUE; -} - -/** - * gts_vertex_principal_curvatures: - * @Kh: mean curvature. - * @Kg: Gaussian curvature. - * @K1: first principal curvature. - * @K2: second principal curvature. - * - * Computes the principal curvatures at a point given the mean and - * Gaussian curvatures at that point. - * - * The mean curvature can be computed as one-half the magnitude of the - * vector computed by gts_vertex_mean_curvature_normal(). - * - * The Gaussian curvature can be computed with - * gts_vertex_gaussian_curvature(). - */ -void gts_vertex_principal_curvatures (gdouble Kh, gdouble Kg, - gdouble * K1, gdouble * K2) -{ - gdouble temp = Kh*Kh - Kg; - - g_return_if_fail (K1 != NULL); - g_return_if_fail (K2 != NULL); - - if (temp < 0.0) temp = 0.0; - temp = sqrt (temp); - *K1 = Kh + temp; - *K2 = Kh - temp; -} - -/* from Maple */ -static void linsolve (gdouble m11, gdouble m12, gdouble b1, - gdouble m21, gdouble m22, gdouble b2, - gdouble * x1, gdouble * x2) -{ - gdouble temp; - - temp = 1.0 / (m21*m12 - m11*m22); - *x1 = (m12*b2 - m22*b1)*temp; - *x2 = (m11*b2 - m21*b1)*temp; -} - -/* from Maple - largest eigenvector of [a b; b c] */ -static void eigenvector (gdouble a, gdouble b, gdouble c, - GtsVector e) -{ - if (b == 0.0) { - e[0] = 0.0; - } else { - e[0] = -(c - a - sqrt (c*c - 2*a*c + a*a + 4*b*b))/(2*b); - } - e[1] = 1.0; - e[2] = 0.0; -} - -/** - * gts_vertex_principal_directions: - * @v: a #GtsVertex. - * @s: a #GtsSurface. - * @Kh: mean curvature normal (a #GtsVector). - * @Kg: Gaussian curvature (a gdouble). - * @e1: first principal curvature direction (direction of largest curvature). - * @e2: second principal curvature direction. - * - * Computes the principal curvature directions at a point given @Kh - * and @Kg, the mean curvature normal and Gaussian curvatures at that - * point, computed with gts_vertex_mean_curvature_normal() and - * gts_vertex_gaussian_curvature(), respectively. - * - * Note that this computation is very approximate and tends to be - * unstable. Smoothing of the surface or the principal directions may - * be necessary to achieve reasonable results. - */ -void gts_vertex_principal_directions (GtsVertex * v, GtsSurface * s, - GtsVector Kh, gdouble Kg, - GtsVector e1, GtsVector e2) -{ - GtsVector N; - gdouble normKh; - GSList * i, * j; - GtsVector basis1, basis2, d, eig; - gdouble ve2, vdotN; - gdouble aterm_da, bterm_da, cterm_da, const_da; - gdouble aterm_db, bterm_db, cterm_db, const_db; - gdouble a, b, c; - gdouble K1, K2; - gdouble *weights, *kappas, *d1s, *d2s; - gint edge_count; - gdouble err_e1, err_e2; - int e; - - /* compute unit normal */ - normKh = sqrt (gts_vector_scalar (Kh, Kh)); - - if (normKh > 0.0) { - N[0] = Kh[0] / normKh; - N[1] = Kh[1] / normKh; - N[2] = Kh[2] / normKh; - } else { - /* This vertex is a point of zero mean curvature (flat or saddle - * point). Compute a normal by averaging the adjacent triangles - */ - N[0] = N[1] = N[2] = 0.0; - i = gts_vertex_faces (v, s, NULL); - while (i) { - gdouble x, y, z; - gts_triangle_normal (GTS_TRIANGLE ((GtsFace *) i->data), - &x, &y, &z); - N[0] += x; - N[1] += y; - N[2] += z; - - i = i->next; - } - g_return_if_fail (gts_vector_norm (N) > 0.0); - gts_vector_normalize (N); - } - - - /* construct a basis from N: */ - /* set basis1 to any component not the largest of N */ - basis1[0] = basis1[1] = basis1[2] = 0.0; - if (fabs (N[0]) > fabs (N[1])) - basis1[1] = 1.0; - else - basis1[0] = 1.0; - - /* make basis2 orthogonal to N */ - gts_vector_cross (basis2, N, basis1); - gts_vector_normalize (basis2); - - /* make basis1 orthogonal to N and basis2 */ - gts_vector_cross (basis1, N, basis2); - gts_vector_normalize (basis1); - - aterm_da = bterm_da = cterm_da = const_da = 0.0; - aterm_db = bterm_db = cterm_db = const_db = 0.0; - - weights = g_malloc (sizeof (gdouble)*g_slist_length (v->segments)); - kappas = g_malloc (sizeof (gdouble)*g_slist_length (v->segments)); - d1s = g_malloc (sizeof (gdouble)*g_slist_length (v->segments)); - d2s = g_malloc (sizeof (gdouble)*g_slist_length (v->segments)); - edge_count = 0; - - i = v->segments; - while (i) { - GtsEdge * e; - GtsFace * f1, * f2; - gdouble weight, kappa, d1, d2; - GtsVector vec_edge; - - if (! GTS_IS_EDGE (i->data)) { - i = i->next; - continue; - } - - e = i->data; - - /* since this vertex passed the tests in - * gts_vertex_mean_curvature_normal(), this should be true. */ - g_assert (gts_edge_face_number (e, s) == 2); - - /* identify the two triangles bordering e in s */ - f1 = f2 = NULL; - j = e->triangles; - while (j) { - if ((! GTS_IS_FACE (j->data)) || - (! gts_face_has_parent_surface (GTS_FACE (j->data), s))) { - j = j->next; - continue; - } - if (f1 == NULL) - f1 = GTS_FACE (j->data); - else { - f2 = GTS_FACE (j->data); - break; - } - j = j->next; - } - g_assert (f2 != NULL); - - /* We are solving for the values of the curvature tensor - * B = [ a b ; b c ]. - * The computations here are from section 5 of [Meyer et al 2002]. - * - * The first step is to calculate the linear equations governing - * the values of (a,b,c). These can be computed by setting the - * derivatives of the error E to zero (section 5.3). - * - * Since a + c = norm(Kh), we only compute the linear equations - * for dE/da and dE/db. (NB: [Meyer et al 2002] has the - * equation a + b = norm(Kh), but I'm almost positive this is - * incorrect.) - * - * Note that the w_ij (defined in section 5.2) are all scaled by - * (1/8*A_mixed). We drop this uniform scale factor because the - * solution of the linear equations doesn't rely on it. - * - * The terms of the linear equations are xterm_dy with x in - * {a,b,c} and y in {a,b}. There are also const_dy terms that are - * the constant factors in the equations. - */ - - /* find the vector from v along edge e */ - gts_vector_init (vec_edge, GTS_POINT (v), - GTS_POINT ((GTS_SEGMENT (e)->v1 == v) ? - GTS_SEGMENT (e)->v2 : GTS_SEGMENT (e)->v1)); - ve2 = gts_vector_scalar (vec_edge, vec_edge); - vdotN = gts_vector_scalar (vec_edge, N); - - /* section 5.2 - There is a typo in the computation of kappa. The - * edges should be x_j-x_i. - */ - kappa = 2.0 * vdotN / ve2; - - /* section 5.2 */ - - /* I don't like performing a minimization where some of the - * weights can be negative (as can be the case if f1 or f2 are - * obtuse). To ensure all-positive weights, we check for - * obtuseness and use values similar to those in region_area(). */ - weight = 0.0; - if (! triangle_obtuse(v, f1)) { - weight += ve2 * - cotan (gts_triangle_vertex_opposite (GTS_TRIANGLE (f1), e), - GTS_SEGMENT (e)->v1, GTS_SEGMENT (e)->v2) / 8.0; - } else { - if (angle_obtuse (v, f1)) { - weight += ve2 * gts_triangle_area (GTS_TRIANGLE (f1)) / 4.0; - } else { - weight += ve2 * gts_triangle_area (GTS_TRIANGLE (f1)) / 8.0; - } - } - - if (! triangle_obtuse(v, f2)) { - weight += ve2 * - cotan (gts_triangle_vertex_opposite (GTS_TRIANGLE (f2), e), - GTS_SEGMENT (e)->v1, GTS_SEGMENT (e)->v2) / 8.0; - } else { - if (angle_obtuse (v, f2)) { - weight += ve2 * gts_triangle_area (GTS_TRIANGLE (f2)) / 4.0; - } else { - weight += ve2 * gts_triangle_area (GTS_TRIANGLE (f2)) / 8.0; - } - } - - /* projection of edge perpendicular to N (section 5.3) */ - d[0] = vec_edge[0] - vdotN * N[0]; - d[1] = vec_edge[1] - vdotN * N[1]; - d[2] = vec_edge[2] - vdotN * N[2]; - gts_vector_normalize (d); - - /* not explicit in the paper, but necessary. Move d to 2D basis. */ - d1 = gts_vector_scalar (d, basis1); - d2 = gts_vector_scalar (d, basis2); - - /* store off the curvature, direction of edge, and weights for later use */ - weights[edge_count] = weight; - kappas[edge_count] = kappa; - d1s[edge_count] = d1; - d2s[edge_count] = d2; - edge_count++; - - /* Finally, update the linear equations */ - aterm_da += weight * d1 * d1 * d1 * d1; - bterm_da += weight * d1 * d1 * 2 * d1 * d2; - cterm_da += weight * d1 * d1 * d2 * d2; - const_da += weight * d1 * d1 * (- kappa); - - aterm_db += weight * d1 * d2 * d1 * d1; - bterm_db += weight * d1 * d2 * 2 * d1 * d2; - cterm_db += weight * d1 * d2 * d2 * d2; - const_db += weight * d1 * d2 * (- kappa); - - i = i->next; - } - - /* now use the identity (Section 5.3) a + c = |Kh| = 2 * kappa_h */ - aterm_da -= cterm_da; - const_da += cterm_da * normKh; - - aterm_db -= cterm_db; - const_db += cterm_db * normKh; - - /* check for solvability of the linear system */ - if (((aterm_da * bterm_db - aterm_db * bterm_da) != 0.0) && - ((const_da != 0.0) || (const_db != 0.0))) { - linsolve (aterm_da, bterm_da, -const_da, - aterm_db, bterm_db, -const_db, - &a, &b); - - c = normKh - a; - - eigenvector (a, b, c, eig); - } else { - /* region of v is planar */ - eig[0] = 1.0; - eig[1] = 0.0; - } - - /* Although the eigenvectors of B are good estimates of the - * principal directions, it seems that which one is attached to - * which curvature direction is a bit arbitrary. This may be a bug - * in my implementation, or just a side-effect of the inaccuracy of - * B due to the discrete nature of the sampling. - * - * To overcome this behavior, we'll evaluate which assignment best - * matches the given eigenvectors by comparing the curvature - * estimates computed above and the curvatures calculated from the - * discrete differential operators. */ - - gts_vertex_principal_curvatures (0.5 * normKh, Kg, &K1, &K2); - - err_e1 = err_e2 = 0.0; - /* loop through the values previously saved */ - for (e = 0; e < edge_count; e++) { - gdouble weight, kappa, d1, d2; - gdouble temp1, temp2; - gdouble delta; - - weight = weights[e]; - kappa = kappas[e]; - d1 = d1s[e]; - d2 = d2s[e]; - - temp1 = fabs (eig[0] * d1 + eig[1] * d2); - temp1 = temp1 * temp1; - temp2 = fabs (eig[1] * d1 - eig[0] * d2); - temp2 = temp2 * temp2; - - /* err_e1 is for K1 associated with e1 */ - delta = K1 * temp1 + K2 * temp2 - kappa; - err_e1 += weight * delta * delta; - - /* err_e2 is for K1 associated with e2 */ - delta = K2 * temp1 + K1 * temp2 - kappa; - err_e2 += weight * delta * delta; - } - g_free (weights); - g_free (kappas); - g_free (d1s); - g_free (d2s); - - /* rotate eig by a right angle if that would decrease the error */ - if (err_e2 < err_e1) { - gdouble temp = eig[0]; - - eig[0] = eig[1]; - eig[1] = -temp; - } - - e1[0] = eig[0] * basis1[0] + eig[1] * basis2[0]; - e1[1] = eig[0] * basis1[1] + eig[1] * basis2[1]; - e1[2] = eig[0] * basis1[2] + eig[1] * basis2[2]; - gts_vector_normalize (e1); - - /* make N,e1,e2 a right handed coordinate sytem */ - gts_vector_cross (e2, N, e1); - gts_vector_normalize (e2); -} Index: trunk/src/3rd/gts/pgraph.c =================================================================== --- trunk/src/3rd/gts/pgraph.c (revision 1064) +++ trunk/src/3rd/gts/pgraph.c (nonexistent) @@ -1,584 +0,0 @@ -/* GTS - Library for the manipulation of triangulated surfaces - * Copyright (C) 1999 Stéphane Popinet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include "gts.h" - -/* GtsGNodeSplit */ - -static void gnode_split_destroy (GtsObject * object) -{ - GtsGNodeSplit * ns = GTS_GNODE_SPLIT (object); - - if (gts_container_size (GTS_CONTAINER (ns->n)) == 0) { - g_assert (GTS_SLIST_CONTAINEE (ns->n)->containers == NULL); - gts_object_destroy (GTS_OBJECT (ns->n)); - } - else { - /* GtsGNode * n1 = GTS_GNODE_SPLIT_N1 (ns); */ - /* GtsGNode * n2 = GTS_GNODE_SPLIT_N2 (ns); */ - - g_warning ("Memory deallocation for GtsGNodeSplit not fully implemented yet: memory leak!"); - } - - (* GTS_OBJECT_CLASS (gts_gnode_split_class ())->parent_class->destroy) - (object); -} - -static void gnode_split_class_init (GtsGNodeSplitClass * klass) -{ - GTS_OBJECT_CLASS (klass)->destroy = gnode_split_destroy; -} - -static void gnode_split_init (GtsGNodeSplit * ns) -{ - ns->n = NULL; - ns->n1 = ns->n2 = NULL; -} - -/** - * gts_gnode_split_class: - * - * Returns: the #GtsGNodeSplitClass. - */ -GtsGNodeSplitClass * gts_gnode_split_class (void) -{ - static GtsGNodeSplitClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo gnode_split_info = { - "GtsGNodeSplit", - sizeof (GtsGNodeSplit), - sizeof (GtsGNodeSplitClass), - (GtsObjectClassInitFunc) gnode_split_class_init, - (GtsObjectInitFunc) gnode_split_init, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (gts_object_class (), &gnode_split_info); - } - - return klass; -} - -/** - * gts_gnode_split_new: - * @klass: a #GtsGNodeSplitClass. - * @n: a #GtsGNode. - * @n1: a #GtsGNodeSplit or #GtsGNode. - * @n2: a #GtsGNodeSplit or #GtsGNode. - * - * Creates a new #GtsGNodeSplit which would collapse @n1 and @n2 into - * @n. The collapse itself is not performed. - * - * Returns: the new #GtsGNodeSplit. - */ -GtsGNodeSplit * gts_gnode_split_new (GtsGNodeSplitClass * klass, - GtsGNode * n, - GtsObject * n1, - GtsObject * n2) -{ - GtsGNodeSplit * ns; - - g_return_val_if_fail (klass != NULL, NULL); - g_return_val_if_fail (n != NULL, NULL); - g_return_val_if_fail (GTS_IS_GNODE_SPLIT (n1) || GTS_IS_GNODE (n1), NULL); - g_return_val_if_fail (GTS_IS_GNODE_SPLIT (n2) || GTS_IS_GNODE (n2), NULL); - - ns = GTS_GNODE_SPLIT (gts_object_new (GTS_OBJECT_CLASS (klass))); - ns->n = n; - ns->n1 = n1; - ns->n2 = n2; - - return ns; -} - -static void connect_edge (GtsGEdge * e, gpointer * data) -{ - GtsGNode * n = data[0]; - GtsGNode * n1 = data[1]; - GtsGNode * n2 = data[2]; - - if (GTS_OBJECT (e)->reserved || /* edge is disconnected */ - gts_gedge_connects (e, n1, n2)) - return; - if (e->n1 == n1 || e->n1 == n2) - e->n1 = n; - else if (e->n2 == n1 || e->n2 == n2) - e->n2 = n; - else - g_assert_not_reached (); - gts_container_add (GTS_CONTAINER (n), GTS_CONTAINEE (e)); -} - -/** - * gts_gnode_split_collapse: - * @ns: a #GtsGNodeSplit. - * @g: a #GtsGraph. - * @klass: a #GtsWGEdgeClass. - * - * Collapses the node split @ns. Any new edge created during the - * process will be of class @klass. - */ -void gts_gnode_split_collapse (GtsGNodeSplit * ns, - GtsGraph * g, - GtsWGEdgeClass * klass) -{ - GtsGNode * n1, * n2; - GSList * i; - gpointer data[3]; - - g_return_if_fail (ns != NULL); - g_return_if_fail (g != NULL); - g_return_if_fail (gts_container_size (GTS_CONTAINER (ns->n)) == 0); - - n1 = GTS_GNODE_SPLIT_N1 (ns); - n2 = GTS_GNODE_SPLIT_N2 (ns); - - /* look for triangles */ - i = GTS_SLIST_CONTAINER (n1)->items; - while (i) { - GtsGEdge * e13 = i->data; - GtsGNode * n3 = GTS_GNODE_NEIGHBOR (n1, e13); - if (n3 != n2) { - GSList * j = GTS_SLIST_CONTAINER (n3)->items; - while (j) { - GtsGEdge * e32 = j->data; - GSList * next = j->next; - GtsGNode * n4 = GTS_GNODE_NEIGHBOR (n3, e32); - if (n4 == n2) { /* found triangle n1 (e13) n3 (e32) n2 */ - gts_wgedge_new (klass, ns->n, n3, - gts_gedge_weight (e13) + gts_gedge_weight (e32)); - GTS_OBJECT (e13)->reserved = n3; - GTS_OBJECT (e32)->reserved = n3; - GTS_SLIST_CONTAINER (n3)->items = - g_slist_remove (GTS_SLIST_CONTAINER (n3)->items, e32); - } - j = next; - } - if (GTS_OBJECT (e13)->reserved == n3) - GTS_SLIST_CONTAINER (n3)->items = - g_slist_remove (GTS_SLIST_CONTAINER (n3)->items, e13); - } - i = i->next; - } - - /* connect edges to new node */ - data[0] = ns->n; - data[1] = n1; - data[2] = n2; - gts_container_foreach (GTS_CONTAINER (n1), (GtsFunc) connect_edge, data); - gts_container_foreach (GTS_CONTAINER (n2), (GtsFunc) connect_edge, data); - - gts_allow_floating_gnodes = TRUE; - gts_container_remove (GTS_CONTAINER (g), GTS_CONTAINEE (n1)); - gts_container_remove (GTS_CONTAINER (g), GTS_CONTAINEE (n2)); - gts_allow_floating_gnodes = FALSE; - gts_container_add (GTS_CONTAINER (g), GTS_CONTAINEE (ns->n)); -} - -static void restore_edge (GtsGEdge * e, gpointer * data) -{ - GtsGNode * n = data[0]; - GtsGNode * n1 = data[1]; - GtsGNode * n2 = data[2]; - GtsGNode * n3 = GTS_OBJECT (e)->reserved; - - if (n3) { /* e is a disconnected edge */ - GTS_OBJECT (e)->reserved = NULL; - gts_container_add (GTS_CONTAINER (n3), GTS_CONTAINEE (e)); - return; - } - - if (gts_gedge_connects (e, n1, n2)) - return; - - if (e->n1 == n) - e->n1 = n1; - else if (e->n2 == n) - e->n2 = n1; - else - g_assert_not_reached (); - GTS_SLIST_CONTAINER (n)->items = - g_slist_remove (GTS_SLIST_CONTAINER (n)->items, e); -} - -/** - * gts_gnode_split_expand: - * @ns: a #GtsGNodeSplit. - * @g: a #GtsGraph. - * - * Expands the node split ns adding the new nodes to @g. - */ -void gts_gnode_split_expand (GtsGNodeSplit * ns, - GtsGraph * g) -{ - GtsGNode * n1, * n2; - gpointer data[3]; - GSList * i; - - g_return_if_fail (ns != NULL); - g_return_if_fail (g != NULL); - g_return_if_fail (gts_containee_is_contained (GTS_CONTAINEE (ns->n), - GTS_CONTAINER (g))); - - n1 = GTS_GNODE_SPLIT_N1 (ns); - n2 = GTS_GNODE_SPLIT_N2 (ns); - - data[0] = ns->n; - data[1] = n1; - data[2] = n2; - gts_container_foreach (GTS_CONTAINER (n1), (GtsFunc) restore_edge, data); - data[1] = n2; - data[2] = n1; - gts_container_foreach (GTS_CONTAINER (n2), (GtsFunc) restore_edge, data); - - i = GTS_SLIST_CONTAINER (ns->n)->items; - while (i) { - GSList * next = i->next; - gts_container_remove (GTS_CONTAINER (ns->n), GTS_CONTAINEE (i->data)); - i = next; - } - g_assert (gts_container_size (GTS_CONTAINER (ns->n)) == 0); - - gts_allow_floating_gnodes = TRUE; - gts_container_remove (GTS_CONTAINER (g), GTS_CONTAINEE (ns->n)); - gts_allow_floating_gnodes = FALSE; - - gts_container_add (GTS_CONTAINER (g), GTS_CONTAINEE (n1)); - gts_container_add (GTS_CONTAINER (g), GTS_CONTAINEE (n2)); -} - -/* GtsPGraph */ - -static void pgraph_destroy (GtsObject * object) -{ - GtsPGraph * pg = GTS_PGRAPH (object); - guint i; - - for (i = 0; i < pg->split->len; i++) - gts_object_destroy (GTS_OBJECT (g_ptr_array_index (pg->split, i))); - g_ptr_array_free (pg->split, TRUE); - g_array_free (pg->levels, TRUE); - - (* GTS_OBJECT_CLASS (gts_pgraph_class ())->parent_class->destroy) (object); -} - -static void pgraph_class_init (GtsPGraphClass * klass) -{ - GTS_OBJECT_CLASS (klass)->destroy = pgraph_destroy; -} - -static void pgraph_init (GtsPGraph * pg) -{ - pg->g = NULL; - pg->split = g_ptr_array_new (); - pg->levels = g_array_new (FALSE, FALSE, sizeof (guint)); - pg->level = 0; - pg->split_class = gts_gnode_split_class (); - pg->edge_class = gts_wgedge_class (); - pg->pos = pg->min = 0; -} - -/** - * gts_pgraph_class: - * - * Returns: the #GtsPGraphClass. - */ -GtsPGraphClass * gts_pgraph_class (void) -{ - static GtsPGraphClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo pgraph_info = { - "GtsPGraph", - sizeof (GtsPGraph), - sizeof (GtsPGraphClass), - (GtsObjectClassInitFunc) pgraph_class_init, - (GtsObjectInitFunc) pgraph_init, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (gts_object_class (), &pgraph_info); - } - - return klass; -} - -static void match_neighbor (GtsGNode * n, gpointer * data) -{ - if (!GTS_OBJECT (n)->reserved) { - GtsGraph * g = data[0]; - GSList ** list = data[1]; - GSList * i = GTS_SLIST_CONTAINER (n)->items; - gfloat wmax = - G_MAXFLOAT; - GtsGEdge * emax = NULL; - - while (i) { - GtsGNode * n1 = GTS_GNODE_NEIGHBOR (n, i->data); - if (!GTS_OBJECT (n1)->reserved && - gts_gedge_weight (i->data) > wmax && - gts_containee_is_contained (GTS_CONTAINEE (n1), GTS_CONTAINER (g))) { - emax = i->data; - wmax = gts_gedge_weight (emax); - } - i = i->next; - } - if (emax) { - GtsGNode * n1 = GTS_GNODE_NEIGHBOR (n, emax); - - GTS_OBJECT (n1)->reserved = n; - GTS_OBJECT (n)->reserved = n1; - *list = g_slist_prepend (*list, emax); - } - } -} - -static GSList * maximal_matching (GtsGraph * g) -{ - GSList * list = NULL; - gpointer data[2]; - - data[0] = g; - data[1] = &list; - gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) match_neighbor, data); - gts_container_foreach (GTS_CONTAINER (g), - (GtsFunc) gts_object_reset_reserved, - NULL); - - return list; -} - -/** - * gts_pgraph_new: - * @klass: a #GtsPGraphClass. - * @g: a #GtsGraph. - * @split_class: a #GtsGNodeSplitClass. - * @node_class: a #GtsWGNodeClass. - * @edge_class: a #GtsWGEdgeClass. - * @min: the minimum number of nodes. - * - * Creates a new multilevel approximation of graph @g. At each level a - * maximal matching is created using the Heavy Edge Matching (HEM) - * technique of Karypis and Kumar (1997). The newly created nodes are - * of type @node_class and their weight is set to the sum of the - * weights of their children. The newly created edges are of type - * @edge_class and their weight is set to the sum of the weight of the - * collapsed edges. The last level is reached when the maximal - * matching obtained would lead to a graph with less than @min nodes. - * - * Returns: the new #GtsPGraph containing the multilevel - * representation of @g. - */ -GtsPGraph * gts_pgraph_new (GtsPGraphClass * klass, - GtsGraph * g, - GtsGNodeSplitClass * split_class, - GtsWGNodeClass * node_class, - GtsWGEdgeClass * edge_class, - guint min) -{ - GtsPGraph * pg; - GSList * matching; - - g_return_val_if_fail (klass != NULL, NULL); - g_return_val_if_fail (g != NULL, NULL); - g_return_val_if_fail (split_class != NULL, NULL); - g_return_val_if_fail (node_class != NULL, NULL); - g_return_val_if_fail (edge_class != NULL, NULL); - - pg = GTS_PGRAPH (gts_object_new (GTS_OBJECT_CLASS (klass))); - pg->g = g; - pg->split_class = split_class; - pg->edge_class = edge_class; - - while (gts_container_size (GTS_CONTAINER (g)) > min && - (matching = maximal_matching (g))) { - GSList * i = matching; - guint size = gts_container_size (GTS_CONTAINER (g)); - - g_array_append_val (pg->levels, size); - - while (i && gts_container_size (GTS_CONTAINER (g)) > min) { - GtsGEdge * e = i->data; - GtsGNode * n = GTS_GNODE (gts_wgnode_new (node_class, - gts_gnode_weight (e->n1) + - gts_gnode_weight (e->n2))); - GtsGNodeSplit * ns = gts_gnode_split_new (split_class, n, - GTS_OBJECT (e->n1), - GTS_OBJECT (e->n2)); - gts_gnode_split_collapse (ns, g, edge_class); - g_ptr_array_add (pg->split, ns); - i = i->next; - } - g_slist_free (matching); - } - - pg->pos = pg->split->len; - pg->min = gts_container_size (GTS_CONTAINER (g)); - pg->level = pg->levels->len; - - return pg; -} - -/** - * gts_pgraph_add_node: - * @pg: a #GtsPGraph. - * - * Adds one node to the multilevel graph @pg by expanding the next - * available #GtsGNodeSplit. - * - * Returns: the expanded #GtsGNodeSplit or #NULL if all the - * #GtsGNodeSplit have already been expanded. - */ -GtsGNodeSplit * gts_pgraph_add_node (GtsPGraph * pg) -{ - GtsGNodeSplit * ns; - - g_return_val_if_fail (pg != NULL, NULL); - - if (pg->pos == 0) - return NULL; - - ns = g_ptr_array_index (pg->split, --pg->pos); - gts_gnode_split_expand (ns, pg->g); - - return ns; -} - -/** - * gts_pgraph_remove_node: - * @pg: a #GtsPGraph. - * - * Removes one node from the multilevel graph @pg by collapsing the - * first available #GtsGNodeSplit. - * - * Returns: the collapsed #GtsGNodeSplit or %NULL if all the - * #GtsGNodeSplit have already been collapsed. - */ -GtsGNodeSplit * gts_pgraph_remove_node (GtsPGraph * pg) -{ - GtsGNodeSplit * ns; - - g_return_val_if_fail (pg != NULL, NULL); - - if (pg->pos == pg->split->len) - return NULL; - - ns = g_ptr_array_index (pg->split, pg->pos++); - gts_gnode_split_collapse (ns, pg->g, pg->edge_class); - - return ns; -} - -/** - * gts_pgraph_max_node_number: - * @pg: a #GtsPGraph. - * - * Returns: the maximum number of nodes of @pg i.e. the number of - * nodes if all the #GtsGNodeSplit were expanded. - */ -guint gts_pgraph_max_node_number (GtsPGraph * pg) -{ - g_return_val_if_fail (pg != NULL, 0); - - return pg->min + pg->split->len; -} - -/** - * gts_pgraph_min_node_number: - * @pg: a #GtsPGraph. - * - * Returns: the minimum number of nodes of @pg i.e. the number of - * nodes if all the #GtsGNodeSplit were collapsed. - */ -guint gts_pgraph_min_node_number (GtsPGraph * pg) -{ - g_return_val_if_fail (pg != NULL, 0); - - return pg->min; -} - -/** - * gts_pgraph_set_node_number: - * @pg: a #GtsPGraph. - * @n: a number of nodes. - * - * Performs the required number of collapses or expansions to set the - * number of nodes of @pg to @n. - */ -void gts_pgraph_set_node_number (GtsPGraph * pg, guint n) -{ - g_return_if_fail (pg != NULL); - - n = pg->min + pg->split->len - n; - while (pg->pos > n && gts_pgraph_add_node (pg)) - ; - while (pg->pos < n && gts_pgraph_remove_node (pg)) - ; -} - -/** - * gts_pgraph_get_node_number: - * @pg: a #GtsPGraph. - * - * Returns: the current number of nodes of @pg. - */ -guint gts_pgraph_get_node_number (GtsPGraph * pg) -{ - g_return_val_if_fail (pg != NULL, 0); - - return pg->min + pg->split->len - pg->pos; -} - -/** - * gts_pgraph_down: - * @pg: a #GtsPGraph. - * @func: a #GtsFunc or %NULL. - * @data: user data to pass to @func. - * - * Performs the required number of expansions to go from the current - * level to the level immediately below. - * - * If @func is not %NULL, it is called after each #GtsGNodeSplit has - * been expanded. - * - * Returns: %FALSE if it is not possible to go down one level, %TRUE - * otherwise. - */ -gboolean gts_pgraph_down (GtsPGraph * pg, - GtsFunc func, - gpointer data) -{ - guint size; - - g_return_val_if_fail (pg != NULL, FALSE); - - if (pg->level == 0) - return FALSE; - - size = g_array_index (pg->levels, guint, --(pg->level)); - while (gts_container_size (GTS_CONTAINER (pg->g)) < size) { - GtsGNodeSplit * ns = gts_pgraph_add_node (pg); - - g_assert (ns); - if (func) - (* func) (ns, data); - } - return TRUE; -} - Index: trunk/src/3rd/gts/named.c =================================================================== --- trunk/src/3rd/gts/named.c (revision 1064) +++ trunk/src/3rd/gts/named.c (nonexistent) @@ -1,188 +0,0 @@ -/* GTS - Library for the manipulation of triangulated surfaces - * Copyright (C) 1999 Stéphane Popinet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include -#include "gts.h" - -static void nvertex_read (GtsObject ** po, GtsFile * fp) -{ - if ((*po)->klass->parent_class->read) - (* (*po)->klass->parent_class->read) (po, fp); - - if (fp->type != '\n' && fp->type != GTS_ERROR) { - strncpy (GTS_NVERTEX (*po)->name, fp->token->str, GTS_NAME_LENGTH); - gts_file_next_token (fp); - } -} - -static void nvertex_write (GtsObject * o, FILE * fptr) -{ - GtsNVertex * nv = GTS_NVERTEX (o); - - (* o->klass->parent_class->write) (o, fptr); - if (nv->name[0] != '\0') - fprintf (fptr, " %s", nv->name); -} - -static void nvertex_class_init (GtsNVertexClass * klass) -{ - GTS_OBJECT_CLASS (klass)->read = nvertex_read; - GTS_OBJECT_CLASS (klass)->write = nvertex_write; -} - -static void nvertex_init (GtsNVertex * nvertex) -{ - nvertex->name[0] = '\0'; -} - -/** - * gts_nvertex_class: - * - * Returns: the #GtsNVertexClass. - */ -GtsNVertexClass * gts_nvertex_class (void) -{ - static GtsNVertexClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo nvertex_info = { - "GtsNVertex", - sizeof (GtsNVertex), - sizeof (GtsNVertexClass), - (GtsObjectClassInitFunc) nvertex_class_init, - (GtsObjectInitFunc) nvertex_init, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_vertex_class ()), - &nvertex_info); - } - - return klass; -} - -static void nedge_read (GtsObject ** po, GtsFile * fp) -{ - if (fp->type != GTS_STRING) { - gts_file_error (fp, "expecting a string (name)"); - return; - } - strncpy (GTS_NEDGE (*po)->name, fp->token->str, GTS_NAME_LENGTH); - gts_file_next_token (fp); -} - -static void nedge_write (GtsObject * o, FILE * fptr) -{ - GtsNEdge * ne = GTS_NEDGE (o); - - if (ne->name[0] != '\0') - fprintf (fptr, " %s", ne->name); -} - -static void nedge_class_init (GtsNEdgeClass * klass) -{ - GTS_OBJECT_CLASS (klass)->read = nedge_read; - GTS_OBJECT_CLASS (klass)->write = nedge_write; -} - -static void nedge_init (GtsNEdge * nedge) -{ - nedge->name[0] = '\0'; -} - -/** - * gts_nedge_class: - * - * Returns: the #GtsNEdgeClass. - */ -GtsNEdgeClass * gts_nedge_class (void) -{ - static GtsNEdgeClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo nedge_info = { - "GtsNEdge", - sizeof (GtsNEdge), - sizeof (GtsNEdgeClass), - (GtsObjectClassInitFunc) nedge_class_init, - (GtsObjectInitFunc) nedge_init, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_edge_class ()), - &nedge_info); - } - - return klass; -} - -static void nface_read (GtsObject ** po, GtsFile * fp) -{ - if (fp->type != GTS_STRING) { - gts_file_error (fp, "expecting a string (name)"); - return; - } - strncpy (GTS_NFACE (*po)->name, fp->token->str, GTS_NAME_LENGTH); - gts_file_next_token (fp); -} - -static void nface_write (GtsObject * o, FILE * fptr) -{ - GtsNFace * nf = GTS_NFACE (o); - - if (nf->name[0] != '\0') - fprintf (fptr, " %s", GTS_NFACE (o)->name); -} - -static void nface_class_init (GtsNFaceClass * klass) -{ - GTS_OBJECT_CLASS (klass)->read = nface_read; - GTS_OBJECT_CLASS (klass)->write = nface_write; -} - -static void nface_init (GtsNFace * nface) -{ - nface->name[0] = '\0'; -} - -/** - * gts_nface_class: - * - * Returns: the #GtsNFaceClass. - */ -GtsNFaceClass * gts_nface_class (void) -{ - static GtsNFaceClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo nface_info = { - "GtsNFace", - sizeof (GtsNFace), - sizeof (GtsNFaceClass), - (GtsObjectClassInitFunc) nface_class_init, - (GtsObjectInitFunc) nface_init, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_face_class ()), - &nface_info); - } - - return klass; -} Index: trunk/src/3rd/gts/vopt.c =================================================================== --- trunk/src/3rd/gts/vopt.c (revision 1064) +++ trunk/src/3rd/gts/vopt.c (nonexistent) @@ -1,521 +0,0 @@ -/* GTS - Library for the manipulation of triangulated surfaces - * Copyright (C) 1999 Stéphane Popinet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include "gts.h" - -/* #define DEBUG_VOPT */ - -/* compute the normal (nx, ny, nz) as the cross-product of the first two - oriented edges and the norm nt = |t| as (v1xv2).v3 */ -static void triangle_normal (GtsTriangle * t, - gdouble * nx, - gdouble * ny, - gdouble * nz, - gdouble * nt) -{ - GtsPoint * p1, * p2 = NULL, * p3 = NULL; - gdouble x1, y1, z1, x2, y2, z2; - - g_return_if_fail (t != NULL); - - p1 = GTS_POINT (GTS_SEGMENT (t->e1)->v1); - if (GTS_SEGMENT (t->e1)->v1 == GTS_SEGMENT (t->e2)->v1) { - p2 = GTS_POINT (GTS_SEGMENT (t->e2)->v2); - p3 = GTS_POINT (GTS_SEGMENT (t->e1)->v2); - } - else if (GTS_SEGMENT (t->e1)->v2 == GTS_SEGMENT (t->e2)->v2) { - p2 = GTS_POINT (GTS_SEGMENT (t->e1)->v2); - p3 = GTS_POINT (GTS_SEGMENT (t->e2)->v1); - } - else if (GTS_SEGMENT (t->e1)->v1 == GTS_SEGMENT (t->e2)->v2) { - p2 = GTS_POINT (GTS_SEGMENT (t->e2)->v1); - p3 = GTS_POINT (GTS_SEGMENT (t->e1)->v2); - } - else if (GTS_SEGMENT (t->e1)->v2 == GTS_SEGMENT (t->e2)->v1) { - p2 = GTS_POINT (GTS_SEGMENT (t->e1)->v2); - p3 = GTS_POINT (GTS_SEGMENT (t->e2)->v2); - } - else - g_assert_not_reached (); - - x1 = p2->x - p1->x; - y1 = p2->y - p1->y; - z1 = p2->z - p1->z; - - x2 = p3->x - p1->x; - y2 = p3->y - p1->y; - z2 = p3->z - p1->z; - - *nt = ((p1->y*p2->z - p1->z*p2->y)*p3->x + - (p1->z*p2->x - p1->x*p2->z)*p3->y + - (p1->x*p2->y - p1->y*p2->x)*p3->z); - *nx = y1*z2 - z1*y2; - *ny = z1*x2 - x1*z2; - *nz = x1*y2 - y1*x2; -} - -static void boundary_preservation (GtsEdge * edge, - GtsFace * f, - GtsVector e1, GtsVector e2, - GtsMatrix * H, GtsVector c) -{ - GtsTriangle * t = GTS_TRIANGLE (f); - GtsEdge * edge2; - GtsVertex * v1 = GTS_SEGMENT (edge)->v1, * v2 = GTS_SEGMENT (edge)->v2; - GtsPoint * p1, * p2; - GtsVector e, e3; - - /* find orientation of segment */ - edge2 = edge == t->e1 ? t->e2 : edge == t->e2 ? t->e3 : t->e1; - if (v2 != GTS_SEGMENT (edge2)->v1 && v2 != GTS_SEGMENT (edge2)->v2) { - v2 = v1; v1 = GTS_SEGMENT (edge)->v2; - } - p1 = GTS_POINT (v1); - p2 = GTS_POINT (v2); - - e[0] = p2->x - p1->x; - e[1] = p2->y - p1->y; - e[2] = p2->z - p1->z; - - e1[0] += e[0]; - e1[1] += e[1]; - e1[2] += e[2]; - - e3[0] = p2->y*p1->z - p2->z*p1->y; - e3[1] = p2->z*p1->x - p2->x*p1->z; - e3[2] = p2->x*p1->y - p2->y*p1->x; - - e2[0] += e3[0]; - e2[1] += e3[1]; - e2[2] += e3[2]; - - H[0][0] += e[1]*e[1] + e[2]*e[2]; - H[0][1] -= e[0]*e[1]; - H[0][2] -= e[0]*e[2]; - H[1][0] = H[0][1]; - H[1][1] += e[0]*e[0] + e[2]*e[2]; - H[1][2] -= e[1]*e[2]; - H[2][0] = H[0][2]; - H[2][1] = H[1][2]; - H[2][2] += e[0]*e[0] + e[1]*e[1]; - - c[0] += e[1]*e3[2] - e[2]*e3[1]; - c[1] += e[2]*e3[0] - e[0]*e3[2]; - c[2] += e[0]*e3[1] - e[1]*e3[0]; -} - -static gdouble boundary_cost (GtsEdge * edge, - GtsFace * f, - GtsVertex * v) -{ - GtsTriangle * t = GTS_TRIANGLE (f); - GtsEdge * edge2; - GtsVertex * v1 = GTS_SEGMENT (edge)->v1, * v2 = GTS_SEGMENT (edge)->v2; - GtsPoint * p1, * p2; - GtsVector e; - GtsPoint * p = GTS_POINT (v); - - /* find orientation of segment */ - edge2 = edge == t->e1 ? t->e2 : edge == t->e2 ? t->e3 : t->e1; - if (v2 != GTS_SEGMENT (edge2)->v1 && v2 != GTS_SEGMENT (edge2)->v2) { - v2 = v1; v1 = GTS_SEGMENT (edge)->v2; - } - p1 = GTS_POINT (v1); - p2 = GTS_POINT (v2); - - e[0] = (p2->y - p1->y)*(p->z - p2->z) - (p2->z - p1->z)*(p->y - p2->y); - e[1] = (p2->z - p1->z)*(p->x - p2->x) - (p2->x - p1->x)*(p->z - p2->z); - e[2] = (p2->x - p1->x)*(p->y - p2->y) - (p2->y - p1->y)*(p->x - p2->x); - - return e[0]*e[0] + e[1]*e[1] + e[2]*e[2]; -} - -static gdouble edge_boundary_cost (GtsEdge * e, GtsVertex * v) -{ - gdouble cost = 0.; - GSList * i; - - i = GTS_SEGMENT (e)->v1->segments; - while (i) { - GtsFace * f; - if (GTS_IS_EDGE (i->data) && - (f = gts_edge_is_boundary (i->data, NULL))) - cost += boundary_cost (i->data, f, v); - i = i->next; - } - i = GTS_SEGMENT (e)->v2->segments; - while (i) { - GtsFace * f; - if (i->data != e && - GTS_IS_EDGE (i->data) && - (f = gts_edge_is_boundary (i->data, NULL))) - cost += boundary_cost (i->data, f, v); - i = i->next; - } - - return cost/4.; -} - -static gdouble edge_volume_cost (GtsEdge * e, GtsVertex * v) -{ - GSList * i, * triangles; - gdouble n1, n2, n3, nt; - gdouble cost = 0.0, a; - - triangles = gts_vertex_triangles (GTS_SEGMENT (e)->v1, NULL); - triangles = gts_vertex_triangles (GTS_SEGMENT (e)->v2, triangles); - - i = triangles; - while (i) { - if (GTS_IS_FACE (i->data)) { - triangle_normal (i->data, &n1, &n2, &n3, &nt); - a = GTS_POINT (v)->x*n1 + - GTS_POINT (v)->y*n2 + - GTS_POINT (v)->z*n3 - nt; - cost += a*a; - } - i = i->next; - } - g_slist_free (triangles); - - return cost/36.; -} - -static gdouble edge_shape_cost (GtsEdge * e, GtsVertex * v) -{ - GSList * list, * i; - GtsVertex - * v1 = GTS_SEGMENT (e)->v1, - * v2 = GTS_SEGMENT (e)->v2; - gdouble cost = 0.; - - list = gts_vertex_neighbors (v1, NULL, NULL); - list = gts_vertex_neighbors (v2, list, NULL); - i = list; - while (i) { - GtsPoint * p = i->data; - if (p != GTS_POINT (v1) && p != GTS_POINT (v2)) - cost += gts_point_distance2 (p, GTS_POINT (v)); - i = i->next; - } - g_slist_free (list); - - return cost; -} - -/** - * gts_volume_optimized_vertex: - * @edge: a #GtsEdge. - * @klass: a #GtsVertexClass to be used for the new vertex. - * @params: a #GtsVolumeOptimizedParms. - * - * Returns: a #GtsVertex which can be used to replace @edge for an - * edge collapse operation. The position of the vertex is optimized in - * order to minimize the changes in area and volume for the surface - * using @edge. The volume enclosed by the surface is locally - * preserved. For more details see "Fast and memory efficient - * polygonal simplification" (1998) and "Evaluation of memoryless - * simplification" (1999) by Lindstrom and Turk. - */ -GtsVertex * gts_volume_optimized_vertex (GtsEdge * edge, - GtsVertexClass * klass, - GtsVolumeOptimizedParams * params) -{ - GSList * triangles, * i; - gdouble sn1 = 0., sn2 = 0., sn3 = 0.; - gdouble sn11 = 0., sn22 = 0., sn33 = 0.; - gdouble sn12 = 0., sn13 = 0., sn23 = 0.; - gdouble st = 0., stn1 = 0., stn2 = 0., stn3 = 0.; - gdouble n1, n2, n3, nt; - GtsMatrix * A, * Ai; - GtsVector A1, b; - GtsVector e1 = {0., 0., 0.}, e2 = {0., 0., 0.}; - GtsMatrix * Hb; - GtsVector cb = {0., 0., 0.}; - GtsVertex * v; - GtsVertex * v1, * v2; - guint n = 0, nb = 0; -#ifdef DEBUG_VOPT - guint nold = 0; -#endif - - g_return_val_if_fail (edge != NULL, NULL); - g_return_val_if_fail (klass != NULL, NULL); - g_return_val_if_fail (params != NULL, NULL); - - A = gts_matrix_zero (NULL); - Hb = gts_matrix_zero (NULL); - v1 = GTS_SEGMENT (edge)->v1; - v2 = GTS_SEGMENT (edge)->v2; - - /* boundary preservation */ - i = v1->segments; - while (i) { - GtsEdge * edge1 = i->data; - GtsFace * f; - if (GTS_IS_EDGE (edge1) && - (f = gts_edge_is_boundary (edge1, NULL))) { - boundary_preservation (edge1, f, e1, e2, Hb, cb); - nb++; - } - i = i->next; - } - i = v2->segments; - while (i) { - GtsEdge * edge1 = i->data; - GtsFace * f; - if (edge1 != edge && - GTS_IS_EDGE (edge1) && - (f = gts_edge_is_boundary (edge1, NULL))) { - boundary_preservation (edge1, f, e1, e2, Hb, cb); - nb++; - } - i = i->next; - } - if (nb > 0) { - GtsMatrix * H = gts_matrix_new ( - e1[2]*e1[2] + e1[1]*e1[1], - e1[0]*e1[1], - e1[0]*e1[2], 0., - - e1[0]*e1[1], e1[2]*e1[2] + e1[0]*e1[0], - e1[1]*e1[2], 0., - - e1[0]*e1[2], - e1[1]*e1[2], e1[1]*e1[1] + e1[0]*e1[0], 0., - 0., 0., 0., 0.); - GtsVector c; - - c[0] = e1[1]*e2[2] - e1[2]*e2[1]; - c[1] = e1[2]*e2[0] - e1[0]*e2[2]; - c[2] = e1[0]*e2[1] - e1[1]*e2[0]; - n = gts_matrix_quadratic_optimization (A, b, n, H, c); - gts_matrix_destroy (H); - } - - g_assert (n <= 2); - -#ifdef DEBUG_VOPT - if (n != nold) { - fprintf (stderr, "--- boundary preservation ---\n"); - gts_matrix_print (A, stderr); - gts_vector_print (b, stderr); - nold = n; - } -#endif - - /* volume preservation */ - triangles = gts_vertex_triangles (v1, NULL); - triangles = gts_vertex_triangles (v2, triangles); - - i = triangles; - while (i) { - if (GTS_IS_FACE (i->data)) { - triangle_normal (i->data, &n1, &n2, &n3, &nt); - sn1 += n1; sn2 += n2; sn3 += n3; - sn11 += n1*n1; sn22 += n2*n2; sn33 += n3*n3; - sn12 += n1*n2; sn13 += n1*n3; sn23 += n2*n3; - st += nt; stn1 += nt*n1; stn2 += nt*n2; stn3 += nt*n3; - } - i = i->next; - } - g_slist_free (triangles); - - A1[0] = sn1; A1[1] = sn2; A1[2] = sn3; - n = gts_matrix_compatible_row (A, b, n, A1, st); - -#ifdef DEBUG_VOPT - if (n != nold) { - fprintf (stderr, "--- volume preservation ---\n"); - gts_matrix_print (A, stderr); - gts_vector_print (b, stderr); - nold = n; - } -#endif - -#if 1 /* Weighted average of volume and boundary optimization */ - if (n < 3) { - /* volume optimization and boundary optimization */ - GtsMatrix * H = gts_matrix_new (sn11, sn12, sn13, 0., - sn12, sn22, sn23, 0., - sn13, sn23, sn33, 0., - 0., 0., 0., 0.); - GtsVector c; - gdouble le = 9.*params->boundary_weight* - gts_point_distance2 (GTS_POINT (v1), - GTS_POINT (v2)); - guint i, j; - - c[0] = - stn1; c[1] = - stn2; c[2] = - stn3; - if (nb > 0) - for (i = 0; i < 3; i++) { - for (j = 0; j < 3; j++) - H[i][j] = params->volume_weight*H[i][j] + le*Hb[i][j]; - c[i] = params->volume_weight*c[i] + le*cb[i]; - } - n = gts_matrix_quadratic_optimization (A, b, n, H, c); - gts_matrix_destroy (H); - } - -#ifdef DEBUG_VOPT - if (n != nold) { - fprintf (stderr, "--- volume and boundary optimization ---\n"); - gts_matrix_print (A, stderr); - gts_vector_print (b, stderr); - nold = n; - } -#endif - - if (n < 3) { - /* triangle shape optimization */ - gdouble nv = 0.0; - GtsMatrix * H; - GtsVector c = {0., 0., 0.}; - GSList * list, * i; - - list = gts_vertex_neighbors (v1, NULL, NULL); - list = gts_vertex_neighbors (v2, list, NULL); - - i = list; - while (i) { - GtsPoint * p1 = i->data; - if (p1 != GTS_POINT (v1) && p1 != GTS_POINT (v2)) { - nv += 1.0; - c[0] -= p1->x; - c[1] -= p1->y; - c[2] -= p1->z; - } - i = i->next; - } - g_slist_free (list); - - H = gts_matrix_new (nv, 0., 0., 0., - 0., nv, 0., 0., - 0., 0., nv, 0., - 0., 0., 0., 0.); - n = gts_matrix_quadratic_optimization (A, b, n, H, c); - gts_matrix_destroy (H); - } - -#ifdef DEBUG_VOPT - if (n != nold) { - fprintf (stderr, "--- triangle shape optimization ---\n"); - gts_matrix_print (A, stderr); - gts_vector_print (b, stderr); - nold = n; - } -#endif -#else /* Weighted average of volume, boundary and shape optimization */ - if (n < 3) { - /* volume optimization, boundary and shape optimization */ - GtsMatrix * H; - GtsVector c; - gdouble l2 = gts_point_distance2 (GTS_POINT (v1), - GTS_POINT (v2)); - gdouble wv = params->volume_weight/32.; - gdouble wb = params->boundary_weight/4.*l2; - gdouble ws = params->shape_weight*l2*l2; - - gdouble nv = 0.0; - GtsVector cs = {0., 0., 0.}; - GSList * list, * i; - - list = gts_vertex_neighbors (v1, NULL, NULL); - list = gts_vertex_neighbors (v2, list, NULL); - - i = list; - while (i) { - GtsPoint * p1 = i->data; - if (p1 != GTS_POINT (v1) && p1 != GTS_POINT (v2)) { - nv += 1.0; - cs[0] -= p1->x; - cs[1] -= p1->y; - cs[2] -= p1->z; - } - i = i->next; - } - g_slist_free (list); - - H = gts_matrix_new (wv*sn11 + wb*Hb[0][0] + ws*nv, - wv*sn12 + wb*Hb[0][1], - wv*sn13 + wb*Hb[0][2], - wv*sn12 + wb*Hb[1][0], - wv*sn22 + wb*Hb[1][1] + ws*nv, - wv*sn23 + wb*Hb[1][2], - wv*sn13 + wb*Hb[2][0], - wv*sn23 + wb*Hb[2][1], - wv*sn33 + wb*Hb[2][2] + ws*nv); - - c[0] = - wv*stn1 + wb*cb[0] + ws*cs[0]; - c[1] = - wv*stn2 + wb*cb[1] + ws*cs[1]; - c[2] = - wv*stn3 + wb*cb[2] + ws*cs[2]; - - n = gts_matrix_quadratic_optimization (A, b, n, H, c); - gts_matrix_destroy (H); - } - -#ifdef DEBUG_VOPT - if (n != nold) { - fprintf (stderr, "--- volume, boundary and shape optimization ---\n"); - gts_matrix_print (A, stderr); - gts_vector_print (b, stderr); - nold = n; - } -#endif -#endif /* Weighted average of volume, boundary and shape optimization */ - - g_assert (n == 3); - g_assert ((Ai = gts_matrix3_inverse (A))); - - v = gts_vertex_new (klass, - Ai[0][0]*b[0] + Ai[0][1]*b[1] + Ai[0][2]*b[2], - Ai[1][0]*b[0] + Ai[1][1]*b[1] + Ai[1][2]*b[2], - Ai[2][0]*b[0] + Ai[2][1]*b[1] + Ai[2][2]*b[2]); - - gts_matrix_destroy (A); - gts_matrix_destroy (Ai); - gts_matrix_destroy (Hb); - - return v; -} - -/** - * gts_volume_optimized_cost: - * @e: a #GtsEdge. - * @params: a #GtsVolumeOptimizedParams. - * - * Returns: the cost for the collapse of @e as minimized by the function - * gts_volume_optimized_vertex(). - */ -gdouble gts_volume_optimized_cost (GtsEdge * e, - GtsVolumeOptimizedParams * params) -{ - GtsVertex * v; - gdouble cost; - gdouble length2; - - g_return_val_if_fail (e != NULL, G_MAXDOUBLE); - g_return_val_if_fail (params != NULL, G_MAXDOUBLE); - - v = gts_volume_optimized_vertex (e, gts_vertex_class (), params); - - length2 = gts_point_distance2 (GTS_POINT (GTS_SEGMENT (e)->v1), - GTS_POINT (GTS_SEGMENT (e)->v2)); - cost = - params->volume_weight*edge_volume_cost (e, v) + - params->boundary_weight*length2*edge_boundary_cost (e, v) + - params->shape_weight*length2*length2*edge_shape_cost (e, v); - gts_object_destroy (GTS_OBJECT (v)); - - return cost; -} Index: trunk/src/3rd/gts/point.c =================================================================== --- trunk/src/3rd/gts/point.c (revision 1064) +++ trunk/src/3rd/gts/point.c (nonexistent) @@ -1,986 +0,0 @@ -/* GTS - Library for the manipulation of triangulated surfaces - * Copyright (C) 1999 Stéphane Popinet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include -#include -#include "gts.h" -#include "gts-private.h" -#include "predicates.h" - -static void point_read (GtsObject ** o, GtsFile * f) -{ - GtsPoint * p = GTS_POINT (*o); - - if (GTS_POINT_CLASS ((*o)->klass)->binary) { - if (gts_file_read (f, &(p->x), sizeof (gdouble), 1) != 1) { - gts_file_error (f, "expecting a binary number (x coordinate)"); - return; - } - if (gts_file_read (f, &(p->y), sizeof (gdouble), 1) != 1) { - gts_file_error (f, "expecting a binary number (y coordinate)"); - return; - } - if (gts_file_read (f, &(p->z), sizeof (gdouble), 1) != 1) { - gts_file_error (f, "expecting a binary number (z coordinate)"); - return; - } - } - else { - if (f->type != GTS_INT && f->type != GTS_FLOAT) { - gts_file_error (f, "expecting a number (x coordinate)"); - return; - } - p->x = atof (f->token->str); - - gts_file_next_token (f); - if (f->type != GTS_INT && f->type != GTS_FLOAT) { - gts_file_error (f, "expecting a number (y coordinate)"); - return; - } - p->y = atof (f->token->str); - - gts_file_next_token (f); - if (f->type != GTS_INT && f->type != GTS_FLOAT) { - gts_file_error (f, "expecting a number (z coordinate)"); - return; - } - p->z = atof (f->token->str); - - gts_file_next_token (f); - } -} - -static void point_write (GtsObject * o, FILE * fptr) -{ - GtsPoint * p = GTS_POINT (o); - - if (GTS_POINT_CLASS ((o)->klass)->binary) { - fwrite (&(p->x), sizeof (gdouble), 1, fptr); - fwrite (&(p->y), sizeof (gdouble), 1, fptr); - fwrite (&(p->z), sizeof (gdouble), 1, fptr); - } - else - fprintf (fptr, "%.10g %.10g %.10g", p->x, p->y, p->z); -} - -static void point_class_init (GtsObjectClass * klass) -{ - klass->read = point_read; - klass->write = point_write; -} - -/** - * gts_point_class: - * - * Returns: the #GtsPointClass. - */ -GtsPointClass * gts_point_class (void) -{ - static GtsPointClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo point_info = { - "GtsPoint", - sizeof (GtsPoint), - sizeof (GtsPointClass), - (GtsObjectClassInitFunc) point_class_init, - (GtsObjectInitFunc) NULL, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (gts_object_class (), - &point_info); - } - - return klass; -} - -/** - * gts_point_new: - * @klass: a #GtsPointClass. - * @x: the x-coordinate. - * @y: the y-coordinate. - * @z: the z-coordinate. - * - * Returns: a new #GtsPoint. - */ -GtsPoint * gts_point_new (GtsPointClass * klass, - gdouble x, gdouble y, gdouble z) -{ - GtsPoint * p; - - p = GTS_POINT (gts_object_new (GTS_OBJECT_CLASS (klass))); - p->x = x; - p->y = y; - p->z = z; - - return p; -} - -/** - * gts_point_set: - * @p: a #GtsPoint. - * @x: the x-coordinate. - * @y: the y-coordinate. - * @z: the z-coordinate. - * - * Sets the coordinates of @p. - */ -void gts_point_set (GtsPoint * p, gdouble x, gdouble y, gdouble z) -{ - g_return_if_fail (p != NULL); - - p->x = x; - p->y = y; - p->z = z; -} - -/** - * gts_point_distance: - * @p1: a #GtsPoint. - * @p2: another #GtsPoint. - * - * Returns: the Euclidean distance between @p1 and @p2. - */ -gdouble gts_point_distance (GtsPoint * p1, GtsPoint * p2) -{ - g_return_val_if_fail (p1 != NULL && p2 != NULL, 0.0); - - return sqrt ((p1->x - p2->x)*(p1->x - p2->x) + - (p1->y - p2->y)*(p1->y - p2->y) + - (p1->z - p2->z)*(p1->z - p2->z)); -} - -/** - * gts_point_distance2: - * @p1: a #GtsPoint. - * @p2: another #GtsPoint. - * - * Returns: the square of the Euclidean distance between @p1 and @p2. - */ -gdouble gts_point_distance2 (GtsPoint * p1, GtsPoint * p2) -{ - g_return_val_if_fail (p1 != NULL && p2 != NULL, 0.0); - - return - (p1->x - p2->x)*(p1->x - p2->x) + - (p1->y - p2->y)*(p1->y - p2->y) + - (p1->z - p2->z)*(p1->z - p2->z); -} - -/** - * gts_point_orientation_3d: - * @p1: a #GtsPoint. - * @p2: a #GtsPoint. - * @p3: a #GtsPoint. - * @p4: a #GtsPoint. - * - * Checks if @p4 lies above, below or on the plane passing through the - * points @p1, @p2 and @p3. Below is defined so that @p1, @p2 and @p3 - * appear in counterclockwise order when viewed from above the - * plane. The returned value is an approximation of six times the - * signed volume of the tetrahedron defined by the four points. This - * function uses adaptive floating point arithmetic and is - * consequently geometrically robust. - * - * Returns: a positive value if @p4 lies below, a negative value if - * @p4 lies above the plane, zero if the four points are coplanar. - */ -gdouble gts_point_orientation_3d (GtsPoint * p1, - GtsPoint * p2, - GtsPoint * p3, - GtsPoint * p4) -{ - g_return_val_if_fail (p1 != NULL && p2 != NULL && - p3 != NULL && p4 != NULL, 0.0); - return orient3d ((gdouble *) &p1->x, - (gdouble *) &p2->x, - (gdouble *) &p3->x, - (gdouble *) &p4->x); -} - -/** - * gts_point_is_in_triangle: - * @p: a #GtsPoint. - * @t: a #GtsTriangle. - * - * Tests if the planar projection (x, y) of @p is inside, outside or - * on the boundary of the planar projection of @t. This function is - * geometrically robust. - * - * Returns: %GTS_IN if @p is inside @t, %GTS_ON if @p is on the boundary of - * @t, %GTS_OUT otherwise. - */ -GtsIntersect gts_point_is_in_triangle (GtsPoint * p, GtsTriangle * t) -{ - GtsVertex * v1, * v2, * v3; - gdouble d1, d2, d3; - - g_return_val_if_fail (p != NULL && t != NULL, FALSE); - - gts_triangle_vertices (t, &v1, &v2, &v3); - - d1 = gts_point_orientation (GTS_POINT (v1), GTS_POINT (v2), p); - if (d1 < 0.0) - return GTS_OUT; - d2 = gts_point_orientation (GTS_POINT (v2), GTS_POINT (v3), p); - if (d2 < 0.0) - return GTS_OUT; - d3 = gts_point_orientation (GTS_POINT (v3), GTS_POINT (v1), p); - if (d3 < 0.0) - return GTS_OUT; - if (d1 == 0.0 || d2 == 0.0 || d3 == 0.0) - return GTS_ON; - return GTS_IN; -} - -/** - * gts_point_in_triangle_circle: - * @p: a #GtsPoint. - * @t: a #GtsTriangle. - * - * Tests if the planar projection (x, y) of @p is inside or outside - * the circumcircle of the planar projection of @t. This function is - * geometrically robust. - * - * Returns: a positive number if @p lies inside, - * a negative number if @p lies outside and zero if @p lies on - * the circumcircle of @t. - */ -gdouble gts_point_in_triangle_circle (GtsPoint * p, GtsTriangle * t) -{ - GtsPoint * p1, * p2, * p3; - - g_return_val_if_fail (p != NULL && t != NULL, 0.0); - - gts_triangle_vertices (t, - (GtsVertex **) &p1, - (GtsVertex **) &p2, - (GtsVertex **) &p3); - - return incircle ((gdouble *) &p1->x, - (gdouble *) &p2->x, - (gdouble *) &p3->x, - (gdouble *) &p->x); -} - -/** - * gts_point_in_circle: - * @p: a #GtsPoint. - * @p1: a #GtsPoint. - * @p2: a #GtsPoint. - * @p3: a #GtsPoint. - * - * Tests if the planar projection (x, y) of @p is inside or outside the - * circle defined by the planar projection of @p1, @p2 and @p3. - * - * Returns: a positive number if @p lies inside, - * a negative number if @p lies outside and zero if @p lies on - * the circle. - */ -gdouble gts_point_in_circle (GtsPoint * p, - GtsPoint * p1, GtsPoint * p2, GtsPoint * p3) -{ - g_return_val_if_fail (p != NULL && p1 != NULL && p2 != NULL && p3 != NULL, - 0.0); - - return incircle ((gdouble *) &p1->x, - (gdouble *) &p2->x, - (gdouble *) &p3->x, - (gdouble *) &p->x); -} - -/** - * gts_point_in_sphere: - * @p: a #GtsPoint. - * @p1: a #GtsPoint. - * @p2: a #GtsPoint. - * @p3: a #GtsPoint. - * @p4: a #GtsPoint. - * - * Tests if @p is inside or outside the sphere defined by @p1, @p2, - * @p3 and @p4. - * - * Returns: a positive number if @p lies inside, - * a negative number if @p lies outside and zero if @p lies on - * the sphere. - */ -gdouble gts_point_in_sphere (GtsPoint * p, - GtsPoint * p1, GtsPoint * p2, GtsPoint * p3, GtsPoint * p4) -{ - g_return_val_if_fail (p != NULL && p1 != NULL && p2 != NULL && p3 != NULL && p4 != NULL, - 0.0); - - return insphere ((gdouble *) &p1->x, - (gdouble *) &p2->x, - (gdouble *) &p3->x, - (gdouble *) &p4->x, - (gdouble *) &p->x); -} - -/** - * gts_point_segment_distance2: - * @p: a #GtsPoint. - * @s: a #GtsSegment. - * - * Returns: the square of the minimun Euclidean distance between @p and @s. - */ -gdouble gts_point_segment_distance2 (GtsPoint * p, GtsSegment * s) -{ - gdouble t, ns2, x, y, z; - GtsPoint * p1, * p2; - - g_return_val_if_fail (p != NULL, 0.0); - g_return_val_if_fail (s != NULL, 0.0); - - p1 = GTS_POINT (s->v1); - p2 = GTS_POINT (s->v2); - ns2 = gts_point_distance2 (p1, p2); - if (ns2 == 0.0) - return gts_point_distance2 (p, p1); - t = ((p2->x - p1->x)*(p->x - p1->x) + - (p2->y - p1->y)*(p->y - p1->y) + - (p2->z - p1->z)*(p->z - p1->z))/ns2; - if (t > 1.0) - return gts_point_distance2 (p, p2); - if (t < 0.0) - return gts_point_distance2 (p, p1); - x = (1. - t)*p1->x + t*p2->x - p->x; - y = (1. - t)*p1->y + t*p2->y - p->y; - z = (1. - t)*p1->z + t*p2->z - p->z; - return x*x + y*y + z*z; -} - -/** - * gts_point_segment_distance: - * @p: a #GtsPoint. - * @s: a #GtsSegment. - * - * Returns: the minimun Euclidean distance between @p and @s. - */ -gdouble gts_point_segment_distance (GtsPoint * p, GtsSegment * s) -{ - g_return_val_if_fail (p != NULL, 0.0); - g_return_val_if_fail (s != NULL, 0.0); - - return sqrt (gts_point_segment_distance2 (p, s)); -} - -/** - * gts_point_segment_closest: - * @p: a #GtsPoint. - * @s: a #GtsSegment. - * @closest: a #GtsPoint. - * - * Set the coordinates of @closest to the coordinates of the point belonging - * to @s closest to @p. - */ -void gts_point_segment_closest (GtsPoint * p, - GtsSegment * s, - GtsPoint * closest) -{ - gdouble t, ns2; - GtsPoint * p1, * p2; - - g_return_if_fail (p != NULL); - g_return_if_fail (s != NULL); - g_return_if_fail (closest != NULL); - - p1 = GTS_POINT (s->v1); - p2 = GTS_POINT (s->v2); - ns2 = gts_point_distance2 (p1, p2); - - if (ns2 == 0.0) { - gts_point_set (closest, p1->x, p1->y, p1->z); - return; - } - - t = ((p2->x - p1->x)*(p->x - p1->x) + - (p2->y - p1->y)*(p->y - p1->y) + - (p2->z - p1->z)*(p->z - p1->z))/ns2; - - if (t > 1.0) - gts_point_set (closest, p2->x, p2->y, p2->z); - else if (t < 0.0) - gts_point_set (closest, p1->x, p1->y, p1->z); - else - gts_point_set (closest, - (1. - t)*p1->x + t*p2->x, - (1. - t)*p1->y + t*p2->y, - (1. - t)*p1->z + t*p2->z); -} - -/** - * gts_point_triangle_distance2: - * @p: a #GtsPoint. - * @t: a #GtsTriangle. - * - * Returns: the square of the minimun Euclidean distance between @p and @t. - */ -gdouble gts_point_triangle_distance2 (GtsPoint * p, GtsTriangle * t) -{ - GtsPoint * p1, * p2, * p3; - GtsEdge * e1, * e2, * e3; - GtsVector p1p2, p1p3, pp1; - gdouble A, B, C, D, E, det; - gdouble t1, t2; - gdouble x, y, z; - - g_return_val_if_fail (p != NULL, 0.0); - g_return_val_if_fail (t != NULL, 0.0); - - gts_triangle_vertices_edges (t, NULL, - (GtsVertex **) &p1, - (GtsVertex **) &p2, - (GtsVertex **) &p3, - &e1, &e2, &e3); - - gts_vector_init (p1p2, p1, p2); - gts_vector_init (p1p3, p1, p3); - gts_vector_init (pp1, p, p1); - - B = gts_vector_scalar (p1p3, p1p2); - E = gts_vector_scalar (p1p2, p1p2); - C = gts_vector_scalar (p1p3, p1p3); - - det = B*B - E*C; - if (det == 0.) { /* p1p2 and p1p3 are colinear */ - gdouble d1 = gts_point_segment_distance2 (p, GTS_SEGMENT (e1)); - gdouble d2 = gts_point_segment_distance2 (p, GTS_SEGMENT (e3)); - if (d1 < d2) - return d1; - return d2; - } - - A = gts_vector_scalar (p1p3, pp1); - D = gts_vector_scalar (p1p2, pp1); - - t1 = (D*C - A*B)/det; - t2 = (A*E - D*B)/det; - - if (t1 < 0.) - return gts_point_segment_distance2 (p, GTS_SEGMENT (e3)); - if (t2 < 0.) - return gts_point_segment_distance2 (p, GTS_SEGMENT (e1)); - if (t1 + t2 > 1.) - return gts_point_segment_distance2 (p, GTS_SEGMENT (e2)); - - x = pp1[0] + t1*p1p2[0] + t2*p1p3[0]; - y = pp1[1] + t1*p1p2[1] + t2*p1p3[1]; - z = pp1[2] + t1*p1p2[2] + t2*p1p3[2]; - - return x*x + y*y + z*z; -} - -/** - * gts_point_triangle_distance: - * @p: a #GtsPoint. - * @t: a #GtsTriangle. - * - * Returns: the minimun Euclidean distance between @p and @t. - */ -gdouble gts_point_triangle_distance (GtsPoint * p, GtsTriangle * t) -{ - g_return_val_if_fail (p != NULL, 0.0); - g_return_val_if_fail (t != NULL, 0.0); - - return sqrt (gts_point_triangle_distance2 (p, t)); -} - -/** - * gts_point_triangle_closest: - * @p: a #GtsPoint. - * @t: a #GtsTriangle. - * @closest: a #GtsPoint. - * - * Set the coordinates of @closest to those of the point belonging to @t and - * closest to @p. - */ -void gts_point_triangle_closest (GtsPoint * p, - GtsTriangle * t, - GtsPoint * closest) -{ - GtsPoint * p1, * p2, * p3; - GtsEdge * e1, * e2, * e3; - GtsVector p1p2, p1p3, pp1; - gdouble A, B, C, D, E, det; - gdouble t1, t2; - - g_return_if_fail (p != NULL); - g_return_if_fail (t != NULL); - g_return_if_fail (closest != NULL); - - gts_triangle_vertices_edges (t, NULL, - (GtsVertex **) &p1, - (GtsVertex **) &p2, - (GtsVertex **) &p3, - &e1, &e2, &e3); - - gts_vector_init (p1p2, p1, p2); - gts_vector_init (p1p3, p1, p3); - gts_vector_init (pp1, p, p1); - - B = gts_vector_scalar (p1p3, p1p2); - E = gts_vector_scalar (p1p2, p1p2); - C = gts_vector_scalar (p1p3, p1p3); - - det = B*B - E*C; - if (det == 0.) { /* p1p2 and p1p3 are colinear */ - GtsPoint * cp = - GTS_POINT (gts_object_new (GTS_OBJECT_CLASS (gts_point_class ()))); - gts_point_segment_closest (p, GTS_SEGMENT (e1), cp); - gts_point_segment_closest (p, GTS_SEGMENT (e3), closest); - - if (gts_point_distance2 (cp, p) < gts_point_distance2 (closest, p)) - gts_point_set (closest, cp->x, cp->y, cp->z); - gts_object_destroy (GTS_OBJECT (cp)); - return; - } - - A = gts_vector_scalar (p1p3, pp1); - D = gts_vector_scalar (p1p2, pp1); - - t1 = (D*C - A*B)/det; - t2 = (A*E - D*B)/det; - - if (t1 < 0.) - gts_point_segment_closest (p, GTS_SEGMENT (e3), closest); - else if (t2 < 0.) - gts_point_segment_closest (p, GTS_SEGMENT (e1), closest); - else if (t1 + t2 > 1.) - gts_point_segment_closest (p, GTS_SEGMENT (e2), closest); - else - gts_point_set (closest, - p1->x + t1*p1p2[0] + t2*p1p3[0], - p1->y + t1*p1p2[1] + t2*p1p3[1], - p1->z + t1*p1p2[2] + t2*p1p3[2]); -} - -/** - * gts_segment_triangle_intersection: - * @s: a #GtsSegment. - * @t: a #GtsTriangle. - * @boundary: if %TRUE, the boundary of @t is taken into account. - * @klass: a #GtsPointClass to be used for the new point. - * - * Checks if @s intersects @t. If this is the case, creates a new - * point pi intersection of @s with @t. - * - * This function is geometrically robust in the sense that it will not - * return a point if @s and @t do not intersect and will return a - * point if @s and @t do intersect. However, the point coordinates are - * subject to round-off errors. - * - * Note that this function will not return any point if @s is contained in - * the plane defined by @t. - * - * Returns: a summit of @t (if @boundary is set to %TRUE), one of the endpoints - * of @s or a new #GtsPoint, intersection of @s with @t or %NULL if @s - * and @t don't intersect. - */ -GtsPoint * gts_segment_triangle_intersection (GtsSegment * s, - GtsTriangle * t, - gboolean boundary, - GtsPointClass * klass) -{ - GtsPoint * A, * B, * C, * D, * E, * I; - gdouble ABCE, ABCD, ADCE, ABDE, BCDE; - gdouble c; - - g_return_val_if_fail (s != NULL, NULL); - g_return_val_if_fail (t != NULL, NULL); - g_return_val_if_fail (klass != NULL, NULL); - - A = GTS_POINT (GTS_SEGMENT (t->e1)->v1); - B = GTS_POINT (GTS_SEGMENT (t->e1)->v2); - C = GTS_POINT (gts_triangle_vertex (t)); - D = GTS_POINT (s->v1); - E = GTS_POINT (s->v2); - - ABCE = gts_point_orientation_3d (A, B, C, E); - ABCD = gts_point_orientation_3d (A, B, C, D); - if (ABCE < 0.0 || ABCD > 0.0) { - GtsPoint * tmpp; - gdouble tmp; - tmpp = E; E = D; D = tmpp; - tmp = ABCE; ABCE = ABCD; ABCD = tmp; - } - if (ABCE < 0.0 || ABCD > 0.0) - return NULL; - ADCE = gts_point_orientation_3d (A, D, C, E); - if ((boundary && ADCE < 0.) || (!boundary && ADCE <= 0.)) - return NULL; - ABDE = gts_point_orientation_3d (A, B, D, E); - if ((boundary && ABDE < 0.) || (!boundary && ABDE <= 0.)) - return NULL; - BCDE = gts_point_orientation_3d (B, C, D, E); - if ((boundary && BCDE < 0.) || (!boundary && BCDE <= 0.)) - return NULL; - if (ABCE == 0.0) { - if (ABCD == 0.0) - /* s is contained in the plane defined by t*/ - return NULL; - return E; - } - if (ABCD == 0.0) - return D; - if (boundary) { /* corners of @t */ - if (ABDE == 0.) { - if (ADCE == 0.) - return A; - if (BCDE == 0.) - return B; - } - else if (BCDE == 0. && ADCE == 0.) - return C; - } - c = ABCE/(ABCE - ABCD); - I = GTS_POINT (gts_object_new (GTS_OBJECT_CLASS (klass))); - gts_point_set (I, - E->x + c*(D->x - E->x), - E->y + c*(D->y - E->y), - E->z + c*(D->z - E->z)); - return I; -} - -/** - * gts_point_transform: - * @p: a #GtsPoint. - * @m: the #GtsMatrix representing the transformation to - * apply to the coordinates of @p. - * - * Transform the coordinates of @p according to @m. (p[] becomes m[][].p[]). - */ -void gts_point_transform (GtsPoint * p, GtsMatrix * m) -{ - gdouble x, y, z; - g_return_if_fail (p != NULL && m != NULL); - x = m[0][0]*p->x + m[0][1]*p->y + m[0][2]*p->z + m[0][3]; - y = m[1][0]*p->x + m[1][1]*p->y + m[1][2]*p->z + m[1][3]; - z = m[2][0]*p->x + m[2][1]*p->y + m[2][2]*p->z + m[2][3]; - p->x = x; p->y = y; p->z = z; -} - -/** - * gts_point_orientation: - * @p1: a #GtsPoint. - * @p2: a #GtsPoint. - * @p3: a #GtsPoint. - * - * Checks for orientation of the projection of three points on the - * (x,y) plane. The result is also an approximation of twice the - * signed area of the triangle defined by the three points. This - * function uses adaptive floating point arithmetic and is - * consequently geometrically robust. - * - * Returns: a positive value if @p1, @p2 and @p3 appear in - * counterclockwise order, a negative value if they appear in - * clockwise order and zero if they are colinear. - */ -gdouble gts_point_orientation (GtsPoint * p1, GtsPoint * p2, GtsPoint * p3) -{ - g_return_val_if_fail (p1 != NULL && p2 != NULL && p3 != NULL, 0.0); - - return orient2d ((gdouble *) &p1->x, - (gdouble *) &p2->x, - (gdouble *) &p3->x); -} - -static gboolean ray_intersects_triangle (GtsPoint * D, GtsPoint * E, - GtsTriangle * t) -{ - GtsPoint * A, * B, * C; - gint ABCE, ABCD, ADCE, ABDE, BCDE; - - gts_triangle_vertices (t, (GtsVertex **) &A, - (GtsVertex **) &B, - (GtsVertex **) &C); - - ABCE = gts_point_orientation_3d_sos (A, B, C, E); - ABCD = gts_point_orientation_3d_sos (A, B, C, D); - if (ABCE < 0 || ABCD > 0) { - GtsPoint * tmpp; - gint tmp; - - tmpp = E; E = D; D = tmpp; - tmp = ABCE; ABCE = ABCD; ABCD = tmp; - } - if (ABCE < 0 || ABCD > 0) - return FALSE; - ADCE = gts_point_orientation_3d_sos (A, D, C, E); - if (ADCE < 0) - return FALSE; - ABDE = gts_point_orientation_3d_sos (A, B, D, E); - if (ABDE < 0) - return FALSE; - BCDE = gts_point_orientation_3d_sos (B, C, D, E); - if (BCDE < 0) - return FALSE; - return TRUE; -} - -/** - * gts_point_is_inside_surface: - * @p: a #GtsPoint. - * @tree: a bounding box tree of the faces of a closed, orientable - * surface (see gts_bb_tree_surface()). - * @is_open: %TRUE if the surface defined by @tree is "open" i.e. its volume - * is negative, %FALSE otherwise. - * - * Returns: %TRUE if @p is inside the surface defined by @tree, %FALSE - * otherwise. - */ -gboolean gts_point_is_inside_surface (GtsPoint * p, - GNode * tree, - gboolean is_open) -{ - GSList * list, * i; - guint nc = 0; - GtsPoint * p1; - GtsBBox * bb; - - g_return_val_if_fail (p != NULL, FALSE); - g_return_val_if_fail (tree != NULL, FALSE); - - bb = tree->data; - p1 = gts_point_new (gts_point_class (), bb->x2 + fabs (bb->x2)/10., p->y, p->z); - i = list = gts_bb_tree_stabbed (tree, p); - while (i) { - GtsTriangle * t = GTS_TRIANGLE (GTS_BBOX (i->data)->bounded); - - if (ray_intersects_triangle (p, p1, t)) - nc++; - i = i->next; - } - g_slist_free (list); - gts_object_destroy (GTS_OBJECT (p1)); - - return is_open ? (nc % 2 == 0) : (nc % 2 != 0); -} - -#define SIGN(x) ((x) > 0. ? 1 : -1) -#define ORIENT1D(a,b) ((a) > (b) ? 1 : (a) < (b) ? -1 : 0) - -static gint sortp (gpointer * p, guint n) -{ - gint sign = 1; - guint i, j; - - for (i = 0; i < n - 1; i++) - for (j = 0; j < n - 1 - i; j++) - if (GPOINTER_TO_UINT (p[j+1]) < GPOINTER_TO_UINT (p[j])) { - gpointer tmp = p[j]; - - p[j] = p[j+1]; - p[j+1] = tmp; - sign = - sign; - } - return sign; -} - -/** - * gts_point_orientation_3d_sos: - * @p1: a #GtsPoint. - * @p2: a #GtsPoint. - * @p3: a #GtsPoint. - * @p4: a #GtsPoint. - * - * Checks if @p4 lies above or below the plane passing through the - * points @p1, @p2 and @p3. Below is defined so that @p1, @p2 and @p3 - * appear in counterclockwise order when viewed from above the - * plane. This function uses adaptive floating point arithmetic and is - * consequently geometrically robust. - * - * Simulation of Simplicity (SoS) is used to break ties when the - * orientation is degenerate (i.e. @p4 lies on the plane defined by - * @p1, @p2 and @p3). - * - * Returns: +1 if @p4 lies below, -1 if @p4 lies above the plane. - */ -gint gts_point_orientation_3d_sos (GtsPoint * p1, - GtsPoint * p2, - GtsPoint * p3, - GtsPoint * p4) -{ - gdouble o; - - g_return_val_if_fail (p1 != NULL && p2 != NULL && - p3 != NULL && p4 != NULL, 0); - - o = orient3d ((gdouble *) &p1->x, - (gdouble *) &p2->x, - (gdouble *) &p3->x, - (gdouble *) &p4->x); - if (o != 0.) - return SIGN (o); - else { - GtsPoint * p[4]; - gdouble a[2], b[2], c[2]; - gint sign; - - p[0] = p1; p[1] = p2; p[2] = p3; p[3] = p4; - sign = sortp ((gpointer *) p, 4); - - /* epsilon^1/8 */ - a[0] = p[1]->x; a[1] = p[1]->y; - b[0] = p[2]->x; b[1] = p[2]->y; - c[0] = p[3]->x; c[1] = p[3]->y; - o = orient2d (a, b, c); - if (o != 0.) - return SIGN (o)*sign; - - /* epsilon^1/4 */ - a[0] = p[1]->x; a[1] = p[1]->z; - b[0] = p[2]->x; b[1] = p[2]->z; - c[0] = p[3]->x; c[1] = p[3]->z; - o = orient2d (a, b, c); - if (o != 0.) - return - SIGN (o)*sign; - - /* epsilon^1/2 */ - a[0] = p[1]->y; a[1] = p[1]->z; - b[0] = p[2]->y; b[1] = p[2]->z; - c[0] = p[3]->y; c[1] = p[3]->z; - o = orient2d (a, b, c); - if (o != 0.) - return SIGN (o)*sign; - - /* epsilon */ - a[0] = p[0]->x; a[1] = p[0]->y; - b[0] = p[2]->x; b[1] = p[2]->y; - c[0] = p[3]->x; c[1] = p[3]->y; - o = orient2d (a, b, c); - if (o != 0.) - return - SIGN (o)*sign; - - /* epsilon^5/4 */ - o = ORIENT1D (p[2]->x, p[3]->x); - if (o != 0.) - return SIGN (o)*sign; - - /* epsilon^3/2 */ - o = ORIENT1D (p[2]->y, p[3]->y); - if (o != 0.) - return - SIGN (o)*sign; - - /* epsilon^2 */ - a[0] = p[0]->x; a[1] = p[0]->z; - b[0] = p[2]->x; b[1] = p[2]->z; - c[0] = p[3]->x; c[1] = p[3]->z; - o = orient2d (a, b, c); - if (o != 0.) - return SIGN (o)*sign; - - /* epsilon^5/2 */ - o = ORIENT1D (p[2]->z, p[3]->z); - if (o != 0.) - return SIGN (o)*sign; - - /* epsilon^4 */ - a[0] = p[0]->y; a[1] = p[0]->z; - b[0] = p[2]->y; b[1] = p[2]->z; - c[0] = p[3]->y; c[1] = p[3]->z; - o = orient2d (a, b, c); - if (o != 0.) - return - SIGN (o)*sign; - - /* epsilon^8 */ - a[0] = p[0]->x; a[1] = p[0]->y; - b[0] = p[1]->x; b[1] = p[1]->y; - c[0] = p[3]->x; c[1] = p[3]->y; - o = orient2d (a, b, c); - if (o != 0.) - return SIGN (o)*sign; - - /* epsilon^33/4 */ - o = ORIENT1D (p[1]->x, p[3]->x); - if (o != 0.) - return - SIGN (o)*sign; - - /* epsilon^17/2 */ - o = ORIENT1D (p[1]->y, p[3]->y); - if (o != 0.) - return SIGN (o)*sign; - - /* epsilon^10 */ - o = ORIENT1D (p[0]->x, p[3]->x); - if (o != 0.) - return SIGN (o)*sign; - - /* epsilon^21/2 */ - return sign; - } -} - -/** - * gts_point_orientation_sos: - * @p1: a #GtsPoint. - * @p2: a #GtsPoint. - * @p3: a #GtsPoint. - * - * Checks for orientation of the projection of three points on the - * (x,y) plane. - * - * Simulation of Simplicity (SoS) is used to break ties when the - * orientation is degenerate (i.e. @p3 lies on the line defined by - * @p1 and @p2). - * - * Returns: a positive value if @p1, @p2 and @p3 appear in - * counterclockwise order or a negative value if they appear in - * clockwise order. - */ -gint gts_point_orientation_sos (GtsPoint * p1, - GtsPoint * p2, - GtsPoint * p3) -{ - gdouble o; - - g_return_val_if_fail (p1 != NULL && p2 != NULL && p3 != NULL, 0); - - o = orient2d ((gdouble *) &p1->x, - (gdouble *) &p2->x, - (gdouble *) &p3->x); - if (o != 0.) - return SIGN (o); - else { - GtsPoint * p[3]; - gint sign; - - p[0] = p1; p[1] = p2; p[2] = p3; - sign = sortp ((gpointer *) p, 3); - - /* epsilon^1/4 */ - o = ORIENT1D (p[1]->x, p[2]->x); - if (o != 0.) - return - SIGN (o)*sign; - - /* epsilon^1/2 */ - o = ORIENT1D (p[1]->y, p[2]->y); - if (o != 0.) - return SIGN (o)*sign; - - /* epsilon */ - o = ORIENT1D (p[0]->x, p[2]->x); - if (o != 0.) - return SIGN (o)*sign; - - /* epsilon^3/2 */ - return sign; - } -} Index: trunk/src/3rd/gts/isotetra.c =================================================================== --- trunk/src/3rd/gts/isotetra.c (revision 1064) +++ trunk/src/3rd/gts/isotetra.c (nonexistent) @@ -1,840 +0,0 @@ -/* GTS-Library conform marching tetrahedra algorithm - * Copyright (C) 2002 Gert Wollny - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include -#include -#include -#ifdef NATIVE_WIN32 -# include -# define M_SQRT2 1.41421356237309504880 -#endif /* NATIVE_WIN32 */ - -typedef struct { - gint nx, ny; - gdouble ** data; -} slice_t; - -typedef struct { - gint x, y, z; - gboolean mid; - gdouble d; -} tetra_vertex_t; - -/* this helper is a lookup table for vertices */ -typedef struct { - gint nx, ny; - GtsVertex ** vtop, ** vmid, **vbot; -} helper_t ; - -typedef struct { - GHashTable * vbot, * vtop; -} helper_bcl ; - - -static helper_t * init_helper (int nx, int ny) -{ - gint nxy = 4*nx*ny; - helper_t *retval = g_malloc0 (sizeof (helper_t)); - - retval->nx = nx; - retval->ny = ny; - retval->vtop = g_malloc0 (sizeof (GtsVertex *)*nxy); - retval->vmid = g_malloc0 (sizeof (GtsVertex *)*nxy); - retval->vbot = g_malloc0 (sizeof (GtsVertex *)*nxy); - return retval; -} - -static helper_bcl * init_helper_bcl (void) -{ - helper_bcl *retval = g_malloc0 (sizeof (helper_bcl)); - - retval->vtop = g_hash_table_new (g_str_hash, g_str_equal); - retval->vbot = g_hash_table_new (g_str_hash, g_str_equal); - return retval; -} - -static void free_helper (helper_t * h) -{ - g_free (h->vtop); - g_free (h->vmid); - g_free (h->vbot); - g_free (h); -} - -static void free_helper_bcl (helper_bcl * h) -{ - g_hash_table_destroy (h->vtop); - g_hash_table_destroy (h->vbot); - g_free (h); -} - -/* move the vertices in the bottom slice to the top, and clear the - other slices in the lookup tables */ -static void helper_advance (helper_t * h) -{ - GtsVertex ** help = h->vbot; - h->vbot = h->vtop; - h->vtop = help; - - memset (h->vmid, 0, 4*sizeof(GtsVertex *) * h->nx * h->ny); - memset (h->vbot, 0, 4*sizeof(GtsVertex *) * h->nx * h->ny); -} - -static void helper_advance_bcl (helper_bcl * h) -{ - GHashTable * help = g_hash_table_new (g_str_hash, g_str_equal); - - g_hash_table_destroy (h->vbot); - h->vbot = h->vtop; - h->vtop = help; -} - -/* find the zero-crossing of line through v1 and v2 and return the - corresponding GtsVertex */ -static GtsVertex * get_vertex (gint mz, - const tetra_vertex_t * v1, - const tetra_vertex_t * v2, - helper_t * help, - GtsCartesianGrid * g, - GtsVertexClass * klass) -{ - GtsVertex ** vertex; - gint x, y, index, idx2, z; - gdouble dx, dy, dz, d; - - g_assert (v1->d - v2->d != 0.); - - dx = dy = dz = 0.0; - d = v1->d/(v1->d - v2->d); - - index = 0; - - if (v1->x != v2->x) { - index |= 1; - dx = d; - } - - if (v1->y != v2->y) { - index |= 2; - dy = d; - } - - if (v1->z != v2->z) { - dz = d; - } - - x = v1->x; - if (v1->x > v2->x) { x = v2->x; dx = 1.0 - dx; } - - y = v1->y; - if (v1->y > v2->y) { y = v2->y; dy = 1.0 - dy;} - - z = v1->z; - if (v1->z > v2->z) { z = v2->z; dz = 1.0 - dz;} - - idx2 = 4 * ( x + y * help->nx ) + index; - - if (v1->z == v2->z) - vertex = (mz == z) ? &help->vtop[idx2] : &help->vbot[idx2]; - else - vertex = &help->vmid[idx2]; - - if (mz != z && dz != 0.0) { - fprintf(stderr, "%f \n", dz); - } - - /* if vertex is not yet created, do it now */ - if (!*vertex) - *vertex = gts_vertex_new (klass, - g->dx * ( x + dx) + g->x, - g->dy * ( y + dy) + g->y, - g->dz * ( z + dz) + g->z); - - return *vertex; -} - -static GtsVertex * get_vertex_bcl (gint mz, - const tetra_vertex_t * v1, - const tetra_vertex_t * v2, - helper_bcl * help, - GtsCartesianGrid * g, - GtsVertexClass * klass) -{ - GtsVertex * v; - GHashTable * table; - gchar * s1, * s2, * hash; - gdouble x1, x2, y1, y2, z1, z2, d; - - g_assert (v1->d - v2->d != 0.); - - /* first find correct hash table */ - if ((v1->z > mz) && (v2->z > mz)) - table = help->vtop; - else - table = help->vbot; - - d = v1->d / (v1->d - v2->d); - - /* sort vertices */ - s1 = g_strdup_printf ("%d %d %d %d", v1->x, v1->y, v1->z, v1->mid); - s2 = g_strdup_printf ("%d %d %d %d", v2->x, v2->y, v2->z, v2->mid); - - hash = (d == 0.0) ? g_strdup (s1) : - (d == 1.0) ? g_strdup (s2) : - (strcmp (s1, s2) < 0) ? g_strjoin (" ", s1, s2, NULL) : - g_strjoin (" ", s2, s1, NULL); - - /* return existing vertex or make a new one */ - v = g_hash_table_lookup (table, hash); - if (!v){ - - x1 = g->dx * (v1->x + (v1->mid / 2.0)) + g->x; - x2 = g->dx * (v2->x + (v2->mid / 2.0)) + g->x; - y1 = g->dy * (v1->y + (v1->mid / 2.0)) + g->y; - y2 = g->dy * (v2->y + (v2->mid / 2.0)) + g->y; - z1 = g->dz * (v1->z + (v1->mid / 2.0)) + g->z; - z2 = g->dz * (v2->z + (v2->mid / 2.0)) + g->z; - - v = gts_vertex_new (klass, x1 * (1.0 - d) + d * x2, - y1 * (1.0 - d) + d * y2, - z1 * (1.0 - d) + d * z2); - - g_hash_table_insert (table, g_strdup(hash), v); - } - g_free (s1); - g_free (s2); - g_free (hash); - - return v; -} - -/* create an edge connecting the zero crossings of lines through a - pair of vertices, or return an existing one */ -static GtsEdge * get_edge (GtsVertex * v1, GtsVertex * v2, - GtsEdgeClass * klass) -{ - GtsSegment *s; - GtsEdge *edge; - - g_assert (v1); - g_assert (v2); - - s = gts_vertices_are_connected (v1, v2); - - if (GTS_IS_EDGE (s)) - edge = GTS_EDGE(s); - else - edge = gts_edge_new (klass, v1, v2); - return edge; -} - -static void add_face (GtsSurface * surface, - const tetra_vertex_t * a1, const tetra_vertex_t * a2, - const tetra_vertex_t * b1, const tetra_vertex_t * b2, - const tetra_vertex_t * c1, const tetra_vertex_t * c2, - gint rev, helper_t * help, - gint z, GtsCartesianGrid * g) -{ - GtsFace * t; - GtsEdge * e1, * e2, * e3; - GtsVertex * v1 = get_vertex (z, a1, a2, help, g, surface->vertex_class); - GtsVertex * v2 = get_vertex (z, b1, b2, help, g, surface->vertex_class); - GtsVertex * v3 = get_vertex (z, c1, c2, help, g, surface->vertex_class); - - g_assert (v1 != v2); - g_assert (v2 != v3); - g_assert (v1 != v3); - - if (!rev) { - e1 = get_edge (v1, v2, surface->edge_class); - e2 = get_edge (v2, v3, surface->edge_class); - e3 = get_edge (v1, v3, surface->edge_class); - } else { - e1 = get_edge (v1, v3, surface->edge_class); - e2 = get_edge (v2, v3, surface->edge_class); - e3 = get_edge (v1, v2, surface->edge_class); - } - - t = gts_face_new (surface->face_class, e1, e2, e3); - gts_surface_add_face (surface, t); -} - -static void add_face_bcl (GtsSurface * surface, - const tetra_vertex_t * a1, - const tetra_vertex_t * a2, - const tetra_vertex_t * b1, - const tetra_vertex_t * b2, - const tetra_vertex_t * c1, - const tetra_vertex_t * c2, - gint rev, helper_bcl * help, - gint z, GtsCartesianGrid * g) -{ - GtsFace * t; - GtsEdge * e1, * e2, * e3; - GtsVertex * v1 = get_vertex_bcl (z, a1, a2, help, g, surface->vertex_class); - GtsVertex * v2 = get_vertex_bcl (z, b1, b2, help, g, surface->vertex_class); - GtsVertex * v3 = get_vertex_bcl (z, c1, c2, help, g, surface->vertex_class); - - if (v1 == v2 || v2 == v3 || v1 == v3) - return; - - if (!rev) { - e1 = get_edge (v1, v2, surface->edge_class); - e2 = get_edge (v2, v3, surface->edge_class); - e3 = get_edge (v1, v3, surface->edge_class); - } else { - e1 = get_edge (v1, v3, surface->edge_class); - e2 = get_edge (v2, v3, surface->edge_class); - e3 = get_edge (v1, v2, surface->edge_class); - } - - t = gts_face_new (surface->face_class, e1, e2, e3); - gts_surface_add_face (surface, t); -} - -/* create a new slice of site nx \times ny */ -static slice_t * new_slice (gint nx, gint ny) -{ - gint x; - slice_t * retval = g_malloc (sizeof (slice_t)); - - retval->data = g_malloc (nx*sizeof(gdouble *)); - retval->nx = nx; - retval->ny = ny; - for (x = 0; x < nx; x++) - retval->data[x] = g_malloc (ny*sizeof (gdouble)); - return retval; -} - -/* initialize a slice with inival */ -static void slice_init (slice_t * slice, gdouble inival) -{ - gint x, y; - - g_assert (slice); - - for (x = 0; x < slice->nx; x++) - for (y = 0; y < slice->ny; y++) - slice->data[x][y] = inival; -} - -/* free the memory of a slice */ -static void free_slice (slice_t * slice) -{ - gint x; - - g_return_if_fail (slice != NULL); - - for (x = 0; x < slice->nx; x++) - g_free (slice->data[x]); - g_free (slice->data); - g_free (slice); -} - -static void analyze_tetrahedra (const tetra_vertex_t * a, - const tetra_vertex_t * b, - const tetra_vertex_t * c, - const tetra_vertex_t * d, - gint parity, GtsSurface * surface, - helper_t * help, - gint z, GtsCartesianGrid * g) -{ - gint rev = parity; - gint code = 0; - - if (a->d >= 0.) code |= 1; - if (b->d >= 0.) code |= 2; - if (c->d >= 0.) code |= 4; - if (d->d >= 0.) code |= 8; - - switch (code) { - case 15: - case 0: return; /* all inside or outside */ - - case 14:rev = !parity; - case 1:add_face (surface, a, b, a, d, a, c, rev, help, z, g); - break; - case 13:rev = ! parity; - case 2:add_face (surface, a, b, b, c, b, d, rev, help, z, g); - break; - case 12:rev = !parity; - case 3:add_face (surface, a, d, a, c, b, c, rev, help, z, g); - add_face (surface, a, d, b, c, b, d, rev, help, z, g); - break; - case 11:rev = !parity; - case 4:add_face (surface, a, c, c, d, b, c, rev, help, z, g); - break; - case 10:rev = !parity; - case 5: add_face (surface, a, b, a, d, c, d, rev, help, z, g); - add_face (surface, a, b, c, d, b, c, rev, help, z, g); - break; - case 9:rev = !parity; - case 6:add_face (surface, a, b, a, c, c, d, rev, help, z, g); - add_face (surface, a, b, c, d, b, d, rev, help, z, g); - break; - case 7:rev = !parity; - case 8:add_face (surface, a, d, b, d, c, d, rev, help, z, g); - break; - } -} - -static void analyze_tetrahedra_bcl (const tetra_vertex_t * a, - const tetra_vertex_t * b, - const tetra_vertex_t * c, - const tetra_vertex_t * d, - GtsSurface * surface, - helper_bcl * help, - gint z, GtsCartesianGrid * g) -{ - gint rev = 0; - gint code = 0; - - if (a->d >= 0.) code |= 1; - if (b->d >= 0.) code |= 2; - if (c->d >= 0.) code |= 4; - if (d->d >= 0.) code |= 8; - - switch (code) { - case 15: - case 0: return; /* all inside or outside */ - - case 14:rev = !rev; - case 1:add_face_bcl (surface, a, b, a, d, a, c, rev, help, z, g); - break; - case 13:rev = !rev; - case 2:add_face_bcl (surface, a, b, b, c, b, d, rev, help, z, g); - break; - case 12:rev = !rev; - case 3:add_face_bcl (surface, a, d, a, c, b, c, rev, help, z, g); - add_face_bcl (surface, a, d, b, c, b, d, rev, help, z, g); - break; - case 11:rev = !rev; - case 4:add_face_bcl (surface, a, c, c, d, b, c, rev, help, z, g); - break; - case 10:rev = !rev; - case 5: add_face_bcl (surface, a, b, a, d, c, d, rev, help, z, g); - add_face_bcl (surface, a, b, c, d, b, c, rev, help, z, g); - break; - case 9:rev = !rev; - case 6:add_face_bcl (surface, a, b, a, c, c, d, rev, help, z, g); - add_face_bcl (surface, a, b, c, d, b, d, rev, help, z, g); - break; - case 7:rev = !rev; - case 8:add_face_bcl (surface, a, d, b, d, c, d, rev, help, z, g); - break; - } -} - -static void iso_slice_evaluate (slice_t * s1, slice_t * s2, - GtsCartesianGrid g, - gint z, GtsSurface * surface, helper_t * help) -{ - gint x,y; - tetra_vertex_t v0, v1, v2, v3, v4, v5, v6, v7; - gdouble ** s1p = s1->data; - gdouble ** s2p = s2->data; - - for (y = 0; y < g.ny-1; y++) - for (x = 0; x < g.nx-1; x++) { - gint parity = (((x ^ y) ^ z) & 1); - - v0.x = x ; v0.y = y ; v0.z = z ; v0.mid = FALSE; v0.d = s1p[x ][y ]; - v1.x = x ; v1.y = y+1; v1.z = z ; v1.mid = FALSE; v1.d = s1p[x ][y+1]; - v2.x = x+1; v2.y = y ; v2.z = z ; v2.mid = FALSE; v2.d = s1p[x+1][y ]; - v3.x = x+1; v3.y = y+1; v3.z = z ; v3.mid = FALSE; v3.d = s1p[x+1][y+1]; - v4.x = x ; v4.y = y ; v4.z = z+1; v4.mid = FALSE; v4.d = s2p[x ][y ]; - v5.x = x ; v5.y = y+1; v5.z = z+1; v5.mid = FALSE; v5.d = s2p[x ][y+1]; - v6.x = x+1; v6.y = y ; v6.z = z+1; v6.mid = FALSE; v6.d = s2p[x+1][y ]; - v7.x = x+1; v7.y = y+1; v7.z = z+1; v7.mid = FALSE; v7.d = s2p[x+1][y+1]; - - if (parity == 0) { - analyze_tetrahedra (&v0, &v1, &v2, &v4, parity, surface, help, z, &g); - analyze_tetrahedra (&v7, &v1, &v4, &v2, parity, surface, help, z, &g); - analyze_tetrahedra (&v1, &v7, &v3, &v2, parity, surface, help, z, &g); - analyze_tetrahedra (&v1, &v7, &v4, &v5, parity, surface, help, z, &g); - analyze_tetrahedra (&v2, &v6, &v4, &v7, parity, surface, help, z, &g); - }else{ - analyze_tetrahedra (&v4, &v5, &v6, &v0, parity, surface, help, z, &g); - analyze_tetrahedra (&v3, &v5, &v0, &v6, parity, surface, help, z, &g); - analyze_tetrahedra (&v5, &v3, &v7, &v6, parity, surface, help, z, &g); - analyze_tetrahedra (&v5, &v3, &v0, &v1, parity, surface, help, z, &g); - analyze_tetrahedra (&v6, &v2, &v0, &v3, parity, surface, help, z, &g); - } - } -} - -static void iso_slice_evaluate_bcl (slice_t * s1, slice_t * s2, slice_t * s3, - GtsCartesianGrid g, - gint z, GtsSurface * surface, - helper_bcl * help) -{ - gint x,y; - tetra_vertex_t v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, w0; - gdouble ** s1p = s1->data; - gdouble ** s2p = s2->data; - gdouble ** s3p = s3->data; - - for (y = 0; y < g.ny-2; y++) - for (x = 0; x < g.nx-2; x++) { - v0.x = x ; v0.y = y ; v0.z = z ; v0.mid = TRUE; - v0.d = (s1p[x ][y ] + s2p[x ][y ] + - s1p[x ][y+1] + s2p[x ][y+1] + - s1p[x+1][y ] + s2p[x+1][y ] + - s1p[x+1][y+1] + s2p[x+1][y+1])/8.0; - - v1.x = x+1; v1.y = y ; v1.z = z ; v1.mid = TRUE; - v1.d = (s1p[x+1][y ] + s2p[x+1][y ] + - s1p[x+1][y+1] + s2p[x+1][y+1] + - s1p[x+2][y ] + s2p[x+2][y ] + - s1p[x+2][y+1] + s2p[x+2][y+1])/8.0; - - v2.x = x ; v2.y = y+1; v2.z = z ; v2.mid = TRUE; - v2.d = (s1p[x ][y+1] + s2p[x ][y+1] + - s1p[x ][y+2] + s2p[x ][y+2] + - s1p[x+1][y+1] + s2p[x+1][y+1] + - s1p[x+1][y+2] + s2p[x+1][y+2])/8.0; - - v3.x = x ; v3.y = y ; v3.z = z+1; v3.mid = TRUE; - v3.d = (s2p[x ][y ] + s3p[x ][y ] + - s2p[x ][y+1] + s3p[x ][y+1] + - s2p[x+1][y ] + s3p[x+1][y ] + - s2p[x+1][y+1] + s3p[x+1][y+1])/8.0; - - v4.x = x+1; v4.y = y ; v4.z = z ; v4.mid = FALSE; v4.d = s1p[x+1][y ]; - v5.x = x ; v5.y = y+1; v5.z = z ; v5.mid = FALSE; v5.d = s1p[x ][y+1]; - v6.x = x+1; v6.y = y+1; v6.z = z ; v6.mid = FALSE; v6.d = s1p[x+1][y+1]; - v7.x = x+1; v7.y = y ; v7.z = z+1; v7.mid = FALSE; v7.d = s2p[x+1][y ]; - v8.x = x ; v8.y = y+1; v8.z = z+1; v8.mid = FALSE; v8.d = s2p[x ][y+1]; - v9.x = x+1; v9.y = y+1; v9.z = z+1; v9.mid = FALSE; v9.d = s2p[x+1][y+1]; - w0.x = x ; w0.y = y ; w0.z = z+1; w0.mid = FALSE; w0.d = s2p[x ][y ]; - - analyze_tetrahedra_bcl (&v0, &v9, &v6, &v1, surface, help, z, &g); - analyze_tetrahedra_bcl (&v0, &v6, &v4, &v1, surface, help, z, &g); - analyze_tetrahedra_bcl (&v0, &v4, &v7, &v1, surface, help, z, &g); - analyze_tetrahedra_bcl (&v0, &v7, &v9, &v1, surface, help, z, &g); - analyze_tetrahedra_bcl (&v0, &v5, &v6, &v2, surface, help, z, &g); - analyze_tetrahedra_bcl (&v0, &v6, &v9, &v2, surface, help, z, &g); - analyze_tetrahedra_bcl (&v0, &v9, &v8, &v2, surface, help, z, &g); - analyze_tetrahedra_bcl (&v0, &v8, &v5, &v2, surface, help, z, &g); - analyze_tetrahedra_bcl (&v0, &v8, &v9, &v3, surface, help, z, &g); - analyze_tetrahedra_bcl (&v0, &v9, &v7, &v3, surface, help, z, &g); - analyze_tetrahedra_bcl (&v0, &v7, &w0, &v3, surface, help, z, &g); - analyze_tetrahedra_bcl (&v0, &w0, &v8, &v3, surface, help, z, &g); - } -} - -/* copy src into dest by stripping off the iso value and leave out - the boundary (which should be G_MINDOUBLE) */ -static void copy_to_bounded (slice_t * dest, slice_t * src, - gdouble iso, gdouble fill) -{ - gint x,y; - gdouble * src_ptr; - gdouble * dest_ptr = dest->data[0]; - - g_assert(dest->ny == src->ny + 2); - g_assert(dest->nx == src->nx + 2); - - for (y = 0; y < dest->ny; ++y, ++dest_ptr) - *dest_ptr = fill; - - for (x = 1; x < src->nx - 1; ++x) { - dest_ptr = dest->data[x]; - src_ptr = src->data[x-1]; - *dest_ptr++ = fill; - for (y = 0; y < src->ny; ++y, ++dest_ptr, ++src_ptr) - *dest_ptr = *src_ptr - iso; - *dest_ptr++ = fill; - } - - dest_ptr = dest->data[y]; - - for (y = 0; y < dest->ny; ++y, ++dest_ptr) - *dest_ptr = fill; -} - -static void iso_sub (slice_t * s, gdouble iso) -{ - gint x,y; - - for (x = 0; x < s->nx; ++x) { - gdouble *ptr = s->data[x]; - - for (y = 0; y < s->ny; ++y, ++ptr) - *ptr -= iso; - } -} - - -/** - * gts_isosurface_tetra_bounded: - * @surface: a #GtsSurface. - * @g: a #GtsCartesianGrid. - * @f: a #GtsIsoCartesianFunc. - * @data: user data to be passed to @f. - * @iso: isosurface value. - * - * Adds to @surface new faces defining the isosurface f(x,y,z) = - * @iso. By convention, the normals to the surface are pointing toward - * the positive values of f(x,y,z) - @iso. To ensure a closed object, - * a boundary of G_MINDOUBLE is added around the domain - * - * The user function @f is called successively for each value of the z - * coordinate defined by @g. It must fill the corresponding (x,y) - * plane with the values of the function for which the isosurface is - * to be computed. - */ -void gts_isosurface_tetra_bounded (GtsSurface * surface, - GtsCartesianGrid g, - GtsIsoCartesianFunc f, - gpointer data, - gdouble iso) -{ - slice_t *slice1, *slice2, *transfer_slice; - GtsCartesianGrid g_intern = g; - helper_t *helper; - gint z; - - g_return_if_fail (surface != NULL); - g_return_if_fail (f != NULL); - g_return_if_fail (g.nx > 1); - g_return_if_fail (g.ny > 1); - g_return_if_fail (g.nz > 1); - - /* create the helper slices */ - slice1 = new_slice (g.nx + 2, g.ny + 2); - slice2 = new_slice (g.nx + 2, g.ny + 2); - - /* initialize the first slice as OUTSIDE */ - slice_init (slice1, -1.0); - - /* create a slice of the original image size */ - transfer_slice = new_slice (g.nx, g.ny); - - /* adapt the parameters to our enlarged image */ - g_intern.x -= g.dx; - g_intern.y -= g.dy; - g_intern.z -= g.dz; - g_intern.nx = g.nx + 2; - g_intern.ny = g.ny + 2; - g_intern.nz = g.nz; - - /* create the helper for vertex-lookup */ - helper = init_helper (g_intern.nx, g_intern.ny); - - /* go slicewise through the data */ - z = 0; - while (z < g.nz) { - slice_t * hs; - - /* request slice */ - f (transfer_slice->data, g, z, data); - g.z += g.dz; - - /* copy slice in enlarged image and mark the border as OUTSIDE */ - copy_to_bounded (slice2, transfer_slice, iso, -1.); - - /* triangulate */ - iso_slice_evaluate (slice1, slice2, g_intern, z, surface, helper); - - /* switch the input slices */ - hs = slice1; slice1 = slice2; slice2 = hs; - - /* switch the vertex lookup tables */ - helper_advance(helper); - ++z; - } - - /* initialize the last slice as OUTSIDE */ - slice_init (slice2, - 1.0); - - /* close the object */ - iso_slice_evaluate(slice1, slice2, g_intern, z, surface, helper); - - free_helper (helper); - free_slice (slice1); - free_slice (slice2); - free_slice (transfer_slice); -} - -/** - * gts_isosurface_tetra: - * @surface: a #GtsSurface. - * @g: a #GtsCartesianGrid. - * @f: a #GtsIsoCartesianFunc. - * @data: user data to be passed to @f. - * @iso: isosurface value. - * - * Adds to @surface new faces defining the isosurface f(x,y,z) = - * @iso. By convention, the normals to the surface are pointing toward - * the positive values of f(x,y,z) - @iso. - * - * The user function @f is called successively for each value of the z - * coordinate defined by @g. It must fill the corresponding (x,y) - * plane with the values of the function for which the isosurface is - * to be computed. - */ -void gts_isosurface_tetra (GtsSurface * surface, - GtsCartesianGrid g, - GtsIsoCartesianFunc f, - gpointer data, - gdouble iso) -{ - slice_t *slice1, *slice2; - helper_t *helper; - gint z; - GtsCartesianGrid g_internal; - - g_return_if_fail (surface != NULL); - g_return_if_fail (f != NULL); - g_return_if_fail (g.nx > 1); - g_return_if_fail (g.ny > 1); - g_return_if_fail (g.nz > 1); - - memcpy (&g_internal, &g, sizeof (GtsCartesianGrid)); - - /* create the helper slices */ - slice1 = new_slice (g.nx, g.ny); - slice2 = new_slice (g.nx, g.ny); - - /* create the helper for vertex-lookup */ - helper = init_helper (g.nx, g.ny); - - z = 0; - f (slice1->data, g, z, data); - iso_sub (slice1, iso); - - z++; - g.z += g.dz; - - /* go slicewise through the data */ - while (z < g.nz) { - slice_t * hs; - - /* request slice */ - f (slice2->data, g, z, data); - iso_sub (slice2, iso); - - g.z += g.dz; - - /* triangulate */ - iso_slice_evaluate (slice1, slice2, g_internal, z-1, surface, helper); - - /* switch the input slices */ - hs = slice1; slice1 = slice2; slice2 = hs; - - /* switch the vertex lookup tables */ - helper_advance (helper); - - ++z; - } - - free_helper(helper); - free_slice(slice1); - free_slice(slice2); -} - -/** - * gts_isosurface_tetra_bcl: - * @surface: a #GtsSurface. - * @g: a #GtsCartesianGrid. - * @f: a #GtsIsoCartesianFunc. - * @data: user data to be passed to @f. - * @iso: isosurface value. - * - * Adds to @surface new faces defining the isosurface f(x,y,z) = - * @iso. By convention, the normals to the surface are pointing toward - * the positive values of f(x,y,z) - @iso. - * - * The user function @f is called successively for each value of the z - * coordinate defined by @g. It must fill the corresponding (x,y) - * plane with the values of the function for which the isosurface is - * to be computed. - * - * This version produces the dual "body-centered" faces relative to - * the faces produced by gts_isosurface_tetra(). - */ -void gts_isosurface_tetra_bcl (GtsSurface * surface, - GtsCartesianGrid g, - GtsIsoCartesianFunc f, - gpointer data, - gdouble iso) -{ - slice_t *slice1, *slice2, *slice3; - helper_bcl *helper; - gint z; - GtsCartesianGrid g_internal; - - g_return_if_fail (surface != NULL); - g_return_if_fail (f != NULL); - g_return_if_fail (g.nx > 1); - g_return_if_fail (g.ny > 1); - g_return_if_fail (g.nz > 1); - - memcpy (&g_internal, &g, sizeof (GtsCartesianGrid)); - - /* create the helper slices */ - slice1 = new_slice (g.nx, g.ny); - slice2 = new_slice (g.nx, g.ny); - slice3 = new_slice (g.nx, g.ny); - - /* create the helper for vertex-lookup */ - helper = init_helper_bcl (); - - z = 0; - f (slice1->data, g, z, data); - iso_sub (slice1, iso); - - z++; - g.z += g.dz; - - f (slice2->data, g, z, data); - iso_sub (slice1, iso); - - z++; - g.z += g.dz; - - /* go slicewise through the data */ - while (z < g.nz) { - slice_t * hs; - - /* request slice */ - f (slice3->data, g, z, data); - iso_sub (slice3, iso); - - g.z += g.dz; - - /* triangulate */ - iso_slice_evaluate_bcl (slice1, slice2, slice3, g_internal, z-2, - surface, helper); - - /* switch the input slices */ - hs = slice1; slice1 = slice2; slice2 = slice3; slice3 = hs; - - /* switch the vertex lookup tables */ - helper_advance_bcl (helper); - - ++z; - } - - free_helper_bcl(helper); - free_slice(slice1); - free_slice(slice2); - free_slice(slice3); -} Index: trunk/src/3rd/gts/tribox3.c =================================================================== --- trunk/src/3rd/gts/tribox3.c (revision 1064) +++ trunk/src/3rd/gts/tribox3.c (nonexistent) @@ -1,192 +0,0 @@ -/** - * History: - * 2004-10-27 Stephane Popinet: changed float to double - */ - -/********************************************************/ -/* AABB-triangle overlap test code */ -/* by Tomas Akenine-Möller */ -/* Function: int triBoxOverlap(float boxcenter[3], */ -/* float boxhalfsize[3],float triverts[3][3]); */ -/* History: */ -/* 2001-03-05: released the code in its first version */ -/* 2001-06-18: changed the order of the tests, faster */ -/* */ -/* Acknowledgement: Many thanks to Pierre Terdiman for */ -/* suggestions and discussions on how to optimize code. */ -/* Thanks to David Hunt for finding a ">="-bug! */ -/********************************************************/ -#include -#include - -#define X 0 -#define Y 1 -#define Z 2 - -#define CROSS(dest,v1,v2) \ - dest[0]=v1[1]*v2[2]-v1[2]*v2[1]; \ - dest[1]=v1[2]*v2[0]-v1[0]*v2[2]; \ - dest[2]=v1[0]*v2[1]-v1[1]*v2[0]; - -#define DOT(v1,v2) (v1[0]*v2[0]+v1[1]*v2[1]+v1[2]*v2[2]) - -#define SUB(dest,v1,v2) \ - dest[0]=v1[0]-v2[0]; \ - dest[1]=v1[1]-v2[1]; \ - dest[2]=v1[2]-v2[2]; - -#define FINDMINMAX(x0,x1,x2,min,max) \ - min = max = x0; \ - if(x1max) max=x1;\ - if(x2max) max=x2; - -int planeBoxOverlap(double normal[3], double vert[3], double maxbox[3]) // -NJMP- -{ - int q; - double vmin[3],vmax[3],v; - for(q=X;q<=Z;q++) - { - v=vert[q]; // -NJMP- - if(normal[q]>0.0f) - { - vmin[q]=-maxbox[q] - v; // -NJMP- - vmax[q]= maxbox[q] - v; // -NJMP- - } - else - { - vmin[q]= maxbox[q] - v; // -NJMP- - vmax[q]=-maxbox[q] - v; // -NJMP- - } - } - if(DOT(normal,vmin)>0.0f) return 0; // -NJMP- - if(DOT(normal,vmax)>=0.0f) return 1; // -NJMP- - - return 0; -} - - -/*======================== X-tests ========================*/ -#define AXISTEST_X01(a, b, fa, fb) \ - p0 = a*v0[Y] - b*v0[Z]; \ - p2 = a*v2[Y] - b*v2[Z]; \ - if(p0rad || max<-rad) return 0; - -#define AXISTEST_X2(a, b, fa, fb) \ - p0 = a*v0[Y] - b*v0[Z]; \ - p1 = a*v1[Y] - b*v1[Z]; \ - if(p0rad || max<-rad) return 0; - -/*======================== Y-tests ========================*/ -#define AXISTEST_Y02(a, b, fa, fb) \ - p0 = -a*v0[X] + b*v0[Z]; \ - p2 = -a*v2[X] + b*v2[Z]; \ - if(p0rad || max<-rad) return 0; - -#define AXISTEST_Y1(a, b, fa, fb) \ - p0 = -a*v0[X] + b*v0[Z]; \ - p1 = -a*v1[X] + b*v1[Z]; \ - if(p0rad || max<-rad) return 0; - -/*======================== Z-tests ========================*/ - -#define AXISTEST_Z12(a, b, fa, fb) \ - p1 = a*v1[X] - b*v1[Y]; \ - p2 = a*v2[X] - b*v2[Y]; \ - if(p2rad || max<-rad) return 0; - -#define AXISTEST_Z0(a, b, fa, fb) \ - p0 = a*v0[X] - b*v0[Y]; \ - p1 = a*v1[X] - b*v1[Y]; \ - if(p0rad || max<-rad) return 0; - -int triBoxOverlap(double boxcenter[3],double boxhalfsize[3],double triverts[3][3]) -{ - - /* use separating axis theorem to test overlap between triangle and box */ - /* need to test for overlap in these directions: */ - /* 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle */ - /* we do not even need to test these) */ - /* 2) normal of the triangle */ - /* 3) crossproduct(edge from tri, {x,y,z}-directin) */ - /* this gives 3x3=9 more tests */ - double v0[3],v1[3],v2[3]; -// double axis[3]; - double min,max,p0,p1,p2,rad,fex,fey,fez; // -NJMP- "d" local variable removed - double normal[3],e0[3],e1[3],e2[3]; - - /* This is the fastest branch on Sun */ - /* move everything so that the boxcenter is in (0,0,0) */ - SUB(v0,triverts[0],boxcenter); - SUB(v1,triverts[1],boxcenter); - SUB(v2,triverts[2],boxcenter); - - /* compute triangle edges */ - SUB(e0,v1,v0); /* tri edge 0 */ - SUB(e1,v2,v1); /* tri edge 1 */ - SUB(e2,v0,v2); /* tri edge 2 */ - - /* Bullet 3: */ - /* test the 9 tests first (this was faster) */ - fex = fabsf(e0[X]); - fey = fabsf(e0[Y]); - fez = fabsf(e0[Z]); - AXISTEST_X01(e0[Z], e0[Y], fez, fey); - AXISTEST_Y02(e0[Z], e0[X], fez, fex); - AXISTEST_Z12(e0[Y], e0[X], fey, fex); - - fex = fabsf(e1[X]); - fey = fabsf(e1[Y]); - fez = fabsf(e1[Z]); - AXISTEST_X01(e1[Z], e1[Y], fez, fey); - AXISTEST_Y02(e1[Z], e1[X], fez, fex); - AXISTEST_Z0(e1[Y], e1[X], fey, fex); - - fex = fabsf(e2[X]); - fey = fabsf(e2[Y]); - fez = fabsf(e2[Z]); - AXISTEST_X2(e2[Z], e2[Y], fez, fey); - AXISTEST_Y1(e2[Z], e2[X], fez, fex); - AXISTEST_Z12(e2[Y], e2[X], fey, fex); - - /* Bullet 1: */ - /* first test overlap in the {x,y,z}-directions */ - /* find min, max of the triangle each direction, and test for overlap in */ - /* that direction -- this is equivalent to testing a minimal AABB around */ - /* the triangle against the AABB */ - - /* test in X-direction */ - FINDMINMAX(v0[X],v1[X],v2[X],min,max); - if(min>boxhalfsize[X] || max<-boxhalfsize[X]) return 0; - - /* test in Y-direction */ - FINDMINMAX(v0[Y],v1[Y],v2[Y],min,max); - if(min>boxhalfsize[Y] || max<-boxhalfsize[Y]) return 0; - - /* test in Z-direction */ - FINDMINMAX(v0[Z],v1[Z],v2[Z],min,max); - if(min>boxhalfsize[Z] || max<-boxhalfsize[Z]) return 0; - - /* Bullet 2: */ - /* test if the box intersects the plane of the triangle */ - /* compute plane equation of triangle: normal*x+d=0 */ - CROSS(normal,e0,e1); - // -NJMP- (line removed here) - if(!planeBoxOverlap(normal,v0,boxhalfsize)) return 0; // -NJMP- - - return 1; /* box and triangle overlaps */ -} - Index: trunk/src/3rd/gts/rounding.h =================================================================== --- trunk/src/3rd/gts/rounding.h (revision 1064) +++ trunk/src/3rd/gts/rounding.h (nonexistent) @@ -1,85 +0,0 @@ -/* GTS - Library for the manipulation of triangulated surfaces - * Copyright (C) 1999 Stéphane Popinet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include "config.h" - -#ifdef HAVE_FPU_CONTROL_H -# include -# ifdef _FPU_EXTENDED -# if !defined(__alpha__) || !defined(__GLIBC__) -# if defined(__arm__) - static fpu_control_t fpu_round_double = _FPU_DEFAULT; -# else - static fpu_control_t fpu_round_double = - (_FPU_DEFAULT & ~ _FPU_EXTENDED)|_FPU_DOUBLE; -# endif - static fpu_control_t fpu_init; -# define FPU_ROUND_DOUBLE { _FPU_GETCW(fpu_init);\ - _FPU_SETCW(fpu_round_double); } -# define FPU_RESTORE {_FPU_SETCW(fpu_init);} -# else /* __alpha__ && __GLIBC__ */ -# define FPU_ROUND_DOUBLE -# define FPU_RESTORE -# endif /* __alpha__ && __GLIBC__ */ -# else /* not FPU_EXTENDED */ -# define FPU_ROUND_DOUBLE -# define FPU_RESTORE -# endif /* not FPU_EXTENDED */ -#else /* not HAVE_FPU_CONTROL_H */ -# ifdef __FreeBSD__ -# include -# define FPU_ROUND_DOUBLE (fpsetprec(FP_PD)) -# define FPU_RESTORE (fpsetprec(FP_PE)) -# else /* not __FreeBSD__ */ -# ifdef WIN32 -# ifdef _MSC_VER -# include - static unsigned int fpu_init; -# define FPU_ROUND_DOUBLE (fpu_init = _controlfp (0, 0),\ - _controlfp (_PC_53, MCW_PC)) -# define FPU_RESTORE (_controlfp (fpu_init, 0xfffff)) -# elif __MINGW32__ -# include - static unsigned int fpu_init; -# define FPU_ROUND_DOUBLE (fpu_init = _controlfp (0, 0),\ - _controlfp (_PC_53, _MCW_PC)) -# define FPU_RESTORE (_controlfp (fpu_init, 0xfffff)) -# else /* not _MSC_VER or __MINGW32__ */ -# error "You need MSVC or MinGW for the Win32 version" -# endif /* not _MSC_VER or __MINGW32__ */ -# else /* not WIN32 */ -# ifdef __CYGWIN__ - typedef unsigned int fpu_control_t __attribute__ ((__mode__ (__HI__))); - static fpu_control_t fpu_round_double = 0x027f; - static fpu_control_t fpu_init; -# define _FPU_GETCW(cw) __asm__ ("fnstcw %0" : "=m" (*&cw)) -# define _FPU_SETCW(cw) __asm__ ("fldcw %0" : : "m" (*&cw)) -# define FPU_ROUND_DOUBLE { _FPU_GETCW(fpu_init);\ - _FPU_SETCW(fpu_round_double); } -# define FPU_RESTORE { _FPU_SETCW(fpu_init);} -# else /* not __CYGWIN__ */ -# ifdef CPP_HAS_WARNING -# warning "Unknown CPU: assuming default double precision rounding" -# endif /* CPP_HAS_WARNING */ -# define FPU_ROUND_DOUBLE -# define FPU_RESTORE -# endif /* not __CYGWIN__ */ -# endif /* not WIN32 */ -# endif /* not __FreeBSD__ */ -#endif /* not HAVE_FPU_CONTROL_H */ Index: trunk/src/3rd/gts/split.c =================================================================== --- trunk/src/3rd/gts/split.c (revision 1064) +++ trunk/src/3rd/gts/split.c (nonexistent) @@ -1,1840 +0,0 @@ -/* GTS - Library for the manipulation of triangulated surfaces - * Copyright (C) 1999 Stéphane Popinet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include -#include -#include "gts.h" - -#define DYNAMIC_SPLIT -#define NEW - -/* #define DEBUG - #define DEBUG_HEXPAND - #define DEBUG_EXPAND */ - -struct _GtsSplitCFace { - GtsFace * f; - GtsTriangle ** a1, ** a2; -}; - -typedef struct _CFace CFace; -typedef struct _CFaceClass CFaceClass; - -struct _CFace { - GtsObject object; - - GtsSplit * parent_split; - GtsTriangle * t; - guint flags; -}; -/* the size of the CFace structure must be smaller or equal to the size - of the GtsFace structure as both structures use the same memory location */ - -struct _CFaceClass { - GtsObjectClass parent_class; -}; - -#define IS_CFACE(obj) (gts_object_is_from_class (obj, cface_class ())) -#define CFACE(obj) ((CFace *) obj) -#define CFACE_ORIENTATION(cf) ((cf)->flags & 0x1) -#define CFACE_ORIENTATION_DIRECT(cf) ((cf)->flags |= 0x1) -#define CFACE_VVS(cf) ((cf)->flags & 0x2) -#define CFACE_VVS_DIRECT(cf) ((cf)->flags |= 0x2) -#define CFACE_E1 0x4 -#define CFACE_E2 0x8 -#define CFACE_KEEP_VVS 0x10 - -#define ROTATE_ORIENT(e, e1, e2, e3) { if (e1 == e) { e1 = e2; e2 = e3; }\ - else if (e2 == e) { e2 = e1; e1 = e3; }\ - else g_assert (e3 == e); } -#define SEGMENT_USE_VERTEX(s, v) ((s)->v1 == v || (s)->v2 == v) -#define TRIANGLE_REPLACE_EDGE(t, e, with) { if ((t)->e1 == e)\ - (t)->e1 = with;\ - else if ((t)->e2 == e)\ - (t)->e2 = with;\ - else {\ - g_assert ((t)->e3 == e);\ - (t)->e3 = with;\ - }\ - } - -#define HEAP_INSERT_OBJECT(h, e) (GTS_OBJECT (e)->reserved =\ - gts_eheap_insert (h, e)) -#define HEAP_REMOVE_OBJECT(h, e) (gts_eheap_remove (h, GTS_OBJECT (e)->reserved),\ - GTS_OBJECT (e)->reserved = NULL) - -static GtsObjectClass * cface_class (void) -{ - static GtsObjectClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo cface_info = { - "GtsCFace", - sizeof (CFace), - sizeof (CFaceClass), - (GtsObjectClassInitFunc) NULL, - (GtsObjectInitFunc) NULL, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (gts_object_class (), &cface_info); - g_assert (sizeof (CFace) <= sizeof (GtsFace)); - } - - return klass; -} - -/* Replace @e with @with for all the triangles using @e but @f. - Destroys @e and removes it from @heap (if not %NULL). - Returns a triangle using e different from f or %NULL. */ -static GtsTriangle * replace_edge_collapse (GtsEdge * e, - GtsEdge * with, - CFace * cf, - GtsEHeap * heap -#ifdef DYNAMIC_SPLIT - , GtsTriangle *** a1 -#endif -#ifdef NEW - , guint edge_flag -#endif - ) -{ - GSList * i; - GtsTriangle * rt = NULL; -#ifdef DYNAMIC_SPLIT - guint size; - GtsTriangle ** a; -#endif - -#ifdef NEW - i = e->triangles; - e->triangles = NULL; - size = g_slist_length (i)*sizeof (GtsTriangle *); - *a1 = a = g_malloc (size > 0 ? size : sizeof (GtsTriangle *)); - while (i) { - GtsTriangle * t = i->data; - GSList * next = i->next; - if (t != ((GtsTriangle *) cf)) { - if (IS_CFACE (t)) { - i->next = e->triangles; - e->triangles = i; - /* set the edge given by edge_flag (CFACE_E1 or CFACE_E2) */ - GTS_OBJECT (t)->reserved = GUINT_TO_POINTER (edge_flag); - cf->flags |= CFACE_KEEP_VVS; - } - else { - TRIANGLE_REPLACE_EDGE (t, e, with); - i->next = with->triangles; - with->triangles = i; - rt = t; - *(a++) = t; - } - } - else - g_slist_free_1 (i); - i = next; - } - *a = NULL; - if (!e->triangles) { - if (heap) - HEAP_REMOVE_OBJECT (heap, e); - gts_object_destroy (GTS_OBJECT (e)); - } -#else /* not NEW */ - i = e->triangles; -#ifdef DYNAMIC_SPLIT - size = g_slist_length (i)*sizeof (GtsTriangle *); - *a1 = a = g_malloc (size > 0 ? size : sizeof (GtsTriangle *)); -#endif - while (i) { - GtsTriangle * t = i->data; - GSList * next = i->next; - if (t != ((GtsTriangle *) cf)) { - TRIANGLE_REPLACE_EDGE (t, e, with); - i->next = with->triangles; - with->triangles = i; - rt = t; -#ifdef DYNAMIC_SPLIT - *(a++) = t; -#endif - } - else - g_slist_free_1 (i); - i = next; - } -#ifdef DYNAMIC_SPLIT - *a = NULL; -#endif - if (heap) - HEAP_REMOVE_OBJECT (heap, e); - e->triangles = NULL; - gts_object_destroy (GTS_OBJECT (e)); -#endif /* NEW */ - - return rt; -} - -static CFace * cface_new (GtsFace * f, - GtsEdge * e, - GtsVertex * v1, - GtsVertex * v2, - GtsSplit * vs, - GtsEHeap * heap, - GtsEdgeClass * klass -#ifdef DYNAMIC_SPLIT - , GtsSplitCFace * scf -#endif - ) -{ - CFace * cf; - GtsVertex * v; - GtsEdge * e1, * e2, * e3, * vvs; - GSList * i; - GtsTriangle * t, * t1 = NULL, * t2 = NULL; - guint flags; - - g_return_val_if_fail (f != NULL, NULL); -#ifndef NEW - g_return_val_if_fail (GTS_IS_FACE (f), NULL); -#endif - g_return_val_if_fail (e != NULL, NULL); - g_return_val_if_fail (vs != NULL, NULL); - - t = ((GtsTriangle *) f); - if (heap) - g_return_val_if_fail (!gts_triangle_is_duplicate (t), NULL); - -#ifdef NEW - /* get CFACE_E1 and CFACE_E2 info */ - flags = GPOINTER_TO_UINT (GTS_OBJECT (f)->reserved); -#endif - GTS_OBJECT_SET_FLAGS (f, GTS_DESTROYED); - - i = f->surfaces; - while (i) { - GSList * next = i->next; - gts_surface_remove_face (i->data, f); - i = next; - } - g_slist_free (f->surfaces); - - e1 = t->e1; e2 = t->e2; e3 = t->e3; - ROTATE_ORIENT (e, e1, e2, e3); - - cf = (CFace *) f; -#ifndef NEW - GTS_OBJECT (cf)->klass = cface_class (); -#else - cf->flags = flags; -#endif - gts_object_init (GTS_OBJECT (cf), cface_class ()); - cf->parent_split = vs; - - if (SEGMENT_USE_VERTEX (GTS_SEGMENT (e1), v2)) { - CFACE_ORIENTATION_DIRECT (cf); /* v1->v2->v */ - e3 = e1; e1 = e2; e2 = e3; - } - v = GTS_SEGMENT (e1)->v1 == v1 ? - GTS_SEGMENT (e1)->v2 : GTS_SEGMENT (e1)->v1; -#ifdef NEW - if ((cf->flags & CFACE_E1) || (cf->flags & CFACE_E2)) - g_assert ((vvs = GTS_EDGE (gts_vertices_are_connected (vs->v, v)))); - else -#endif - vvs = gts_edge_new (klass, v, vs->v); - - t1 = replace_edge_collapse (e1, vvs, cf, heap -#ifdef DYNAMIC_SPLIT - , &scf->a1 -#endif -#ifdef NEW - , CFACE_E1 -#endif - ); - t2 = replace_edge_collapse (e2, vvs, cf, heap -#ifdef DYNAMIC_SPLIT - , &scf->a2 -#endif -#ifdef NEW - , CFACE_E2 -#endif - ); - t = cf->t = t1 ? t1 : t2; - g_assert (t); - - /* set up flags necessary to find vvs */ - if (t->e1 == vvs) e2 = t->e2; - else if (t->e2 == vvs) e2 = t->e3; - else { - g_assert (t->e3 == vvs); - e2 = t->e1; - } - if (SEGMENT_USE_VERTEX (GTS_SEGMENT (e2), v)) - CFACE_VVS_DIRECT (cf); - - return cf; -} - -static void find_vvs (GtsVertex * vs, - GtsTriangle * t, - GtsVertex ** v, GtsEdge ** vvs, - gboolean orientation) -{ - GtsEdge * e1 = t->e1, * e2 = t->e2, * e3 = t->e3, * tmp; - - if (SEGMENT_USE_VERTEX (GTS_SEGMENT (e2), vs)) { - tmp = e1; e1 = e2; e2 = e3; e3 = tmp; - } - else if (SEGMENT_USE_VERTEX (GTS_SEGMENT (e3), vs)) { - tmp = e1; e1 = e3; e3 = e2; e2 = tmp; - } - else - g_assert (SEGMENT_USE_VERTEX (GTS_SEGMENT (e1), vs)); - if (SEGMENT_USE_VERTEX (GTS_SEGMENT (e2), vs) || - !gts_segments_touch (GTS_SEGMENT (e1), GTS_SEGMENT (e2))) { - tmp = e1; e1 = e2; e2 = e3; e3 = tmp; - g_assert (gts_segments_touch (GTS_SEGMENT (e1), GTS_SEGMENT (e2))); - } - - *vvs = orientation ? e1 : e3; - - if (GTS_SEGMENT (*vvs)->v1 != vs) { - g_assert (GTS_SEGMENT (*vvs)->v2 == vs); - *v = GTS_SEGMENT (*vvs)->v1; - } - else - *v = GTS_SEGMENT (*vvs)->v2; -} - -static void replace_edge_expand (GtsEdge * e, - GtsEdge * with, - GtsTriangle ** a, - GtsVertex * v) -{ - GtsTriangle ** i = a, * t; - - while ((t = *(i++))) { -#ifdef DEBUG_EXPAND - g_assert (!IS_CFACE (t)); - fprintf (stderr, "replacing %p->%d: e: %p->%d with: %p->%d\n", - t, id (t), e, id (e), with, id (with)); -#endif - TRIANGLE_REPLACE_EDGE (t, e, with); - with->triangles = g_slist_prepend (with->triangles, t); - if (GTS_OBJECT (t)->reserved) { - /* apart from the triangles having e as an edge, t is the only - triangle using v */ - g_assert (GTS_OBJECT (t)->reserved == v); - GTS_OBJECT (t)->reserved = NULL; - } - else - GTS_OBJECT (t)->reserved = v; - } -} - -static void cface_expand (CFace * cf, - GtsTriangle ** a1, - GtsTriangle ** a2, - GtsEdge * e, - GtsVertex * v1, - GtsVertex * v2, - GtsVertex * vs, - GtsEdgeClass * klass) -{ - GtsVertex * v; - GtsEdge * e1, * e2, * vvs; - gboolean orientation; - guint flags; - - g_return_if_fail (cf != NULL); - g_return_if_fail (IS_CFACE (cf)); - g_return_if_fail (e != NULL); - g_return_if_fail (vs != NULL); - - flags = cf->flags; - orientation = CFACE_ORIENTATION (cf); - - find_vvs (vs, cf->t, &v, &vvs, CFACE_VVS (cf)); - -#ifdef NEW - if (flags & CFACE_E1) - e1 = GTS_EDGE (gts_vertices_are_connected (v1, v)); - else - e1 = gts_edge_new (klass, v, v1); - if (flags & CFACE_E2) - e2 = GTS_EDGE (gts_vertices_are_connected (v2, v)); - else - e2 = gts_edge_new (klass, v, v2); -#else - e1 = gts_edge_new (v, v1); - e2 = gts_edge_new (v, v2); -#endif - - replace_edge_expand (vvs, e1, a1, v1); - replace_edge_expand (vvs, e2, a2, v2); - -#ifdef NEW - if (!(flags & CFACE_KEEP_VVS)) { - g_slist_free (vvs->triangles); - vvs->triangles = NULL; - gts_object_destroy (GTS_OBJECT (vvs)); - } -#else - g_slist_free (vvs->triangles); - vvs->triangles = NULL; - gts_object_destroy (GTS_OBJECT (vvs)); -#endif - - /* gts_face_new : because I am "creating" a face */ - GTS_OBJECT (cf)->klass = GTS_OBJECT_CLASS (gts_face_class ()); - gts_object_init (GTS_OBJECT (cf), GTS_OBJECT (cf)->klass); - - if (orientation) - gts_triangle_set (GTS_TRIANGLE (cf), e, e2, e1); - else - gts_triangle_set (GTS_TRIANGLE (cf), e, e1, e2); -} - -static void split_destroy (GtsObject * object) -{ - GtsSplit * vs = GTS_SPLIT (object); - guint i = vs->ncf; - GtsSplitCFace * cf = vs->cfaces; - - while (i--) { - if (IS_CFACE (cf->f)) - gts_object_destroy (GTS_OBJECT (cf->f)); - g_free (cf->a1); - g_free (cf->a2); - cf++; - } - g_free (vs->cfaces); - - if (!gts_allow_floating_vertices && vs->v && vs->v->segments == NULL) - gts_object_destroy (GTS_OBJECT (vs->v)); - - (* GTS_OBJECT_CLASS (gts_split_class ())->parent_class->destroy) (object); -} - -static void split_class_init (GtsObjectClass * klass) -{ - klass->destroy = split_destroy; -} - -static void split_init (GtsSplit * split) -{ - split->v1 = split->v2 = NULL; - split->v = NULL; - split->cfaces = NULL; - split->ncf = 0; -} - -/** - * gts_split_class: - * - * Returns: the #GtsSplitClass. - */ -GtsSplitClass * gts_split_class (void) -{ - static GtsSplitClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo split_info = { - "GtsSplit", - sizeof (GtsSplit), - sizeof (GtsSplitClass), - (GtsObjectClassInitFunc) split_class_init, - (GtsObjectInitFunc) split_init, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (gts_object_class (), - &split_info); - } - - return klass; -} - -#ifdef DEBUG -static gboolean edge_collapse_is_valid (GtsEdge * e) -{ - GSList * i; - - g_return_val_if_fail (e != NULL, FALSE); - - if (gts_segment_is_duplicate (GTS_SEGMENT (e))) { - g_warning ("collapsing duplicate edge"); - return FALSE; - } - - i = GTS_SEGMENT (e)->v1->segments; - while (i) { - GtsEdge * e1 = i->data; - if (e1 != e && GTS_IS_EDGE (e1)) { - GtsEdge * e2 = NULL; - GSList * j = GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e)->v1 ? - GTS_SEGMENT (e1)->v2->segments : GTS_SEGMENT (e1)->v1->segments; - while (j && !e2) { - GtsEdge * e1 = j->data; - if (GTS_IS_EDGE (e1) && - (GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e)->v2 || - GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e)->v2)) - e2 = e1; - j = j->next; - } - if (e2 && !gts_triangle_use_edges (e, e1, e2)) { - g_warning ("collapsing empty triangle"); - return FALSE; - } - } - i = i->next; - } - - if (gts_edge_is_boundary (e, NULL)) { - GtsTriangle * t = e->triangles->data; - if (gts_edge_is_boundary (t->e1, NULL) && - gts_edge_is_boundary (t->e2, NULL) && - gts_edge_is_boundary (t->e3, NULL)) { - g_warning ("collapsing single triangle"); - return FALSE; - } - } - else { - if (gts_vertex_is_boundary (GTS_SEGMENT (e)->v1, NULL) && - gts_vertex_is_boundary (GTS_SEGMENT (e)->v2, NULL)) { - g_warning ("collapsing two sides of a strip"); - return FALSE; - } - if (gts_edge_belongs_to_tetrahedron (e)) { - g_warning ("collapsing tetrahedron"); - return FALSE; - } - } - - return TRUE; -} -#endif /* DEBUG */ - -/* Not currently used. May be useful for some debug code */ -#ifdef DEBUG -static void print_split (GtsSplit * vs, FILE * fptr) -{ - guint j; - GtsSplitCFace * cf; - - g_return_if_fail (vs != NULL); - g_return_if_fail (fptr != NULL); - - fprintf (fptr, "%p: v: %p v1: %p v2: %p ncf: %u cfaces: %p\n", - vs, vs->v, vs->v1, vs->v2, vs->ncf, vs->cfaces); - cf = vs->cfaces; - j = vs->ncf; - while (j--) { - fprintf (stderr, " f: %p a1: %p a2: %p\n", - cf->f, cf->a1, cf->a2); - cf++; - } -} -#endif - -/** - * gts_split_collapse: - * @vs: a #GtsSplit. - * @klass: a #GtsEdgeClass. - * @heap: a #GtsEHeap or %NULL. - * - * Collapses the vertex split @vs. Any new edge created during the process will - * be of class @klass. If heap is not %NULL, the new edges will be inserted - * into it and the destroyed edges will be removed from it. - */ -void gts_split_collapse (GtsSplit * vs, - GtsEdgeClass * klass, - GtsEHeap * heap) -{ - GtsEdge * e; - GtsVertex * v, * v1, * v2; - GSList * i, * end; -#ifdef DYNAMIC_SPLIT - GtsSplitCFace * cf; - guint j; -#endif -#ifdef DEBUG - gboolean invalid = FALSE; - static guint ninvalid = 0; -#endif - - g_return_if_fail (vs != NULL); - g_return_if_fail (klass != NULL); - - v = vs->v; - - g_return_if_fail (v->segments == NULL); - - /* we don't want to destroy vertices */ - gts_allow_floating_vertices = TRUE; - - v1 = GTS_SPLIT_V1 (vs); - v2 = GTS_SPLIT_V2 (vs); - g_assert ((e = GTS_EDGE (gts_vertices_are_connected (v1, v2)))); - -#ifdef DEBUG - fprintf (stderr, "collapsing %p: v1: %p v2: %p v: %p\n", vs, v1, v2, v); - if (!edge_collapse_is_valid (e)) { - char fname[80]; - FILE * fptr; - GSList * triangles, * i; - - g_warning ("invalid edge collapse"); - invalid = TRUE; - sprintf (fname, "invalid.%d", ninvalid); - fptr = fopen (fname, "wt"); - gts_write_segment (GTS_SEGMENT (e), GTS_POINT (v), fptr); - triangles = gts_vertex_triangles (v1, NULL); - triangles = gts_vertex_triangles (v2, triangles); - i = triangles; - while (i) { - gts_write_triangle (i->data, GTS_POINT (v), fptr); - i = i->next; - } - g_slist_free (triangles); - fclose (fptr); - } -#endif - - i = e->triangles; -#ifdef DYNAMIC_SPLIT - cf = vs->cfaces; - j = vs->ncf; - while (j--) { - g_free (cf->a1); - g_free (cf->a2); - cf++; - } - g_free (vs->cfaces); - - vs->ncf = g_slist_length (i); - g_assert (vs->ncf > 0); - cf = vs->cfaces = g_malloc (vs->ncf*sizeof (GtsSplitCFace)); -#endif /* DYNAMIC_SPLIT */ -#ifdef NEW - while (i) { - cf->f = i->data; - g_assert (GTS_IS_FACE (cf->f)); - GTS_OBJECT (cf->f)->klass = GTS_OBJECT_CLASS (cface_class ()); - cf++; - i = i->next; - } - i = e->triangles; - cf = vs->cfaces; - while (i) { - cface_new (i->data, e, v1, v2, vs, heap, klass, cf); -#ifdef DEBUG - fprintf (stderr, "cface: %p->%d t: %p->%d a1: ", - cf->f, id (cf->f), CFACE (cf->f)->t, id (CFACE (cf->f)->t)); - { - GtsTriangle * t, ** a; - a = cf->a1; - while ((t = *(a++))) - fprintf (stderr, "%p->%d ", t, id (t)); - fprintf (stderr, "a2: "); - a = cf->a2; - while ((t = *(a++))) - fprintf (stderr, "%p->%d ", t, id (t)); - fprintf (stderr, "\n"); - } -#endif - cf++; - i = i->next; - } -#else /* not NEW */ - while (i) { - cface_new (i->data, e, v1, v2, vs, heap -#ifdef DYNAMIC_SPLIT - , cf -#endif /* DYNAMIC_SPLIT */ - ); -#ifdef DYNAMIC_SPLIT - cf->f = i->data; - cf++; -#endif /* DYNAMIC_SPLIT */ - i = i->next; - } -#endif /* NEW */ - g_slist_free (e->triangles); - e->triangles = NULL; - gts_object_destroy (GTS_OBJECT (e)); - - gts_allow_floating_vertices = FALSE; - - end = NULL; - i = v1->segments; - while (i) { - GtsSegment * s = i->data; - if (s->v1 == v1) - s->v1 = v; - else - s->v2 = v; - end = i; - i = i->next; - } - if (end) { - end->next = v->segments; - v->segments = v1->segments; - v1->segments = NULL; - } - - end = NULL; - i = v2->segments; - while (i) { - GtsSegment * s = i->data; - if (s->v1 == v2) - s->v1 = v; - else - s->v2 = v; - end = i; - i = i->next; - } - if (end) { - end->next = v->segments; - v->segments = v2->segments; - v2->segments = NULL; - } - -#ifdef DEBUG - if (invalid) { - char fname[80]; - FILE * fptr; - GSList * triangles, * i; - GtsSurface * surface = NULL; - - sprintf (fname, "invalid_after.%d", ninvalid); - fptr = fopen (fname, "wt"); - triangles = gts_vertex_triangles (v, NULL); - i = triangles; - while (i) { - GtsTriangle * t = i->data; - fprintf (stderr, "checking %p->%d\n", t, id (t)); - g_assert (GTS_IS_FACE (t)); - gts_write_triangle (t, GTS_POINT (v), fptr); - surface = GTS_FACE (t)->surfaces->data; - if (gts_triangle_is_duplicate (t)) - fprintf (stderr, "%p->%d is duplicate\n", t, id (t)); - if (gts_segment_is_duplicate (GTS_SEGMENT (t->e1))) - fprintf (stderr, "e1 of %p->%d is duplicate\n", t, id (t)); - if (gts_segment_is_duplicate (GTS_SEGMENT (t->e2))) - fprintf (stderr, "e2 of %p->%d is duplicate\n", t, id (t)); - if (gts_segment_is_duplicate (GTS_SEGMENT (t->e3))) - fprintf (stderr, "e3 of %p->%d is duplicate\n", t, id (t)); - i = i->next; - } - fclose (fptr); - g_slist_free (triangles); -#if 0 - gts_split_expand (vs, surface); - - sprintf (fname, "invalid_after_after.%d", ninvalid); - fptr = fopen (fname, "wt"); - triangles = gts_vertex_triangles (v1, NULL); - triangles = gts_vertex_triangles (v2, triangles); - i = triangles; - while (i) { - GtsTriangle * t = i->data; - gts_write_triangle (t, GTS_POINT (v), fptr); - surface = GTS_FACE (t)->surfaces->data; - if (gts_triangle_is_duplicate (t)) - fprintf (stderr, "%p->%d is duplicate\n", t, id (t)); - if (gts_segment_is_duplicate (GTS_SEGMENT (t->e1))) - fprintf (stderr, "e1 of %p->%d is duplicate\n", t, id (t)); - if (gts_segment_is_duplicate (GTS_SEGMENT (t->e2))) - fprintf (stderr, "e2 of %p->%d is duplicate\n", t, id (t)); - if (gts_segment_is_duplicate (GTS_SEGMENT (t->e3))) - fprintf (stderr, "e3 of %p->%d is duplicate\n", t, id (t)); - i = i->next; - } - fclose (fptr); - g_slist_free (triangles); - - exit (1); -#endif - ninvalid++; - } -#endif -} - -/** - * gts_split_expand: - * @vs: a #GtsSplit. - * @s: a #GtsSurface. - * @klass: a #GtsEdgeClass. - * - * Expands the vertex split @vs adding the newly created faces to @s. Any - * new edge will be of class @klass. - */ -void gts_split_expand (GtsSplit * vs, - GtsSurface * s, - GtsEdgeClass * klass) -{ - GSList * i; - GtsEdge * e; - GtsVertex * v, * v1, * v2; - gboolean changed = FALSE; - GtsSplitCFace * cf; - guint j; - - g_return_if_fail (vs != NULL); - g_return_if_fail (s != NULL); - g_return_if_fail (klass != NULL); - - /* we don't want to destroy vertices */ - gts_allow_floating_vertices = TRUE; - - v1 = GTS_SPLIT_V1 (vs); - v2 = GTS_SPLIT_V2 (vs); - v = vs->v; -#ifdef DEBUG_EXPAND - fprintf (stderr, "expanding %p->%d: v1: %p->%d v2: %p->%d v: %p->%d\n", - vs, id (vs), v1, id (v1), v2, id (v2), v, id (v)); -#endif - e = gts_edge_new (klass, v1, v2); - cf = vs->cfaces; - j = vs->ncf; - while (j--) { - cface_expand (CFACE (cf->f), cf->a1, cf->a2, e, v1, v2, v, klass); - gts_surface_add_face (s, cf->f); - cf++; - } - - gts_allow_floating_vertices = FALSE; - - /* this part is described by figure "expand.fig" */ - i = v->segments; - while (i) { - GtsEdge * e1 = i->data; - GtsVertex * with = NULL; - GSList * j = e1->triangles, * next = i->next; - // fprintf (stderr, "e1: %p->%d\n", e1, id (e1)); - while (j && !with) { - with = GTS_OBJECT (j->data)->reserved; - j = j->next; - } - if (with) { - j = e1->triangles; - while (j) { - GtsTriangle * t = j->data; - if (GTS_OBJECT (t)->reserved) { - g_assert (GTS_OBJECT (t)->reserved == with); - GTS_OBJECT (t)->reserved = NULL; - } - else - GTS_OBJECT (t)->reserved = with; - j = j->next; - } - if (GTS_SEGMENT (e1)->v1 == v) - GTS_SEGMENT (e1)->v1 = with; - else - GTS_SEGMENT (e1)->v2 = with; - - v->segments = g_slist_remove_link (v->segments, i); - i->next = with->segments; - with->segments = i; - changed = TRUE; - } - if (next) - i = next; - else { - /* check for infinite loop (the crossed out case in - figure "expand.fig") */ - g_assert (changed); - changed = FALSE; - i = v->segments; - } - } -} - -#ifndef DYNAMIC_SPLIT -static void cface_neighbors (GtsSplitCFace * cf, - GtsEdge * e, - GtsVertex * v1, - GtsVertex * v2) -{ - GtsTriangle * t = GTS_TRIANGLE (cf->f), ** a; - GtsEdge * e1 = t->e1, * e2 = t->e2, * e3 = t->e3; - GSList * i; - guint size; - - ROTATE_ORIENT (e, e1, e2, e3); - if (SEGMENT_USE_VERTEX (GTS_SEGMENT (e1), v2)) { - e3 = e1; e1 = e2; e2 = e3; - } - - i = e1->triangles; - size = g_slist_length (i)*sizeof (GtsTriangle *); - a = cf->a1 = g_malloc (size > 0 ? size : sizeof (GtsTriangle *)); - while (i) { - if (i->data != t) - *(a++) = i->data; - i = i->next; - } - *a = NULL; - - i = e2->triangles; - size = g_slist_length (i)*sizeof (GtsTriangle *); - a = cf->a2 = g_malloc (size > 0 ? size : sizeof (GtsTriangle *)); - while (i) { - if (i->data != t) - *(a++) = i->data; - i = i->next; - } - *a = NULL; -} -#endif /*ifndef DYNAMIC_SPLIT */ - -/** - * gts_split_new: - * @klass: a #GtsSplitClass. - * @v: a #GtsVertex. - * @o1: either a #GtsVertex or a #GtsSplit. - * @o2: either a #GtsVertex or a #GtsSplit. - * - * Creates a new #GtsSplit which would collapse @o1 and @o2 into @v. The - * collapse itself is not performed. - * - * Returns: the new #GtsSplit. - */ -GtsSplit * gts_split_new (GtsSplitClass * klass, - GtsVertex * v, - GtsObject * o1, - GtsObject * o2) -{ - GtsSplit * vs; -#ifndef DYNAMIC_SPLIT - GtsVertex * v1, * v2; - GtsEdge * e; - GSList * i; - GtsSplitCFace * cf; -#endif - - g_return_val_if_fail (klass != NULL, NULL); - g_return_val_if_fail (v != NULL, NULL); - g_return_val_if_fail (GTS_IS_SPLIT (o1) || GTS_IS_VERTEX (o1), NULL); - g_return_val_if_fail (GTS_IS_SPLIT (o2) || GTS_IS_VERTEX (o2), NULL); - - vs = GTS_SPLIT (gts_object_new (GTS_OBJECT_CLASS (klass))); - vs->v = v; - vs->v1 = o1; - vs->v2 = o2; -#ifdef DYNAMIC_SPLIT - vs->ncf = 0; - vs->cfaces = NULL; -#else - v1 = GTS_SPLIT_V1 (vs); - v2 = GTS_SPLIT_V2 (vs); - g_assert ((e = GTS_EDGE (gts_vertices_are_connected (v1, v2)))); - i = e->triangles; - vs->ncf = g_slist_length (i); - g_assert (vs->ncf > 0); - cf = vs->cfaces = g_malloc (vs->ncf*sizeof (GtsSplitCFace)); - while (i) { - cf->f = i->data; - cface_neighbors (cf, e, v1, v2); - i = i->next; - cf++; - } -#endif - - return vs; -} - -static gboolean -split_traverse_pre_order (GtsSplit * vs, - GtsSplitTraverseFunc func, - gpointer data) -{ - if (func (vs, data)) - return TRUE; - if (GTS_IS_SPLIT (vs->v1) && - split_traverse_pre_order (GTS_SPLIT (vs->v1), func, data)) - return TRUE; - if (GTS_IS_SPLIT (vs->v2) && - split_traverse_pre_order (GTS_SPLIT (vs->v2), func, data)) - return TRUE; - return FALSE; -} - -static gboolean -split_depth_traverse_pre_order (GtsSplit * vs, - guint depth, - GtsSplitTraverseFunc func, - gpointer data) -{ - if (func (vs, data)) - return TRUE; - - depth--; - if (!depth) - return FALSE; - - if (GTS_IS_SPLIT (vs->v1) && - split_depth_traverse_pre_order (GTS_SPLIT (vs->v1), depth, func, data)) - return TRUE; - if (GTS_IS_SPLIT (vs->v2) && - split_depth_traverse_pre_order (GTS_SPLIT (vs->v2), depth, func, data)) - return TRUE; - return FALSE; -} - -static gboolean -split_traverse_post_order (GtsSplit * vs, - GtsSplitTraverseFunc func, - gpointer data) -{ - if (GTS_IS_SPLIT (vs->v1) && - split_traverse_post_order (GTS_SPLIT (vs->v1), func, data)) - return TRUE; - if (GTS_IS_SPLIT (vs->v2) && - split_traverse_post_order (GTS_SPLIT (vs->v2), func, data)) - return TRUE; - if (func (vs, data)) - return TRUE; - return FALSE; -} - -static gboolean -split_depth_traverse_post_order (GtsSplit * vs, - guint depth, - GtsSplitTraverseFunc func, - gpointer data) -{ - depth--; - if (depth) { - if (GTS_IS_SPLIT (vs->v1) && - split_depth_traverse_post_order (GTS_SPLIT (vs->v1), - depth, func, data)) - return TRUE; - if (GTS_IS_SPLIT (vs->v2) && - split_depth_traverse_post_order (GTS_SPLIT (vs->v2), - depth, func, data)) - return TRUE; - } - if (func (vs, data)) - return TRUE; - return FALSE; -} - -/** - * gts_split_traverse: - * @root: the #GtsSplit to start the traversal from. - * @order: the order in which nodes are visited - G_PRE_ORDER or G_POST_ORDER. - * @depth: the maximum depth of the traversal. Nodes below this depth - * will not be visited. If depth is -1 all nodes in the tree are - * visited. If depth is 1, only the root is visited. If depth is 2, - * the root and its children are visited. And so on. - * @func: the function to call for each visited #GtsHSplit. - * @data: user data to pass to the function. - * - * Traverses the #GtsSplit tree having @root as root. Calls @func for each - * #GtsSplit of the tree in the order specified by @order. If order is set - * to G_PRE_ORDER @func is called for the #GtsSplit then its children, if order - * is set to G_POST_ORDER @func is called for the children and then for the - * #GtsSplit. - */ -void gts_split_traverse (GtsSplit * root, - GTraverseType order, - gint depth, - GtsSplitTraverseFunc func, - gpointer data) -{ - g_return_if_fail (root != NULL); - g_return_if_fail (func != NULL); - g_return_if_fail (order < G_LEVEL_ORDER); - g_return_if_fail (depth == -1 || depth > 0); - - switch (order) { - case G_PRE_ORDER: - if (depth < 0) - split_traverse_pre_order (root, func, data); - else - split_depth_traverse_pre_order (root, depth, func, data); - break; - case G_POST_ORDER: - if (depth < 0) - split_traverse_post_order (root, func, data); - else - split_depth_traverse_post_order (root, depth, func, data); - break; - default: - g_assert_not_reached (); - } -} - -/** - * gts_split_height: - * @root: a #GtsSplit. - * - * Returns: the maximum height of the vertex split tree having @root as root. - */ -guint gts_split_height (GtsSplit * root) -{ - guint height = 0, tmp_height; - - g_return_val_if_fail (root != NULL, 0); - - if (GTS_IS_SPLIT (root->v1)) { - tmp_height = gts_split_height (GTS_SPLIT (root->v1)); - if (tmp_height > height) - height = tmp_height; - } - if (GTS_IS_SPLIT (root->v2)) { - tmp_height = gts_split_height (GTS_SPLIT (root->v2)); - if (tmp_height > height) - height = tmp_height; - } - - return height + 1; -} - -#ifndef DYNAMIC_SPLIT -static gboolean list_array_are_identical (GSList * list, - gpointer * array, - gpointer excluded) -{ - while (list) { - gpointer data = list->data; - if (data != excluded) { - gboolean found = FALSE; - gpointer * a = array; - - while (!found && *a) - if (*(a++) == data) - found = TRUE; - if (!found) - return FALSE; - } - list = list->next; - } - return TRUE; -} -#endif /* ifndef DYNAMIC_SPLIT */ - -#ifndef NEW -gboolean gts_split_is_collapsable (GtsSplit * vs) -{ - guint i; - GtsSplitCFace * cf; - GtsVertex * v1, * v2; - GtsEdge * e; - - g_return_val_if_fail (vs != NULL, FALSE); - - v1 = GTS_SPLIT_V1 (vs); - v2 = GTS_SPLIT_V2 (vs); - g_return_val_if_fail ((e = GTS_EDGE (gts_vertices_are_connected (v1, v2))), - FALSE); - -#ifdef DYNAMIC_SPLIT - if (!gts_edge_collapse_is_valid (e)) - return FALSE; -#else - i = vs->ncf; - cf = vs->cfaces; - while (i--) { - GtsTriangle * t = GTS_TRIANGLE (cf->f); - GtsEdge * e1 = t->e1, * e2 = t->e2, * e3 = t->e3; - - ROTATE_ORIENT (e, e1, e2, e3); - if (SEGMENT_USE_VERTEX (GTS_SEGMENT (e1), v2)) { - e3 = e1; e1 = e2; e2 = e3; - } - - if (!list_array_are_identical (e1->triangles, (gpointer *) cf->a1, t)) - return FALSE; - if (!list_array_are_identical (e2->triangles, (gpointer *) cf->a2, t)) - return FALSE; - - cf++; - } -#endif - return TRUE; -} -#endif /* not NEW */ - -#ifdef DEBUG_HEXPAND -static guint expand_level = 0; - -static void expand_indent (FILE * fptr) -{ - guint i = expand_level; - while (i--) - fputc (' ', fptr); -} -#endif - -/** - * gts_hsplit_force_expand: - * @hs: a #GtsHSplit. - * @hsurface: a #GtsHSurface. - * - * Forces the expansion of @hs by first expanding all its dependencies not - * already expanded. - */ -void gts_hsplit_force_expand (GtsHSplit * hs, - GtsHSurface * hsurface) -{ - guint i; - GtsSplitCFace * cf; - - g_return_if_fail (hs != NULL); - g_return_if_fail (hsurface != NULL); - g_return_if_fail (hs->nchild == 0); - -#ifdef DEBUG_HEXPAND - expand_level += 2; -#endif - - if (hs->parent && hs->parent->nchild == 0) { -#ifdef DEBUG_HEXPAND - expand_indent (stderr); - fprintf (stderr, "expand parent %p\n", hs->parent); -#endif - gts_hsplit_force_expand (hs->parent, hsurface); - } - - i = GTS_SPLIT (hs)->ncf; - cf = GTS_SPLIT (hs)->cfaces; - while (i--) { - GtsTriangle ** j, * t; - - j = cf->a1; - while ((t = *(j++))) - if (IS_CFACE (t)) { -#ifdef DEBUG_HEXPAND - expand_indent (stderr); - fprintf (stderr, "expand a1: cf->f: %p t: %p parent_split: %p\n", - cf->f, - t, - GTS_HSPLIT (CFACE (t)->parent_split)); -#endif - gts_hsplit_force_expand (GTS_HSPLIT (CFACE (t)->parent_split), - hsurface); -#ifdef DEBUG_HEXPAND - g_assert (!IS_CFACE (t)); -#endif - } - j = cf->a2; - while ((t = *(j++))) - if (IS_CFACE (t)) { -#ifdef DEBUG_HEXPAND - expand_indent (stderr); - fprintf (stderr, "expand a2: cf->f: %p t: %p parent_split: %p\n", - cf->f, - t, - GTS_HSPLIT (CFACE (t)->parent_split)); -#endif - gts_hsplit_force_expand (GTS_HSPLIT (CFACE (t)->parent_split), - hsurface); - } - cf++; - } - - gts_hsplit_expand (hs, hsurface); - -#ifdef DEBUG_HEXPAND - expand_level -= 2; - expand_indent (stderr); - fprintf (stderr, "%p expanded\n", hs); -#endif -} - -static void index_object (GtsObject * o, guint * n) -{ - o->reserved = GUINT_TO_POINTER ((*n)++); -} - -static void index_face (GtsFace * f, gpointer * data) -{ - guint * nf = data[1]; - - g_hash_table_insert (data[0], f, GUINT_TO_POINTER ((*nf)++)); -} - -/** - * gts_psurface_write: - * @ps: a #GtsPSurface. - * @fptr: a file pointer. - * - * Writes to @fptr a GTS progressive surface description. - */ -void gts_psurface_write (GtsPSurface * ps, FILE * fptr) -{ - guint nv = 1; - guint nf = 1; - GHashTable * hash; - gpointer data[2]; - - g_return_if_fail (ps != NULL); - g_return_if_fail (fptr != NULL); - g_return_if_fail (GTS_PSURFACE_IS_CLOSED (ps)); - - while (gts_psurface_remove_vertex (ps)) - ; - - GTS_POINT_CLASS (ps->s->vertex_class)->binary = FALSE; - gts_surface_write (ps->s, fptr); - - gts_surface_foreach_vertex (ps->s, (GtsFunc) index_object, &nv); - hash = g_hash_table_new (NULL, NULL); - data[0] = hash; - data[1] = &nf; - gts_surface_foreach_face (ps->s, (GtsFunc) index_face, data); - - fprintf (fptr, "%u\n", ps->split->len); - while (ps->pos) { - GtsSplit * vs = g_ptr_array_index (ps->split, --ps->pos); - GtsSplitCFace * scf = vs->cfaces; - GtsVertex * v1, * v2; - guint i = vs->ncf; - - fprintf (fptr, "%u %u", - GPOINTER_TO_UINT (GTS_OBJECT (vs->v)->reserved), - vs->ncf); - if (GTS_OBJECT (vs)->klass->write) - (*GTS_OBJECT (vs)->klass->write) (GTS_OBJECT (vs), fptr); - fputc ('\n', fptr); - - v1 = GTS_IS_SPLIT (vs->v1) ? GTS_SPLIT (vs->v1)->v : GTS_VERTEX (vs->v1); - GTS_OBJECT (v1)->reserved = GUINT_TO_POINTER (nv++); - v2 = GTS_IS_SPLIT (vs->v2) ? GTS_SPLIT (vs->v2)->v : GTS_VERTEX (vs->v2); - GTS_OBJECT (v2)->reserved = GUINT_TO_POINTER (nv++); - - (*GTS_OBJECT (v1)->klass->write) (GTS_OBJECT (v1), fptr); - fputc ('\n', fptr); - - (*GTS_OBJECT (v2)->klass->write) (GTS_OBJECT (v2), fptr); - fputc ('\n', fptr); - - while (i--) { - CFace * cf = CFACE (scf->f); - GtsTriangle ** a, * t; - - fprintf (fptr, "%u %u", - GPOINTER_TO_UINT (g_hash_table_lookup (hash, cf->t)), - cf->flags); - if (GTS_OBJECT_CLASS (ps->s->face_class)->write) - (*GTS_OBJECT_CLASS (ps->s->face_class)->write) (GTS_OBJECT (cf), fptr); - fputc ('\n', fptr); - - a = scf->a1; - while ((t = *(a++))) - fprintf (fptr, "%u ", - GPOINTER_TO_UINT (g_hash_table_lookup (hash, t))); - fprintf (fptr, "\n"); - - a = scf->a2; - while ((t = *(a++))) - fprintf (fptr, "%u ", - GPOINTER_TO_UINT (g_hash_table_lookup (hash, t))); - fprintf (fptr, "\n"); - - g_hash_table_insert (hash, cf, GUINT_TO_POINTER (nf++)); - - scf++; - } - - gts_split_expand (vs, ps->s, ps->s->edge_class); - } - - gts_surface_foreach_vertex (ps->s, - (GtsFunc) gts_object_reset_reserved, NULL); - g_hash_table_destroy (hash); -} - -static guint surface_read (GtsSurface * surface, - GtsFile * f, - GPtrArray * vertices, - GPtrArray * faces) -{ - GtsEdge ** edges; - guint n, nv, ne, nf; - - g_return_val_if_fail (surface != NULL, 1); - g_return_val_if_fail (f != NULL, 1); - - if (f->type != GTS_INT) { - gts_file_error (f, "expecting an integer (number of vertices)"); - return f->line; - } - nv = atoi (f->token->str); - - gts_file_next_token (f); - if (f->type != GTS_INT) { - gts_file_error (f, "expecting an integer (number of edges)"); - return f->line; - } - ne = atoi (f->token->str); - - gts_file_next_token (f); - if (f->type != GTS_INT) { - gts_file_error (f, "expecting an integer (number of faces)"); - return f->line; - } - nf = atoi (f->token->str); - - gts_file_next_token (f); - if (f->type == GTS_STRING) { - if (f->type != GTS_STRING) { - gts_file_error (f, "expecting a string (GtsSurfaceClass)"); - return f->line; - } - gts_file_next_token (f); - if (f->type != GTS_STRING) { - gts_file_error (f, "expecting a string (GtsFaceClass)"); - return f->line; - } - gts_file_next_token (f); - if (f->type != GTS_STRING) { - gts_file_error (f, "expecting a string (GtsEdgeClass)"); - return f->line; - } - gts_file_next_token (f); - if (f->type != GTS_STRING) { - gts_file_error (f, "expecting a string (GtsVertexClass)"); - return f->line; - } - if (!strcmp (f->token->str, "GtsVertexBinary")) - GTS_POINT_CLASS (surface->vertex_class)->binary = TRUE; - else - gts_file_first_token_after (f, '\n'); - } - else - gts_file_first_token_after (f, '\n'); - - g_ptr_array_set_size (vertices, nv); - g_ptr_array_set_size (faces, nf); - /* allocate nv + 1 just in case nv == 0 */ - edges = g_malloc ((ne + 1)*sizeof (GtsEdge *)); - - n = 0; - while (n < nv && f->type != GTS_ERROR) { - GtsObject * new_vertex = - gts_object_new (GTS_OBJECT_CLASS (surface->vertex_class)); - - (* GTS_OBJECT_CLASS (surface->vertex_class)->read) (&new_vertex, f); - if (f->type != GTS_ERROR) { - if (!GTS_POINT_CLASS (surface->vertex_class)->binary) - gts_file_first_token_after (f, '\n'); - g_ptr_array_index (vertices, n++) = new_vertex; - } - else - gts_object_destroy (new_vertex); - } - if (f->type == GTS_ERROR) - nv = n; - if (GTS_POINT_CLASS (surface->vertex_class)->binary) - gts_file_first_token_after (f, '\n'); - - n = 0; - while (n < ne && f->type != GTS_ERROR) { - guint p1, p2; - - if (f->type != GTS_INT) - gts_file_error (f, "expecting an integer (first vertex index)"); - else { - p1 = atoi (f->token->str); - if (p1 == 0 || p1 > nv) - gts_file_error (f, "vertex index `%d' is out of range `[1,%d]'", - p1, nv); - else { - gts_file_next_token (f); - if (f->type != GTS_INT) - gts_file_error (f, "expecting an integer (second vertex index)"); - else { - p2 = atoi (f->token->str); - if (p2 == 0 || p2 > nv) - gts_file_error (f, "vertex index `%d' is out of range `[1,%d]'", - p2, nv); - else { - GtsEdge * new_edge = - gts_edge_new (surface->edge_class, - g_ptr_array_index (vertices, p1 - 1), - g_ptr_array_index (vertices, p2 - 1)); - - gts_file_next_token (f); - if (f->type != '\n') - if (GTS_OBJECT_CLASS (surface->edge_class)->read) - (*GTS_OBJECT_CLASS (surface->edge_class)->read) - ((GtsObject **) &new_edge, f); - gts_file_first_token_after (f, '\n'); - edges[n++] = new_edge; - } - } - } - } - } - if (f->type == GTS_ERROR) - ne = n; - - n = 0; - while (n < nf && f->type != GTS_ERROR) { - guint s1, s2, s3; - - if (f->type != GTS_INT) - gts_file_error (f, "expecting an integer (first edge index)"); - else { - s1 = atoi (f->token->str); - if (s1 == 0 || s1 > ne) - gts_file_error (f, "edge index `%d' is out of range `[1,%d]'", - s1, ne); - else { - gts_file_next_token (f); - if (f->type != GTS_INT) - gts_file_error (f, "expecting an integer (second edge index)"); - else { - s2 = atoi (f->token->str); - if (s2 == 0 || s2 > ne) - gts_file_error (f, "edge index `%d' is out of range `[1,%d]'", - s2, ne); - else { - gts_file_next_token (f); - if (f->type != GTS_INT) - gts_file_error (f, "expecting an integer (third edge index)"); - else { - s3 = atoi (f->token->str); - if (s3 == 0 || s3 > ne) - gts_file_error (f, "edge index `%d' is out of range `[1,%d]'", - s3, ne); - else { - GtsFace * new_face = gts_face_new (surface->face_class, - edges[s1 - 1], - edges[s2 - 1], - edges[s3 - 1]); - - gts_file_next_token (f); - if (f->type != '\n') - if (GTS_OBJECT_CLASS (surface->face_class)->read) - (*GTS_OBJECT_CLASS (surface->face_class)->read) - ((GtsObject **) &new_face, f); - gts_file_first_token_after (f, '\n'); - gts_surface_add_face (surface, new_face); - g_ptr_array_index (faces, n++) = new_face; - } - } - } - } - } - } - } - - g_free (edges); - - if (f->type == GTS_ERROR) { - gts_allow_floating_vertices = TRUE; - while (nv) - gts_object_destroy (GTS_OBJECT (g_ptr_array_index (vertices, nv-- - 1))); - gts_allow_floating_vertices = FALSE; - return f->line; - } - - return 0; -} - -/** - * gts_psurface_open: - * @klass: a #GtsPSurfaceClass. - * @s: a #GtsSurface. - * @split_class: a #GtsSplitClass to use for the #GtsSplit. - * @f: a #GtsFile. - * - * Creates a new #GtsPSurface prepared for input from the file @f - * containing a valid GTS representation of a progressive surface. The initial - * shape of the progressive surface is loaded into @s. - * - * Before being usable as such this progressive surface must be closed using - * gts_psurface_close(). While open however, the functions - * gts_psurface_get_vertex_number(), gts_psurface_min_vertex_number() and - * gts_psurface_max_vertex_number() can still be used. - * - * Returns: a new #GtsPSurface or %NULL if there was a format error while - * reading the file, in which case @f contains information about the error. - */ -GtsPSurface * gts_psurface_open (GtsPSurfaceClass * klass, - GtsSurface * s, - GtsSplitClass * split_class, - GtsFile * f) -{ - GtsPSurface * ps; - - g_return_val_if_fail (klass != NULL, NULL); - g_return_val_if_fail (s != NULL, NULL); - g_return_val_if_fail (split_class != NULL, NULL); - g_return_val_if_fail (f != NULL, NULL); - - ps = GTS_PSURFACE (gts_object_new (GTS_OBJECT_CLASS (klass))); - ps->s = s; - ps->split_class = split_class; - - ps->vertices = g_ptr_array_new (); - ps->faces = g_ptr_array_new (); - - if (surface_read (s, f, ps->vertices, ps->faces)) { - ps->s = NULL; - gts_object_destroy (GTS_OBJECT (ps)); - return NULL; - } - - ps->min = gts_surface_vertex_number (ps->s); - ps->pos = 0; - - if (f->type == GTS_INT) { - gint ns = atoi (f->token->str); - - if (ns > 0) { - g_ptr_array_set_size (ps->split, ns); - gts_file_first_token_after (f, '\n'); - } - } - - return ps; -} - -/** - * gts_psurface_read_vertex: - * @ps: a #GtsPSurface prealably created with gts_psurface_open(). - * @fp: a #GtsFile. - * - * Reads in one vertex split operation from @fp and performs the expansion. - * - * If an error occurs while reading the file, the @error field of @fp is set. - * - * Returns: the newly created #GtsSplit or %NULL if no vertex split could be - * read from @fp. - */ -GtsSplit * gts_psurface_read_vertex (GtsPSurface * ps, GtsFile * fp) -{ - guint nv, ncf; - GtsSplit * vs, * parent; - GtsSplitCFace * scf; - - g_return_val_if_fail (ps != NULL, NULL); - g_return_val_if_fail (fp != NULL, NULL); - g_return_val_if_fail (!GTS_PSURFACE_IS_CLOSED (ps), NULL); - - if (ps->pos >= ps->split->len) - return NULL; - - if (fp->type == GTS_NONE) - return NULL; - if (fp->type != GTS_INT) { - gts_file_error (fp, "expecting an integer (vertex index)"); - return NULL; - } - nv = atoi (fp->token->str); - if (nv == 0 || nv > ps->vertices->len) { - gts_file_error (fp, "vertex index `%d' is out of range `[1,%d]'", - nv, ps->vertices->len); - return NULL; - } - - gts_file_next_token (fp); - if (fp->type != GTS_INT) { - gts_file_error (fp, "expecting an integer (ncf)"); - return NULL; - } - ncf = atoi (fp->token->str); - - vs = GTS_SPLIT (gts_object_new (GTS_OBJECT_CLASS (ps->split_class))); - - vs->v = g_ptr_array_index (ps->vertices, nv - 1); - vs->v1 = vs->v2 = NULL; - vs->cfaces = NULL; - vs->ncf = 0; - - gts_file_next_token (fp); - if (fp->type != '\n') - if (GTS_OBJECT (vs)->klass->read) - (* GTS_OBJECT (vs)->klass->read) ((GtsObject **) &vs, fp); - gts_file_first_token_after (fp, '\n'); - - if (fp->type != GTS_ERROR) { - vs->v1 = gts_object_new (GTS_OBJECT_CLASS (ps->s->vertex_class)); - (* GTS_OBJECT_CLASS (ps->s->vertex_class)->read) (&(vs->v1), fp); - if (fp->type != GTS_ERROR) { - vs->v1->reserved = vs; - g_ptr_array_add (ps->vertices, vs->v1); - - gts_file_first_token_after (fp, '\n'); - - vs->v2 = gts_object_new (GTS_OBJECT_CLASS (ps->s->vertex_class)); - (*GTS_OBJECT_CLASS (ps->s->vertex_class)->read) (&(vs->v2), fp); - if (fp->type != GTS_ERROR) { - vs->v2->reserved = vs; - g_ptr_array_add (ps->vertices, vs->v2); - gts_file_first_token_after (fp, '\n'); - } - } - } - - if (fp->type != GTS_ERROR) { - scf = vs->cfaces = g_malloc (sizeof (GtsSplitCFace)*ncf); - while (fp->type != GTS_ERROR && ncf--) { - guint it, flags; - GtsFace * f; - CFace * cf; - GPtrArray * a; - - if (fp->type != GTS_INT) - gts_file_error (fp, "expecting an integer (face index)"); - else { - it = atoi (fp->token->str); - if (it == 0 || it > ps->faces->len) - gts_file_error (fp, "face index `%d' is out of range `[1,%d]'", - it, ps->faces->len); - else { - gts_file_next_token (fp); - if (fp->type != GTS_INT) - gts_file_error (fp, "expecting an integer (flags)"); - else { - flags = atoi (fp->token->str); - f = - GTS_FACE (gts_object_new (GTS_OBJECT_CLASS (ps->s->face_class))); - - gts_file_next_token (fp); - if (fp->type != '\n') - if (GTS_OBJECT (f)->klass->read) - (*GTS_OBJECT (f)->klass->read) ((GtsObject **) &f, fp); - gts_file_first_token_after (fp, '\n'); - if (fp->type != GTS_ERROR) { - scf->f = f; - - cf = (CFace *) f; - GTS_OBJECT (cf)->klass = GTS_OBJECT_CLASS (cface_class ()); - cf->parent_split = vs; - cf->t = g_ptr_array_index (ps->faces, it - 1); - cf->flags = flags; - - a = g_ptr_array_new (); - do { - if (fp->type != GTS_INT) - gts_file_error (fp, "expecting an integer (face index)"); - else { - it = atoi (fp->token->str); - if (it > ps->faces->len) - gts_file_error (fp, - "face index `%d' is out of range `[1,%d]'", - it, ps->faces->len); - else { - g_ptr_array_add (a, g_ptr_array_index (ps->faces, - it - 1)); - gts_file_next_token (fp); - } - } - } while (fp->type != GTS_ERROR && fp->type != '\n'); - gts_file_first_token_after (fp, '\n'); - g_ptr_array_add (a, NULL); - scf->a1 = (GtsTriangle **) a->pdata; - g_ptr_array_free (a, FALSE); - - if (fp->type != GTS_ERROR) { - a = g_ptr_array_new (); - do { - if (fp->type != GTS_INT) - gts_file_error (fp, "expecting an integer (face index)"); - else { - it = atoi (fp->token->str); - if (it > ps->faces->len) - gts_file_error (fp, - "face index `%d' is out of range `[1,%d]'", - it, ps->faces->len); - else { - g_ptr_array_add (a, g_ptr_array_index (ps->faces, - it - 1)); - gts_file_next_token (fp); - } - } - } while (fp->type != GTS_ERROR && fp->type != '\n'); - gts_file_first_token_after (fp, '\n'); - g_ptr_array_add (a, NULL); - scf->a2 = (GtsTriangle **) a->pdata; - g_ptr_array_free (a, FALSE); - - g_ptr_array_add (ps->faces, f); - - vs->ncf++; - scf++; - } - } - } - } - } - } - } - - if (fp->type != GTS_ERROR) { - if ((parent = GTS_OBJECT (vs->v)->reserved)) { - GTS_OBJECT (vs->v)->reserved = NULL; - if (parent->v1 == GTS_OBJECT (vs->v)) - parent->v1 = GTS_OBJECT (vs); - else { - g_assert (parent->v2 == GTS_OBJECT (vs->v)); - parent->v2 = GTS_OBJECT (vs); - } - } - g_ptr_array_index (ps->split, ps->pos++) = vs; - gts_split_expand (vs, ps->s, ps->s->edge_class); - - return vs; - } - - if (vs->v1) gts_object_destroy (vs->v1); - if (vs->v2) gts_object_destroy (vs->v2); - gts_object_destroy (GTS_OBJECT (vs)); - - return NULL; -} - -/** - * gts_psurface_close: - * @ps: a #GtsPSurface prealably created with gts_psurface_open(). - * - * Closes a progressive surface. - */ -void gts_psurface_close (GtsPSurface * ps) -{ - g_return_if_fail (ps != NULL); - g_return_if_fail (!GTS_PSURFACE_IS_CLOSED (ps)); - - g_ptr_array_free (ps->vertices, TRUE); - g_ptr_array_free (ps->faces, TRUE); - ps->faces = ps->vertices = NULL; - - gts_surface_foreach_vertex (ps->s, - (GtsFunc) gts_object_reset_reserved, NULL); - if (ps->pos > 0) - g_ptr_array_set_size (ps->split, ps->pos); - if (ps->split->len > 1) { - guint i, half = ps->split->len/2, n = ps->split->len - 1; - - for (i = 0; i < half; i++) { - gpointer p1 = g_ptr_array_index (ps->split, i); - gpointer p2 = g_ptr_array_index (ps->split, n - i); - g_ptr_array_index (ps->split, n - i) = p1; - g_ptr_array_index (ps->split, i) = p2; - } - } - ps->pos = 0; -} Index: trunk/src/3rd/gts/hsurface.c =================================================================== --- trunk/src/3rd/gts/hsurface.c (revision 1064) +++ trunk/src/3rd/gts/hsurface.c (nonexistent) @@ -1,405 +0,0 @@ -/* GTS - Library for the manipulation of triangulated surfaces - * Copyright (C) 1999 Stéphane Popinet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include -#include -#include "gts.h" - -#define HEAP_INSERT_HSPLIT(h, e) ((e)->index = gts_eheap_insert (h, e)) -#define HEAP_REMOVE_HSPLIT(h, e) (gts_eheap_remove (h, (e)->index),\ - (e)->index = NULL) - -static void hsplit_init (GtsHSplit * hsplit) -{ - hsplit->index = NULL; - hsplit->parent = NULL; - hsplit->nchild = 0; -} - -/** - * gts_hsplit_class: - * - * Returns: the #GtsHSplitClass. - */ -GtsHSplitClass * gts_hsplit_class (void) -{ - static GtsHSplitClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo hsplit_info = { - "GtsHSplit", - sizeof (GtsHSplit), - sizeof (GtsHSplitClass), - (GtsObjectClassInitFunc) NULL, - (GtsObjectInitFunc) hsplit_init, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_split_class ()), - &hsplit_info); - } - - return klass; -} - -/** - * gts_hsplit_new: - * @klass: a #GtsHSplitClass. - * @vs: a #GtsSplit. - * - * Returns: a new #GtsHSplit, hierarchical extension of @vs. - */ -GtsHSplit * gts_hsplit_new (GtsHSplitClass * klass, GtsSplit * vs) -{ - GtsHSplit * hs; - - g_return_val_if_fail (vs != NULL, NULL); - - hs = GTS_HSPLIT (gts_object_new (GTS_OBJECT_CLASS (klass))); - memcpy (hs, vs, sizeof (GtsSplit)); - GTS_OBJECT (hs)->reserved = NULL; - - return hs; -} - -/** - * gts_hsplit_collapse: - * @hs: a #GtsHSplit. - * @hsurface: a #GtsHSurface. - * - * Collapses the #GtsSplit defined by @hs, updates the expandable and - * collapsable priority heaps of @hsurface. - */ -void gts_hsplit_collapse (GtsHSplit * hs, - GtsHSurface * hsurface) -{ - GtsHSplit * parent; - GtsSplit * vs; - - g_return_if_fail (hs != NULL); - g_return_if_fail (hs->nchild == 2); - g_return_if_fail (hsurface != NULL); - - gts_split_collapse (GTS_SPLIT (hs), hsurface->s->edge_class, NULL); - - hsurface->nvertex--; - hs->nchild = 0; - HEAP_REMOVE_HSPLIT (hsurface->collapsable, hs); - HEAP_INSERT_HSPLIT (hsurface->expandable, hs); - - vs = GTS_SPLIT (hs); - if (GTS_IS_HSPLIT (vs->v1)) - HEAP_REMOVE_HSPLIT (hsurface->expandable, GTS_HSPLIT (vs->v1)); - if (GTS_IS_HSPLIT (vs->v2)) - HEAP_REMOVE_HSPLIT (hsurface->expandable, GTS_HSPLIT (vs->v2)); - - parent = hs->parent; - if (parent && ++parent->nchild == 2) - HEAP_INSERT_HSPLIT (hsurface->collapsable, parent); -} - -/** - * gts_hsplit_expand: - * @hs: a #GtsHSplit. - * @hsurface: a #GtsHSurface. - * - * Expands the #GtsSplit defined by @hs (which must be expandable) - * and updates the priority heaps of @hsurface. - */ -void gts_hsplit_expand (GtsHSplit * hs, - GtsHSurface * hsurface) -{ - GtsHSplit * parent; - GtsSplit * vs; - - g_return_if_fail (hs != NULL); - g_return_if_fail (hsurface != NULL); - g_return_if_fail (hs->nchild == 0); - - gts_split_expand (GTS_SPLIT (hs), hsurface->s, hsurface->s->edge_class); - hsurface->nvertex++; - hs->nchild = 2; - HEAP_REMOVE_HSPLIT (hsurface->expandable, hs); - HEAP_INSERT_HSPLIT (hsurface->collapsable, hs); - - vs = GTS_SPLIT (hs); - if (GTS_IS_HSPLIT (vs->v1)) - HEAP_INSERT_HSPLIT (hsurface->expandable, GTS_HSPLIT (vs->v1)); - if (GTS_IS_HSPLIT (vs->v2)) - HEAP_INSERT_HSPLIT (hsurface->expandable, GTS_HSPLIT (vs->v2)); - - parent = hs->parent; - if (parent && parent->nchild-- == 2) - HEAP_REMOVE_HSPLIT (hsurface->collapsable, parent); -} - -static void hsurface_destroy (GtsObject * object) -{ - GtsHSurface * hs = GTS_HSURFACE (object); - - gts_hsurface_traverse (hs, G_POST_ORDER, -1, - (GtsSplitTraverseFunc) gts_object_destroy, - NULL); - g_slist_free (hs->roots); - if (hs->expandable) - gts_eheap_destroy (hs->expandable); - if (hs->collapsable) - gts_eheap_destroy (hs->collapsable); - g_ptr_array_free (hs->split, TRUE); - - (* GTS_OBJECT_CLASS (gts_hsurface_class ())->parent_class->destroy) (object); -} - -static void hsurface_class_init (GtsObjectClass * klass) -{ - klass->destroy = hsurface_destroy; -} - -static void hsurface_init (GtsHSurface * hsurface) -{ - hsurface->s = NULL; - hsurface->roots = NULL; - hsurface->expandable = hsurface->collapsable = NULL; - hsurface->split = g_ptr_array_new (); - hsurface->nvertex = 0; -} - -/** - * gts_hsurface_class: - * - * Returns: the #GtsHSurfaceClass. - */ -GtsHSurfaceClass * gts_hsurface_class (void) -{ - static GtsHSurfaceClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo hsurface_info = { - "GtsHSurface", - sizeof (GtsHSurface), - sizeof (GtsHSurfaceClass), - (GtsObjectClassInitFunc) hsurface_class_init, - (GtsObjectInitFunc) hsurface_init, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (gts_object_class (), - &hsurface_info); - } - - return klass; -} - -/** - * gts_hsurface_new: - * @klass: a #GtsHSurfaceClass. - * @hsplit_class: a #GtsHSplitClass. - * @psurface: a #GtsPSurface. - * @expand_key: a #GtsKeyFunc used to order the priority heap of expandable - * #GtsHSplit. - * @expand_data: data to be passed to @expand_key. - * @collapse_key: a #GtsKeyFunc used to order the priority heap of collapsable - * #GtsHSplit. - * @collapse_data: data to be passed to @collapsed_key. - * - * Returns: a new #GtsHSurface, hierarchical extension of @psurface - * and using #GtsHSplit of class @hsplit_class. Note that @psurface is - * destroyed in the process. - */ -GtsHSurface * gts_hsurface_new (GtsHSurfaceClass * klass, - GtsHSplitClass * hsplit_class, - GtsPSurface * psurface, - GtsKeyFunc expand_key, - gpointer expand_data, - GtsKeyFunc collapse_key, - gpointer collapse_data) -{ - GtsHSurface * hsurface; - - g_return_val_if_fail (klass != NULL, NULL); - g_return_val_if_fail (hsplit_class != NULL, NULL); - g_return_val_if_fail (psurface != NULL, NULL); - g_return_val_if_fail (expand_key != NULL, NULL); - g_return_val_if_fail (collapse_key != NULL, NULL); - - hsurface = GTS_HSURFACE (gts_object_new (GTS_OBJECT_CLASS (klass))); - hsurface->s = psurface->s; - hsurface->expandable = gts_eheap_new (expand_key, expand_data); - hsurface->collapsable = gts_eheap_new (collapse_key, collapse_data); - g_ptr_array_set_size (hsurface->split, psurface->split->len); - - while (gts_psurface_remove_vertex (psurface)) - ; - while (psurface->pos) { - GtsSplit * vs = g_ptr_array_index (psurface->split, psurface->pos - 1); - GtsHSplit * hs = gts_hsplit_new (hsplit_class, vs); - - g_ptr_array_index (hsurface->split, psurface->pos - 1) = hs; - psurface->pos--; - - hs->parent = GTS_OBJECT (vs)->reserved; - if (hs->parent) { - GtsSplit * vsp = GTS_SPLIT (hs->parent); - - if (vsp->v1 == GTS_OBJECT (vs)) { - g_assert (vsp->v2 != GTS_OBJECT (vs)); - vsp->v1 = GTS_OBJECT (hs); - } - else { - g_assert (vsp->v2 == GTS_OBJECT (vs)); - vsp->v2 = GTS_OBJECT (hs); - } - } - else - hsurface->roots = g_slist_prepend (hsurface->roots, hs); - - hs->nchild = 0; - if (GTS_IS_SPLIT (vs->v1)) - GTS_OBJECT (vs->v1)->reserved = hs; - else - hs->nchild++; - if (GTS_IS_SPLIT (vs->v2)) - GTS_OBJECT (vs->v2)->reserved = hs; - else - hs->nchild++; - - gts_split_expand (vs, psurface->s, psurface->s->edge_class); - - if (hs->nchild == 2) - HEAP_INSERT_HSPLIT (hsurface->collapsable, hs); - } - - hsurface->nvertex = gts_surface_vertex_number (hsurface->s); - gts_object_destroy (GTS_OBJECT (psurface)); - - return hsurface; -} - -/** - * gts_hsurface_traverse: - * @hsurface: a #GtsHSurface. - * @order: the order in which nodes are visited - G_PRE_ORDER or G_POST_ORDER. - * @depth: the maximum depth of the traversal. Nodes below this depth - * will not be visited. If max_depth is -1 all nodes in the tree are - * visited. If depth is 1, only the root is visited. If depth is 2, - * the root and its children are visited. And so on. - * @func: the function to call for each visited #GtsHSplit. - * @data: user data to pass to the function. - * - * Traverses a hierarchical surface starting from its roots. It calls - * the given function for each #GtsHSplit visited. - * See also gts_split_traverse(). - */ -void gts_hsurface_traverse (GtsHSurface * hsurface, - GTraverseType order, - gint depth, - GtsSplitTraverseFunc func, - gpointer data) -{ - GSList * i; - - g_return_if_fail (hsurface != NULL); - g_return_if_fail (func != NULL); - g_return_if_fail (order < G_LEVEL_ORDER); - g_return_if_fail (depth == -1 || depth > 0); - - i = hsurface->roots; - while (i) { - gts_split_traverse (i->data, order, depth, func, data); - i = i->next; - } -} - -/** - * gts_hsurface_foreach: - * @hsurface: a #GtsHSurface. - * @order: the order in which #GtsHSplit are visited - G_PRE_ORDER or - * G_POST_ORDER. - * @func: the function to call for each visited #GtsHSplit. - * @data: user data to pass to the function. - * - * Starts by expanding all the #GtsHSplit of @hsurface. If @order is - * G_PRE_ORDER, calls @func for each #GtsHSplit and collapses it. If - * order is G_POST_ORDER, collapses each #GtsHSplit first and then - * calls @func. The traversal can be halted at any point by returning - * TRUE from func. - */ -void gts_hsurface_foreach (GtsHSurface * hsurface, - GTraverseType order, - GtsFunc func, - gpointer data) -{ - GtsHSplit * hs; - guint i = 0, len; - gboolean stop = FALSE; - - g_return_if_fail (hsurface != NULL); - g_return_if_fail (func != NULL); - g_return_if_fail (order == G_PRE_ORDER || order == G_POST_ORDER); - - while ((hs = gts_eheap_top (hsurface->expandable, NULL))) - gts_hsplit_expand (hs, hsurface); - - len = hsurface->split->len; - switch (order) { - case G_PRE_ORDER: - while (i < len && !stop) { - GtsHSplit * hs = g_ptr_array_index (hsurface->split, i); - stop = (*func) (hs, data); - if (!stop) - gts_hsplit_collapse (hs, hsurface); - i++; - } - break; - case G_POST_ORDER: - while (i < len && !stop) { - GtsHSplit * hs = g_ptr_array_index (hsurface->split, i); - gts_hsplit_collapse (hs, hsurface); - stop = (*func) (hs, data); - i++; - } - break; - default: - g_assert_not_reached (); - } -} - -/** - * gts_hsurface_height: - * @hsurface: a #GtsHSurface. - * - * Returns: the maximum height of the tree described by @hsurface. - */ -guint gts_hsurface_height (GtsHSurface * hsurface) -{ - GSList * i; - guint height = 0; - - g_return_val_if_fail (hsurface != NULL, 0); - - i = hsurface->roots; - while (i) { - guint tmp_height = gts_split_height (i->data); - if (tmp_height > height) - height = tmp_height; - i = i->next; - } - - return height; -} Index: trunk/src/3rd/gts/graph.c =================================================================== --- trunk/src/3rd/gts/graph.c (revision 1064) +++ trunk/src/3rd/gts/graph.c (nonexistent) @@ -1,1776 +0,0 @@ -/* GTS - Library for the manipulation of triangulated surfaces - * Copyright (C) 1999 Stéphane Popinet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include -#include -#include "gts.h" - -/* GtsGNode */ - -gboolean gts_allow_floating_gnodes = FALSE; - -static void gnode_remove_container (GtsContainee * i, GtsContainer * c) -{ - (* GTS_CONTAINEE_CLASS (GTS_OBJECT_CLASS (gts_gnode_class ())->parent_class)->remove_container) (i, c); - if (GTS_SLIST_CONTAINEE (i)->containers == NULL && - !gts_allow_floating_gnodes && - !GTS_OBJECT_DESTROYED(GTS_OBJECT (i))) - gts_object_destroy (GTS_OBJECT (i)); -} - -static void gnode_class_init (GtsGNodeClass * klass) -{ - klass->weight = NULL; - - GTS_CONTAINEE_CLASS (klass)->remove_container = gnode_remove_container; -} - -static void gnode_init (GtsGNode * n) -{ - n->level = 0; -} - -/** - * gts_gnode_class: - * - * Returns: the #GtsGNodeClass. - */ -GtsGNodeClass * gts_gnode_class (void) -{ - static GtsGNodeClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo gnode_info = { - "GtsGNode", - sizeof (GtsGNode), - sizeof (GtsGNodeClass), - (GtsObjectClassInitFunc) gnode_class_init, - (GtsObjectInitFunc) gnode_init, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = - gts_object_class_new (GTS_OBJECT_CLASS (gts_slist_container_class ()), - &gnode_info); - } - - return klass; -} - -/** - * gts_gnode_new: - * @klass: a #GtsGNodeClass. - * - * Returns: a new #GtsGNode. - */ -GtsGNode * gts_gnode_new (GtsGNodeClass * klass) -{ - GtsGNode * object; - - object = GTS_GNODE (gts_object_new (GTS_OBJECT_CLASS (klass))); - - return object; -} - -/** - * gts_gnode_foreach_neighbor: - * @n: a #GtsGNode. - * @g: a #GtsGraph or %NULL. - * @func: a #GtsFunc. - * @data: user data to be passed to @func. - * - * Calls @func for each neighbor #GtsGNode of @n (belonging to @g if - * @g is not %NULL. - */ -void gts_gnode_foreach_neighbor (GtsGNode * n, - GtsGraph * g, - GtsFunc func, - gpointer data) -{ - GSList * i; - - g_return_if_fail (n != NULL); - g_return_if_fail (func != NULL); - - i = GTS_SLIST_CONTAINER (n)->items; - while (i) { - GtsGNode * n1 = GTS_GNODE_NEIGHBOR (n, i->data); - if (g == NULL || gts_containee_is_contained (GTS_CONTAINEE (n1), - GTS_CONTAINER (g))) - (* func) (n1, data); - i = i->next; - } -} - -/** - * gts_gnode_foreach_edge: - * @n: a #GtsGNode. - * @g: a #GtsGraph or %NULL. - * @func: a #GtsFunc. - * @data: user data to be passed to @func. - * - * Calls @func for each #GtsGEdge connecting @n to another #GtsGNode - * (belonging to @g if @g is not %NULL. - */ -void gts_gnode_foreach_edge (GtsGNode * n, - GtsGraph * g, - GtsFunc func, - gpointer data) -{ - GSList * i; - - g_return_if_fail (n != NULL); - g_return_if_fail (func != NULL); - - i = GTS_SLIST_CONTAINER (n)->items; - while (i) { - GtsGNode * n1 = GTS_GNODE_NEIGHBOR (n, i->data); - if (g == NULL || gts_containee_is_contained (GTS_CONTAINEE (n1), - GTS_CONTAINER (g))) - (* func) (i->data, data); - i = i->next; - } -} - -/** - * gts_gnode_degree: - * @n: a #GtsGNode. - * @g: a #GtsGraph or %NULL. - * - * Returns: the number of neighbors of @n (belonging to @g if @g is not %NULL). - */ -guint gts_gnode_degree (GtsGNode * n, - GtsGraph * g) -{ - GSList * i; - guint nn = 0; - - g_return_val_if_fail (n != NULL, 0); - - i = GTS_SLIST_CONTAINER (n)->items; - while (i) { - GtsGNode * n1 = GTS_GNODE_NEIGHBOR (n, i->data); - if (g == NULL || gts_containee_is_contained (GTS_CONTAINEE (n1), - GTS_CONTAINER (g))) - nn++; - i = i->next; - } - - return nn; -} - -/** - * gts_gnode_move_cost: - * @n: a #GtsGNode. - * @src: a #GtsGraph containing @n. - * @dst: another #GtsGraph. - * - * Returns: the cost (increase in the sum of the weights of the edges cut) of - * moving @n from @src to @dst. - */ -gfloat gts_gnode_move_cost (GtsGNode * n, - GtsGraph * src, - GtsGraph * dst) -{ - GSList * i; - gfloat cost = 0.; - - g_return_val_if_fail (n != NULL, G_MAXFLOAT); - g_return_val_if_fail (src != NULL, G_MAXFLOAT); - g_return_val_if_fail (dst != NULL, G_MAXFLOAT); - g_return_val_if_fail (gts_containee_is_contained (GTS_CONTAINEE (n), - GTS_CONTAINER (src)), - G_MAXFLOAT); - - i = GTS_SLIST_CONTAINER (n)->items; - while (i) { - GtsGEdge * ge = i->data; - GtsGNode * neighbor = GTS_GNODE_NEIGHBOR (n, ge); - - if (gts_containee_is_contained (GTS_CONTAINEE (neighbor), - GTS_CONTAINER (src))) - cost += gts_gedge_weight (ge); - else if (gts_containee_is_contained (GTS_CONTAINEE (neighbor), - GTS_CONTAINER (dst))) - cost -= gts_gedge_weight (ge); - i = i->next; - } - - return cost; -} - -/** - * gts_gnode_weight: - * @n: a #GtsGNode. - * - * Returns: the weight of @n as defined by the weight() method of the - * #GtsGNodeClass. - */ -gfloat gts_gnode_weight (GtsGNode * n) -{ - g_return_val_if_fail (n != NULL, 0.); - - if (GTS_GNODE_CLASS (GTS_OBJECT (n)->klass)->weight) - return (* GTS_GNODE_CLASS (GTS_OBJECT (n)->klass)->weight) (n); - return 1.; -} - -/* GtsNGNode */ - -static void ngnode_init (GtsNGNode * n) -{ - n->id = 0; -} - -/** - * gts_ngnode_class: - * - * Returns: the #GtsNGNodeClass. - */ -GtsNGNodeClass * gts_ngnode_class (void) -{ - static GtsNGNodeClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo ngnode_info = { - "GtsNGNode", - sizeof (GtsNGNode), - sizeof (GtsNGNodeClass), - (GtsObjectClassInitFunc) NULL, - (GtsObjectInitFunc) ngnode_init, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_gnode_class ()), - &ngnode_info); - } - - return klass; -} - -/** - * gts_ngnode_new: - * @klass: a #GtsNGNodeClass. - * - * Returns: a new #GtsNGNode with identity @id. - */ -GtsNGNode * gts_ngnode_new (GtsNGNodeClass * klass, - guint id) -{ - GtsNGNode * n; - - n = GTS_NGNODE (gts_gnode_new (GTS_GNODE_CLASS (klass))); - n->id = id; - - return n; -} - -/* GtsWGNode */ - -static gfloat wgnode_weight (GtsGNode * n) -{ - return GTS_WGNODE (n)->weight; -} - -static void wgnode_class_init (GtsWGNodeClass * klass) -{ - GTS_GNODE_CLASS (klass)->weight = wgnode_weight; -} - -static void wgnode_init (GtsWGNode * n) -{ - n->weight = 1.; -} - -/** - * gts_wgnode_class: - * - * Returns: the #GtsWGNodeClass. - */ -GtsWGNodeClass * gts_wgnode_class (void) -{ - static GtsWGNodeClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo wgnode_info = { - "GtsWGNode", - sizeof (GtsWGNode), - sizeof (GtsWGNodeClass), - (GtsObjectClassInitFunc) wgnode_class_init, - (GtsObjectInitFunc) wgnode_init, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_gnode_class ()), - &wgnode_info); - } - - return klass; -} - -/** - * gts_wgnode_new: - * @klass: a #GtsWGNodeClass. - * @weight: the weight of the #GtsWGNode to create. - * - * Returns: a new #GtsWGNode of weight @weight. - */ -GtsWGNode * gts_wgnode_new (GtsWGNodeClass * klass, - gfloat weight) -{ - GtsWGNode * n; - - n = GTS_WGNODE (gts_gnode_new (GTS_GNODE_CLASS (klass))); - n->weight = weight; - - return n; -} - -/* GtsPNode */ - -static void pnode_write (GtsGNode * n, FILE * fp) -{ - if (GTS_IS_NVERTEX (GTS_PNODE (n)->data)) - fprintf (fp, "label=\"%p:%s\",", - GTS_PNODE (n)->data, - GTS_NVERTEX (GTS_PNODE (n)->data)->name); - else - fprintf (fp, "label=\"%p\",", GTS_PNODE (n)->data); -} - -static void pnode_class_init (GtsPNodeClass * klass) -{ - GTS_GNODE_CLASS (klass)->write = pnode_write; -} - -static void pnode_init (GtsPNode * pn) -{ - pn->data = NULL; -} - -/** - * gts_pnode_class: - * - * Returns: the #GtsPNodeClass. - */ -GtsPNodeClass * gts_pnode_class (void) -{ - static GtsPNodeClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo pnode_info = { - "GtsPNode", - sizeof (GtsPNode), - sizeof (GtsPNodeClass), - (GtsObjectClassInitFunc) pnode_class_init, - (GtsObjectInitFunc) pnode_init, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_gnode_class ()), - &pnode_info); - } - - return klass; -} - -/** - * gts_pnode_new: - * @klass: a #GtsPNodeClass. - * @data: user data. - * - * Returns: a new #GtsPNode associated with @data. - */ -GtsPNode * gts_pnode_new (GtsPNodeClass * klass, gpointer data) -{ - GtsPNode * pn; - - pn = GTS_PNODE (gts_object_new (GTS_OBJECT_CLASS (klass))); - pn->data = data; - - return pn; -} - -/* GtsFNode */ - -static void fnode_write (GtsGNode * n, FILE * fp) -{ - fprintf (fp, "label=\"%p\",", GTS_FNODE (n)->f); -} - -static void fnode_class_init (GtsGNodeClass * klass) -{ - klass->write = fnode_write; -} - -static void fnode_init (GtsFNode * fn) -{ - fn->f = NULL; -} - -/** - * gts_fnode_class: - * - * Returns: the #GtsFNodeClass. - */ -GtsFNodeClass * gts_fnode_class (void) -{ - static GtsFNodeClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo fnode_info = { - "GtsFNode", - sizeof (GtsFNode), - sizeof (GtsFNodeClass), - (GtsObjectClassInitFunc) fnode_class_init, - (GtsObjectInitFunc) fnode_init, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_gnode_class ()), - &fnode_info); - } - - return klass; -} - -/** - * gts_fnode_new: - * @klass: a #GtsFNodeClass. - * @f: a #GtsFace. - * - * Returns: a new #GtsFNode associated with face @f. - */ -GtsFNode * gts_fnode_new (GtsFNodeClass * klass, GtsFace * f) -{ - GtsFNode * fn; - - g_return_val_if_fail (f != NULL, NULL); - - fn = GTS_FNODE (gts_object_new (GTS_OBJECT_CLASS (klass))); - fn->f = f; - - return fn; -} - -/* GtsGEdge */ - -static void gedge_destroy (GtsObject * object) -{ - GtsGEdge * ge = GTS_GEDGE (object); - - if (ge->n1) - gts_container_remove (GTS_CONTAINER (ge->n1), GTS_CONTAINEE (ge)); - if (ge->n2) - gts_container_remove (GTS_CONTAINER (ge->n2), GTS_CONTAINEE (ge)); - - (* GTS_OBJECT_CLASS (gts_gedge_class ())->parent_class->destroy) (object); -} - -static void gedge_remove_container (GtsContainee * i, GtsContainer * c) -{ - GtsGEdge * ge = GTS_GEDGE (i); - GtsGNode * n1 = ge->n1; - GtsGNode * n2 = ge->n2; - - ge->n1 = ge->n2 = NULL; - if (n1 != NULL && n2 != NULL) { - if (GTS_CONTAINER (n1) == c) { - if (n2 && n2 != n1) gts_container_remove (GTS_CONTAINER (n2), i); - } - else if (GTS_CONTAINER (n2) == c) { - if (n1 && n1 != n2) gts_container_remove (GTS_CONTAINER (n1), i); - } - else - g_assert_not_reached (); - (* GTS_OBJECT_CLASS (gts_gedge_class ())->parent_class->destroy) - (GTS_OBJECT (i)); - } -} - -static gboolean gedge_is_contained (GtsContainee * i, GtsContainer * c) -{ - GtsGEdge * ge = GTS_GEDGE (i); - - if (GTS_CONTAINER (ge->n1) == c || GTS_CONTAINER (ge->n2) == c) - return TRUE; - return FALSE; -} - -static void gedge_class_init (GtsGEdgeClass * klass) -{ - klass->link = NULL; - klass->weight = NULL; - - GTS_CONTAINEE_CLASS (klass)->remove_container = gedge_remove_container; - GTS_CONTAINEE_CLASS (klass)->is_contained = gedge_is_contained; - - GTS_OBJECT_CLASS (klass)->destroy = gedge_destroy; -} - -static void gedge_init (GtsGEdge * object) -{ - object->n1 = object->n2 = NULL; -} - -/** - * gts_gedge_class: - * - * Returns: the #GtsGEdgeClass. - */ -GtsGEdgeClass * gts_gedge_class (void) -{ - static GtsGEdgeClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo gedge_info = { - "GtsGEdge", - sizeof (GtsGEdge), - sizeof (GtsGEdgeClass), - (GtsObjectClassInitFunc) gedge_class_init, - (GtsObjectInitFunc) gedge_init, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_containee_class ()), - &gedge_info); - } - - return klass; -} - -/** - * gts_gedge_new: - * @klass: a #GtsGEdgeClass. - * @n1: a #GtsGNode. - * @n2: another #GtsGNode. - * - * Returns: a new #GtsGEdge linking @n1 and @n2. - */ -GtsGEdge * gts_gedge_new (GtsGEdgeClass * klass, GtsGNode * n1, GtsGNode * n2) -{ - GtsGEdge * object; - - g_return_val_if_fail (n1 != NULL, NULL); - g_return_val_if_fail (n2 != NULL, NULL); - - object = GTS_GEDGE (gts_object_new (GTS_OBJECT_CLASS (klass))); - object->n1 = n1; - gts_container_add (GTS_CONTAINER (n1), GTS_CONTAINEE (object)); - object->n2 = n2; - if (n1 != n2) - gts_container_add (GTS_CONTAINER (n2), GTS_CONTAINEE (object)); - - if (klass->link) - object = (* klass->link) (object, n1, n2); - - return object; -} - -/** - * gts_gedge_weight: - * @e: a #GtsGEdge. - * - * Returns: the weight of edge @e as defined by the weight() method of - * #GtsGEdgeClass. - */ -gfloat gts_gedge_weight (GtsGEdge * e) -{ - g_return_val_if_fail (e != NULL, 0.); - - if (GTS_GEDGE_CLASS (GTS_OBJECT (e)->klass)->weight) - return (* GTS_GEDGE_CLASS (GTS_OBJECT (e)->klass)->weight) (e); - return 1.; -} - -/* GtsPGEdge */ - -static void pgedge_write (GtsGEdge * ge, FILE * fp) -{ - if (GTS_IS_EDGE (GTS_PGEDGE (ge)->data)) { - GtsEdge * e = GTS_PGEDGE (ge)->data; - guint n = g_slist_length (e->triangles); - - fprintf (fp, "label=\"%p:%s:%d\",color=%s", e, - GTS_IS_NEDGE (e) ? GTS_NEDGE (e)->name : "", - n, - n == 0 ? "black" : - n == 1 ? "blue" : - n == 2 ? "green" : - n == 3 ? "violet" : - n == 4 ? "red" : - "pink"); - } - else - fprintf (fp, "label=\"%p\",", GTS_PGEDGE (ge)->data); -} - -static void pgedge_class_init (GtsPGEdgeClass * klass) -{ - GTS_GEDGE_CLASS (klass)->write = pgedge_write; -} - -static void pgedge_init (GtsPGEdge * e) -{ - e->data = NULL; -} - -/** - * gts_pgedge_class: - * - * Returns: the #GtsPGEdgeClass. - */ -GtsPGEdgeClass * gts_pgedge_class (void) -{ - static GtsPGEdgeClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo pgedge_info = { - "GtsPGEdge", - sizeof (GtsPGEdge), - sizeof (GtsPGEdgeClass), - (GtsObjectClassInitFunc) pgedge_class_init, - (GtsObjectInitFunc) pgedge_init, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_gedge_class ()), - &pgedge_info); - } - - return klass; -} - -/** - * gts_pgedge_new: - * @klass: a #GtsPGEdgeClass. - * @n1: a #GtsGNode. - * @n2: another #GtsGNode. - * @data: user data. - * - * Returns: a new #GtsPGEdge associated with @data linking @n1 and @n2. - */ -GtsPGEdge * gts_pgedge_new (GtsPGEdgeClass * klass, - GtsGNode * g1, - GtsGNode * g2, - gpointer data) -{ - GtsPGEdge * we; - - we = GTS_PGEDGE (gts_gedge_new (GTS_GEDGE_CLASS (klass), g1, g2)); - we->data = data; - - return we; -} - -/* GtsWGEdge */ - -static gfloat wgedge_weight (GtsGEdge * e) -{ - return GTS_WGEDGE (e)->weight; -} - -static void wgedge_class_init (GtsWGEdgeClass * klass) -{ - GTS_GEDGE_CLASS (klass)->weight = wgedge_weight; -} - -static void wgedge_init (GtsWGEdge * e) -{ - e->weight = 1.; -} - -/** - * gts_wgedge_class: - * - * Returns: the #GtsWGEdgeClass. - */ -GtsWGEdgeClass * gts_wgedge_class (void) -{ - static GtsWGEdgeClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo wgedge_info = { - "GtsWGEdge", - sizeof (GtsWGEdge), - sizeof (GtsWGEdgeClass), - (GtsObjectClassInitFunc) wgedge_class_init, - (GtsObjectInitFunc) wgedge_init, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_gedge_class ()), - &wgedge_info); - } - - return klass; -} - -/** - * gts_wgedge_new: - * @klass: a #GtsWGEdgeClass. - * @n1: a #GtsGNode. - * @n2: another #GtsGNode. - * @weight: the weight of the new edge. - * - * Returns: a new #GtsWGEdge of weight @weight linking @n1 and @n2. - */ -GtsWGEdge * gts_wgedge_new (GtsWGEdgeClass * klass, - GtsGNode * g1, - GtsGNode * g2, - gfloat weight) -{ - GtsWGEdge * we; - - we = GTS_WGEDGE (gts_gedge_new (GTS_GEDGE_CLASS (klass), g1, g2)); - we->weight = weight; - - return we; -} - -/* GtsGraph */ - -static void graph_init (GtsGraph * g) -{ - g->graph_class = gts_graph_class (); - g->node_class = gts_gnode_class (); - g->edge_class = gts_gedge_class (); -} - -static void graph_write (GtsObject * object, FILE * fp) -{ - GtsGraph * graph = GTS_GRAPH (object); - - fprintf (fp, " %s %s %s", - object->klass->info.name, - GTS_OBJECT_CLASS (graph->node_class)->info.name, - GTS_OBJECT_CLASS (graph->edge_class)->info.name); -} - -static void graph_read (GtsObject ** object, GtsFile * f) -{ - GtsObjectClass * klass; - - if (f->type != GTS_STRING) { - gts_file_error (f, "expecting a string (GtsGNodeClass)"); - return; - } - klass = gts_object_class_from_name (f->token->str); - if (klass == NULL) { - gts_file_error (f, "unknown class `%s'", f->token->str); - return; - } - if (!gts_object_class_is_from_class (klass, gts_gnode_class ())) { - gts_file_error (f, "class `%s' is not a GtsGNodeClass", f->token->str); - return; - } - GTS_GRAPH (*object)->node_class = GTS_GNODE_CLASS (klass); - gts_file_next_token (f); - - if (f->type != GTS_STRING) { - gts_file_error (f, "expecting a string (GtsGEdgeClass)"); - return; - } - klass = gts_object_class_from_name (f->token->str); - if (klass == NULL) { - gts_file_error (f, "unknown class `%s'", f->token->str); - return; - } - if (!gts_object_class_is_from_class (klass, gts_gedge_class ())) { - gts_file_error (f, "class `%s' is not a GtsGEdgeClass", f->token->str); - return; - } - GTS_GRAPH (*object)->edge_class = GTS_GEDGE_CLASS (klass); - gts_file_next_token (f); -} - -static void graph_class_init (GtsGraphClass * klass) -{ - klass->weight = NULL; - - GTS_OBJECT_CLASS (klass)->write = graph_write; - GTS_OBJECT_CLASS (klass)->read = graph_read; -} - -/** - * gts_graph_class: - * - * Returns: the #GtsGraphClass. - */ -GtsGraphClass * gts_graph_class (void) -{ - static GtsGraphClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo graph_info = { - "GtsGraph", - sizeof (GtsGraph), - sizeof (GtsGraphClass), - (GtsObjectClassInitFunc) graph_class_init, - (GtsObjectInitFunc) graph_init, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_hash_container_class ()), - &graph_info); - } - - return klass; -} - -/** - * gts_graph_new: - * @klass: a #GtsGraphClass. - * @node_class: a #GtsGNodeClass. - * @edge_class: a #GtsGEdgeClass. - * - * Returns: a new #GtsGraph using @node_class and @edge_class as node types. - */ -GtsGraph * gts_graph_new (GtsGraphClass * klass, - GtsGNodeClass * node_class, - GtsGEdgeClass * edge_class) -{ - GtsGraph * g; - - g_return_val_if_fail (klass != NULL, NULL); - g_return_val_if_fail (node_class != NULL, NULL); - g_return_val_if_fail (edge_class != NULL, NULL); - - g = GTS_GRAPH (gts_object_new (GTS_OBJECT_CLASS (klass))); - g->node_class = node_class; - g->edge_class = edge_class; - - return g; -} - -static void compute_degree (GtsGNode * n, gpointer * data) -{ - GtsGraph * g = data[0]; - GtsRange * degree = data[1]; - - gts_range_add_value (degree, gts_gnode_degree (n, g)); -} - -/** - * gts_graph_print_stats: - * @g: a #GtsGraph. - * @fp: a file pointer. - * - * Writes to @fp a summary of the properties of @g. - */ -void gts_graph_print_stats (GtsGraph * g, FILE * fp) -{ - GtsRange degree; - gpointer data[2]; - - g_return_if_fail (g != NULL); - g_return_if_fail (fp != NULL); - - fprintf (fp, "# nodes: %d weight: %g\n", - gts_container_size (GTS_CONTAINER (g)), - gts_graph_weight (g)); - fprintf (fp, "# degree: "); - gts_range_init (°ree); - data[0] = g; - data[1] = °ree; - gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) compute_degree, data); - gts_range_update (°ree); - gts_range_print (°ree, fp); - fprintf (fp, "\n"); - fprintf (fp, "# edges cut: %d edges cut weight: %g\n", - gts_graph_edges_cut (g), - gts_graph_edges_cut_weight (g)); -} - -struct _GtsGraphTraverse { - GtsFifo * q; - GtsGraph * g; -}; - -static void reset_level (GtsGNode * n) -{ - n->level = 0; -} - -/** - * gts_graph_traverse_new: - * @g: a #GtsGraph. - * @n: a #GtsGNode belonging to @g. - * @type: the type of traversal. - * @reinit: if %TRUE, the traversal is reinitialized. - * - * Returns: a new #GtsGraphTraverse initialized for the traversal of - * @g of type @type, starting from @n. - */ -GtsGraphTraverse * gts_graph_traverse_new (GtsGraph * g, - GtsGNode * n, - GtsTraverseType type, - gboolean reinit) -{ - GtsGraphTraverse * t; - - g_return_val_if_fail (g != NULL, NULL); - g_return_val_if_fail (n != NULL, NULL); - g_return_val_if_fail (gts_containee_is_contained (GTS_CONTAINEE (n), - GTS_CONTAINER (g)), - NULL); - - if (reinit) - gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) reset_level, NULL); - - t = g_malloc (sizeof (GtsGraphTraverse)); - t->q = gts_fifo_new (); - t->g = g; - n->level = 1; - gts_fifo_push (t->q, n); - - return t; -} - -static void push_neighbor (GtsGNode * n, gpointer * data) -{ - GtsFifo * q = data[0]; - GtsGNode * u = data[1]; - - if (n->level == 0) { - n->level = u->level + 1; - gts_fifo_push (q, n); - } -} - -/** - * gts_graph_traverse_next: - * @t: a #GtsGraphTraverse. - * - * Returns: the next #GtsGNode of the traversal defined by @t or %NULL - * if the traversal is complete. - */ -GtsGNode * gts_graph_traverse_next (GtsGraphTraverse * t) -{ - GtsGNode * u; - - g_return_val_if_fail (t != NULL, NULL); - - u = gts_fifo_pop (t->q); - if (u) { - gpointer data[2]; - - data[0] = t->q; - data[1] = u; - gts_gnode_foreach_neighbor (u, t->g, (GtsFunc) push_neighbor, data); - } - - return u; -} - -/** - * gts_graph_traverse_what_next: - * @t: a #GtsGraphTraverse. - * - * Returns: the next #GtsGNode of the traversal defined by @t or %NULL - * if the traversal is complete but without advancing the traversal. - */ -GtsGNode * gts_graph_traverse_what_next (GtsGraphTraverse * t) -{ - g_return_val_if_fail (t != NULL, NULL); - - return gts_fifo_top (t->q); -} - -/** - * gts_graph_traverse_destroy: - * @t: a #GtsGraphTraverse. - * - * Frees all the memory allocated for @t. - */ -void gts_graph_traverse_destroy (GtsGraphTraverse * t) -{ - g_return_if_fail (t != NULL); - - gts_fifo_destroy (t->q); - g_free (t); -} - -static void edge_foreach_node (GtsGNode * n, gpointer * info) -{ - GtsFunc func = (GtsFunc) info[0]; - gpointer data = info[1]; - GHashTable * hash = info[2]; - GSList * i = GTS_SLIST_CONTAINER (n)->items; - - while (i) { - GtsGEdge * e = i->data; - if (!g_hash_table_lookup (hash, e)) { - (* func) (e, data); - g_hash_table_insert (hash, e, e); - } - i = i->next; - } -} - -/** - * gts_graph_foreach_edge: - * @g: a #GtsGraph. - * @func: a #GtsFunc. - * @data: user data to be passed to @func. - * - * Calls @func for each #GtsEdge of @g. - */ -void gts_graph_foreach_edge (GtsGraph * g, GtsFunc func, gpointer data) -{ - gpointer info[3]; - GHashTable * hash; - - g_return_if_fail (g != NULL); - g_return_if_fail (func != NULL); - - info[0] = func; - info[1] = data; - info[2] = hash = g_hash_table_new (NULL, NULL); - gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) edge_foreach_node, info); - g_hash_table_destroy (hash); -} - -/** - * gts_graph_weight: - * @g: a #GtsGraph. - * - * Returns: the weight of graph @g as defined by the weight() method - * of #GtsGraphClass. - */ -gfloat gts_graph_weight (GtsGraph * g) -{ - g_return_val_if_fail (g != NULL, 0.); - - if (GTS_GRAPH_CLASS (GTS_OBJECT (g)->klass)->weight) - return (* GTS_GRAPH_CLASS (GTS_OBJECT (g)->klass)->weight) (g); - return (gfloat) gts_container_size (GTS_CONTAINER (g)); -} - -/** - * gts_graph_distance_sum: - * @g: a #GtsGraph. - * @center: a #GtsGNode of @g. - * - * Returns: the sum of the distances between all the other #GtsGNode - * of @g and @center. - */ -guint gts_graph_distance_sum (GtsGraph * g, GtsGNode * center) -{ - GtsGraphTraverse * t; - GtsGNode * n; - guint sum = 0; - - g_return_val_if_fail (g != NULL, 0); - g_return_val_if_fail (center != NULL, 0); - - t = gts_graph_traverse_new (g, center, GTS_BREADTH_FIRST, TRUE); - while ((n = gts_graph_traverse_next (t))) - sum += n->level - 1; - gts_graph_traverse_destroy (t); - - return sum; -} - -/** - * gts_graph_farthest: - * @g: a #GtsGraph. - * @gnodes: a list of #GtsGNode belonging to @g. - * - * Returns: the #GtsGNode belonging to @g and farthest from all the nodes in - * @gnodes (hmmm, definition of "farthest"?). - */ -GtsGNode * gts_graph_farthest (GtsGraph * g, GSList * gnodes) -{ - GtsGNode * farthest = NULL; - GSList * i; - gboolean reinit = TRUE, changed = TRUE; - guint level = 1; - - g_return_val_if_fail (g != NULL, NULL); - - /* initialize traversals */ - i = gnodes; - while (i) { - GTS_OBJECT (i->data)->reserved = - gts_graph_traverse_new (g, i->data, GTS_BREADTH_FIRST, reinit); - reinit = FALSE; - i = i->next; - } - - while (changed) { - changed = FALSE; - i = gnodes; - while (i) { - GtsGraphTraverse * t = GTS_OBJECT (i->data)->reserved; - GtsGNode * n; - while ((n = gts_graph_traverse_what_next (t)) && n->level == level) { - changed = TRUE; - farthest = n; - gts_graph_traverse_next (t); - } - i = i->next; - } - level++; - } - - /* destroy traversals */ - i = gnodes; - while (i) { - gts_graph_traverse_destroy (GTS_OBJECT (i->data)->reserved); - GTS_OBJECT (i->data)->reserved = NULL; - i = i->next; - } - return farthest; -} - -static void neighbor_count (GtsGNode * n, gpointer * data) -{ - guint * cuts = data[0]; - GtsGraph * g = data[1]; - - if (!gts_containee_is_contained (GTS_CONTAINEE (n), GTS_CONTAINER (g))) - (*cuts)++; -} - -static void count_edge_cuts (GtsGNode * n, gpointer * data) -{ - gts_gnode_foreach_neighbor (n, NULL, (GtsFunc) neighbor_count, data); -} - -/** - * gts_graph_edges_cut: - * @g: a #GtsGraph. - * - * Returns: the number of edges of @g connecting nodes belonging to @g - * to nodes not belonging to @g. - */ -guint gts_graph_edges_cut (GtsGraph * g) -{ - guint cuts = 0; - gpointer data[2]; - - g_return_val_if_fail (g != NULL, 0); - - data[0] = &cuts; - data[1] = g; - gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) count_edge_cuts, data); - - return cuts; -} - -static void sum_edge_cuts_weight (GtsGNode * n, gpointer * data) -{ - gfloat * weight = data[0]; - GtsGraph * g = data[1]; - GSList * i = GTS_SLIST_CONTAINER (n)->items; - - while (i) { - GtsGNode * n1 = GTS_GNODE_NEIGHBOR (n, i->data); - if (!gts_containee_is_contained (GTS_CONTAINEE (n1), GTS_CONTAINER (g))) - *weight += gts_gedge_weight (i->data); - i = i->next; - } -} - -/** - * gts_graph_edges_cut_weight: - * @g: a #GtsGraph. - * - * Returns: the sum of the weights of the edges of @g connecting nodes - * belonging to @g to nodes not belonging to @g. - */ -gfloat gts_graph_edges_cut_weight (GtsGraph * g) -{ - gfloat weight = 0.; - gpointer data[2]; - - g_return_val_if_fail (g != NULL, 0); - - data[0] = &weight; - data[1] = g; - gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) sum_edge_cuts_weight, - data); - - return weight; -} - -/** - * gts_graph_read_jostle: - * @g: a #GtsGraph. - * @fp: a #GtsFile. - * - * Adds to @g the nodes and edges defined in the file pointed to by - * @fp. This file must use the Jostle "graph" ASCII format. - * The nodes created are of type #GtsNGNode and their identities are the - * line number at which they appear in @fp. - * - * Returns: 0 if the lecture was successful, the line number at which - * an error occured otherwise (in which case the @error field of @fp - * is set). - */ -guint gts_graph_read_jostle (GtsGraph * g, GtsFile * fp) -{ - guint nn, ne, n; - GtsGNode ** nodes; - - g_return_val_if_fail (g != NULL, 1); - g_return_val_if_fail (fp != NULL, 1); - - if (fp->type != GTS_INT) { - gts_file_error (fp, "expecting an integer (number of nodes)"); - return fp->line; - } - nn = atoi (fp->token->str); - gts_file_next_token (fp); - - if (fp->type != GTS_INT) { - gts_file_error (fp, "expecting an integer (number of edges)"); - return fp->line; - } - ne = atoi (fp->token->str); - - gts_file_first_token_after (fp, '\n'); - nodes = g_malloc (sizeof (GtsGNode *)*(nn + 1)); - - n = 0; - while (n < nn && fp->type != GTS_ERROR) { - GtsNGNode * node = gts_ngnode_new (gts_ngnode_class (), fp->line); - - nodes[n++] = GTS_GNODE (node); - gts_container_add (GTS_CONTAINER (g), GTS_CONTAINEE (node)); - do { - if (fp->type != GTS_INT) - gts_file_error (fp, "expecting an integer (node index)"); - else { - guint in = atoi (fp->token->str); - - if (in == 0 || in > nn) - gts_file_error (fp, "node index `%d' is out of range `[1,%d]'", - in, nn); - else if (in == n) - gts_file_error (fp, "node index `%d' references itself", in); - else if (in < n) { - gts_gedge_new (g->edge_class, GTS_GNODE (node), nodes[in - 1]); - ne--; - gts_file_next_token (fp); - } - } - } while (fp->type != GTS_ERROR && fp->type != '\n'); - } - g_free (nodes); - - if (fp->type != GTS_ERROR) { - if (n != nn) - gts_file_error (fp, "only `%d' nodes read out of `%d'", - n, nn); - else if (ne > 0) - gts_file_error (fp, "`%d' unallocated edges remaining", - ne); - } - - if (fp->type == GTS_ERROR) - return fp->line; - return 0; -} - -static void count_edges (GtsGEdge * e, guint * nedge) -{ - (*nedge)++; -} - -static void write_node (GtsObject * node, gpointer * data) -{ - FILE * fp = data[0]; - guint * nnode = data[1]; - - node->reserved = GUINT_TO_POINTER ((*nnode)++); - if (node->klass->write) - (* node->klass->write) (node, fp); - fputc ('\n', fp); -} - -static void write_edge (GtsGEdge * edge, FILE * fp) -{ - fprintf (fp, "%u %u", - GPOINTER_TO_UINT (GTS_OBJECT (edge->n1)->reserved), - GPOINTER_TO_UINT (GTS_OBJECT (edge->n2)->reserved)); - if (GTS_OBJECT (edge)->klass->write) - (* GTS_OBJECT (edge)->klass->write) (GTS_OBJECT (edge), fp); - fputc ('\n', fp); -} - -/** - * gts_graph_write: - * @g: a #GtsGraph. - * @fp: a file pointer. - * - * Writes in the file @fp an ASCII representation of @g. The file - * format is as follows. - * - * All the lines beginning with #GTS_COMMENTS are ignored. The first line - * contains two unsigned integers separated by spaces. The first - * integer is the number of nodes, nn, the second is the number of - * edges, ne. - * - * Follows nn lines containing node description. - * Follows ne lines containing the two indices (starting - * from one) of the nodes of each edge. - * - * The format described above is the least common denominator to all - * GTS files. Consistent with an object-oriented approach, the GTS - * file format is extensible. Each of the lines of the file can be - * extended with user-specific attributes accessible through the - * read() and write() virtual methods of each of the objects written - * (graph, nodes or edges). When read with different object classes, - * these extra attributes are just ignored. - */ -void gts_graph_write (GtsGraph * g, FILE * fp) -{ - guint nnode = 1, nedge = 0; - gpointer data[2]; - - g_return_if_fail (g != NULL); - g_return_if_fail (fp != NULL); - - gts_graph_foreach_edge (g, (GtsFunc) count_edges, &nedge); - fprintf (fp, "%u %u", gts_container_size (GTS_CONTAINER (g)), nedge); - if (GTS_OBJECT (g)->klass->write) - (* GTS_OBJECT (g)->klass->write) (GTS_OBJECT (g), fp); - fputc ('\n', fp); - data[0] = fp; - data[1] = &nnode; - gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) write_node, data); - gts_graph_foreach_edge (g, (GtsFunc) write_edge, fp); - gts_container_foreach (GTS_CONTAINER (g), - (GtsFunc) gts_object_reset_reserved, NULL); -} - -/** - * gts_graph_read: - * @fp: a #GtsFile. - * - * Reads a graph from a file. - * - * Returns: the new #GtsGraph or %NULL if an error occured (in which - * case the @error field of @fp is set). - */ -GtsGraph * gts_graph_read (GtsFile * fp) -{ - GtsGraph * g; - GtsGNode ** nodes; - guint nn, ne, n; - - g_return_val_if_fail (fp != NULL, NULL); - - if (fp->type != GTS_INT) { - gts_file_error (fp, "expecting an integer (number of nodes)"); - return NULL; - } - nn = atoi (fp->token->str); - gts_file_next_token (fp); - - if (fp->type != GTS_INT) { - gts_file_error (fp, "expecting an integer (number of edges)"); - return NULL; - } - ne = atoi (fp->token->str); - - gts_file_next_token (fp); - if (fp->type != '\n') { - GtsObjectClass * klass; - - gts_graph_class (); - gts_gnode_class (); - gts_gedge_class (); - - if (fp->type != GTS_STRING) { - gts_file_error (fp, "expecting a string (GtsGraphClass)"); - return NULL; - } - klass = gts_object_class_from_name (fp->token->str); - if (klass == NULL) { - gts_file_error (fp, "unknown class `%s'", fp->token->str); - return NULL; - } - if (!gts_object_class_is_from_class (klass, gts_graph_class ())) { - gts_file_error (fp, "class `%s' is not a GtsGraphClass", fp->token->str); - return NULL; - } - g = GTS_GRAPH (gts_object_new (klass)); - g->graph_class = GTS_GRAPH_CLASS (klass); - gts_file_next_token (fp); - (* klass->read) ((GtsObject **) &g, fp); - if (fp->type == GTS_ERROR) { - gts_object_destroy (GTS_OBJECT (g)); - return NULL; - } - } - else - g = GTS_GRAPH (gts_object_new (GTS_OBJECT_CLASS (gts_graph_class ()))); - gts_file_first_token_after (fp, '\n'); - if (nn <= 0) - return g; - - nodes = g_malloc ((nn + 1)*sizeof (GtsGNode *)); - - n = 0; - while (n < nn && fp->type != GTS_ERROR) { - GtsObject * new_node = - gts_object_new (GTS_OBJECT_CLASS (g->node_class)); - - gts_container_add (GTS_CONTAINER (g), GTS_CONTAINEE (new_node)); - if (GTS_OBJECT_CLASS (g->node_class)->read) - (*GTS_OBJECT_CLASS (g->node_class)->read) (&new_node, fp); - gts_file_first_token_after (fp, '\n'); - nodes[n++] = GTS_GNODE (new_node); - } - if (fp->type == GTS_ERROR) - nn = n; - - n = 0; - while (n < ne && fp->type != GTS_ERROR) { - guint n1, n2; - - if (fp->type != GTS_INT) - gts_file_error (fp, "expecting an integer (first node index)"); - else { - n1 = atoi (fp->token->str); - if (n1 == 0 || n1 > nn) - gts_file_error (fp, "node index `%d' is out of range `[1,%d]'", - n1, nn); - else { - gts_file_next_token (fp); - if (fp->type != GTS_INT) - gts_file_error (fp, "expecting an integer (second node index)"); - else { - n2 = atoi (fp->token->str); - if (n2 == 0 || n2 > nn) - gts_file_error (fp, "node index `%d' is out of range `[1,%d]'", - n2, nn); - else { - GtsGEdge * new_edge = - gts_gedge_new (g->edge_class, nodes[n1 - 1], nodes [n2 - 1]); - - gts_file_next_token (fp); - if (fp->type != '\n') - if (GTS_OBJECT_CLASS (g->edge_class)->read) - (*GTS_OBJECT_CLASS (g->edge_class)->read) - ((GtsObject **) &new_edge, fp); - gts_file_first_token_after (fp, '\n'); - n++; - } - } - } - } - } - - if (fp->type == GTS_ERROR) { - gts_allow_floating_gnodes = TRUE; - while (nn) - gts_object_destroy (GTS_OBJECT (nodes[nn-- - 1])); - gts_allow_floating_gnodes = FALSE; - } - g_free (nodes); - - if (fp->type == GTS_ERROR) { - gts_object_destroy (GTS_OBJECT (g)); - return NULL; - } - return g; -} - -static void write_dot_node (GtsGNode * node, gpointer * data) -{ - FILE * fp = data[0]; - guint * nnode = data[1]; - - fprintf (fp, " n%u", *nnode); - if (GTS_GNODE_CLASS (GTS_OBJECT (node)->klass)->write) { - fputs (" [", fp); - (* GTS_GNODE_CLASS (GTS_OBJECT (node)->klass)->write) (node, fp); - fputc (']', fp); - } - fputs (";\n", fp); - GTS_OBJECT (node)->reserved = GUINT_TO_POINTER ((*nnode)++); -} - -static void write_dot_edge (GtsGEdge * edge, FILE * fp) -{ - fprintf (fp, " n%u -> n%u", - GPOINTER_TO_UINT (GTS_OBJECT (edge->n1)->reserved), - GPOINTER_TO_UINT (GTS_OBJECT (edge->n2)->reserved)); - if (GTS_GEDGE_CLASS (GTS_OBJECT (edge)->klass)->write) { - fputs (" [", fp); - (* GTS_GEDGE_CLASS (GTS_OBJECT (edge)->klass)->write) (edge, fp); - fputc (']', fp); - } - fputs (";\n", fp); -} - -/** - * gts_graph_write_dot: - * @g: a #GtsGraph. - * @fp: a file pointer. - * - * Writes in the file @fp an ASCII representation of @g in the dot format of - * AT&T Bell Labs. - */ -void gts_graph_write_dot (GtsGraph * g, FILE * fp) -{ - guint nnode = 1; - gpointer data[2]; - - g_return_if_fail (g != NULL); - g_return_if_fail (fp != NULL); - - fprintf (fp, "digraph \"%p\" {\n", g); - data[0] = fp; - data[1] = &nnode; - gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) write_dot_node, data); - gts_graph_foreach_edge (g, (GtsFunc) write_dot_edge, fp); - fputs ("}\n", fp); - - gts_container_foreach (GTS_CONTAINER (g), - (GtsFunc) gts_object_reset_reserved, NULL); -} - -/* GtsWGraph */ - -static gfloat wgraph_weight (GtsGraph * g) -{ - return GTS_WGRAPH (g)->weight; -} - -static void wgraph_add (GtsContainer * g, GtsContainee * n) -{ - GtsWGraph * wg = GTS_WGRAPH (g); - gfloat w = gts_gnode_weight (GTS_GNODE (n)); - - wg->weight += w; - - (* GTS_CONTAINER_CLASS (GTS_OBJECT_CLASS (gts_wgraph_class ())->parent_class)->add) (g, n); -} - -static void wgraph_remove (GtsContainer * g, GtsContainee * n) -{ - GTS_WGRAPH (g)->weight -= gts_gnode_weight (GTS_GNODE (n)); - - (* GTS_CONTAINER_CLASS (GTS_OBJECT_CLASS (gts_wgraph_class ())->parent_class)->remove) (g, n); -} - -static void wgraph_class_init (GtsWGraphClass * klass) -{ - GTS_GRAPH_CLASS (klass)->weight = wgraph_weight; - - GTS_CONTAINER_CLASS (klass)->add = wgraph_add; - GTS_CONTAINER_CLASS (klass)->remove = wgraph_remove; -} - -static void wgraph_init (GtsWGraph * g) -{ - g->weight = 0.; -} - -/** - * gts_wgraph_class: - * - * Returns: the #GtsWGraphClass. - */ -GtsWGraphClass * gts_wgraph_class (void) -{ - static GtsWGraphClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo wgraph_info = { - "GtsWGraph", - sizeof (GtsWGraph), - sizeof (GtsWGraphClass), - (GtsObjectClassInitFunc) wgraph_class_init, - (GtsObjectInitFunc) wgraph_init, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_graph_class ()), - &wgraph_info); - } - - return klass; -} - -static void weight_max (GtsGNode * n, gfloat * wmax) -{ - gfloat w = gts_gnode_weight (n); - - if (w > *wmax) - *wmax = w; -} - -/** - * gts_wgraph_weight_max: - * @wg: a #GtsWGraph. - * - * Returns: the maximum weight of any vertices belonging to @g. - */ -gfloat gts_wgraph_weight_max (GtsWGraph * wg) -{ - gfloat wmax = - G_MAXFLOAT; - - g_return_val_if_fail (wg != NULL, 0.); - - gts_container_foreach (GTS_CONTAINER (wg), (GtsFunc) weight_max, &wmax); - - return wmax; -} - -/* Surface graph */ - -static void create_node (GtsFace * f, GtsGraph * graph) -{ - GtsFNode * fn = gts_fnode_new (gts_fnode_class (), f); - - gts_container_add (GTS_CONTAINER (graph), GTS_CONTAINEE (fn)); - GTS_OBJECT (f)->reserved = fn; -} - -static void create_edge (GtsEdge * e, GtsSurface * s) -{ - GSList * i = e->triangles; - - while (i) { - GtsFace * f = i->data; - if (GTS_IS_FACE (f) && gts_face_has_parent_surface (f, s)) { - GSList * j = i->next; - while (j) { - GtsFace * f1 = j->data; - if (GTS_IS_FACE (f1) && gts_face_has_parent_surface (f1, s)) - gts_pgedge_new (gts_pgedge_class (), - GTS_OBJECT (f)->reserved, - GTS_OBJECT (f1)->reserved, - e); - j = j->next; - } - } - i = i->next; - } -} - -/** - * gts_surface_graph_new: - * @klass: a #GtsGraphClass. - * @s: a #GtsSurface. - * - * Returns: a new #GtsGraph representing the connectivity of the faces - * of @s. This graph uses #GtsFGNode as nodes which allows to store - * the dependencies between nodes and faces of @s. - */ -GtsGraph * gts_surface_graph_new (GtsGraphClass * klass, - GtsSurface * s) -{ - GtsGraph * graph; - - g_return_val_if_fail (klass != NULL, NULL); - g_return_val_if_fail (s != NULL, NULL); - - graph = GTS_GRAPH (gts_object_new (GTS_OBJECT_CLASS (klass))); - gts_surface_foreach_face (s, (GtsFunc) create_node, graph); - gts_surface_foreach_edge (s, (GtsFunc) create_edge, s); - gts_surface_foreach_face (s, (GtsFunc) gts_object_reset_reserved, NULL); - - return graph; -} - -static void create_segment_edge (GtsSegment * s, GtsGraph * graph) -{ - GtsGNode * n1 = GTS_OBJECT (s->v1)->reserved, * n2; - - if (n1 == NULL) { - n1 = GTS_GNODE (gts_pnode_new (gts_pnode_class (), s->v1)); - gts_container_add (GTS_CONTAINER (graph), GTS_CONTAINEE (n1)); - GTS_OBJECT (s->v1)->reserved = n1; - } - - n2 = GTS_OBJECT (s->v2)->reserved; - if (n2 == NULL) { - n2 = GTS_GNODE (gts_pnode_new (gts_pnode_class (), s->v2)); - gts_container_add (GTS_CONTAINER (graph), GTS_CONTAINEE (n2)); - GTS_OBJECT (s->v2)->reserved = n2; - } - - gts_pgedge_new (gts_pgedge_class (), n1, n2, s); -} - -static void reset_reserved (GtsSegment * s) -{ - GTS_OBJECT (s->v1)->reserved = GTS_OBJECT (s->v2)->reserved = NULL; -} - -/** - * gts_segments_graph_new: - * @klass: a #GtsGraphClass. - * @segments: a list of #GtsSegment. - * - * Returns: a new #GtsGraph representing the connectivity of the segments - * in @segments. - */ -GtsGraph * gts_segments_graph_new (GtsGraphClass * klass, - GSList * segments) -{ - GtsGraph * graph; - - g_return_val_if_fail (klass != NULL, NULL); - - graph = GTS_GRAPH (gts_object_new (GTS_OBJECT_CLASS (klass))); - g_slist_foreach (segments, (GFunc) create_segment_edge, graph); - g_slist_foreach (segments, (GFunc) reset_reserved, NULL); - - return graph; -} - -static void add_to_surface (GtsGNode * n, GtsSurface * s) -{ - if (GTS_IS_FNODE (n)) - gts_surface_add_face (s, GTS_FNODE (n)->f); -} - -/** - * gts_surface_graph_surface: - * @surface_graph: a #GtsGraph using #GtsFGNode as nodes. - * @s: a #GtsSurface. - * - * Returns: a new #GtsSurface using the same classes as @s and - * composed of the faces defined by @surface_graph. - */ -GtsSurface * gts_surface_graph_surface (GtsGraph * surface_graph, - GtsSurface * s) -{ - GtsSurface * s1; - - g_return_val_if_fail (surface_graph != NULL, NULL); - g_return_val_if_fail (s != NULL, NULL); - - s1 = gts_surface_new (GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass), - s->face_class, - s->edge_class, - s->vertex_class); - gts_container_foreach (GTS_CONTAINER (surface_graph), - (GtsFunc) add_to_surface, s1); - return s1; -} - Index: trunk/src/3rd/gts/gts.h =================================================================== --- trunk/src/3rd/gts/gts.h (revision 1064) +++ trunk/src/3rd/gts/gts.h (nonexistent) @@ -1,2577 +0,0 @@ -/* GTS - Library for the manipulation of triangulated surfaces - * Copyright (C) 1999 Stéphane Popinet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef __GTS_H__ -#define __GTS_H__ - -#include -#include - -#define GTS_MAJOR_VERSION 0 -#define GTS_MINOR_VERSION 7 -#define GTS_MICRO_VERSION 6 - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/* Added based on glib.h by M J Loehr 01/01/01 */ -/* GTS version. - * we prefix variable declarations so they can - * properly get exported in windows dlls. - */ -#ifdef NATIVE_WIN32 -# ifdef GTS_COMPILATION -# define GTS_C_VAR __declspec(dllexport) -# else /* not GTS_COMPILATION */ -# define GTS_C_VAR extern __declspec(dllimport) -# endif /* not GTS_COMPILATION */ -#else /* not NATIVE_WIN32 */ -# define GTS_C_VAR extern -#endif /* not NATIVE_WIN32 */ - -GTS_C_VAR const guint gts_major_version; -GTS_C_VAR const guint gts_minor_version; -GTS_C_VAR const guint gts_micro_version; -GTS_C_VAR const guint gts_interface_age; -GTS_C_VAR const guint gts_binary_age; - -#define GTS_CHECK_VERSION(major,minor,micro) \ - (gts_major_version > (major) || \ - (gts_major_version == (major) && gts_minor_version > (minor)) || \ - (gts_major_version == (major) && gts_minor_version == (minor) && \ - gts_micro_version >= (micro))) - -#define GTS_COMMENTS "#!" -#define GTS_MAINTAINER "popinet@users.sourceforge.net" - - - - -void gts_predicates_init(); - - -/* Class declarations for base types */ - -typedef struct _GtsObjectClassInfo GtsObjectClassInfo; -typedef struct _GtsObject GtsObject; -typedef struct _GtsObjectClass GtsObjectClass; -typedef struct _GtsPoint GtsPoint; -typedef struct _GtsPointClass GtsPointClass; -typedef struct _GtsVertex GtsVertex; -typedef struct _GtsVertexClass GtsVertexClass; -typedef struct _GtsSegment GtsSegment; -typedef struct _GtsSegmentClass GtsSegmentClass; -typedef struct _GtsEdge GtsEdge; -typedef struct _GtsEdgeClass GtsEdgeClass; -typedef struct _GtsTriangle GtsTriangle; -typedef struct _GtsTriangleClass GtsTriangleClass; -typedef struct _GtsFace GtsFace; -typedef struct _GtsFaceClass GtsFaceClass; -typedef struct _GtsBBox GtsBBox; -typedef struct _GtsBBoxClass GtsBBoxClass; -typedef struct _GtsSurface GtsSurface; -typedef struct _GtsSurfaceClass GtsSurfaceClass; - -typedef void (*GtsObjectClassInitFunc) (GtsObjectClass * objclass); -typedef void (*GtsObjectInitFunc) (GtsObject * obj); -typedef void (*GtsArgSetFunc) (GtsObject * obj); -typedef void (*GtsArgGetFunc) (GtsObject * obj); - -typedef gdouble GtsVector[3]; -typedef gdouble GtsVector4[4]; -typedef GtsVector4 GtsMatrix; -/** - * GtsKeyFunc: - * @item: A pointer to an item to be stored in the heap. - * @data: User data passed to gts_eheap_new(). - * - * Returns: the value of the key for the given item. - */ -typedef gdouble (*GtsKeyFunc) (gpointer item, - gpointer data); -typedef enum -{ - GTS_OUT = -1, - GTS_ON = 0, - GTS_IN = 1 -} GtsIntersect; - -typedef struct _GtsColor GtsColor; - -struct _GtsColor { - gfloat r, g, b; -}; - -typedef gint (*GtsFunc) (gpointer item, - gpointer data); - -/* misc.c */ - -typedef struct _GtsFile GtsFile; - -typedef enum { - GTS_NONE = 1 << 8, - GTS_INT = 1 << 9, - GTS_UINT = 1 << 10, - GTS_FLOAT = 1 << 11, - GTS_DOUBLE = 1 << 12, - GTS_STRING = 1 << 13, - GTS_FILE = 1 << 14, - GTS_ERROR = 1 << 15 -} GtsTokenType; - -struct _GtsFile { - FILE * fp; - gchar * s, * s1; - guint line, pos; - GString * token; - GtsTokenType type; - gchar * error; - - guint curline, curpos; - guint scope, scope_max; - gint next_token; - gchar * delimiters; - gchar * comments; - gchar * tokens; -}; - -typedef struct _GtsFileVariable GtsFileVariable; - -struct _GtsFileVariable { - GtsTokenType type; - gchar name[30]; - gboolean unique; - gpointer data; - gboolean set; - guint line, pos; -}; - - -GtsFile * gts_file_new (FILE * fp); -GtsFile * gts_file_new_from_string (const gchar * s); -void gts_file_verror (GtsFile * f, - const gchar * format, - va_list args); -void gts_file_error (GtsFile * f, - const gchar * format, - ...); -gint gts_file_getc (GtsFile * f); -guint gts_file_read (GtsFile * f, - gpointer ptr, - guint size, - guint nmemb); -gint gts_file_getc_scope (GtsFile * f); -void gts_file_next_token (GtsFile * f); -void gts_file_first_token_after (GtsFile * f, - GtsTokenType type); -void gts_file_assign_start (GtsFile * f, - GtsFileVariable * vars); -GtsFileVariable * gts_file_assign_next (GtsFile * f, - GtsFileVariable * vars); -void gts_file_assign_variables (GtsFile * f, - GtsFileVariable * vars); -void gts_file_variable_error (GtsFile * f, - GtsFileVariable * vars, - const gchar * name, - const gchar * format, - ...); -void gts_file_destroy (GtsFile * f); - -/* Objects: object.c */ - -#ifdef GTS_CHECK_CASTS -# define GTS_OBJECT_CAST(obj, type, klass) ((type *) gts_object_check_cast (obj, klass)) -# define GTS_OBJECT_CLASS_CAST(objklass, type, klass) ((type *) gts_object_class_check_cast (objklass, klass)) -#else /* not GTS_CHECK_CASTS */ -# define GTS_OBJECT_CAST(obj, type, klass) ((type *) (obj)) -# define GTS_OBJECT_CLASS_CAST(objklass, type, klass) ((type *) (objklass)) -#endif /* not GTS_CHECK_CASTS */ - -#define GTS_CLASS_NAME_LENGTH 40 -#define GTS_OBJECT(obj) GTS_OBJECT_CAST (obj,\ - GtsObject,\ - gts_object_class ()) -#define GTS_OBJECT_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ - GtsObjectClass,\ - gts_object_class()) -#define GTS_IS_OBJECT(obj) (gts_object_is_from_class (obj,\ - gts_object_class ())) - -typedef enum -{ - GTS_DESTROYED = 1 << 0, - GTS_USER_FLAG = 1 /* user flags start from here */ -} GtsObjectFlags; - -#define GTS_OBJECT_FLAGS(obj) (GTS_OBJECT (obj)->flags) -#define GTS_OBJECT_DESTROYED(obj) ((GTS_OBJECT_FLAGS (obj) & GTS_DESTROYED) != 0) -#define GTS_OBJECT_SET_FLAGS(obj,flag) G_STMT_START{ (GTS_OBJECT_FLAGS (obj) |= (flag)); }G_STMT_END -#define GTS_OBJECT_UNSET_FLAGS(obj,flag) G_STMT_START{ (GTS_OBJECT_FLAGS (obj) &= ~(flag)); }G_STMT_END - -struct _GtsObjectClassInfo { - gchar name[GTS_CLASS_NAME_LENGTH]; - guint object_size; - guint class_size; - GtsObjectClassInitFunc class_init_func; - GtsObjectInitFunc object_init_func; - GtsArgSetFunc arg_set_func; - GtsArgGetFunc arg_get_func; -}; - -struct _GtsObject { - GtsObjectClass * klass; - - gpointer reserved; - guint32 flags; -}; - -struct _GtsObjectClass { - GtsObjectClassInfo info; - GtsObjectClass * parent_class; - - void (* clone) (GtsObject *, GtsObject *); - void (* destroy) (GtsObject *); - void (* read) (GtsObject **, GtsFile *); - void (* write) (GtsObject *, FILE *); - GtsColor (* color) (GtsObject *); - void (* attributes) (GtsObject *, GtsObject *); -}; - -gpointer gts_object_class_new (GtsObjectClass * parent_class, - GtsObjectClassInfo * info); -GtsObjectClass * gts_object_class (void); -gpointer gts_object_check_cast (gpointer object, - gpointer klass); -gpointer gts_object_class_check_cast (gpointer klass, - gpointer from); - -static inline -gpointer gts_object_is_from_class (gpointer object, - gpointer klass) -{ - GtsObjectClass * c; - - g_return_val_if_fail (klass != NULL, NULL); - - if (object == NULL) - return NULL; - - c = ((GtsObject *) object)->klass; - - g_return_val_if_fail (c != NULL, NULL); - - while (c) { - if (c == klass) - return object; - c = c->parent_class; - } - - return NULL; -} - -static inline -gpointer gts_object_class_is_from_class (gpointer klass, - gpointer from) -{ - GtsObjectClass * c; - - g_return_val_if_fail (klass != NULL, NULL); - g_return_val_if_fail (from != NULL, NULL); - - c = (GtsObjectClass *) klass; - while (c) { - if (c == from) - return klass; - c = c->parent_class; - } - - return NULL; -} - -GtsObjectClass * gts_object_class_from_name (const gchar * name); - -GtsObject * gts_object_new (GtsObjectClass * klass); -GtsObject * gts_object_clone (GtsObject * object); -void gts_object_attributes (GtsObject * object, - GtsObject * from); -void gts_object_init (GtsObject * object, - GtsObjectClass * klass); -void gts_object_reset_reserved (GtsObject * object); -void gts_object_destroy (GtsObject * object); -void gts_finalize (void); - -/* Ranges: surface.c */ -typedef struct _GtsRange GtsRange; - -struct _GtsRange { - gdouble min, max, sum, sum2, mean, stddev; - guint n; -}; - -void gts_range_init (GtsRange * r); -void gts_range_reset (GtsRange * r); -void gts_range_add_value (GtsRange * r, - gdouble val); -void gts_range_update (GtsRange * r); -void gts_range_print (GtsRange * r, - FILE * fptr); - -/* Points: point.c */ - -#define GTS_IS_POINT(obj) (gts_object_is_from_class (obj,\ - gts_point_class ())) -#define GTS_POINT(obj) GTS_OBJECT_CAST (obj,\ - GtsPoint,\ - gts_point_class ()) -#define GTS_POINT_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ - GtsPointClass,\ - gts_point_class ()) - -struct _GtsPoint { - GtsObject object; - - gdouble x, y, z; /* must be contiguous (cast to robust functions) */ -}; - -struct _GtsPointClass { - GtsObjectClass parent_class; - gboolean binary; -}; - -GtsPointClass * gts_point_class (void); -GtsPoint * gts_point_new (GtsPointClass * klass, - gdouble x, - gdouble y, - gdouble z); -void gts_point_set (GtsPoint * p, - gdouble x, - gdouble y, - gdouble z); -#define gts_point_is_in_rectangle(p, p1, p2) ((p)->x >= (p1)->x &&\ - (p)->x <= (p2)->x &&\ - (p)->y >= (p1)->y &&\ - (p)->y <= (p2)->y &&\ - (p)->z >= (p1)->z &&\ - (p)->z <= (p2)->z) -GtsPoint * gts_segment_triangle_intersection (GtsSegment * s, - GtsTriangle * t, - gboolean boundary, - GtsPointClass * klass); -void gts_point_transform (GtsPoint * p, - GtsMatrix * m); -gdouble gts_point_distance (GtsPoint * p1, - GtsPoint * p2); -gdouble gts_point_distance2 (GtsPoint * p1, - GtsPoint * p2); -gdouble gts_point_orientation_3d (GtsPoint * p1, - GtsPoint * p2, - GtsPoint * p3, - GtsPoint * p4); -gint gts_point_orientation_3d_sos (GtsPoint * p1, - GtsPoint * p2, - GtsPoint * p3, - GtsPoint * p4); -GtsIntersect gts_point_is_in_triangle (GtsPoint * p, - GtsTriangle * t); -gdouble gts_point_in_circle (GtsPoint * p, - GtsPoint * p1, - GtsPoint * p2, - GtsPoint * p3); -gdouble gts_point_in_sphere (GtsPoint * p, - GtsPoint * p1, - GtsPoint * p2, - GtsPoint * p3, - GtsPoint * p4); -gdouble gts_point_in_triangle_circle (GtsPoint * p, - GtsTriangle * t); -gdouble gts_point_orientation (GtsPoint * p1, - GtsPoint * p2, - GtsPoint * p3); -gint gts_point_orientation_sos (GtsPoint * p1, - GtsPoint * p2, - GtsPoint * p3); -gdouble gts_point_segment_distance2 (GtsPoint * p, - GtsSegment * s); -gdouble gts_point_segment_distance (GtsPoint * p, - GtsSegment * s); -void gts_point_segment_closest (GtsPoint * p, - GtsSegment * s, - GtsPoint * closest); -gdouble gts_point_triangle_distance2 (GtsPoint * p, - GtsTriangle * t); -gdouble gts_point_triangle_distance (GtsPoint * p, - GtsTriangle * t); -void gts_point_triangle_closest (GtsPoint * p, - GtsTriangle * t, - GtsPoint * closest); -gboolean gts_point_is_inside_surface (GtsPoint * p, - GNode * tree, - gboolean is_open); - -/* Vertices: vertex.c */ - -#define GTS_IS_VERTEX(obj) (gts_object_is_from_class (obj,\ - gts_vertex_class ())) -#define GTS_VERTEX(obj) GTS_OBJECT_CAST (obj,\ - GtsVertex,\ - gts_vertex_class ()) -#define GTS_VERTEX_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ - GtsVertexClass,\ - gts_vertex_class ()) -struct _GtsVertex { - GtsPoint p; - - GSList * segments; -}; - -struct _GtsVertexClass { - GtsPointClass parent_class; - - void (* intersection_attributes) (GtsVertex *, - GtsObject *, - GtsObject *); -}; - -GTS_C_VAR -gboolean gts_allow_floating_vertices; - -GtsVertexClass * gts_vertex_class (void); -GtsVertex * gts_vertex_new (GtsVertexClass * klass, - gdouble x, - gdouble y, - gdouble z); -void gts_vertex_replace (GtsVertex * v, - GtsVertex * with); -gboolean gts_vertex_is_unattached (GtsVertex * v); -GtsSegment * gts_vertices_are_connected (GtsVertex * v1, - GtsVertex * v2); -GSList * gts_vertex_triangles (GtsVertex * v, - GSList * list); -GSList * gts_vertex_faces (GtsVertex * v, - GtsSurface * surface, - GSList * list); -GSList * gts_vertex_neighbors (GtsVertex * v, - GSList * list, - GtsSurface * surface); -GSList * gts_vertices_from_segments (GSList * segments); -gboolean gts_vertex_is_boundary (GtsVertex * v, - GtsSurface * surface); -GList * gts_vertices_merge (GList * vertices, - gdouble epsilon, - gboolean (* check) (GtsVertex *, GtsVertex *)); -GSList * gts_vertex_fan_oriented (GtsVertex * v, - GtsSurface * surface); -guint gts_vertex_is_contact (GtsVertex * v, gboolean sever); - -/* GtsVertexNormal: Header */ - -typedef struct _GtsVertexNormal GtsVertexNormal; - -struct _GtsVertexNormal { - /*< private >*/ - GtsVertex parent; - - /*< public >*/ - GtsVector n; -}; - -#define GTS_VERTEX_NORMAL(obj) GTS_OBJECT_CAST (obj,\ - GtsVertexNormal,\ - gts_vertex_normal_class ()) -#define GTS_IS_VERTEX_NORMAL(obj) (gts_object_is_from_class (obj,\ - gts_vertex_normal_class ())) - -GtsVertexClass * gts_vertex_normal_class (void); - -/* GtsColorVertex: Header */ - -typedef struct _GtsColorVertex GtsColorVertex; - -struct _GtsColorVertex { - /*< private >*/ - GtsVertex parent; - - /*< public >*/ - GtsColor c; -}; - -#define GTS_COLOR_VERTEX(obj) GTS_OBJECT_CAST (obj,\ - GtsColorVertex,\ - gts_color_vertex_class ()) -#define GTS_IS_COLOR_VERTEX(obj) (gts_object_is_from_class (obj,\ - gts_color_vertex_class ())) - -GtsVertexClass * gts_color_vertex_class (void); - -/* Segments: segment.c */ - -#define GTS_IS_SEGMENT(obj) (gts_object_is_from_class (obj,\ - gts_segment_class ())) -#define GTS_SEGMENT(obj) GTS_OBJECT_CAST (obj,\ - GtsSegment,\ - gts_segment_class ()) -#define GTS_SEGMENT_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ - GtsSegmentClass,\ - gts_segment_class ()) - -struct _GtsSegment { - GtsObject object; - - GtsVertex * v1; - GtsVertex * v2; -}; - -struct _GtsSegmentClass { - GtsObjectClass parent_class; -}; - -GtsSegmentClass * gts_segment_class (void); -GtsSegment * gts_segment_new (GtsSegmentClass * klass, - GtsVertex * v1, - GtsVertex * v2); -#define gts_segment_connect(s, e1, e2) (((s)->v1 == e1 &&\ - (s)->v2 == e2) || \ - ((s)->v1 == e2 &&\ - (s)->v2 == e1)) -#define gts_segments_are_identical(s1, s2) (((s1)->v1 == (s2)->v1 &&\ - (s1)->v2 == (s2)->v2)\ - ||\ - ((s1)->v1 == (s2)->v2 &&\ - (s1)->v2 == (s2)->v1)) -#define gts_segments_touch(s1, s2) ((s1)->v1 == (s2)->v1 ||\ - (s1)->v1 == (s2)->v2 ||\ - (s1)->v2 == (s2)->v1 ||\ - (s1)->v2 == (s2)->v2) -GtsIntersect gts_segments_are_intersecting (GtsSegment * s1, - GtsSegment * s2); -GtsSegment * gts_segment_is_duplicate (GtsSegment * s); -GtsVertex * gts_segment_midvertex (GtsSegment * s, - GtsVertexClass * klass); -GSList * gts_segments_from_vertices (GSList * vertices); -gboolean gts_segment_is_ok (GtsSegment * s); - -/* Edges: edge.c */ - -#define GTS_IS_EDGE(obj) (gts_object_is_from_class (obj,\ - gts_edge_class ())) -#define GTS_EDGE(obj) GTS_OBJECT_CAST (obj,\ - GtsEdge,\ - gts_edge_class ()) -#define GTS_EDGE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ - GtsEdgeClass,\ - gts_edge_class ()) - -struct _GtsEdge { - GtsSegment segment; - - GSList * triangles; -}; - -struct _GtsEdgeClass { - GtsSegmentClass parent_class; -}; - -GTS_C_VAR -gboolean gts_allow_floating_edges; - -GtsEdgeClass * gts_edge_class (void); -GtsEdge * gts_edge_new (GtsEdgeClass * klass, - GtsVertex * v1, - GtsVertex * v2); -/** - * gts_edge_is_unattached: - * @s: a #GtsEdge. - * - * Evaluates to %TRUE if no triangles uses @s as an edge, %FALSE otherwise. - */ -#define gts_edge_is_unattached(s) ((s)->triangles == NULL ? TRUE : FALSE) -GtsFace * gts_edge_has_parent_surface (GtsEdge * e, - GtsSurface * surface); -GtsFace * gts_edge_has_any_parent_surface (GtsEdge * e); -GtsFace * gts_edge_is_boundary (GtsEdge * e, - GtsSurface * surface); -void gts_edge_replace (GtsEdge * e, - GtsEdge * with); -GSList * gts_edges_from_vertices (GSList * vertices, - GtsSurface * parent); -guint gts_edge_face_number (GtsEdge * e, - GtsSurface * s); -gboolean gts_edge_collapse_is_valid (GtsEdge * e); -gboolean gts_edge_collapse_creates_fold (GtsEdge * e, - GtsVertex * v, - gdouble max); -GtsEdge * gts_edge_is_duplicate (GtsEdge * e); -GList * gts_edges_merge (GList * edges); -gboolean gts_edge_belongs_to_tetrahedron (GtsEdge * e); -guint gts_edge_is_contact (GtsEdge * e); -void gts_edge_swap (GtsEdge * e, - GtsSurface * s); -gboolean gts_edge_manifold_faces (GtsEdge * e, - GtsSurface * s, - GtsFace ** f1, - GtsFace ** f2); - -/* Triangles: triangle.c */ - -#define GTS_IS_TRIANGLE(obj) (gts_object_is_from_class (obj,\ - gts_triangle_class ())) -#define GTS_TRIANGLE(obj) GTS_OBJECT_CAST (obj,\ - GtsTriangle,\ - gts_triangle_class ()) -#define GTS_TRIANGLE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ - GtsTriangleClass,\ - gts_triangle_class ()) - -struct _GtsTriangle { - GtsObject object; - - GtsEdge * e1; - GtsEdge * e2; - GtsEdge * e3; -}; - -struct _GtsTriangleClass { - GtsObjectClass parent_class; -}; - -GtsTriangleClass * gts_triangle_class (void); -void gts_triangle_set (GtsTriangle * triangle, - GtsEdge * e1, - GtsEdge * e2, - GtsEdge * e3); -GtsTriangle * gts_triangle_new (GtsTriangleClass * klass, - GtsEdge * e1, - GtsEdge * e2, - GtsEdge * e3); -#define gts_triangle_vertex(t) (GTS_SEGMENT (GTS_TRIANGLE (t)->e1)->v1 ==\ - GTS_SEGMENT (GTS_TRIANGLE (t)->e2)->v1 || \ - GTS_SEGMENT (GTS_TRIANGLE (t)->e1)->v2 ==\ - GTS_SEGMENT (GTS_TRIANGLE (t)->e2)->v1 ? \ - GTS_SEGMENT (GTS_TRIANGLE (t)->e2)->v2 :\ - GTS_SEGMENT (GTS_TRIANGLE (t)->e2)->v1) -GtsVertex * gts_triangle_vertex_opposite (GtsTriangle * t, - GtsEdge * e); -GtsEdge * gts_triangle_edge_opposite (GtsTriangle * t, - GtsVertex * v); -gdouble gts_triangles_angle (GtsTriangle * t1, - GtsTriangle * t2); -gboolean gts_triangles_are_compatible (GtsTriangle * t1, - GtsTriangle * t2, - GtsEdge * e); -gdouble gts_triangle_area (GtsTriangle * t); -gdouble gts_triangle_perimeter (GtsTriangle * t); -gdouble gts_triangle_quality (GtsTriangle * t); -void gts_triangle_normal (GtsTriangle * t, - gdouble * x, - gdouble * y, - gdouble * z); -gdouble gts_triangle_orientation (GtsTriangle * t); -void gts_triangle_revert (GtsTriangle * t); -GSList * gts_triangles_from_edges (GSList * edges); -void gts_triangle_vertices_edges (GtsTriangle * t, - GtsEdge * e, - GtsVertex ** v1, - GtsVertex ** v2, - GtsVertex ** v3, - GtsEdge ** e1, - GtsEdge ** e2, - GtsEdge ** e3); -GtsTriangle * gts_triangle_enclosing (GtsTriangleClass * klass, - GSList * points, - gdouble scale); -guint gts_triangle_neighbor_number (GtsTriangle * t); -GSList * gts_triangle_neighbors (GtsTriangle * t); -GtsEdge * gts_triangles_common_edge (GtsTriangle * t1, - GtsTriangle * t2); -GtsTriangle * gts_triangle_is_duplicate (GtsTriangle * t); -GtsTriangle * gts_triangle_use_edges (GtsEdge * e1, - GtsEdge * e2, - GtsEdge * e3); -gboolean gts_triangle_is_ok (GtsTriangle * t); -void gts_triangle_vertices (GtsTriangle * t, - GtsVertex ** v1, - GtsVertex ** v2, - GtsVertex ** v3); -GtsPoint * gts_triangle_circumcircle_center (GtsTriangle * t, - GtsPointClass * point_class); -gboolean gts_triangles_are_folded (GSList * triangles, - GtsVertex * A, GtsVertex * B, - gdouble max); -GtsObject * gts_triangle_is_stabbed (GtsTriangle * t, - GtsPoint * p, - gdouble * orientation); -void gts_triangle_interpolate_height (GtsTriangle * t, - GtsPoint * p); - -/* Faces: face.c */ - -#define GTS_IS_FACE(obj) (gts_object_is_from_class (obj,\ - gts_face_class ())) -#define GTS_FACE(obj) GTS_OBJECT_CAST (obj,\ - GtsFace,\ - gts_face_class ()) -#define GTS_FACE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ - GtsFaceClass,\ - gts_face_class ()) - -struct _GtsFace { - GtsTriangle triangle; - - GSList * surfaces; -}; - -struct _GtsFaceClass { - GtsTriangleClass parent_class; -}; - -GTS_C_VAR -gboolean gts_allow_floating_faces; - -GtsFaceClass * gts_face_class (void); -GtsFace * gts_face_new (GtsFaceClass * klass, - GtsEdge * e1, - GtsEdge * e2, - GtsEdge * e3); -gboolean gts_face_has_parent_surface (GtsFace * f, - GtsSurface * s); -GSList * gts_faces_from_edges (GSList * edges, - GtsSurface * s); -guint gts_face_neighbor_number (GtsFace * f, - GtsSurface * s); -GSList * gts_face_neighbors (GtsFace * f, - GtsSurface * s); -void gts_face_foreach_neighbor (GtsFace * f, - GtsSurface * s, - GtsFunc func, - gpointer data); -gboolean gts_face_is_compatible (GtsFace * f, - GtsSurface * s); - -/* Matrices: matrix.c */ - -#define gts_vector_cross(C,A,B) ((C)[0] = (A)[1]*(B)[2] - (A)[2]*(B)[1],\ - (C)[1] = (A)[2]*(B)[0] - (A)[0]*(B)[2],\ - (C)[2] = (A)[0]*(B)[1] - (A)[1]*(B)[0]) - -#define gts_vector_init(v, p1, p2) ((v)[0] = (p2)->x - (p1)->x,\ - (v)[1] = (p2)->y - (p1)->y,\ - (v)[2] = (p2)->z - (p1)->z) -#define gts_vector_scalar(v1, v2) ((v1)[0]*(v2)[0] +\ - (v1)[1]*(v2)[1] +\ - (v1)[2]*(v2)[2]) -#define gts_vector_norm(v) (sqrt ((v)[0]*(v)[0] +\ - (v)[1]*(v)[1] +\ - (v)[2]*(v)[2])) -#define gts_vector_normalize(v) {\ - gdouble __gts_n = gts_vector_norm (v);\ - if (__gts_n > 0.) {\ - (v)[0] /= __gts_n;\ - (v)[1] /= __gts_n;\ - (v)[2] /= __gts_n;\ - }\ -} -GtsMatrix * gts_matrix_new (gdouble a00, gdouble a01, gdouble a02, gdouble a03, - gdouble a10, gdouble a11, gdouble a12, gdouble a13, - gdouble a20, gdouble a21, gdouble a22, gdouble a23, - gdouble a30, gdouble a31, gdouble a32, gdouble a33); -void gts_matrix_assign (GtsMatrix * m, - gdouble a00, gdouble a01, gdouble a02, gdouble a03, - gdouble a10, gdouble a11, gdouble a12, gdouble a13, - gdouble a20, gdouble a21, gdouble a22, gdouble a23, - gdouble a30, gdouble a31, gdouble a32, gdouble a33); -GtsMatrix * gts_matrix_projection (GtsTriangle * t); -GtsMatrix * gts_matrix_transpose (GtsMatrix * m); -gdouble gts_matrix_determinant (GtsMatrix * m); -GtsMatrix * gts_matrix_inverse (GtsMatrix * m); -GtsMatrix * gts_matrix3_inverse (GtsMatrix * m); -void gts_matrix_print (GtsMatrix * m, - FILE * fptr); -guint gts_matrix_compatible_row (GtsMatrix * A, - GtsVector b, - guint n, - GtsVector A1, - gdouble b1); -guint gts_matrix_quadratic_optimization (GtsMatrix * A, - GtsVector b, - guint n, - GtsMatrix * H, - GtsVector c); -GtsMatrix * gts_matrix_product (GtsMatrix * m1, - GtsMatrix * m2); -GtsMatrix * gts_matrix_zero (GtsMatrix * m); -GtsMatrix * gts_matrix_identity (GtsMatrix * m); -GtsMatrix * gts_matrix_scale (GtsMatrix * m, - GtsVector s); -GtsMatrix * gts_matrix_translate (GtsMatrix * m, - GtsVector t); -GtsMatrix * gts_matrix_rotate (GtsMatrix * m, - GtsVector r, - gdouble angle); -void gts_matrix_destroy (GtsMatrix * m); -void gts_vector_print (GtsVector v, - FILE * fptr); -void gts_vector4_print (GtsVector4 v, - FILE * fptr); - -/* Kdtrees: kdtree.c */ - -#define gts_kdtree_destroy(tree) g_node_destroy(tree) - -GNode * gts_kdtree_new (GPtrArray * points, - int (*compare) - (const void *, - const void *)); -GSList * gts_kdtree_range (GNode * tree, - GtsBBox * bbox, - int (*compare) - (const void *, - const void *)); - -/* Bboxtrees: bbtree.c */ - -/** - * GtsBBTreeTraverseFunc: - * @bb1: a #GtsBBox. - * @bb2: another #GtsBBox. - * @data: user data passed to the function. - * - * User function called for each pair of overlapping bounding - * boxes. See gts_bb_tree_traverse_overlapping(). - */ -typedef void (*GtsBBTreeTraverseFunc) (GtsBBox * bb1, - GtsBBox * bb2, - gpointer data); -/** - * GtsBBoxDistFunc: - * @p: a #GtsPoint. - * @bounded: an object bounded by a #GtsBBox. - * - * User function returning the (minimum) distance between the object - * defined by @bounded and point @p. - * - * Returns: the distance between @p and @bounded. - */ -typedef gdouble (*GtsBBoxDistFunc) (GtsPoint * p, - gpointer bounded); -/** - * GtsBBoxClosestFunc: - * @p: a #GtsPoint. - * @bounded: an object bounded by a #GtsBBox. - * - * User function returning a #GtsPoint belonging to the object defined - * by @bounded and closest to @p. - * - * Returns: a #GtsPoint. - */ -typedef GtsPoint * (*GtsBBoxClosestFunc) (GtsPoint * p, - gpointer bounded); - -/** - * GTS_IS_BBOX: - * @obj: a #GtsObject. - * - * Evaluates to %TRUE if @obj is a #GtsBBox, %FALSE otherwise. - */ -#define GTS_IS_BBOX(obj) (gts_object_is_from_class (obj,\ - gts_bbox_class ())) -/** - * GTS_BBOX: - * @obj: a #GtsObject. - * - * Casts @obj to #GtsBBox. - */ -#define GTS_BBOX(obj) GTS_OBJECT_CAST (obj,\ - GtsBBox,\ - gts_bbox_class ()) -/** - * GTS_BBOX_CLASS: - * @klass: a descendant of #GtsBBoxClass. - * - * Casts @klass to #GtsBBoxClass. - */ -#define GTS_BBOX_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ - GtsBBoxClass,\ - gts_bbox_class ()) - -struct _GtsBBox { - GtsObject object; - gpointer bounded; - gdouble x1, y1, z1; - gdouble x2, y2, z2; -}; - -struct _GtsBBoxClass { - GtsObjectClass parent_class; -}; - -GtsBBoxClass * gts_bbox_class (void); -GtsBBox * gts_bbox_new (GtsBBoxClass * klass, - gpointer bounded, - gdouble x1, - gdouble y1, - gdouble z1, - gdouble x2, - gdouble y2, - gdouble z2); -void gts_bbox_set (GtsBBox * bbox, - gpointer bounded, - gdouble x1, - gdouble y1, - gdouble z1, - gdouble x2, - gdouble y2, - gdouble z2); -GtsBBox * gts_bbox_segment (GtsBBoxClass * klass, - GtsSegment * s); -GtsBBox * gts_bbox_triangle (GtsBBoxClass * klass, - GtsTriangle * t); -GtsBBox * gts_bbox_surface (GtsBBoxClass * klass, - GtsSurface * surface); -GtsBBox * gts_bbox_bboxes (GtsBBoxClass * klass, - GSList * bboxes); -GtsBBox * gts_bbox_points (GtsBBoxClass * klass, - GSList * points); -/** - * gts_bbox_point_is_inside: - * @bbox: a #GtsBBox. - * @p: a #GtsPoint. - * - * Evaluates to %TRUE if @p is inside (or on the boundary) of @bbox, %FALSE otherwise. - */ -#define gts_bbox_point_is_inside(bbox, p) ((p)->x >= (bbox)->x1 &&\ - (p)->y >= (bbox)->y1 &&\ - (p)->z >= (bbox)->z1 &&\ - (p)->x <= (bbox)->x2 &&\ - (p)->y <= (bbox)->y2 &&\ - (p)->z <= (bbox)->z2) -gboolean gts_bboxes_are_overlapping (GtsBBox * bb1, - GtsBBox * bb2); -void gts_bbox_draw (GtsBBox * bb, - FILE * fptr); -gdouble gts_bbox_diagonal2 (GtsBBox * bb); -void gts_bbox_point_distance2 (GtsBBox * bb, - GtsPoint * p, - gdouble * min, - gdouble * max); -gboolean gts_bbox_is_stabbed (GtsBBox * bb, - GtsPoint * p); -gboolean gts_bbox_overlaps_triangle (GtsBBox * bb, - GtsTriangle * t); -gboolean gts_bbox_overlaps_segment (GtsBBox * bb, - GtsSegment * s); - -GNode * gts_bb_tree_new (GSList * bboxes); -GNode * gts_bb_tree_surface (GtsSurface * s); -GSList * gts_bb_tree_stabbed (GNode * tree, - GtsPoint * p); -GSList * gts_bb_tree_overlap (GNode * tree, - GtsBBox * bbox); -gboolean gts_bb_tree_is_overlapping (GNode * tree, - GtsBBox * bbox); -void gts_bb_tree_traverse_overlapping (GNode * tree1, - GNode * tree2, - GtsBBTreeTraverseFunc func, - gpointer data); -void gts_bb_tree_draw (GNode * tree, - guint depth, - FILE * fptr); -GSList * gts_bb_tree_point_closest_bboxes (GNode * tree, - GtsPoint * p); -gdouble gts_bb_tree_point_distance (GNode * tree, - GtsPoint * p, - GtsBBoxDistFunc distance, - GtsBBox ** bbox); -GtsPoint * gts_bb_tree_point_closest (GNode * tree, - GtsPoint * p, - GtsBBoxClosestFunc closest, - gdouble * distance); -void gts_bb_tree_segment_distance (GNode * tree, - GtsSegment * s, - GtsBBoxDistFunc distance, - gdouble delta, - GtsRange * range); -void gts_bb_tree_triangle_distance (GNode * tree, - GtsTriangle * t, - GtsBBoxDistFunc distance, - gdouble delta, - GtsRange * range); -void gts_bb_tree_surface_distance (GNode * tree, - GtsSurface * s, - GtsBBoxDistFunc distance, - gdouble delta, - GtsRange * range); -void gts_bb_tree_surface_boundary_distance - (GNode * tree, - GtsSurface * s, - GtsBBoxDistFunc distance, - gdouble delta, - GtsRange * range); -void gts_bb_tree_destroy (GNode * tree, - gboolean free_leaves); - -/* Surfaces: surface.c */ - -typedef struct _GtsSurfaceStats GtsSurfaceStats; -typedef struct _GtsSurfaceQualityStats GtsSurfaceQualityStats; -typedef GtsVertex * (*GtsRefineFunc) (GtsEdge * e, - GtsVertexClass * klass, - gpointer data); -typedef GtsVertex * (*GtsCoarsenFunc) (GtsEdge * e, - GtsVertexClass * klass, - gpointer data); -typedef gboolean (*GtsStopFunc) (gdouble cost, - guint nedge, - gpointer data); - -struct _GtsSurfaceStats { - guint n_faces; - guint n_incompatible_faces; - guint n_duplicate_faces; - guint n_duplicate_edges; - guint n_boundary_edges; - guint n_non_manifold_edges; - GtsRange edges_per_vertex, faces_per_edge; - GtsSurface * parent; -}; - -struct _GtsSurfaceQualityStats { - GtsRange face_quality; - GtsRange face_area; - GtsRange edge_length; - GtsRange edge_angle; - GtsSurface * parent; -}; - -struct _GtsSurface { - GtsObject object; - -#ifdef USE_SURFACE_BTREE - GTree * faces; -#else /* not USE_SURFACE_BTREE */ - GHashTable * faces; -#endif /* not USE_SURFACE_BTREE */ - GtsFaceClass * face_class; - GtsEdgeClass * edge_class; - GtsVertexClass * vertex_class; - gboolean keep_faces; -}; - -struct _GtsSurfaceClass { - GtsObjectClass parent_class; - - void (* add_face) (GtsSurface *, GtsFace *); - void (* remove_face) (GtsSurface *, GtsFace *); -}; - -#define GTS_IS_SURFACE(obj) (gts_object_is_from_class (obj,\ - gts_surface_class ())) -#define GTS_SURFACE(obj) GTS_OBJECT_CAST (obj,\ - GtsSurface,\ - gts_surface_class ()) -#define GTS_SURFACE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ - GtsSurfaceClass,\ - gts_surface_class ()) - -GtsSurfaceClass * gts_surface_class (void); -GtsSurface * gts_surface_new (GtsSurfaceClass * klass, - GtsFaceClass * face_class, - GtsEdgeClass * edge_class, - GtsVertexClass * vertex_class); -void gts_surface_add_face (GtsSurface * s, - GtsFace * f); -void gts_surface_remove_face (GtsSurface * s, - GtsFace * f); -guint gts_surface_read (GtsSurface * surface, - GtsFile * f); -gdouble gts_surface_area (GtsSurface * s); -void gts_surface_stats (GtsSurface * s, - GtsSurfaceStats * stats); -void gts_surface_quality_stats (GtsSurface * s, - GtsSurfaceQualityStats * stats); -void gts_surface_print_stats (GtsSurface * s, - FILE * fptr); -void gts_surface_write (GtsSurface * s, - FILE * fptr); -void gts_surface_write_oogl (GtsSurface * s, - FILE * fptr); -void gts_surface_write_vtk (GtsSurface * s, - FILE * fptr); -void gts_surface_write_oogl_boundary (GtsSurface * s, - FILE * fptr); -void gts_surface_foreach_vertex (GtsSurface * s, - GtsFunc func, - gpointer data); -void gts_surface_foreach_edge (GtsSurface * s, - GtsFunc func, - gpointer data); -void gts_surface_foreach_face (GtsSurface * s, - GtsFunc func, - gpointer data); -guint gts_surface_foreach_face_remove (GtsSurface * s, - GtsFunc func, - gpointer data); -typedef struct _GtsSurfaceTraverse GtsSurfaceTraverse; -GtsSurfaceTraverse * gts_surface_traverse_new (GtsSurface * s, - GtsFace * f); -GtsFace * gts_surface_traverse_next (GtsSurfaceTraverse * t, - guint * level); -void gts_surface_traverse_destroy (GtsSurfaceTraverse * t); -void gts_surface_refine (GtsSurface * surface, - GtsKeyFunc cost_func, - gpointer cost_data, - GtsRefineFunc refine_func, - gpointer refine_data, - GtsStopFunc stop_func, - gpointer stop_data); -gboolean gts_edge_collapse_is_valid (GtsEdge * e); -void gts_surface_coarsen (GtsSurface * surface, - GtsKeyFunc cost_func, - gpointer cost_data, - GtsCoarsenFunc coarsen_func, - gpointer coarsen_data, - GtsStopFunc stop_func, - gpointer stop_data, - gdouble minangle); -gboolean gts_coarsen_stop_number (gdouble cost, - guint nedge, - guint * min_number); -gboolean gts_coarsen_stop_cost (gdouble cost, - guint nedge, - gdouble * max_cost); -void gts_surface_tessellate (GtsSurface * s, - GtsRefineFunc refine_func, - gpointer refine_data); -GtsSurface * gts_surface_generate_sphere (GtsSurface * s, - guint geodesation_order); -GtsSurface * gts_surface_copy (GtsSurface * s1, - GtsSurface * s2); -void gts_surface_merge (GtsSurface * s, - GtsSurface * with); -gboolean gts_surface_is_manifold (GtsSurface * s); -gboolean gts_surface_is_closed (GtsSurface * s); -gboolean gts_surface_is_orientable (GtsSurface * s); -gdouble gts_surface_volume (GtsSurface * s); -gdouble gts_surface_center_of_mass (GtsSurface * s, - GtsVector cm); -gdouble gts_surface_center_of_area (GtsSurface * s, - GtsVector cm); -guint gts_surface_vertex_number (GtsSurface * s); -guint gts_surface_edge_number (GtsSurface * s); -guint gts_surface_face_number (GtsSurface * s); -void gts_surface_distance (GtsSurface * s1, - GtsSurface * s2, - gdouble delta, - GtsRange * face_range, - GtsRange * boundary_range); -GSList * gts_surface_boundary (GtsSurface * surface); -GSList * gts_surface_split (GtsSurface * s); - -/* Discrete differential operators: curvature.c */ - -gboolean gts_vertex_mean_curvature_normal (GtsVertex * v, - GtsSurface * s, - GtsVector Kh); -gboolean gts_vertex_gaussian_curvature (GtsVertex * v, - GtsSurface * s, - gdouble * Kg); -void gts_vertex_principal_curvatures (gdouble Kh, - gdouble Kg, - gdouble * K1, - gdouble * K2); -void gts_vertex_principal_directions (GtsVertex * v, - GtsSurface * s, - GtsVector Kh, - gdouble Kg, - GtsVector e1, - GtsVector e2); - -/* Volume optimization: vopt.c */ -typedef struct _GtsVolumeOptimizedParams GtsVolumeOptimizedParams; - -struct _GtsVolumeOptimizedParams { - gdouble volume_weight; - gdouble boundary_weight; - gdouble shape_weight; -}; - -GtsVertex * gts_volume_optimized_vertex (GtsEdge * edge, - GtsVertexClass * klass, - GtsVolumeOptimizedParams * params); -gdouble gts_volume_optimized_cost (GtsEdge * e, - GtsVolumeOptimizedParams * params); - -/* bool operations: boolean.c */ - -GSList * gts_surface_intersection (GtsSurface * s1, - GtsSurface * s2, - GNode * faces_tree1, - GNode * faces_tree2); - -typedef struct _GtsSurfaceInter GtsSurfaceInter; -typedef struct _GtsSurfaceInterClass GtsSurfaceInterClass; -/** - * GtsBooleanOperation: - * @GTS_1_OUT_2: identifies the part of the first surface which lies - * outside the second surface. - * @GTS_1_IN_2: identifies the part of the first surface which lies - * inside the second surface. - * @GTS_2_OUT_1: identifies the part of the second surface which lies - * outside the first surface. - * @GTS_2_IN_1: identifies the part of the second surface which lies - * inside the first surface. - */ -typedef enum { GTS_1_OUT_2, - GTS_1_IN_2, - GTS_2_OUT_1, - GTS_2_IN_1 } GtsBooleanOperation; - -/** - * GTS_IS_SURFACE_INTER: - * @obj: a #GtsObject. - * - * Evaluates to %TRUE if @obj is a #GtsSurfaceInter, %FALSE otherwise. - */ -#define GTS_IS_SURFACE_INTER(obj) (gts_object_is_from_class (obj,\ - gts_surface_inter_class ())) -/** - * GTS_SURFACE_INTER: - * @obj: a descendant of #GtsSurfaceInter. - * - * Casts @obj to #GtsSurfaceInter. - */ -#define GTS_SURFACE_INTER(obj) GTS_OBJECT_CAST (obj,\ - GtsSurfaceInter,\ - gts_surface_inter_class ()) -/** - * GTS_SURFACE_INTER_CLASS: - * @klass: a descendant of #GtsSurfaceInterClass. - * - * Casts @klass to #GtsSurfaceInterClass. - */ -#define GTS_SURFACE_INTER_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ - GtsSurfaceInterClass,\ - gts_surface_inter_class ()) - -struct _GtsSurfaceInter { - GtsObject object; - - GtsSurface * s1; - GtsSurface * s2; - GSList * edges; -}; - -struct _GtsSurfaceInterClass { - GtsObjectClass parent_class; -}; - -GtsSurfaceInterClass * -gts_surface_inter_class (void); -GtsSurfaceInter * -gts_surface_inter_new (GtsSurfaceInterClass * klass, - GtsSurface * s1, - GtsSurface * s2, - GNode * faces_tree1, - GNode * faces_tree2, - gboolean is_open1, - gboolean is_open2); -gboolean -gts_surface_inter_check (GtsSurfaceInter * si, - gboolean * closed); -void -gts_surface_inter_boolean (GtsSurfaceInter * si, - GtsSurface * surface, - GtsBooleanOperation op); -gboolean gts_surface_foreach_intersecting_face (GtsSurface * s, - GtsBBTreeTraverseFunc func, - gpointer data); -GtsSurface * -gts_surface_is_self_intersecting (GtsSurface * s); - -/* Binary Heap: heap.c */ - -typedef struct _GtsHeap GtsHeap; - -GtsHeap * gts_heap_new (GCompareFunc compare_func); -void gts_heap_insert (GtsHeap * heap, gpointer p); -gpointer gts_heap_remove_top (GtsHeap * heap); -gpointer gts_heap_top (GtsHeap * heap); -void gts_heap_thaw (GtsHeap * heap); -void gts_heap_foreach (GtsHeap * heap, - GFunc func, - gpointer user_data); -void gts_heap_freeze (GtsHeap * heap); -guint gts_heap_size (GtsHeap * heap); -void gts_heap_destroy (GtsHeap * heap); - -/* Extended Binary Heap: eheap.c */ - -typedef struct _GtsEHeap GtsEHeap; -typedef struct _GtsEHeapPair GtsEHeapPair; - -struct _GtsEHeap { - GPtrArray * elts; - GtsKeyFunc func; - gpointer data; - gboolean frozen, randomized; -}; - -/** - * _GtsEHeapPair: - * @data: Points to the item stored in the heap. - * @key: Value of the key for this item. - * @pos: Private field. - */ -struct _GtsEHeapPair { - gpointer data; - gdouble key; - guint pos; -}; - -GtsEHeap * gts_eheap_new (GtsKeyFunc key_func, - gpointer data); -GtsEHeapPair * gts_eheap_insert (GtsEHeap * heap, - gpointer p); -GtsEHeapPair * gts_eheap_insert_with_key (GtsEHeap * heap, - gpointer p, - gdouble key); -gpointer gts_eheap_remove_top (GtsEHeap * heap, - gdouble * key); -gpointer gts_eheap_top (GtsEHeap * heap, - gdouble * key); -void gts_eheap_thaw (GtsEHeap * heap); -void gts_eheap_foreach (GtsEHeap * heap, - GFunc func, - gpointer data); -gpointer gts_eheap_remove (GtsEHeap * heap, - GtsEHeapPair * p); -void gts_eheap_decrease_key (GtsEHeap * heap, - GtsEHeapPair * p, - gdouble new_key); -void gts_eheap_freeze (GtsEHeap * heap); -guint gts_eheap_size (GtsEHeap * heap); -void gts_eheap_update (GtsEHeap * heap); -gdouble gts_eheap_key (GtsEHeap * heap, - gpointer p); -void gts_eheap_randomized (GtsEHeap * heap, - gboolean randomized); -void gts_eheap_destroy (GtsEHeap * heap); - -/* FIFO queues: fifo.c */ - -typedef struct _GtsFifo GtsFifo; - -GtsFifo * gts_fifo_new (void); -void gts_fifo_write (GtsFifo * fifo, - FILE * fp); -void gts_fifo_push (GtsFifo * fifo, - gpointer data); -gpointer gts_fifo_pop (GtsFifo * fifo); -gpointer gts_fifo_top (GtsFifo * fifo); -guint gts_fifo_size (GtsFifo * fifo); -gboolean gts_fifo_is_empty (GtsFifo * fifo); -void gts_fifo_foreach (GtsFifo * fifo, - GtsFunc func, - gpointer data); -void gts_fifo_reverse (GtsFifo * fifo); -void gts_fifo_destroy (GtsFifo * fifo); - -/* Progressive surfaces */ - -/* split.c */ - -typedef struct _GtsSplit GtsSplit; -typedef struct _GtsSplitClass GtsSplitClass; -typedef struct _GtsSplitCFace GtsSplitCFace; - -struct _GtsSplit { - GtsObject object; - - GtsVertex * v; - GtsObject * v1; - GtsObject * v2; - GtsSplitCFace * cfaces; - guint ncf; -}; - -struct _GtsSplitClass { - GtsObjectClass parent_class; -}; - -#define GTS_IS_SPLIT(obj) (gts_object_is_from_class (obj,\ - gts_split_class ())) -#define GTS_SPLIT(obj) GTS_OBJECT_CAST (obj,\ - GtsSplit,\ - gts_split_class ()) -#define GTS_SPLIT_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ - GtsSplitClass,\ - gts_split_class ()) -#define GTS_SPLIT_V1(vs) (GTS_IS_SPLIT ((vs)->v1) ?\ - GTS_SPLIT ((vs)->v1)->v :\ - GTS_VERTEX ((vs)->v1)) -#define GTS_SPLIT_V2(vs) (GTS_IS_SPLIT ((vs)->v2) ?\ - GTS_SPLIT ((vs)->v2)->v :\ - GTS_VERTEX ((vs)->v2)) - -GtsSplitClass * gts_split_class (void); -GtsSplit * gts_split_new (GtsSplitClass * klass, - GtsVertex * v, - GtsObject * o1, - GtsObject * o2); -void gts_split_collapse (GtsSplit * vs, - GtsEdgeClass * klass, - GtsEHeap * heap); -void gts_split_expand (GtsSplit * vs, - GtsSurface * s, - GtsEdgeClass * klass); -typedef gboolean (*GtsSplitTraverseFunc) (GtsSplit * vs, - gpointer data); -void gts_split_traverse (GtsSplit * root, - GTraverseType order, - gint depth, - GtsSplitTraverseFunc func, - gpointer data); -guint gts_split_height (GtsSplit * root); - -/* psurface.c */ - -typedef struct _GtsPSurface GtsPSurface; -typedef struct _GtsPSurfaceClass GtsPSurfaceClass; - -struct _GtsPSurface { - GtsObject object; - - GtsSurface * s; - GPtrArray * split; - GtsSplitClass * split_class; - guint pos, min; - - GPtrArray * vertices; - GPtrArray * faces; -}; - -struct _GtsPSurfaceClass { - GtsObjectClass parent_class; -}; - -#define GTS_IS_PSURFACE(obj) (gts_object_is_from_class (obj,\ - gts_psurface_class ())) -#define GTS_PSURFACE(obj) GTS_OBJECT_CAST (obj,\ - GtsPSurface,\ - gts_psurface_class ()) -#define GTS_PSURFACE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ - GtsPSurfaceClass,\ - gts_psurface_class ()) -#define GTS_PSURFACE_IS_CLOSED(ps) (!(ps)->vertices) - -GtsPSurfaceClass * gts_psurface_class (void); -GtsPSurface * gts_psurface_new (GtsPSurfaceClass * klass, - GtsSurface * surface, - GtsSplitClass * split_class, - GtsKeyFunc cost_func, - gpointer cost_data, - GtsCoarsenFunc coarsen_func, - gpointer coarsen_data, - GtsStopFunc stop_func, - gpointer stop_data, - gdouble minangle); -GtsSplit * gts_psurface_add_vertex (GtsPSurface * ps); -GtsSplit * gts_psurface_remove_vertex (GtsPSurface * ps); -guint gts_psurface_max_vertex_number (GtsPSurface * ps); -guint gts_psurface_min_vertex_number (GtsPSurface * ps); -void gts_psurface_set_vertex_number (GtsPSurface * ps, - guint n); -guint gts_psurface_get_vertex_number (GtsPSurface * ps); -void gts_psurface_write (GtsPSurface * ps, - FILE * fptr); -GtsPSurface * gts_psurface_open (GtsPSurfaceClass * klass, - GtsSurface * s, - GtsSplitClass * split_class, - GtsFile * f); -GtsSplit * gts_psurface_read_vertex (GtsPSurface * ps, - GtsFile * fp); -void gts_psurface_close (GtsPSurface * ps); -void gts_psurface_foreach_vertex (GtsPSurface * ps, - GtsFunc func, - gpointer data); - -/* hsurface.c */ - -typedef struct _GtsHSplit GtsHSplit; -typedef struct _GtsHSplitClass GtsHSplitClass; -typedef struct _GtsHSurface GtsHSurface; -typedef struct _GtsHSurfaceClass GtsHSurfaceClass; - -struct _GtsHSplit { - GtsSplit split; - - GtsEHeapPair * index; - GtsHSplit * parent; - guint nchild; -}; - -struct _GtsHSplitClass { - GtsSplitClass parent_class; -}; - -#define GTS_IS_HSPLIT(obj) (gts_object_is_from_class (obj,\ - gts_hsplit_class ())) -#define GTS_HSPLIT(obj) GTS_OBJECT_CAST (obj,\ - GtsHSplit,\ - gts_hsplit_class ()) -#define GTS_HSPLIT_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ - GtsHSplitClass,\ - gts_hsplit_class ()) - -GtsHSplitClass * gts_hsplit_class (void); -GtsHSplit * gts_hsplit_new (GtsHSplitClass * klass, - GtsSplit * vs); -void gts_hsplit_collapse (GtsHSplit * hs, - GtsHSurface * hsurface); -void gts_hsplit_expand (GtsHSplit * hs, - GtsHSurface * hsurface); -void gts_hsplit_force_expand (GtsHSplit * hs, - GtsHSurface * hsurface); - -struct _GtsHSurface { - GtsObject object; - - GtsSurface * s; - GSList * roots; - GtsEHeap * expandable; - GtsEHeap * collapsable; - GPtrArray * split; - guint nvertex; -}; - -struct _GtsHSurfaceClass { - GtsObjectClass parent_class; -}; - -#define GTS_IS_HSURFACE(obj) (gts_object_is_from_class (obj,\ - gts_hsurface_class ())) -#define GTS_HSURFACE(obj) GTS_OBJECT_CAST (obj,\ - GtsHSurface,\ - gts_hsurface_class ()) -#define GTS_HSURFACE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ - GtsHSurfaceClass,\ - gts_hsurface_class ()) - -GtsHSurfaceClass * gts_hsurface_class (void); -GtsHSurface * gts_hsurface_new (GtsHSurfaceClass * klass, - GtsHSplitClass * hsplit_class, - GtsPSurface * psurface, - GtsKeyFunc expand_key, - gpointer expand_data, - GtsKeyFunc collapse_key, - gpointer collapse_data); -void gts_hsurface_traverse (GtsHSurface * hsurface, - GTraverseType order, - gint depth, - GtsSplitTraverseFunc func, - gpointer data); -void gts_hsurface_foreach (GtsHSurface * hsurface, - GTraverseType order, - GtsFunc func, - gpointer data); -guint gts_hsurface_height (GtsHSurface * hsurface); - -/* Constrained Delaunay triangulation: cdt.c */ - -/** - * GTS_IS_CONSTRAINT: - * @obj: a #GtsObject. - * - * Evaluates to %TRUE if @obj is a #GtsConstraint, %FALSE otherwise. - */ -#define GTS_IS_CONSTRAINT(obj) (gts_object_is_from_class (obj,\ - gts_constraint_class ())) -/** - * GTS_CONSTRAINT: - * @obj: a descendant of #GtsConstraint. - * - * Casts @obj to #GtsConstraint. - */ -#define GTS_CONSTRAINT(obj) GTS_OBJECT_CAST (obj,\ - GtsConstraint,\ - gts_constraint_class ()) -/** - * GTS_CONSTRAINT_CLASS: - * @klass: a desscendant of #GtsConstraintClass. - * - * Casts @klass to #GtsConstraintClass. - */ -#define GTS_CONSTRAINT_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ - GtsConstraintClass,\ - gts_constraint_class ()) - -struct _GtsConstraint { - GtsEdge edge; -}; - -struct _GtsConstraintClass { - GtsEdgeClass parent_class; -}; - -typedef struct _GtsConstraint GtsConstraint; -typedef struct _GtsConstraintClass GtsConstraintClass; - -GtsConstraintClass * gts_constraint_class (void); - -GtsFace * gts_point_locate (GtsPoint * p, - GtsSurface * surface, - GtsFace * guess); -GtsVertex * gts_delaunay_add_vertex_to_face (GtsSurface * surface, - GtsVertex * v, - GtsFace * f); -GtsVertex * gts_delaunay_add_vertex (GtsSurface * surface, - GtsVertex * v, - GtsFace * guess); -void gts_delaunay_remove_vertex (GtsSurface * surface, - GtsVertex * v); -GtsFace * gts_delaunay_check (GtsSurface * surface); -GSList * gts_delaunay_add_constraint (GtsSurface * surface, - GtsConstraint * c); -void gts_delaunay_remove_hull (GtsSurface * surface); - -/* GtsListFace: Header */ - -typedef struct _GtsListFace GtsListFace; - -struct _GtsListFace { - /*< private >*/ - GtsFace parent; - - /*< public >*/ - GSList * points; -}; - -#define GTS_LIST_FACE(obj) GTS_OBJECT_CAST (obj,\ - GtsListFace,\ - gts_list_face_class ()) -#define GTS_IS_LIST_FACE(obj) (gts_object_is_from_class (obj,\ - gts_list_face_class ())) - -GtsFaceClass * gts_list_face_class (void); - -/* Constrained Delaunay refinement: refine.c */ - -typedef gboolean (* GtsEncroachFunc) (GtsVertex * v, - GtsEdge * e, - GtsSurface * s, - gpointer data); - -gboolean gts_vertex_encroaches_edge (GtsVertex * v, - GtsEdge * e); -GtsVertex * gts_edge_is_encroached (GtsEdge * e, - GtsSurface * s, - GtsEncroachFunc encroaches, - gpointer data); -guint gts_delaunay_conform (GtsSurface * surface, - gint steiner_max, - GtsEncroachFunc encroaches, - gpointer data); -guint gts_delaunay_refine (GtsSurface * surface, - gint steiner_max, - GtsEncroachFunc encroaches, - gpointer encroach_data, - GtsKeyFunc cost, - gpointer cost_data); - -/* Isosurfaces (marching cubes): iso.c */ - -typedef struct _GtsGridPlane GtsGridPlane; -typedef struct _GtsIsoSlice GtsIsoSlice; -typedef struct _GtsCartesianGrid GtsCartesianGrid; - -struct _GtsGridPlane { - GtsPoint ** p; - guint nx, ny; -}; - -struct _GtsCartesianGrid { - guint nx, ny, nz; - gdouble x, dx, y, dy, z, dz; -}; - -typedef void (*GtsIsoCartesianFunc) (gdouble ** a, - GtsCartesianGrid g, - guint i, - gpointer data); - -GtsGridPlane * gts_grid_plane_new (guint nx, - guint ny); -void gts_grid_plane_destroy (GtsGridPlane * g); -GtsIsoSlice * gts_iso_slice_new (guint nx, guint ny); -void gts_iso_slice_fill (GtsIsoSlice * slice, - GtsGridPlane * plane1, - GtsGridPlane * plane2, - gdouble ** f1, - gdouble ** f2, - gdouble iso, - GtsVertexClass * klass); -void gts_iso_slice_fill_cartesian (GtsIsoSlice * slice, - GtsCartesianGrid g, - gdouble ** f1, - gdouble ** f2, - gdouble iso, - GtsVertexClass * klass); -void gts_iso_slice_destroy (GtsIsoSlice * slice); -void gts_isosurface_slice (GtsIsoSlice * slice1, - GtsIsoSlice * slice2, - GtsSurface * surface); -void gts_isosurface_cartesian (GtsSurface * surface, - GtsCartesianGrid g, - GtsIsoCartesianFunc f, - gpointer data, - gdouble iso); - -/* Isosurfaces (marching tetrahedra): isotetra.c */ - -void gts_isosurface_tetra (GtsSurface * surface, - GtsCartesianGrid g, - GtsIsoCartesianFunc f, - gpointer data, - gdouble iso); -void gts_isosurface_tetra_bcl (GtsSurface * surface, - GtsCartesianGrid g, - GtsIsoCartesianFunc f, - gpointer data, - gdouble iso); -void gts_isosurface_tetra_bounded (GtsSurface * surface, - GtsCartesianGrid g, - GtsIsoCartesianFunc f, - gpointer data, - gdouble iso); - -/* Named vertices, edges and triangles: named.c */ - -#define GTS_NAME_LENGTH 40 - -#define GTS_NVERTEX(obj) GTS_OBJECT_CAST (obj,\ - GtsNVertex,\ - gts_nvertex_class ()) -#define GTS_NVERTEX_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ - GtsNVertexClass,\ - gts_nvertex_class()) -#define GTS_IS_NVERTEX(obj) (gts_object_is_from_class (obj,\ - gts_nvertex_class ())) - -typedef struct _GtsNVertex GtsNVertex; -typedef struct _GtsNVertexClass GtsNVertexClass; - -struct _GtsNVertex { - GtsVertex parent; - char name[GTS_NAME_LENGTH]; -}; - -struct _GtsNVertexClass { - GtsVertexClass parent_class; -}; - -GtsNVertexClass * gts_nvertex_class (void); - -#define GTS_NEDGE(obj) GTS_OBJECT_CAST (obj,\ - GtsNEdge,\ - gts_nedge_class ()) -#define GTS_NEDGE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ - GtsNEdgeClass,\ - gts_nedge_class()) -#define GTS_IS_NEDGE(obj) (gts_object_is_from_class (obj,\ - gts_nedge_class ())) - -typedef struct _GtsNEdge GtsNEdge; -typedef struct _GtsNEdgeClass GtsNEdgeClass; - -struct _GtsNEdge { - GtsEdge parent; - char name[GTS_NAME_LENGTH]; -}; - -struct _GtsNEdgeClass { - GtsEdgeClass parent_class; -}; - -GtsNEdgeClass * gts_nedge_class (void); - -#define GTS_NFACE(obj) GTS_OBJECT_CAST (obj,\ - GtsNFace,\ - gts_nface_class ()) -#define GTS_NFACE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ - GtsNFaceClass,\ - gts_nface_class()) -#define GTS_IS_NFACE(obj) (gts_object_is_from_class (obj,\ - gts_nface_class ())) - -typedef struct _GtsNFace GtsNFace; -typedef struct _GtsNFaceClass GtsNFaceClass; - -struct _GtsNFace { - GtsFace parent; - char name[GTS_NAME_LENGTH]; -}; - -struct _GtsNFaceClass { - GtsFaceClass parent_class; -}; - -GtsNFaceClass * gts_nface_class (void); - -/* Cluster object for out-of-core simplification: oocs.c */ - -#define GTS_CLUSTER(obj) GTS_OBJECT_CAST (obj,\ - GtsCluster,\ - gts_cluster_class ()) -#define GTS_CLUSTER_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ - GtsClusterClass,\ - gts_cluster_class()) -#define GTS_IS_CLUSTER(obj) (gts_object_is_from_class (obj,\ - gts_cluster_class ())) - -typedef struct _GtsCluster GtsCluster; -typedef struct _GtsClusterClass GtsClusterClass; -typedef struct _GtsClusterId GtsClusterId; - -struct _GtsClusterId { - guint x, y, z; -}; - -struct _GtsCluster { - GtsObject parent; - - GtsClusterId id; - GtsVertex * v; - guint n; -}; - -struct _GtsClusterClass { - GtsObjectClass parent_class; - - void (* add) (GtsCluster * c, GtsPoint * p, gpointer data); - void (* update) (GtsCluster * c); -}; - -GtsClusterClass * gts_cluster_class (void); -GtsCluster * gts_cluster_new (GtsClusterClass * klass, - GtsClusterId id, - GtsVertexClass * vklass); -void gts_cluster_add (GtsCluster * c, - GtsPoint * p, - gpointer data); -void gts_cluster_update (GtsCluster * c); - -/* Cluster group object for out-of-core simplification: oocs.c */ - -#define GTS_CLUSTER_GRID(obj) GTS_OBJECT_CAST (obj,\ - GtsClusterGrid,\ - gts_cluster_grid_class ()) -#define GTS_CLUSTER_GRID_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ - GtsClusterGridClass,\ - gts_cluster_grid_class()) -#define GTS_IS_CLUSTER_GRID(obj) (gts_object_is_from_class (obj,\ - gts_cluster_grid_class ())) - -typedef struct _GtsClusterGrid GtsClusterGrid; -typedef struct _GtsClusterGridClass GtsClusterGridClass; - -struct _GtsClusterGrid { - GtsObject parent; - - GtsSurface * surface; - GtsBBox * bbox; - GtsVector size; - - GtsClusterClass * cluster_class; - GHashTable * clusters; -}; - -struct _GtsClusterGridClass { - GtsObjectClass parent_class; -}; - -GtsClusterGridClass * gts_cluster_grid_class (void); -GtsClusterGrid * gts_cluster_grid_new (GtsClusterGridClass * klass, - GtsClusterClass * cluster_class, - GtsSurface * s, - GtsBBox * bbox, - gdouble delta); -void gts_cluster_grid_add_triangle (GtsClusterGrid * cluster_grid, - GtsPoint * p1, - GtsPoint * p2, - GtsPoint * p3, - gpointer data); -GtsRange gts_cluster_grid_update (GtsClusterGrid * cluster_grid); - -/* Triangle strip generation: stripe.c */ -GSList * gts_surface_strip (GtsSurface * s); - -/* GtsContainee: container.c */ - -typedef struct _GtsContainee GtsContainee; -typedef struct _GtsContaineeClass GtsContaineeClass; -typedef struct _GtsContainer GtsContainer; -typedef struct _GtsContainerClass GtsContainerClass; - -struct _GtsContainee { - GtsObject object; -}; - -struct _GtsContaineeClass { - GtsObjectClass parent_class; - - void (* add_container) (GtsContainee *, GtsContainer *); - void (* remove_container) (GtsContainee *, GtsContainer *); - void (* foreach) (GtsContainee *, GtsFunc, gpointer); - gboolean (* is_contained) (GtsContainee *, GtsContainer *); - void (* replace) (GtsContainee *, GtsContainee *); -}; - -#define GTS_CONTAINEE(obj) GTS_OBJECT_CAST (obj,\ - GtsContainee,\ - gts_containee_class ()) -#define GTS_CONTAINEE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ - GtsContaineeClass,\ - gts_containee_class()) -#define GTS_IS_CONTAINEE(obj) (gts_object_is_from_class (obj,\ - gts_containee_class ())) - -GtsContaineeClass * gts_containee_class (void); -GtsContainee * gts_containee_new (GtsContaineeClass * klass); -gboolean gts_containee_is_contained (GtsContainee * item, - GtsContainer * c); -void gts_containee_replace (GtsContainee * item, - GtsContainee * with); - -/* GtsSListContainee: container.c */ - -typedef struct _GtsSListContainee GtsSListContainee; -typedef struct _GtsSListContaineeClass GtsSListContaineeClass; - -struct _GtsSListContainee { - GtsContainee containee; - - GSList * containers; -}; - -struct _GtsSListContaineeClass { - GtsContaineeClass parent_class; -}; - -#define GTS_SLIST_CONTAINEE(obj) GTS_OBJECT_CAST (obj,\ - GtsSListContainee,\ - gts_slist_containee_class ()) -#define GTS_SLIST_CONTAINEE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ - GtsSListContaineeClass,\ - gts_slist_containee_class()) -#define GTS_IS_SLIST_CONTAINEE(obj) (gts_object_is_from_class (obj,\ - gts_slist_containee_class ())) - -GtsSListContaineeClass * gts_slist_containee_class (void); - -/* GtsContainer: container.c */ - -struct _GtsContainer { - GtsSListContainee object; -}; - -struct _GtsContainerClass { - GtsSListContaineeClass parent_class; - - void (* add) (GtsContainer *, GtsContainee *); - void (* remove) (GtsContainer *, GtsContainee *); - void (* foreach) (GtsContainer *, GtsFunc, gpointer); - guint (* size) (GtsContainer *); -}; - -#define GTS_CONTAINER(obj) GTS_OBJECT_CAST (obj,\ - GtsContainer,\ - gts_container_class ()) -#define GTS_CONTAINER_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ - GtsContainerClass,\ - gts_container_class()) -#define GTS_IS_CONTAINER(obj) (gts_object_is_from_class (obj,\ - gts_container_class ())) - -GtsContainerClass * gts_container_class (void); -GtsContainer * gts_container_new (GtsContainerClass * klass); -void gts_container_add (GtsContainer * c, - GtsContainee * item); -void gts_container_remove (GtsContainer * c, - GtsContainee * item); -void gts_container_foreach (GtsContainer * c, - GtsFunc func, - gpointer data); -guint gts_container_size (GtsContainer * c); - -/* GtsHashContainer: container.c */ - -typedef struct _GtsHashContainer GtsHashContainer; -typedef struct _GtsHashContainerClass GtsHashContainerClass; - -struct _GtsHashContainer { - GtsContainer c; - - GHashTable * items; - gboolean frozen; -}; - -struct _GtsHashContainerClass { - GtsContainerClass parent_class; -}; - -#define GTS_HASH_CONTAINER(obj) GTS_OBJECT_CAST (obj,\ - GtsHashContainer,\ - gts_hash_container_class ()) -#define GTS_HASH_CONTAINER_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ - GtsHashContainerClass,\ - gts_hash_container_class()) -#define GTS_IS_HASH_CONTAINER(obj) (gts_object_is_from_class (obj,\ - gts_hash_container_class ())) - -GtsHashContainerClass * gts_hash_container_class (void); - -/* GtsSListContainer: container.c */ - -typedef struct _GtsSListContainer GtsSListContainer; -typedef struct _GtsSListContainerClass GtsSListContainerClass; - -struct _GtsSListContainer { - GtsContainer c; - - GSList * items; - gboolean frozen; -}; - -struct _GtsSListContainerClass { - GtsContainerClass parent_class; -}; - -#define GTS_SLIST_CONTAINER(obj) GTS_OBJECT_CAST (obj,\ - GtsSListContainer,\ - gts_slist_container_class ()) -#define GTS_SLIST_CONTAINER_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ - GtsSListContainerClass,\ - gts_slist_container_class()) -#define GTS_IS_SLIST_CONTAINER(obj) (gts_object_is_from_class (obj,\ - gts_slist_container_class ())) - -GtsSListContainerClass * gts_slist_container_class (void); - -/* GtsGNode: graph.c */ - -typedef struct _GtsGNode GtsGNode; -typedef struct _GtsGNodeClass GtsGNodeClass; -typedef struct _GtsGraph GtsGraph; -typedef struct _GtsGraphClass GtsGraphClass; - -struct _GtsGNode { - GtsSListContainer container; - - guint level; -}; - -struct _GtsGNodeClass { - GtsSListContainerClass parent_class; - - gfloat (* weight) (GtsGNode *); - void (* write) (GtsGNode *, FILE *); -}; - -#define GTS_GNODE(obj) GTS_OBJECT_CAST (obj,\ - GtsGNode,\ - gts_gnode_class ()) -#define GTS_GNODE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ - GtsGNodeClass,\ - gts_gnode_class()) -#define GTS_IS_GNODE(obj) (gts_object_is_from_class (obj,\ - gts_gnode_class ())) -#define GTS_GNODE_NEIGHBOR(n,e) (GTS_GEDGE (e)->n1 == n ? GTS_GEDGE (e)->n2 : GTS_GEDGE (e)->n2 == n ? GTS_GEDGE (e)->n1 : NULL) - -GtsGNodeClass * gts_gnode_class (void); -GtsGNode * gts_gnode_new (GtsGNodeClass * klass); -void gts_gnode_foreach_neighbor (GtsGNode * n, - GtsGraph * g, - GtsFunc func, - gpointer data); -void gts_gnode_foreach_edge (GtsGNode * n, - GtsGraph * g, - GtsFunc func, - gpointer data); -guint gts_gnode_degree (GtsGNode * n, - GtsGraph * g); -gfloat gts_gnode_move_cost (GtsGNode * n, - GtsGraph * src, - GtsGraph * dst); -gfloat gts_gnode_weight (GtsGNode * n); - -GTS_C_VAR -gboolean gts_allow_floating_gnodes; - -/* GtsNGNode: graph.c */ - -typedef struct _GtsNGNode GtsNGNode; -typedef struct _GtsNGNodeClass GtsNGNodeClass; - -struct _GtsNGNode { - GtsGNode node; - - guint id; -}; - -struct _GtsNGNodeClass { - GtsGNodeClass parent_class; -}; - -#define GTS_NGNODE(obj) GTS_OBJECT_CAST (obj,\ - GtsNGNode,\ - gts_ngnode_class ()) -#define GTS_NGNODE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ - GtsNGNodeClass,\ - gts_ngnode_class()) -#define GTS_IS_NGNODE(obj) (gts_object_is_from_class (obj,\ - gts_ngnode_class ())) - -GtsNGNodeClass * gts_ngnode_class (void); -GtsNGNode * gts_ngnode_new (GtsNGNodeClass * klass, - guint id); - -/* GtsWGNode: graph.c */ - -typedef struct _GtsWGNode GtsWGNode; -typedef struct _GtsWGNodeClass GtsWGNodeClass; - -struct _GtsWGNode { - GtsGNode node; - - gfloat weight; -}; - -struct _GtsWGNodeClass { - GtsGNodeClass parent_class; -}; - -#define GTS_WGNODE(obj) GTS_OBJECT_CAST (obj,\ - GtsWGNode,\ - gts_wgnode_class ()) -#define GTS_WGNODE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ - GtsWGNodeClass,\ - gts_wgnode_class()) -#define GTS_IS_WGNODE(obj) (gts_object_is_from_class (obj,\ - gts_wgnode_class ())) - -GtsWGNodeClass * gts_wgnode_class (void); -GtsWGNode * gts_wgnode_new (GtsWGNodeClass * klass, - gfloat weight); - -/* GtsPNode */ - -typedef struct _GtsPNode GtsPNode; -typedef struct _GtsPNodeClass GtsPNodeClass; - -struct _GtsPNode { - GtsGNode node; - - gpointer data; -}; - -struct _GtsPNodeClass { - GtsGNodeClass parent_class; -}; - -#define GTS_PNODE(obj) GTS_OBJECT_CAST (obj,\ - GtsPNode,\ - gts_pnode_class ()) -#define GTS_PNODE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ - GtsPNodeClass,\ - gts_pnode_class()) -#define GTS_IS_PNODE(obj) (gts_object_is_from_class (obj,\ - gts_pnode_class ())) - -GtsPNodeClass * gts_pnode_class (void); -GtsPNode * gts_pnode_new (GtsPNodeClass * klass, - gpointer data); - -/* GtsFNode */ - -typedef struct _GtsFNode GtsFNode; -typedef struct _GtsFNodeClass GtsFNodeClass; - -struct _GtsFNode { - GtsGNode node; - - GtsFace * f; -}; - -struct _GtsFNodeClass { - GtsGNodeClass parent_class; -}; - -#define GTS_FNODE(obj) GTS_OBJECT_CAST (obj,\ - GtsFNode,\ - gts_fnode_class ()) -#define GTS_FNODE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ - GtsFNodeClass,\ - gts_fnode_class()) -#define GTS_IS_FNODE(obj) (gts_object_is_from_class (obj,\ - gts_fnode_class ())) - -GtsFNodeClass * gts_fnode_class (void); -GtsFNode * gts_fnode_new (GtsFNodeClass * klass, - GtsFace * f); - -/* GtsGEdge: graph.c */ - -typedef struct _GtsGEdge GtsGEdge; -typedef struct _GtsGEdgeClass GtsGEdgeClass; - -struct _GtsGEdge { - GtsContainee containee; - - GtsGNode * n1; - GtsGNode * n2; -}; - -struct _GtsGEdgeClass { - GtsContaineeClass parent_class; - - GtsGEdge * (* link) (GtsGEdge * e, GtsGNode * n1, GtsGNode * n2); - gfloat (* weight) (GtsGEdge * e); - void (* write) (GtsGEdge * e, FILE * fp); -}; - -#define GTS_GEDGE(obj) GTS_OBJECT_CAST (obj,\ - GtsGEdge,\ - gts_gedge_class ()) -#define GTS_GEDGE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ - GtsGEdgeClass,\ - gts_gedge_class()) -#define GTS_IS_GEDGE(obj) (gts_object_is_from_class (obj,\ - gts_gedge_class ())) - -GtsGEdgeClass * gts_gedge_class (void); -GtsGEdge * gts_gedge_new (GtsGEdgeClass * klass, - GtsGNode * n1, - GtsGNode * n2); -gfloat gts_gedge_weight (GtsGEdge * e); -#define gts_gedge_connects(e, a1, a2)\ - (((e)->n1 == a1 && (e)->n2 == a2) || ((e)->n1 == a2 && (e)->n2 == a1)) - -/* GtsPGEdge: graph.c */ - -typedef struct _GtsPGEdge GtsPGEdge; -typedef struct _GtsPGEdgeClass GtsPGEdgeClass; - -struct _GtsPGEdge { - GtsGEdge gedge; - - gpointer data; -}; - -struct _GtsPGEdgeClass { - GtsGEdgeClass parent_class; -}; - -#define GTS_PGEDGE(obj) GTS_OBJECT_CAST (obj,\ - GtsPGEdge,\ - gts_pgedge_class ()) -#define GTS_PGEDGE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ - GtsPGEdgeClass,\ - gts_pgedge_class()) -#define GTS_IS_PGEDGE(obj) (gts_object_is_from_class (obj,\ - gts_pgedge_class ())) - -GtsPGEdgeClass * gts_pgedge_class (void); -GtsPGEdge * gts_pgedge_new (GtsPGEdgeClass * klass, - GtsGNode * n1, - GtsGNode * n2, - gpointer data); - -/* GtsWGEdge: graph.c */ - -typedef struct _GtsWGEdge GtsWGEdge; -typedef struct _GtsWGEdgeClass GtsWGEdgeClass; - -struct _GtsWGEdge { - GtsGEdge gedge; - - gfloat weight; -}; - -struct _GtsWGEdgeClass { - GtsGEdgeClass parent_class; -}; - -#define GTS_WGEDGE(obj) GTS_OBJECT_CAST (obj,\ - GtsWGEdge,\ - gts_wgedge_class ()) -#define GTS_WGEDGE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ - GtsWGEdgeClass,\ - gts_wgedge_class()) -#define GTS_IS_WGEDGE(obj) (gts_object_is_from_class (obj,\ - gts_wgedge_class ())) - -GtsWGEdgeClass * gts_wgedge_class (void); -GtsWGEdge * gts_wgedge_new (GtsWGEdgeClass * klass, - GtsGNode * n1, - GtsGNode * n2, - gfloat weight); - -/* GtsGraph: graph.c */ - -struct _GtsGraph { - GtsHashContainer object; - - GtsGraphClass * graph_class; - GtsGNodeClass * node_class; - GtsGEdgeClass * edge_class; -}; - -struct _GtsGraphClass { - GtsHashContainerClass parent_class; - - gfloat (* weight) (GtsGraph *); -}; - -#define GTS_GRAPH(obj) GTS_OBJECT_CAST (obj,\ - GtsGraph,\ - gts_graph_class ()) -#define GTS_GRAPH_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ - GtsGraphClass,\ - gts_graph_class()) -#define GTS_IS_GRAPH(obj) (gts_object_is_from_class (obj,\ - gts_graph_class ())) - -GtsGraphClass * gts_graph_class (void); -GtsGraph * gts_graph_new (GtsGraphClass * klass, - GtsGNodeClass * node_class, - GtsGEdgeClass * edge_class); -void gts_graph_print_stats (GtsGraph * g, - FILE * fp); -typedef struct _GtsGraphTraverse GtsGraphTraverse; -typedef enum { GTS_BREADTH_FIRST - } GtsTraverseType; -GtsGraphTraverse * gts_graph_traverse_new (GtsGraph * g, - GtsGNode * n, - GtsTraverseType type, - gboolean reinit); -GtsGNode * gts_graph_traverse_next (GtsGraphTraverse * t); -GtsGNode * gts_graph_traverse_what_next (GtsGraphTraverse * t); -void gts_graph_traverse_destroy (GtsGraphTraverse * t); -void gts_graph_foreach_edge (GtsGraph * g, - GtsFunc func, - gpointer data); -gfloat gts_graph_weight (GtsGraph * g); -guint gts_graph_distance_sum (GtsGraph * g, - GtsGNode * center); -GtsGNode * gts_graph_farthest (GtsGraph * g, - GSList * gnodes); -guint gts_graph_edges_cut (GtsGraph * g); -gfloat gts_graph_edges_cut_weight (GtsGraph * g); -void gts_graph_write (GtsGraph * g, - FILE * fp); -void gts_graph_write_dot (GtsGraph * g, - FILE * fp); -GtsGraph * gts_graph_read (GtsFile * fp); -guint gts_graph_read_jostle (GtsGraph * g, - GtsFile * fp); - -/* GtsWGraph: graph.c */ - -typedef struct _GtsWGraph GtsWGraph; -typedef struct _GtsWGraphClass GtsWGraphClass; - -struct _GtsWGraph { - GtsGraph graph; - - gfloat weight; -}; - -struct _GtsWGraphClass { - GtsGraphClass parent_class; -}; - -#define GTS_WGRAPH(obj) GTS_OBJECT_CAST (obj,\ - GtsWGraph,\ - gts_wgraph_class ()) -#define GTS_WGRAPH_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ - GtsWGraphClass,\ - gts_wgraph_class()) -#define GTS_IS_WGRAPH(obj) (gts_object_is_from_class (obj,\ - gts_wgraph_class ())) - -GtsWGraphClass * gts_wgraph_class (void); -gfloat gts_wgraph_weight_max (GtsWGraph * wg); - -/* Surface graph: graph.c */ - -GtsGraph * gts_surface_graph_new (GtsGraphClass * klass, - GtsSurface * s); -GtsSurface * gts_surface_graph_surface (GtsGraph * surface_graph, - GtsSurface * s); - -/* Segments graph: graph.c */ - -GtsGraph * gts_segments_graph_new (GtsGraphClass * klass, - GSList * segments); - -/* GtsGNodeSplit: pgraph.c */ - -typedef struct _GtsGNodeSplit GtsGNodeSplit; -typedef struct _GtsGNodeSplitClass GtsGNodeSplitClass; - -struct _GtsGNodeSplit { - GtsObject object; - - GtsGNode * n; - GtsObject * n1; - GtsObject * n2; -}; - -struct _GtsGNodeSplitClass { - GtsObjectClass parent_class; -}; - -#define GTS_GNODE_SPLIT(obj) GTS_OBJECT_CAST (obj,\ - GtsGNodeSplit,\ - gts_gnode_split_class ()) -#define GTS_GNODE_SPLIT_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ - GtsGNodeSplitClass,\ - gts_gnode_split_class()) -#define GTS_IS_GNODE_SPLIT(obj) (gts_object_is_from_class (obj,\ - gts_gnode_split_class ())) -#define GTS_GNODE_SPLIT_N1(ns) (GTS_IS_GNODE_SPLIT ((ns)->n1) ? GTS_GNODE_SPLIT ((ns)->n1)->n : GTS_GNODE ((ns)->n1)) -#define GTS_GNODE_SPLIT_N2(ns) (GTS_IS_GNODE_SPLIT ((ns)->n2) ? GTS_GNODE_SPLIT ((ns)->n2)->n : GTS_GNODE ((ns)->n2)) - -GtsGNodeSplitClass * gts_gnode_split_class (void); -GtsGNodeSplit * gts_gnode_split_new (GtsGNodeSplitClass * klass, - GtsGNode * n, - GtsObject * n1, - GtsObject * n2); -void gts_gnode_split_collapse (GtsGNodeSplit * ns, - GtsGraph * g, - GtsWGEdgeClass * klass); -void gts_gnode_split_expand (GtsGNodeSplit * ns, - GtsGraph * g); - -/* GtsPGraph: pgraph.c */ - -typedef struct _GtsPGraph GtsPGraph; -typedef struct _GtsPGraphClass GtsPGraphClass; - -struct _GtsPGraph { - GtsObject object; - - GtsGraph * g; - GPtrArray * split; - GArray * levels; - GtsGNodeSplitClass * split_class; - GtsWGEdgeClass * edge_class; - guint pos, min, level; -}; - -struct _GtsPGraphClass { - GtsObjectClass parent_class; -}; - -#define GTS_PGRAPH(obj) GTS_OBJECT_CAST (obj,\ - GtsPGraph,\ - gts_pgraph_class ()) -#define GTS_PGRAPH_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ - GtsPGraphClass,\ - gts_pgraph_class()) -#define GTS_IS_PGRAPH(obj) (gts_object_is_from_class (obj,\ - gts_pgraph_class ())) - -GtsPGraphClass * gts_pgraph_class (void); -GtsPGraph * gts_pgraph_new (GtsPGraphClass * klass, - GtsGraph * g, - GtsGNodeSplitClass * split_class, - GtsWGNodeClass * node_class, - GtsWGEdgeClass * edge_class, - guint min); -GtsGNodeSplit * gts_pgraph_add_node (GtsPGraph * pg); -GtsGNodeSplit * gts_pgraph_remove_node (GtsPGraph * pg); -void gts_pgraph_set_node_number (GtsPGraph *pg, - guint n); -guint gts_pgraph_get_node_number (GtsPGraph *pg); -guint gts_pgraph_min_node_number (GtsPGraph *pg); -guint gts_pgraph_max_node_number (GtsPGraph *pg); -void gts_pgraph_foreach_node (GtsPGraph *pg, - GtsFunc func, - gpointer data); -gboolean gts_pgraph_down (GtsPGraph * pg, - GtsFunc func, - gpointer data); -/* Graph partition: partition.c */ - -GSList * gts_graph_bubble_partition (GtsGraph * g, - guint np, - guint niter, - GtsFunc step_info, - gpointer data); -guint gts_graph_partition_edges_cut (GSList * partition); -gfloat gts_graph_partition_edges_cut_weight (GSList * partition); -void gts_graph_partition_print_stats (GSList * partition, - FILE * fp); -gfloat gts_graph_partition_balance (GSList * partition); -GSList * gts_graph_partition_clone (GSList * partition); -GSList * gts_graph_recursive_bisection (GtsWGraph * wg, - guint n, - guint ntry, - guint mmax, - guint nmin, - gfloat imbalance); -void gts_graph_partition_destroy (GSList * partition); - -/* Graph bisection: partition.c */ - -typedef struct _GtsGraphBisection GtsGraphBisection; - -struct _GtsGraphBisection { - GtsGraph * g; - GtsGraph * g1; - GtsGraph * g2; - GHashTable * bg1; - GHashTable * bg2; -}; - -gboolean gts_graph_bisection_check (GtsGraphBisection * bg); -GtsGraphBisection * gts_graph_ggg_bisection (GtsGraph * g, - guint ntry); -GtsGraphBisection * gts_graph_bfgg_bisection (GtsGraph * g, - guint ntry); -gdouble gts_graph_bisection_kl_refine (GtsGraphBisection * bg, - guint mmax); -gdouble gts_graph_bisection_bkl_refine (GtsGraphBisection * bg, - guint mmax, - gfloat imbalance); -GtsGraphBisection * gts_graph_bisection_new (GtsWGraph * wg, - guint ntry, - guint mmax, - guint nmin, - gfloat imbalance); -void gts_graph_bisection_destroy (GtsGraphBisection * bg, - gboolean destroy_graphs); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* __GTS_H__ */ Index: trunk/src/3rd/gts/bbtree.c =================================================================== --- trunk/src/3rd/gts/bbtree.c (revision 1064) +++ trunk/src/3rd/gts/bbtree.c (nonexistent) @@ -1,1289 +0,0 @@ -/* GTS - Library for the manipulation of triangulated surfaces - * Copyright (C) 1999 Stéphane Popinet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include -#include "gts.h" - -static void bbox_init (GtsBBox * bbox) -{ - bbox->bounded = NULL; -} - -/** - * gts_bbox_class: - * - * Returns: the #GtsBBoxClass. - */ -GtsBBoxClass * gts_bbox_class (void) -{ - static GtsBBoxClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo bbox_info = { - "GtsBBox", - sizeof (GtsBBox), - sizeof (GtsBBoxClass), - (GtsObjectClassInitFunc) NULL, - (GtsObjectInitFunc) bbox_init, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (gts_object_class (), &bbox_info); - } - - return klass; -} - -/** - * gts_bbox_set: - * @bbox: a #GtsBBox. - * @bounded: the object to be bounded. - * @x1: x-coordinate of the lower left corner. - * @y1: y-coordinate of the lower left corner. - * @z1: z-coordinate of the lower left corner. - * @x2: x-coordinate of the upper right corner. - * @y2: y-coordinate of the upper right corner. - * @z2: z-coordinate of the upper right corner. - * - * Sets fields of @bbox. - */ -void gts_bbox_set (GtsBBox * bbox, - gpointer bounded, - gdouble x1, gdouble y1, gdouble z1, - gdouble x2, gdouble y2, gdouble z2) -{ - g_return_if_fail (bbox != NULL); - g_return_if_fail (x2 >= x1 && y2 >= y1 && z2 >= z1); - - bbox->x1 = x1; bbox->y1 = y1; bbox->z1 = z1; - bbox->x2 = x2; bbox->y2 = y2; bbox->z2 = z2; - bbox->bounded = bounded; -} - -/** - * gts_bbox_new: - * @klass: a #GtsBBoxClass. - * @bounded: the object to be bounded. - * @x1: x-coordinate of the lower left corner. - * @y1: y-coordinate of the lower left corner. - * @z1: z-coordinate of the lower left corner. - * @x2: x-coordinate of the upper right corner. - * @y2: y-coordinate of the upper right corner. - * @z2: z-coordinate of the upper right corner. - * - * Returns: a new #GtsBBox. - */ -GtsBBox * gts_bbox_new (GtsBBoxClass * klass, - gpointer bounded, - gdouble x1, gdouble y1, gdouble z1, - gdouble x2, gdouble y2, gdouble z2) -{ - GtsBBox * bbox; - - g_return_val_if_fail (klass != NULL, NULL); - - bbox = GTS_BBOX (gts_object_new (GTS_OBJECT_CLASS (klass))); - gts_bbox_set (bbox, bounded, x1, y1, z1, x2, y2, z2); - return bbox; -} - -/** - * gts_bbox_triangle: - * @klass: a #GtsBBoxClass. - * @t: a #GtsTriangle. - * - * Returns: a new #GtsBBox bounding box of @t. - */ -GtsBBox * gts_bbox_triangle (GtsBBoxClass * klass, - GtsTriangle * t) -{ - GtsBBox * bbox; - GtsPoint * p; - - g_return_val_if_fail (t != NULL, NULL); - g_return_val_if_fail (klass != NULL, NULL); - - p = GTS_POINT (GTS_SEGMENT (t->e1)->v1); - bbox = gts_bbox_new (klass, t, p->x, p->y, p->z, p->x, p->y, p->z); - - p = GTS_POINT (GTS_SEGMENT (t->e1)->v2); - if (p->x > bbox->x2) bbox->x2 = p->x; - if (p->x < bbox->x1) bbox->x1 = p->x; - if (p->y > bbox->y2) bbox->y2 = p->y; - if (p->y < bbox->y1) bbox->y1 = p->y; - if (p->z > bbox->z2) bbox->z2 = p->z; - if (p->z < bbox->z1) bbox->z1 = p->z; - p = GTS_POINT (gts_triangle_vertex (t)); - if (p->x > bbox->x2) bbox->x2 = p->x; - if (p->x < bbox->x1) bbox->x1 = p->x; - if (p->y > bbox->y2) bbox->y2 = p->y; - if (p->y < bbox->y1) bbox->y1 = p->y; - if (p->z > bbox->z2) bbox->z2 = p->z; - if (p->z < bbox->z1) bbox->z1 = p->z; - - return bbox; -} - -/** - * gts_bbox_segment: - * @klass: a #GtsBBoxClass. - * @s: a #GtsSegment. - * - * Returns: a new #GtsBBox bounding box of @s. - */ -GtsBBox * gts_bbox_segment (GtsBBoxClass * klass, GtsSegment * s) -{ - GtsBBox * bbox; - GtsPoint * p1, * p2; - - g_return_val_if_fail (s != NULL, NULL); - g_return_val_if_fail (klass != NULL, NULL); - - bbox = gts_bbox_new (klass, s, 0., 0., 0., 0., 0., 0.); - - p1 = GTS_POINT (s->v1); - p2 = GTS_POINT (s->v2); - if (p1->x > p2->x) { - bbox->x2 = p1->x; bbox->x1 = p2->x; - } - else { - bbox->x1 = p1->x; bbox->x2 = p2->x; - } - if (p1->y > p2->y) { - bbox->y2 = p1->y; bbox->y1 = p2->y; - } - else { - bbox->y1 = p1->y; bbox->y2 = p2->y; - } - if (p1->z > p2->z) { - bbox->z2 = p1->z; bbox->z1 = p2->z; - } - else { - bbox->z1 = p1->z; bbox->z2 = p2->z; - } - - return bbox; -} - -static void bbox_foreach_vertex (GtsPoint * p, GtsBBox * bb) -{ - if (p->x < bb->x1) bb->x1 = p->x; - if (p->y < bb->y1) bb->y1 = p->y; - if (p->z < bb->z1) bb->z1 = p->z; - if (p->x > bb->x2) bb->x2 = p->x; - if (p->y > bb->y2) bb->y2 = p->y; - if (p->z > bb->z2) bb->z2 = p->z; -} - -/** - * gts_bbox_surface: - * @klass: a #GtsBBoxClass. - * @surface: a #GtsSurface. - * - * Returns: a new #GtsBBox bounding box of @surface. - */ -GtsBBox * gts_bbox_surface (GtsBBoxClass * klass, GtsSurface * surface) -{ - GtsBBox * bbox; - - g_return_val_if_fail (klass != NULL, NULL); - g_return_val_if_fail (surface != NULL, NULL); - - bbox = gts_bbox_new (klass, surface, 0., 0., 0., 0., 0., 0.); - bbox->x1 = bbox->y1 = bbox->z1 = G_MAXDOUBLE; - bbox->x2 = bbox->y2 = bbox->z2 = -G_MAXDOUBLE; - - gts_surface_foreach_vertex (surface, (GtsFunc) bbox_foreach_vertex, bbox); - - return bbox; -} - -/** - * gts_bbox_bboxes: - * @klass: a #GtsBBoxClass. - * @bboxes: a list of #GtsBBox. - * - * Returns: a new #GtsBBox bounding box of all the bounding boxes in - * @bboxes. - */ -GtsBBox * gts_bbox_bboxes (GtsBBoxClass * klass, GSList * bboxes) -{ - GtsBBox * bbox; - GtsBBox * bb; - - g_return_val_if_fail (bboxes != NULL, NULL); - g_return_val_if_fail (klass != NULL, NULL); - - bb = bboxes->data; - bbox = gts_bbox_new (klass, bboxes, - bb->x1, bb->y1, bb->z1, bb->x2, bb->y2, bb->z2); - bboxes = bboxes->next; - while (bboxes) { - bb = bboxes->data; - if (bb->x1 < bbox->x1) bbox->x1 = bb->x1; - if (bb->y1 < bbox->y1) bbox->y1 = bb->y1; - if (bb->z1 < bbox->z1) bbox->z1 = bb->z1; - if (bb->x2 > bbox->x2) bbox->x2 = bb->x2; - if (bb->y2 > bbox->y2) bbox->y2 = bb->y2; - if (bb->z2 > bbox->z2) bbox->z2 = bb->z2; - bboxes = bboxes->next; - } - - return bbox; -} - -/** - * gts_bbox_points: - * @klass: a #GtsBBoxClass. - * @points: a list of #GtsPoint. - * - * Returns: a new #GtsBBox bounding box of @points. - */ -GtsBBox * gts_bbox_points (GtsBBoxClass * klass, GSList * points) -{ - GtsPoint * p; - GtsBBox * bbox; - GSList * i; - - if (points == NULL) - return NULL; - - p = points->data; - bbox = gts_bbox_new (klass, points, p->x, p->y, p->z, p->x, p->y, p->z); - - i = points->next; - while (i) { - p = i->data; - if (p->x > bbox->x2) - bbox->x2 = p->x; - else if (p->x < bbox->x1) - bbox->x1 = p->x; - if (p->y > bbox->y2) - bbox->y2 = p->y; - else if (p->y < bbox->y1) - bbox->y1 = p->y; - if (p->z > bbox->z2) - bbox->z2 = p->z; - else if (p->z < bbox->z1) - bbox->z1 = p->z; - i = i->next; - } - - return bbox; -} - -/** - * gts_bboxes_are_overlapping: - * @bb1: a #GtsBBox. - * @bb2: a #GtsBBox. - * - * Returns: %TRUE if the bounding boxes @bb1 and @bb2 are overlapping - * (including just touching), %FALSE otherwise. - */ -gboolean gts_bboxes_are_overlapping (GtsBBox * bb1, GtsBBox * bb2) -{ - if (bb1 == bb2) - return TRUE; - if (bb1->x1 > bb2->x2) - return FALSE; - if (bb2->x1 > bb1->x2) - return FALSE; - if (bb1->y1 > bb2->y2) - return FALSE; - if (bb2->y1 > bb1->y2) - return FALSE; - if (bb1->z1 > bb2->z2) - return FALSE; - if (bb2->z1 > bb1->z2) - return FALSE; - return TRUE; -} - -#define bbox_volume(bb) (((bb)->x2 -\ - (bb)->x1)*\ - ((bb)->y2 -\ - (bb)->y1)*\ - ((bb)->z2 -\ - (bb)->z1)) - -/** - * gts_bbox_diagonal2: - * @bb: a #GtsBBox. - * - * Returns: the squared length of the diagonal of @bb. - */ -gdouble gts_bbox_diagonal2 (GtsBBox * bb) -{ - gdouble x, y, z; - - g_return_val_if_fail (bb != NULL, 0.); - - x = bb->x2 - bb->x1; - y = bb->y2 - bb->y1; - z = bb->z2 - bb->z1; - - return x*x + y*y + z*z; -} - -/** - * gts_bbox_draw: - * @bb: a #GtsBBox. - * @fptr: a file pointer. - * - * Writes in file @fptr an OOGL (Geomview) description of @bb. - */ -void gts_bbox_draw (GtsBBox * bb, FILE * fptr) -{ - g_return_if_fail (bb != NULL); - - fprintf (fptr, "OFF 8 6 12\n"); - fprintf (fptr, "%g %g %g\n", - bb->x1, bb->y1, bb->z1); - fprintf (fptr, "%g %g %g\n", - bb->x2, bb->y1, bb->z1); - fprintf (fptr, "%g %g %g\n", - bb->x2, bb->y2, bb->z1); - fprintf (fptr, "%g %g %g\n", - bb->x1, bb->y2, bb->z1); - fprintf (fptr, "%g %g %g\n", - bb->x1, bb->y1, bb->z2); - fprintf (fptr, "%g %g %g\n", - bb->x2, bb->y1, bb->z2); - fprintf (fptr, "%g %g %g\n", - bb->x2, bb->y2, bb->z2); - fprintf (fptr, "%g %g %g\n", - bb->x1, bb->y2, bb->z2); - fputs ("4 3 2 1 0\n" - "4 4 5 6 7\n" - "4 2 3 7 6\n" - "4 0 1 5 4\n" - "4 0 4 7 3\n" - "4 1 2 6 5\n", - fptr); -} - -#define MINMAX(x1, x2, xmin, xmax) { if (x1 < x2) { xmin = x1; xmax = x2; }\ - else { xmin = x2; xmax = x1; } } - -/** - * gts_bbox_point_distance2: - * @bb: a #GtsBBox. - * @p: a #GtsPoint. - * @min: a pointer on a gdouble. - * @max: a pointer on a gdouble. - * - * Sets @min and @max to lower and upper bounds for the square of the - * Euclidean distance between the object contained in @bb and @p. For these - * bounds to make any sense the bounding box must be "tight" i.e. each of the - * 6 faces of the box must at least be touched by one point of the bounded - * object. - */ -void gts_bbox_point_distance2 (GtsBBox * bb, GtsPoint * p, - gdouble * min, gdouble * max) -{ - gdouble x1, y1, z1, x2, y2, z2, x, y, z; - gdouble dmin, dmax, xd1, xd2, yd1, yd2, zd1, zd2; - gdouble mx, Mx, my, My, mz, Mz; - - g_return_if_fail (bb != NULL); - g_return_if_fail (p != NULL); - g_return_if_fail (min != NULL); - g_return_if_fail (max != NULL); - - x1 = bb->x1; y1 = bb->y1; z1 = bb->z1; - x2 = bb->x2; y2 = bb->y2; z2 = bb->z2; - x = p->x; y = p->y; z = p->z; - - xd1 = (x1 - x)*(x1 - x); - xd2 = (x - x2)*(x - x2); - yd1 = (y1 - y)*(y1 - y); - yd2 = (y - y2)*(y - y2); - zd1 = (z1 - z)*(z1 - z); - zd2 = (z - z2)*(z - z2); - - dmin = x < x1 ? xd1 : x > x2 ? xd2 : 0.0; - dmin += y < y1 ? yd1 : y > y2 ? yd2 : 0.0; - dmin += z < z1 ? zd1 : z > z2 ? zd2 : 0.0; - - MINMAX (xd1, xd2, mx, Mx); - MINMAX (yd1, yd2, my, My); - MINMAX (zd1, zd2, mz, Mz); - - dmax = mx + My + Mz; - dmax = MIN (dmax, Mx + my + Mz); - dmax = MIN (dmax, Mx + My + mz); - - *min = dmin; - *max = dmax; -} - -/** - * gts_bbox_is_stabbed: - * @bb: a #GtsBBox. - * @p: a #GtsPoint. - * - * Returns: %TRUE if the ray starting at @p and ending at (+infty, - * @p->y, @p->z) intersects with @bb, %FALSE otherwise. - */ -gboolean gts_bbox_is_stabbed (GtsBBox * bb, GtsPoint * p) -{ - g_return_val_if_fail (bb != NULL, FALSE); - g_return_val_if_fail (p != NULL, FALSE); - - if (p->x > bb->x2 || - p->y < bb->y1 || p->y > bb->y2 || - p->z < bb->z1 || p->z > bb->z2) - return FALSE; - return TRUE; -} - -extern int triBoxOverlap (double boxcenter[3], - double boxhalfsize[3], - double triverts[3][3]); - -/** - * gts_bbox_overlaps_triangle: - * @bb: a #GtsBBox. - * @t: a #GtsTriangle. - * - * This is a wrapper around the fast overlap test of Tomas - * Akenine-Moller (http://www.cs.lth.se/home/Tomas_Akenine_Moller/). - * - * Returns: %TRUE if @bb overlaps with @t, %FALSE otherwise. - */ -gboolean gts_bbox_overlaps_triangle (GtsBBox * bb, GtsTriangle * t) -{ - double bc[3], bh[3], tv[3][3]; - GtsPoint * p1, * p2, * p3; - - g_return_val_if_fail (bb != NULL, FALSE); - g_return_val_if_fail (t != NULL, FALSE); - - bc[0] = (bb->x2 + bb->x1)/2.; - bh[0] = (bb->x2 - bb->x1)/2.; - bc[1] = (bb->y2 + bb->y1)/2.; - bh[1] = (bb->y2 - bb->y1)/2.; - bc[2] = (bb->z2 + bb->z1)/2.; - bh[2] = (bb->z2 - bb->z1)/2.; - p1 = GTS_POINT (GTS_SEGMENT (t->e1)->v1); - p2 = GTS_POINT (GTS_SEGMENT (t->e1)->v2); - p3 = GTS_POINT (gts_triangle_vertex (t)); - tv[0][0] = p1->x; tv[0][1] = p1->y; tv[0][2] = p1->z; - tv[1][0] = p2->x; tv[1][1] = p2->y; tv[1][2] = p2->z; - tv[2][0] = p3->x; tv[2][1] = p3->y; tv[2][2] = p3->z; - - return triBoxOverlap (bc, bh, tv); -} - -/** - * gts_bbox_overlaps_segment: - * @bb: a #GtsBBox. - * @s: a #GtsSegment. - * - * This functions uses gts_bbox_overlaps_triangle() with a degenerate - * triangle. - * - * Returns: %TRUE if @bb overlaps with @s, %FALSE otherwise. - */ -gboolean gts_bbox_overlaps_segment (GtsBBox * bb, GtsSegment * s) -{ - double bc[3], bh[3], tv[3][3]; - GtsPoint * p1, * p2, * p3; - - g_return_val_if_fail (bb != NULL, FALSE); - g_return_val_if_fail (s != NULL, FALSE); - - bc[0] = (bb->x2 + bb->x1)/2.; - bh[0] = (bb->x2 - bb->x1)/2.; - bc[1] = (bb->y2 + bb->y1)/2.; - bh[1] = (bb->y2 - bb->y1)/2.; - bc[2] = (bb->z2 + bb->z1)/2.; - bh[2] = (bb->z2 - bb->z1)/2.; - p1 = GTS_POINT (s->v1); - p2 = GTS_POINT (s->v2); - p3 = p1; - tv[0][0] = p1->x; tv[0][1] = p1->y; tv[0][2] = p1->z; - tv[1][0] = p2->x; tv[1][1] = p2->y; tv[1][2] = p2->z; - tv[2][0] = p3->x; tv[2][1] = p3->y; tv[2][2] = p3->z; - - return triBoxOverlap (bc, bh, tv); -} - -/** - * gts_bb_tree_new: - * @bboxes: a list of #GtsBBox. - * - * Builds a new hierarchy of bounding boxes for @bboxes. At each - * level, the GNode->data field contains a #GtsBBox bounding box of - * all the children. The tree is binary and is built by repeatedly - * cutting in two approximately equal halves the bounding boxes at - * each level until a leaf node (i.e. a bounding box given in @bboxes) - * is reached. In order to minimize the depth of the tree, the cutting - * direction is always chosen as perpendicular to the longest - * dimension of the bounding box. - * - * Returns: a new hierarchy of bounding boxes. - */ -GNode * gts_bb_tree_new (GSList * bboxes) -{ - GSList * i, * positive = NULL, * negative = NULL; - GNode * node; - GtsBBox * bbox; - guint dir, np = 0, nn = 0; - gdouble * p1, * p2; - gdouble cut; - - g_return_val_if_fail (bboxes != NULL, NULL); - - if (bboxes->next == NULL) /* leaf node */ - return g_node_new (bboxes->data); - - bbox = gts_bbox_bboxes (gts_bbox_class (), bboxes); - node = g_node_new (bbox); - - if (bbox->x2 - bbox->x1 > bbox->y2 - bbox->y1) { - if (bbox->z2 - bbox->z1 > bbox->x2 - bbox->x1) - dir = 2; - else - dir = 0; - } - else if (bbox->z2 - bbox->z1 > bbox->y2 - bbox->y1) - dir = 2; - else - dir = 1; - - p1 = (gdouble *) &bbox->x1; - p2 = (gdouble *) &bbox->x2; - cut = (p1[dir] + p2[dir])/2.; - i = bboxes; - while (i) { - bbox = i->data; - p1 = (gdouble *) &bbox->x1; - p2 = (gdouble *) &bbox->x2; - if ((p1[dir] + p2[dir])/2. > cut) { - positive = g_slist_prepend (positive, bbox); - np++; - } - else { - negative = g_slist_prepend (negative, bbox); - nn++; - } - i = i->next; - } - if (!positive) { - GSList * last = g_slist_nth (negative, (nn - 1)/2); - positive = last->next; - last->next = NULL; - } - else if (!negative) { - GSList * last = g_slist_nth (positive, (np - 1)/2); - negative = last->next; - last->next = NULL; - } - g_node_prepend (node, gts_bb_tree_new (positive)); - g_slist_free (positive); - g_node_prepend (node, gts_bb_tree_new (negative)); - g_slist_free (negative); - - return node; -} - -static void prepend_triangle_bbox (GtsTriangle * t, GSList ** bboxes) -{ - *bboxes = g_slist_prepend (*bboxes, - gts_bbox_triangle (gts_bbox_class (), t)); -} - -/** - * gts_bb_tree_surface: - * @s: a #GtsSurface. - * - * Returns: a new hierarchy of bounding boxes bounding the faces of @s. - */ -GNode * gts_bb_tree_surface (GtsSurface * s) -{ - GSList * bboxes = NULL; - GNode * tree; - - g_return_val_if_fail (s != NULL, NULL); - - gts_surface_foreach_face (s, (GtsFunc) prepend_triangle_bbox, &bboxes); - tree = gts_bb_tree_new (bboxes); - g_slist_free (bboxes); - - return tree; -} - -/** - * gts_bb_tree_stabbed: - * @tree: a bounding box tree. - * @p: a #GtsPoint. - * - * Returns: a list of bounding boxes, leaves of @tree which are - * stabbed by the ray defined by @p (see gts_bbox_is_stabbed()). - */ -GSList * gts_bb_tree_stabbed (GNode * tree, GtsPoint * p) -{ - GSList * list = NULL; - GtsBBox * bb; - GNode * i; - - g_return_val_if_fail (tree != NULL, NULL); - g_return_val_if_fail (p != NULL, NULL); - - bb = tree->data; - if (!gts_bbox_is_stabbed (bb, p)) - return NULL; - if (tree->children == NULL) /* leaf node */ - return g_slist_prepend (NULL, bb); - i = tree->children; - while (i) { - list = g_slist_concat (list, gts_bb_tree_stabbed (i, p)); - i = i->next; - } - return list; -} - -/** - * gts_bb_tree_overlap: - * @tree: a bounding box tree. - * @bbox: a #GtsBBox. - * - * Returns: a list of bounding boxes, leaves of @tree which overlap @bbox. - */ -GSList * gts_bb_tree_overlap (GNode * tree, GtsBBox * bbox) -{ - GSList * list = NULL; - GtsBBox * bb; - GNode * i; - - g_return_val_if_fail (tree != NULL, NULL); - g_return_val_if_fail (bbox != NULL, NULL); - - bb = tree->data; - if (!gts_bboxes_are_overlapping (bbox, bb)) - return NULL; - if (tree->children == NULL) /* leaf node */ - return g_slist_prepend (NULL, bb); - i = tree->children; - while (i) { - list = g_slist_concat (list, gts_bb_tree_overlap (i, bbox)); - i = i->next; - } - return list; -} - -/** - * gts_bb_tree_is_overlapping: - * @tree: a bounding box tree. - * @bbox: a #GtsBBox. - * - * Returns: %TRUE if any leaf of @tree overlaps @bbox, %FALSE otherwise. - */ -gboolean gts_bb_tree_is_overlapping (GNode * tree, GtsBBox * bbox) -{ - GtsBBox * bb; - GNode * i; - - g_return_val_if_fail (tree != NULL, FALSE); - g_return_val_if_fail (bbox != NULL, FALSE); - - bb = tree->data; - if (!gts_bboxes_are_overlapping (bbox, bb)) - return FALSE; - if (tree->children == NULL) /* leaf node */ - return TRUE; - i = tree->children; - while (i) { - if (gts_bb_tree_is_overlapping (i, bbox)) - return TRUE; - i = i->next; - } - return FALSE; -} - -/** - * gts_bb_tree_traverse_overlapping: - * @tree1: a bounding box tree. - * @tree2: a bounding box tree. - * @func: a #GtsBBTreeTraverseFunc. - * @data: user data to be passed to @func. - * - * Calls @func for each overlapping pair of leaves of @tree1 and @tree2. - */ -void gts_bb_tree_traverse_overlapping (GNode * tree1, GNode * tree2, - GtsBBTreeTraverseFunc func, - gpointer data) -{ - GtsBBox * bb1, * bb2; - - g_return_if_fail (tree1 != NULL && tree2 != NULL); - - bb1 = tree1->data; bb2 = tree2->data; - if (!gts_bboxes_are_overlapping (bb1, bb2)) - return; - - if (tree1->children == NULL && tree2->children == NULL) - (*func) (tree1->data, tree2->data, data); - else if (tree2->children == NULL || - (tree1->children != NULL && - bbox_volume (bb1) > bbox_volume (bb2))) { - GNode * i = tree1->children; - while (i) { - gts_bb_tree_traverse_overlapping (i, tree2, func, data); - i = i->next; - } - } - else { - GNode * i = tree2->children; - while (i) { - gts_bb_tree_traverse_overlapping (tree1, i, func, data); - i = i->next; - } - } -} - -/** - * gts_bb_tree_draw: - * @tree: a bounding box tree. - * @depth: a specified depth. - * @fptr: a file pointer. - * - * Write in @fptr an OOGL (Geomview) description of @tree for the - * depth specified by @depth. - */ -void gts_bb_tree_draw (GNode * tree, guint depth, FILE * fptr) -{ - guint d; - - g_return_if_fail (tree != NULL); - g_return_if_fail (fptr != NULL); - - d = g_node_depth (tree); - - if (d == 1) - fprintf (fptr, "{ LIST"); - - if (d == depth) - gts_bbox_draw (tree->data, fptr); - else if (d < depth) { - GNode * i = tree->children; - while (i) { - gts_bb_tree_draw (i, depth, fptr); - i = i->next; - } - } - - if (d == 1) - fprintf (fptr, "}\n"); -} - -static void bb_tree_free (GNode * tree, gboolean free_leaves) -{ - GNode * i; - - g_return_if_fail (tree != NULL); - - if (!free_leaves && tree->children == NULL) /* leaf node */ - return; - - gts_object_destroy (tree->data); - - i = tree->children; - while (i) { - bb_tree_free (i, free_leaves); - i = i->next; - } -} - -/** - * gts_bb_tree_destroy: - * @tree: a bounding box tree. - * @free_leaves: if %TRUE the bounding boxes given by the user are freed. - * - * Destroys all the bounding boxes created by @tree and destroys the - * tree itself. If @free_leaves is set to %TRUE, destroys boxes given - * by the user when creating the tree (i.e. leaves of the tree). - */ -void gts_bb_tree_destroy (GNode * tree, gboolean free_leaves) -{ - g_return_if_fail (tree != NULL); - - bb_tree_free (tree, free_leaves); - g_node_destroy (tree); -} - -static gdouble bb_tree_min_max (GNode * tree, - GtsPoint * p, - gdouble min_max, - GSList ** list) -{ - GNode * tree1, * tree2; - gdouble min1, max1, min2, max2; - - if (tree->children == NULL) { - *list = g_slist_prepend (*list, tree->data); - return min_max; - } - tree1 = tree->children; - gts_bbox_point_distance2 (tree1->data, p, &min1, &max1); - if (max1 < min_max) - min_max = max1; - - tree2 = tree1->next; - gts_bbox_point_distance2 (tree2->data, p, &min2, &max2); - if (max2 < min_max) - min_max = max2; - - if (min1 < min2) { - if (min1 <= min_max) { - min_max = bb_tree_min_max (tree1, p, min_max, list); - if (min2 <= min_max) - min_max = bb_tree_min_max (tree2, p, min_max, list); - } - } - else { - if (min2 <= min_max) { - min_max = bb_tree_min_max (tree2, p, min_max, list); - if (min1 <= min_max) - min_max = bb_tree_min_max (tree1, p, min_max, list); - } - } - - return min_max; -} - -/** - * gts_bb_tree_point_closest_bboxes: - * @tree: a bounding box tree. - * @p: a #GtsPoint. - * - * Returns: a list of #GtsBBox. One of the bounding boxes is assured to contain - * the object of @tree closest to @p. - */ -GSList * gts_bb_tree_point_closest_bboxes (GNode * tree, - GtsPoint * p) -{ - gdouble min, min_max; - GSList * list = NULL, * i, * prev = NULL; - - g_return_val_if_fail (tree != NULL, NULL); - g_return_val_if_fail (p != NULL, NULL); - - gts_bbox_point_distance2 (tree->data, p, &min, &min_max); - min_max = bb_tree_min_max (tree, p, min_max, &list); - - i = list; - while (i) { - GSList * next = i->next; - gdouble min, max; - - gts_bbox_point_distance2 (i->data, p, &min, &max); - - if (min > min_max) { - if (prev == NULL) - list = next; - else - prev->next = next; - g_slist_free_1 (i); - } - else - prev = i; - i = next; - } - - return list; -} - -/** - * gts_bb_tree_point_distance: - * @tree: a bounding box tree. - * @p: a #GtsPoint. - * @distance: a #GtsBBoxDistFunc. - * @bbox: if not %NULL is set to the bounding box containing the closest - * object. - * - * Returns: the distance as evaluated by @distance between @p and the closest - * object in @tree. - */ -gdouble gts_bb_tree_point_distance (GNode * tree, - GtsPoint * p, - GtsBBoxDistFunc distance, - GtsBBox ** bbox) -{ - GSList * list, * i; - gdouble dmin = G_MAXDOUBLE; - - g_return_val_if_fail (tree != NULL, dmin); - g_return_val_if_fail (p != NULL, dmin); - g_return_val_if_fail (distance != NULL, dmin); - - i = list = gts_bb_tree_point_closest_bboxes (tree, p); - while (i) { - gdouble d = (*distance) (p, GTS_BBOX (i->data)->bounded); - - if (fabs (d) < fabs (dmin)) { - dmin = d; - if (bbox) - *bbox = i->data; - } - i = i->next; - } - g_slist_free (list); - - return dmin; -} - -/** - * gts_bb_tree_point_closest: - * @tree: a bounding box tree. - * @p: a #GtsPoint. - * @closest: a #GtsBBoxClosestFunc. - * @distance: if not %NULL is set to the distance between @p and the - * new #GtsPoint. - * - * Returns: a new #GtsPoint, closest point to @p and belonging to an object of - * @tree. - */ -GtsPoint * gts_bb_tree_point_closest (GNode * tree, - GtsPoint * p, - GtsBBoxClosestFunc closest, - gdouble * distance) -{ - GSList * list, * i; - gdouble dmin = G_MAXDOUBLE; - GtsPoint * np = NULL; - - g_return_val_if_fail (tree != NULL, NULL); - g_return_val_if_fail (p != NULL, NULL); - g_return_val_if_fail (closest != NULL, NULL); - - i = list = gts_bb_tree_point_closest_bboxes (tree, p); - while (i) { - GtsPoint * tp = (*closest) (p, GTS_BBOX (i->data)->bounded); - gdouble d = gts_point_distance2 (tp, p); - - if (d < dmin) { - if (np) - gts_object_destroy (GTS_OBJECT (np)); - np = tp; - dmin = d; - } - else - gts_object_destroy (GTS_OBJECT (tp)); - i = i->next; - } - g_slist_free (list); - - if (distance) - *distance = dmin; - - return np; -} - -/** - * gts_bb_tree_triangle_distance: - * @tree: a bounding box tree. - * @t: a #GtsTriangle. - * @distance: a #GtsBBoxDistFunc. - * @delta: spatial scale of the sampling to be used. - * @range: a #GtsRange to be filled with the results. - * - * Given a triangle @t, points are sampled regularly on its surface - * using @delta as increment. The distance from each of these points - * to the closest object of @tree is computed using @distance and the - * gts_bb_tree_point_distance() function. The fields of @range are - * filled with the number of points sampled, the minimum, average and - * maximum value and the standard deviation. - */ -void gts_bb_tree_triangle_distance (GNode * tree, - GtsTriangle * t, - GtsBBoxDistFunc distance, - gdouble delta, - GtsRange * range) -{ - GtsPoint * p1, * p2, * p3, * p; - GtsVector p1p2, p1p3; - gdouble l1, t1, dt1; - guint i, n1; - - g_return_if_fail (tree != NULL); - g_return_if_fail (t != NULL); - g_return_if_fail (distance != NULL); - g_return_if_fail (delta > 0.); - g_return_if_fail (range != NULL); - - gts_triangle_vertices (t, - (GtsVertex **) &p1, - (GtsVertex **) &p2, - (GtsVertex **) &p3); - - gts_vector_init (p1p2, p1, p2); - gts_vector_init (p1p3, p1, p3); - gts_range_init (range); - p = GTS_POINT (gts_object_new (GTS_OBJECT_CLASS (gts_point_class ()))); - - l1 = sqrt (gts_vector_scalar (p1p2, p1p2)); - n1 = l1/delta + 1; - dt1 = 1.0/(gdouble) n1; - t1 = 0.0; - for (i = 0; i <= n1; i++, t1 += dt1) { - gdouble t2 = 1. - t1; - gdouble x = t2*p1p3[0]; - gdouble y = t2*p1p3[1]; - gdouble z = t2*p1p3[2]; - gdouble l2 = sqrt (x*x + y*y + z*z); - guint j, n2 = (guint) (l2/delta + 1); - gdouble dt2 = t2/(gdouble) n2; - - x = t2*p1->x + t1*p2->x; - y = t2*p1->y + t1*p2->y; - z = t2*p1->z + t1*p2->z; - - t2 = 0.0; - for (j = 0; j <= n2; j++, t2 += dt2) { - p->x = x + t2*p1p3[0]; - p->y = y + t2*p1p3[1]; - p->z = z + t2*p1p3[2]; - - gts_range_add_value (range, - gts_bb_tree_point_distance (tree, p, distance, NULL)); - } - } - - gts_object_destroy (GTS_OBJECT (p)); - gts_range_update (range); -} - -/** - * gts_bb_tree_segment_distance: - * @tree: a bounding box tree. - * @s: a #GtsSegment. - * @distance: a #GtsBBoxDistFunc. - * @delta: spatial scale of the sampling to be used. - * @range: a #GtsRange to be filled with the results. - * - * Given a segment @s, points are sampled regularly on its length - * using @delta as increment. The distance from each of these points - * to the closest object of @tree is computed using @distance and the - * gts_bb_tree_point_distance() function. The fields of @range are - * filled with the number of points sampled, the minimum, average and - * maximum value and the standard deviation. - */ -void gts_bb_tree_segment_distance (GNode * tree, - GtsSegment * s, - gdouble (*distance) (GtsPoint *, - gpointer), - gdouble delta, - GtsRange * range) -{ - GtsPoint * p1, * p2, * p; - GtsVector p1p2; - gdouble l, t, dt; - guint i, n; - - g_return_if_fail (tree != NULL); - g_return_if_fail (s != NULL); - g_return_if_fail (distance != NULL); - g_return_if_fail (delta > 0.); - g_return_if_fail (range != NULL); - - p1 = GTS_POINT (s->v1); - p2 = GTS_POINT (s->v2); - - gts_vector_init (p1p2, p1, p2); - gts_range_init (range); - p = GTS_POINT (gts_object_new (GTS_OBJECT_CLASS (gts_point_class()))); - - l = sqrt (gts_vector_scalar (p1p2, p1p2)); - n = (guint) (l/delta + 1); - dt = 1.0/(gdouble) n; - t = 0.0; - for (i = 0; i <= n; i++, t += dt) { - p->x = p1->x + t*p1p2[0]; - p->y = p1->y + t*p1p2[1]; - p->z = p1->z + t*p1p2[2]; - - gts_range_add_value (range, - gts_bb_tree_point_distance (tree, p, distance, NULL)); - } - - gts_object_destroy (GTS_OBJECT (p)); - gts_range_update (range); -} - -static void surface_distance_foreach_triangle (GtsTriangle * t, - gpointer * data) -{ - gdouble * delta = data[1]; - GtsRange * range = data[2]; - gdouble * total_area = data[3], area; - GtsRange range_triangle; - - gts_bb_tree_triangle_distance (data[0], t, data[4], *delta, &range_triangle); - - if (range_triangle.min < range->min) - range->min = range_triangle.min; - if (range_triangle.max > range->max) - range->max = range_triangle.max; - range->n += range_triangle.n; - - area = gts_triangle_area (t); - *total_area += area; - range->sum += area*range_triangle.mean; - range->sum2 += area*range_triangle.mean*range_triangle.mean; -} - -/** - * gts_bb_tree_surface_distance: - * @tree: a bounding box tree. - * @s: a #GtsSurface. - * @distance: a #GtsBBoxDistFunc. - * @delta: a sampling increment defined as the percentage of the diagonal - * of the root bounding box of @tree. - * @range: a #GtsRange to be filled with the results. - * - * Calls gts_bb_tree_triangle_distance() for each face of @s. The - * fields of @range are filled with the minimum, maximum and average - * distance. The average distance is defined as the sum of the average - * distances for each triangle weighthed by their area and divided by - * the total area of the surface. The standard deviation is defined - * accordingly. The @n field of @range is filled with the number of - * sampled points used. - */ -void gts_bb_tree_surface_distance (GNode * tree, - GtsSurface * s, - GtsBBoxDistFunc distance, - gdouble delta, - GtsRange * range) -{ - gpointer data[5]; - gdouble total_area = 0.; - - g_return_if_fail (tree != NULL); - g_return_if_fail (s != NULL); - g_return_if_fail (delta > 0. && delta < 1.); - g_return_if_fail (range != NULL); - - gts_range_init (range); - delta *= sqrt (gts_bbox_diagonal2 (tree->data)); - data[0] = tree; - data[1] = δ - data[2] = range; - data[3] = &total_area; - data[4] = distance; - - gts_surface_foreach_face (s, - (GtsFunc) surface_distance_foreach_triangle, - data); - - if (total_area > 0.) { - if (range->sum2 - range->sum*range->sum/total_area >= 0.) - range->stddev = sqrt ((range->sum2 - range->sum*range->sum/total_area) - /total_area); - else - range->stddev = 0.; - range->mean = range->sum/total_area; - } - else - range->min = range->max = range->mean = range->stddev = 0.; -} - -static void surface_distance_foreach_boundary (GtsEdge * e, - gpointer * data) -{ - gdouble * delta = data[1]; - GtsRange * range = data[2]; - gdouble * total_length = data[3], length; - GtsRange range_edge; - - if (gts_edge_is_boundary (e, NULL)) { - GtsSegment * s = GTS_SEGMENT (e); - - gts_bb_tree_segment_distance (data[0], s, data[4], *delta, &range_edge); - - if (range_edge.min < range->min) - range->min = range_edge.min; - if (range_edge.max > range->max) - range->max = range_edge.max; - range->n += range_edge.n; - - length = gts_point_distance (GTS_POINT (s->v1), GTS_POINT (s->v2)); - *total_length += length; - range->sum += length*range_edge.mean; - range->sum2 += length*range_edge.mean*range_edge.mean; - } -} - -/** - * gts_bb_tree_surface_boundary_distance: - * @tree: a bounding box tree. - * @s: a #GtsSurface. - * @distance: a #GtsBBoxDistFunc. - * @delta: a sampling increment defined as the percentage of the diagonal - * of the root bounding box of @tree. - * @range: a #GtsRange to be filled with the results. - * - * Calls gts_bb_tree_segment_distance() for each edge boundary of @s. - * The fields of @range are filled with the minimum, maximum and - * average distance. The average distance is defined as the sum of the - * average distances for each boundary edge weighthed by their length - * and divided by the total length of the boundaries. The standard - * deviation is defined accordingly. The @n field of @range is filled - * with the number of sampled points used. - */ -void gts_bb_tree_surface_boundary_distance (GNode * tree, - GtsSurface * s, - gdouble (*distance) (GtsPoint *, - gpointer), - gdouble delta, - GtsRange * range) -{ - gpointer data[5]; - gdouble total_length = 0.; - - g_return_if_fail (tree != NULL); - g_return_if_fail (s != NULL); - g_return_if_fail (delta > 0. && delta < 1.); - g_return_if_fail (range != NULL); - - gts_range_init (range); - delta *= sqrt (gts_bbox_diagonal2 (tree->data)); - data[0] = tree; - data[1] = δ - data[2] = range; - data[3] = &total_length; - data[4] = distance; - - gts_surface_foreach_edge (s, - (GtsFunc) surface_distance_foreach_boundary, - data); - - if (total_length > 0.) { - if (range->sum2 - range->sum*range->sum/total_length >= 0.) - range->stddev = sqrt ((range->sum2 - - range->sum*range->sum/total_length) - /total_length); - else - range->stddev = 0.; - range->mean = range->sum/total_length; - } - else - range->min = range->max = range->mean = range->stddev = 0.; -} Index: trunk/src/3rd/gts/Makefile.dep =================================================================== --- trunk/src/3rd/gts/Makefile.dep (revision 1064) +++ trunk/src/3rd/gts/Makefile.dep (nonexistent) @@ -1,38 +0,0 @@ -### Generated file, do not edit, run make dep ### - -object.o: object.c gts.h gts-private.h -point.o: point.c gts.h gts-private.h predicates.h -vertex.o: vertex.c gts.h -segment.o: segment.c gts.h -edge.o: edge.c gts.h -triangle.o: triangle.c gts.h -face.o: face.c gts.h -kdtree.o: kdtree.c gts.h -bbtree.o: bbtree.c gts.h -misc.o: misc.c gts.h gts-private.h ../config.h ../config.manual.h \ - ../config.auto.h -predicates.o: predicates.c predicates.h rounding.h ../config.h \ - ../config.manual.h ../config.auto.h -heap.o: heap.c gts.h -eheap.o: eheap.c gts.h -fifo.o: fifo.c gts.h -matrix.o: matrix.c gts.h -surface.o: surface.c gts.h gts-private.h -stripe.o: stripe.c gts.h -vopt.o: vopt.c gts.h -refine.o: refine.c gts.h -iso.o: iso.c gts.h -isotetra.o: isotetra.c -split.o: split.c gts.h -psurface.o: psurface.c gts.h -hsurface.o: hsurface.c gts.h -cdt.o: cdt.c gts.h -boolean.o: boolean.c gts.h -named.o: named.c gts.h -oocs.o: oocs.c gts.h -container.o: container.c gts.h -graph.o: graph.c gts.h -pgraph.o: pgraph.c gts.h -partition.o: partition.c gts.h -curvature.o: curvature.c gts.h -tribox3.o: tribox3.c Index: trunk/src/3rd/gts/edge.c =================================================================== --- trunk/src/3rd/gts/edge.c (revision 1064) +++ trunk/src/3rd/gts/edge.c (nonexistent) @@ -1,582 +0,0 @@ -/* GTS - Library for the manipulation of triangulated surfaces - * Copyright (C) 1999 Stéphane Popinet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include "gts.h" - -gboolean gts_allow_floating_edges = FALSE; - -static void edge_destroy (GtsObject * object) -{ - GtsEdge * edge = GTS_EDGE (object); - GSList * i; - - i = edge->triangles; - while (i) { - GSList * next = i->next; - gts_object_destroy (i->data); - i = next; - } - g_assert (edge->triangles == NULL); - - (* GTS_OBJECT_CLASS (gts_edge_class ())->parent_class->destroy) (object); -} - -static void edge_clone (GtsObject * clone, GtsObject * object) -{ - (* GTS_OBJECT_CLASS (gts_edge_class ())->parent_class->clone) (clone, - object); - GTS_SEGMENT (clone)->v1 = GTS_SEGMENT (clone)->v2 = NULL; - GTS_EDGE (clone)->triangles = NULL; -} - -static void edge_class_init (GtsObjectClass * klass) -{ - klass->clone = edge_clone; - klass->destroy = edge_destroy; -} - -static void edge_init (GtsEdge * edge) -{ - edge->triangles = NULL; -} - -/** - * gts_edge_class: - * - * Returns: the #GtsEdgeClass. - */ -GtsEdgeClass * gts_edge_class (void) -{ - static GtsEdgeClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo edge_info = { - "GtsEdge", - sizeof (GtsEdge), - sizeof (GtsEdgeClass), - (GtsObjectClassInitFunc) edge_class_init, - (GtsObjectInitFunc) edge_init, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_segment_class ()), - &edge_info); - } - - return klass; -} - -/** - * gts_edge_new: - * @klass: a #GtsEdgeClass. - * @v1: a #GtsVertex. - * @v2: a #GtsVertex. - * - * Returns: a new #GtsEdge linking @v1 and @v2. - */ -GtsEdge * gts_edge_new (GtsEdgeClass * klass, - GtsVertex * v1, GtsVertex * v2) -{ - return GTS_EDGE (gts_segment_new (GTS_SEGMENT_CLASS (klass), v1, v2)); -} - -void gts_edge_remove(GtsEdge *edge) -{ - edge->segment.v1->segments = g_slist_remove(edge->segment.v1->segments, &edge->segment); - edge->segment.v2->segments = g_slist_remove(edge->segment.v2->segments, &edge->segment); - edge_destroy(GTS_OBJECT (edge)); -} - -/** - * gts_edge_replace: - * @e: a #GtsEdge. - * @with: a #GtsEdge. - * - * Replaces @e with @with. For each triangle which uses @e as an - * edge, @e is replaced with @with. The @with->triangles list is - * updated appropriately and the @e->triangles list is freed and set - * to %NULL. - */ -void gts_edge_replace (GtsEdge * e, GtsEdge * with) -{ - GSList * i; - - g_return_if_fail (e != NULL && with != NULL && e != with); - - i = e->triangles; - while (i) { - GtsTriangle * t = i->data; - if (t->e1 == e) t->e1 = with; - if (t->e2 == e) t->e2 = with; - if (t->e3 == e) t->e3 = with; - if (!g_slist_find (with->triangles, t)) - with->triangles = g_slist_prepend (with->triangles, t); - i = i->next; - } - g_slist_free (e->triangles); - e->triangles = NULL; -} - -/** - * gts_edge_has_parent_surface: - * @e: a #GtsEdge. - * @surface: a #GtsSurface. - * - * Returns: a #GtsFace of @surface having @e as an edge, %NULL otherwise. - */ -GtsFace * gts_edge_has_parent_surface (GtsEdge * e, GtsSurface * surface) -{ - GSList * i; - - g_return_val_if_fail (e != NULL, NULL); - - i = e->triangles; - while (i) { - if (GTS_IS_FACE (i->data) && - gts_face_has_parent_surface (i->data, surface)) - return i->data; - i = i->next; - } - return NULL; -} - -/** - * gts_edge_has_any_parent_surface: - * @e: a #GtsEdge. - * - * Returns: %NULL if @e is not an edge of any triangle or if all the - * faces having @e has an edge do not belong to any surface, - * a #GtsFace belonging to a surface and having @e as an edge. - */ -GtsFace * gts_edge_has_any_parent_surface (GtsEdge * e) -{ - GSList * i; - - g_return_val_if_fail (e != NULL, NULL); - - i = e->triangles; - while (i) { - GtsTriangle * t = i->data; - if (GTS_IS_FACE (t) && GTS_FACE (t)->surfaces != NULL) - return GTS_FACE (t); - i = i->next; - } - return NULL; -} - -/** - * gts_edge_is_boundary: - * @e: a #GtsEdge. - * @surface: a #GtsSurface or %NULL. - * - * Returns: the unique #GtsFace (which belongs to @surface) and which - * has @e as an edge (i.e. @e is a boundary edge (of @surface)) or %NULL - * if there is more than one or no faces (belonging to @surface) and - * with @e as an edge. - */ -GtsFace * gts_edge_is_boundary (GtsEdge * e, GtsSurface * surface) -{ - GSList * i; - GtsFace * f = NULL; - - g_return_val_if_fail (e != NULL, NULL); - - i = e->triangles; - while (i) { - if (GTS_IS_FACE (i->data)) { - if (!surface || gts_face_has_parent_surface (i->data, surface)) { - if (f != NULL) - return NULL; - f = i->data; - } - } - i = i->next; - } - return f; -} - -/** - * gts_edges_from_vertices: - * @vertices: a list of #GtsVertex. - * @parent: a #GtsSurface. - * - * Returns: a list of unique #GtsEdge which have one of their vertices in - * @vertices and are used by a face of @parent. - */ -GSList * gts_edges_from_vertices (GSList * vertices, GtsSurface * parent) -{ - GHashTable * hash; - GSList * edges = NULL, * i; - - g_return_val_if_fail (parent != NULL, NULL); - - hash = g_hash_table_new (NULL, NULL); - i = vertices; - while (i) { - GSList * j = GTS_VERTEX (i->data)->segments; - while (j) { - GtsSegment * s = j->data; - if (GTS_IS_EDGE (s) && - gts_edge_has_parent_surface (GTS_EDGE (s), parent) && - g_hash_table_lookup (hash, s) == NULL) { - edges = g_slist_prepend (edges, s); - g_hash_table_insert (hash, s, i); - } - j = j->next; - } - i = i->next; - } - g_hash_table_destroy (hash); - return edges; -} - -/** - * gts_edge_face_number: - * @e: a #GtsEdge. - * @s: a #GtsSurface. - * - * Returns: the number of faces using @e and belonging to @s. - */ -guint gts_edge_face_number (GtsEdge * e, GtsSurface * s) -{ - GSList * i; - guint nt = 0; - - g_return_val_if_fail (e != NULL, 0); - g_return_val_if_fail (s != NULL, 0); - - i = e->triangles; - while (i) { - if (GTS_IS_FACE (i->data) && - gts_face_has_parent_surface (GTS_FACE (i->data), s)) - nt++; - i = i->next; - } - return nt; -} - -/** - * gts_edge_is_duplicate: - * @e: a #GtsEdge. - * - * Returns: the first #GtsEdge different from @e which shares the - * same endpoints or %NULL if there is none. - */ -GtsEdge * gts_edge_is_duplicate (GtsEdge * e) -{ - GSList * i; - GtsVertex * v2; - - g_return_val_if_fail (e != NULL, NULL); - - v2 = GTS_SEGMENT (e)->v2; - i = GTS_SEGMENT (e)->v1->segments; - if (GTS_SEGMENT (e)->v1 == v2) /* e is degenerate: special treatment */ - while (i) { - GtsSegment * s = i->data; - if (s != GTS_SEGMENT (e) && - GTS_IS_EDGE (s) && - s->v1 == v2 && s->v2 == v2) - return GTS_EDGE (s); - i = i->next; - } - else /* e is not degenerate */ - while (i) { - GtsSegment * s = i->data; - if (s != GTS_SEGMENT (e) && - GTS_IS_EDGE (s) && - (s->v1 == v2 || s->v2 == v2)) - return GTS_EDGE (s); - i = i->next; - } - return NULL; -} - -/** - * gts_edges_merge: - * @edges: a list of #GtsEdge. - * - * For each edge in @edges check if it is duplicated (as - * returned by gts_edge_is_duplicate()). If it is replace it by its - * duplicate, destroy it and remove it from the list. - * - * Returns: the updated @edges list. - */ -GList * gts_edges_merge (GList * edges) -{ - GList * i = edges; - - /* we want to control edge destruction */ - gts_allow_floating_edges = TRUE; - while (i) { - GtsEdge * e = i->data; - GtsEdge * de = gts_edge_is_duplicate (e); - if (de) { - GList * next = i->next; - edges = g_list_remove_link (edges, i); - g_list_free_1 (i); - i = next; - gts_edge_replace (e, de); - gts_object_destroy (GTS_OBJECT (e)); - } - else - i = i->next; - } - gts_allow_floating_edges = FALSE;; - - return edges; -} - -static void triangle_vertices_edges (GtsTriangle * t, - GtsEdge * e, - GtsVertex ** v, - GtsEdge ** ee1, - GtsEdge ** ee2) -{ - GtsEdge * e1 = t->e1, * e2 = t->e2, * e3 = t->e3; - GtsVertex * v1 = GTS_SEGMENT (e)->v1; - - if (e1 == e) e1 = e3; - else if (e2 == e) e2 = e3; - else g_assert (e3 == e); - - if (GTS_SEGMENT (e2)->v1 == v1 || GTS_SEGMENT (e2)->v2 == v1) { - e3 = e1; e1 = e2; e2 = e3; - } - if (GTS_SEGMENT (e1)->v1 == v1) - *v = GTS_SEGMENT (e1)->v2; - else - *v = GTS_SEGMENT (e1)->v1; - *ee1 = e1; - *ee2 = e2; -} - -/** - * gts_edge_belongs_to_tetrahedron: - * @e: a #GtsEdge. - * - * Returns: %TRUE if @e is used by faces forming a tetrahedron, %FALSE - * otherwise. - */ -gboolean gts_edge_belongs_to_tetrahedron (GtsEdge * e) -{ - GSList * i; - - g_return_val_if_fail (e != NULL, FALSE); - - i = e->triangles; - while (i) { - GtsEdge * e1, * e2; - GtsVertex * vt1; - GSList * j = i->next; - triangle_vertices_edges (i->data, e, &vt1, &e1, &e2); - while (j) { - GtsSegment * s5; - GtsEdge * e3, * e4; - GtsVertex * vt2; - - triangle_vertices_edges (j->data, e, &vt2, &e3, &e4); - s5 = gts_vertices_are_connected (vt1, vt2); - if (GTS_IS_EDGE (s5) && - gts_triangle_use_edges (e1, e3, GTS_EDGE (s5)) && - gts_triangle_use_edges (e2, e4, GTS_EDGE (s5))) - return TRUE; - j = j->next; - } - i = i->next; - } - - return FALSE; -} - -#define edge_use_vertex(e, v) (GTS_SEGMENT(e)->v1 == v ||\ - GTS_SEGMENT(e)->v2 == v) - -static GtsEdge * next_edge (GtsTriangle * t, - GtsEdge * e1, - GtsEdge * e) -{ - GtsVertex * v1 = GTS_SEGMENT (e)->v1; - GtsVertex * v2 = GTS_SEGMENT (e)->v2; - - if (t->e1 != e1 && t->e1 != e && - (edge_use_vertex (t->e1, v1) || edge_use_vertex (t->e1, v2))) - return t->e1; - else if (t->e2 != e1 && t->e2 != e && - (edge_use_vertex (t->e2, v1) || edge_use_vertex (t->e2, v2))) - return t->e2; - else if (t->e3 != e1 && t->e3 != e && - (edge_use_vertex (t->e3, v1) || edge_use_vertex (t->e3, v2))) - return t->e3; - g_assert_not_reached (); - return NULL; -} - -static void triangle_next (GtsEdge * e1, GtsEdge * e) -{ - GSList * i; - - i = e1->triangles; - while (i) { - GtsTriangle * t = i->data; - if (GTS_OBJECT (t)->reserved) { - GTS_OBJECT (t)->reserved = NULL; - triangle_next (next_edge (t, e1, e), e); - } - i = i->next; - } -} - -/** - * gts_edge_is_contact: - * @e: a #GtsEdge. - * - * Returns: the number of sets of connected triangles sharing @e as a - * contact edge. - */ -guint gts_edge_is_contact (GtsEdge * e) -{ - GSList * i, * triangles; - guint ncomponent = 0; - - g_return_val_if_fail (e != NULL, 0); - - triangles = gts_vertex_triangles (GTS_SEGMENT (e)->v1, NULL); - i = triangles = gts_vertex_triangles (GTS_SEGMENT (e)->v2, triangles); - while (i) { - GTS_OBJECT (i->data)->reserved = i; - i = i->next; - } - - i = e->triangles; - while (i) { - GtsTriangle * t = i->data; - if (GTS_OBJECT (t)->reserved) { - GtsEdge * e1; - GTS_OBJECT (t)->reserved = NULL; - e1 = next_edge (t, NULL, e); - triangle_next (e1, e); - triangle_next (next_edge (t, e1, e), e); - ncomponent++; - } - i = i->next; - } - - g_slist_foreach (triangles, (GFunc) gts_object_reset_reserved, NULL); - g_slist_free (triangles); - - return ncomponent; -} - -/** - * gts_edge_swap: - * @e: a #GtsEdge. - * @s: a #GtsSurface. - * - * Performs an "edge swap" on the two triangles sharing @e and - * belonging to @s. - */ -void gts_edge_swap (GtsEdge * e, GtsSurface * s) -{ - GtsTriangle * t1 = NULL, * t2 = NULL, * t; - GtsFace * f; - GSList * i; - GtsVertex * v1, * v2, * v3, * v4, * v5, * v6; - GtsEdge * e1, * e2, * e3, * e4; - GtsSegment * v3v6; - - g_return_if_fail (e != NULL); - g_return_if_fail (s != NULL); - - i = e->triangles; - while (i) { - if (GTS_IS_FACE (i->data) && gts_face_has_parent_surface (i->data, s)) { - if (!t1) - t1 = i->data; - else if (!t2) - t2 = i->data; - else - g_return_if_fail (gts_edge_face_number (e, s) == 2); - } - i = i->next; - } - g_assert (t1 && t2); - - gts_triangle_vertices_edges (t1, e, &v1, &v2, &v3, &e, &e1, &e2); - gts_triangle_vertices_edges (t2, e, &v4, &v5, &v6, &e, &e3, &e4); - g_assert (v2 == v4 && v1 == v5); - - v3v6 = gts_vertices_are_connected (v3, v6); - if (!GTS_IS_EDGE (v3v6)) - v3v6 = GTS_SEGMENT (gts_edge_new (s->edge_class, v3, v6)); - f = gts_face_new (s->face_class, e1, GTS_EDGE (v3v6), e4); - if ((t = gts_triangle_is_duplicate (GTS_TRIANGLE (f))) && - GTS_IS_FACE (t)) { - gts_object_destroy (GTS_OBJECT (f)); - f = GTS_FACE (t); - } - gts_surface_add_face (s, f); - - f = gts_face_new (s->face_class, GTS_EDGE (v3v6), e2, e3); - if ((t = gts_triangle_is_duplicate (GTS_TRIANGLE (f))) && - GTS_IS_FACE (t)) { - gts_object_destroy (GTS_OBJECT (f)); - f = GTS_FACE (t); - } - gts_surface_add_face (s, f); - - gts_surface_remove_face (s, GTS_FACE (t1)); - gts_surface_remove_face (s, GTS_FACE (t2)); -} - -/** - * gts_edge_manifold_faces: - * @e: a #GtsEdge. - * @s: a #GtsSurface. - * @f1: pointer for first face. - * @f2: pointer for second face. - * - * If @e is a manifold edge of surface @s, fills @f1 and @f2 with the - * faces belonging to @s and sharing @e. - * - * Returns: %TRUE if @e is a manifold edge, %FALSE otherwise. - */ -gboolean gts_edge_manifold_faces (GtsEdge * e, GtsSurface * s, - GtsFace ** f1, GtsFace ** f2) -{ - GSList * i; - - g_return_val_if_fail (e != NULL, FALSE); - g_return_val_if_fail (s != NULL, FALSE); - g_return_val_if_fail (f1 != NULL, FALSE); - g_return_val_if_fail (f2 != NULL, FALSE); - - *f1 = *f2 = NULL; - i = e->triangles; - while (i) { - if (GTS_IS_FACE (i->data) && gts_face_has_parent_surface (i->data, s)) { - if (!(*f1)) *f1 = i->data; - else if (!(*f2)) *f2 = i->data; - else return FALSE; - } - i = i->next; - } - - return (*f1 && *f2); -} Index: trunk/src/3rd/gts/matrix.c =================================================================== --- trunk/src/3rd/gts/matrix.c (revision 1064) +++ trunk/src/3rd/gts/matrix.c (nonexistent) @@ -1,725 +0,0 @@ -/* GTS - Library for the manipulation of triangulated surfaces - * Copyright (C) 1999 Stéphane Popinet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include -#include "gts.h" - -/** - * gts_matrix_new: - * @a00: element [0][0]. - * @a01: element [0][1]. - * @a02: element [0][2]. - * @a03: element [0][3]. - * @a10: element [1][0]. - * @a11: element [1][1]. - * @a12: element [1][2]. - * @a13: element [1][3]. - * @a20: element [2][0]. - * @a21: element [2][1]. - * @a22: element [2][2]. - * @a23: element [2][3]. - * @a30: element [3][0]. - * @a31: element [3][1]. - * @a32: element [3][2]. - * @a33: element [3][3]. - * - * Allocates memory and initializes a new #GtsMatrix. - * - * Returns: a pointer to the newly created #GtsMatrix. - */ -GtsMatrix * gts_matrix_new (gdouble a00, gdouble a01, gdouble a02, gdouble a03, - gdouble a10, gdouble a11, gdouble a12, gdouble a13, - gdouble a20, gdouble a21, gdouble a22, gdouble a23, - gdouble a30, gdouble a31, gdouble a32, gdouble a33) -{ - GtsMatrix * m; - - m = g_malloc (4*sizeof (GtsVector4)); - - m[0][0] = a00; m[1][0] = a10; m[2][0] = a20; m[3][0] = a30; - m[0][1] = a01; m[1][1] = a11; m[2][1] = a21; m[3][1] = a31; - m[0][2] = a02; m[1][2] = a12; m[2][2] = a22; m[3][2] = a32; - m[0][3] = a03; m[1][3] = a13; m[2][3] = a23; m[3][3] = a33; - - return m; -} - -/** - * gts_matrix_assign: - * @m: a #GtsMatrix. - * @a00: element [0][0]. - * @a01: element [0][1]. - * @a02: element [0][2]. - * @a03: element [0][3]. - * @a10: element [1][0]. - * @a11: element [1][1]. - * @a12: element [1][2]. - * @a13: element [1][3]. - * @a20: element [2][0]. - * @a21: element [2][1]. - * @a22: element [2][2]. - * @a23: element [2][3]. - * @a30: element [3][0]. - * @a31: element [3][1]. - * @a32: element [3][2]. - * @a33: element [3][3]. - * - * Set values of matrix elements. - */ -void gts_matrix_assign (GtsMatrix * m, - gdouble a00, gdouble a01, gdouble a02, gdouble a03, - gdouble a10, gdouble a11, gdouble a12, gdouble a13, - gdouble a20, gdouble a21, gdouble a22, gdouble a23, - gdouble a30, gdouble a31, gdouble a32, gdouble a33) -{ - g_return_if_fail (m != NULL); - - m[0][0] = a00; m[1][0] = a10; m[2][0] = a20; m[3][0] = a30; - m[0][1] = a01; m[1][1] = a11; m[2][1] = a21; m[3][1] = a31; - m[0][2] = a02; m[1][2] = a12; m[2][2] = a22; m[3][2] = a32; - m[0][3] = a03; m[1][3] = a13; m[2][3] = a23; m[3][3] = a33; -} - -/** - * gts_matrix_projection: - * @t: a #GtsTriangle. - * - * Creates a new #GtsMatrix representing the projection onto a plane of normal - * given by @t. - * - * Returns: a pointer to the newly created #GtsMatrix. - */ -GtsMatrix * gts_matrix_projection (GtsTriangle * t) -{ - GtsVertex * v1, * v2, * v3; - GtsEdge * e1, * e2, * e3; - GtsMatrix * m; - gdouble x1, y1, z1, x2, y2, z2, x3, y3, z3, l; - - g_return_val_if_fail (t != NULL, NULL); - - m = g_malloc (4*sizeof (GtsVector4)); - gts_triangle_vertices_edges (t, NULL, &v1, &v2, &v3, &e1, &e2, &e3); - - x1 = GTS_POINT (v2)->x - GTS_POINT (v1)->x; - y1 = GTS_POINT (v2)->y - GTS_POINT (v1)->y; - z1 = GTS_POINT (v2)->z - GTS_POINT (v1)->z; - x2 = GTS_POINT (v3)->x - GTS_POINT (v1)->x; - y2 = GTS_POINT (v3)->y - GTS_POINT (v1)->y; - z2 = GTS_POINT (v3)->z - GTS_POINT (v1)->z; - x3 = y1*z2 - z1*y2; y3 = z1*x2 - x1*z2; z3 = x1*y2 - y1*x2; - x2 = y3*z1 - z3*y1; y2 = z3*x1 - x3*z1; z2 = x3*y1 - y3*x1; - - g_assert ((l = sqrt (x1*x1 + y1*y1 + z1*z1)) > 0.0); - m[0][0] = x1/l; m[1][0] = y1/l; m[2][0] = z1/l; m[3][0] = 0.; - g_assert ((l = sqrt (x2*x2 + y2*y2 + z2*z2)) > 0.0); - m[0][1] = x2/l; m[1][1] = y2/l; m[2][1] = z2/l; m[3][1] = 0.; - g_assert ((l = sqrt (x3*x3 + y3*y3 + z3*z3)) > 0.0); - m[0][2] = x3/l; m[1][2] = y3/l; m[2][2] = z3/l; m[3][2] = 0.; - m[0][3] = 0; m[1][3] = 0.; m[2][3] = 0.; m[3][3] = 1.; - - return m; -} - -/** - * gts_matrix_transpose: - * @m: a #GtsMatrix. - * - * Returns: a pointer to a newly created #GtsMatrix transposed of @m. - */ -GtsMatrix * gts_matrix_transpose (GtsMatrix * m) -{ - GtsMatrix * mi; - - g_return_val_if_fail (m != NULL, NULL); - - mi = g_malloc (4*sizeof (GtsVector4)); - - mi[0][0] = m[0][0]; mi[1][0] = m[0][1]; - mi[2][0] = m[0][2]; mi[3][0] = m[0][3]; - mi[0][1] = m[1][0]; mi[1][1] = m[1][1]; - mi[2][1] = m[1][2]; mi[3][1] = m[1][3]; - mi[0][2] = m[2][0]; mi[1][2] = m[2][1]; - mi[2][2] = m[2][2]; mi[3][2] = m[2][3]; - mi[0][3] = m[3][0]; mi[1][3] = m[3][1]; - mi[2][3] = m[3][2]; mi[3][3] = m[3][3]; - - return mi; -} - -/* - * calculate the determinant of a 2x2 matrix. - * - * Adapted from: - * Matrix Inversion - * by Richard Carling - * from "Graphics Gems", Academic Press, 1990 - */ -static gdouble det2x2 (gdouble a, gdouble b, gdouble c, gdouble d) -{ - gdouble ans2; - - ans2 = a*d - b*c; - return ans2; -} - -/* - * calculate the determinant of a 3x3 matrix - * in the form - * - * | a1, b1, c1 | - * | a2, b2, c2 | - * | a3, b3, c3 | - * - * Adapted from: - * Matrix Inversion - * by Richard Carling - * from "Graphics Gems", Academic Press, 1990 - */ -static gdouble det3x3 (gdouble a1, gdouble a2, gdouble a3, - gdouble b1, gdouble b2, gdouble b3, - gdouble c1, gdouble c2, gdouble c3) -{ - gdouble ans3; - - ans3 = a1 * det2x2( b2, b3, c2, c3 ) - - b1 * det2x2( a2, a3, c2, c3 ) - + c1 * det2x2( a2, a3, b2, b3 ); - return ans3; -} - -/** - * gts_matrix_determinant: - * @m: a #GtsMatrix. - * - * Returns: the value of det(@m). - */ -gdouble gts_matrix_determinant (GtsMatrix * m) -{ - gdouble ans4; - gdouble a1, a2, a3, a4, b1, b2, b3, b4, c1, c2, c3, c4, d1, d2, d3, d4; - - g_return_val_if_fail (m != NULL, 0.0); - - a1 = m[0][0]; b1 = m[0][1]; - c1 = m[0][2]; d1 = m[0][3]; - - a2 = m[1][0]; b2 = m[1][1]; - c2 = m[1][2]; d2 = m[1][3]; - - a3 = m[2][0]; b3 = m[2][1]; - c3 = m[2][2]; d3 = m[2][3]; - - a4 = m[3][0]; b4 = m[3][1]; - c4 = m[3][2]; d4 = m[3][3]; - - ans4 = a1 * det3x3 (b2, b3, b4, c2, c3, c4, d2, d3, d4) - - b1 * det3x3 (a2, a3, a4, c2, c3, c4, d2, d3, d4) - + c1 * det3x3 (a2, a3, a4, b2, b3, b4, d2, d3, d4) - - d1 * det3x3 (a2, a3, a4, b2, b3, b4, c2, c3, c4); - - return ans4; -} - -/* - * adjoint( original_matrix, inverse_matrix ) - * - * calculate the adjoint of a 4x4 matrix - * - * Let a denote the minor determinant of matrix A obtained by - * ij - * - * deleting the ith row and jth column from A. - * - * i+j - * Let b = (-1) a - * ij ji - * - * The matrix B = (b ) is the adjoint of A - * ij - */ -static GtsMatrix * adjoint (GtsMatrix * m) -{ - gdouble a1, a2, a3, a4, b1, b2, b3, b4; - gdouble c1, c2, c3, c4, d1, d2, d3, d4; - GtsMatrix * ma; - - a1 = m[0][0]; b1 = m[0][1]; - c1 = m[0][2]; d1 = m[0][3]; - - a2 = m[1][0]; b2 = m[1][1]; - c2 = m[1][2]; d2 = m[1][3]; - - a3 = m[2][0]; b3 = m[2][1]; - c3 = m[2][2]; d3 = m[2][3]; - - a4 = m[3][0]; b4 = m[3][1]; - c4 = m[3][2]; d4 = m[3][3]; - - ma = g_malloc (4*sizeof (GtsVector4)); - - /* row column labeling reversed since we transpose rows & columns */ - - ma[0][0] = det3x3 (b2, b3, b4, c2, c3, c4, d2, d3, d4); - ma[1][0] = - det3x3 (a2, a3, a4, c2, c3, c4, d2, d3, d4); - ma[2][0] = det3x3 (a2, a3, a4, b2, b3, b4, d2, d3, d4); - ma[3][0] = - det3x3 (a2, a3, a4, b2, b3, b4, c2, c3, c4); - - ma[0][1] = - det3x3 (b1, b3, b4, c1, c3, c4, d1, d3, d4); - ma[1][1] = det3x3 (a1, a3, a4, c1, c3, c4, d1, d3, d4); - ma[2][1] = - det3x3 (a1, a3, a4, b1, b3, b4, d1, d3, d4); - ma[3][1] = det3x3 (a1, a3, a4, b1, b3, b4, c1, c3, c4); - - ma[0][2] = det3x3 (b1, b2, b4, c1, c2, c4, d1, d2, d4); - ma[1][2] = - det3x3 (a1, a2, a4, c1, c2, c4, d1, d2, d4); - ma[2][2] = det3x3 (a1, a2, a4, b1, b2, b4, d1, d2, d4); - ma[3][2] = - det3x3 (a1, a2, a4, b1, b2, b4, c1, c2, c4); - - ma[0][3] = - det3x3 (b1, b2, b3, c1, c2, c3, d1, d2, d3); - ma[1][3] = det3x3 (a1, a2, a3, c1, c2, c3, d1, d2, d3); - ma[2][3] = - det3x3 (a1, a2, a3, b1, b2, b3, d1, d2, d3); - ma[3][3] = det3x3 (a1, a2, a3, b1, b2, b3, c1, c2, c3); - - return ma; -} - - -/** - * gts_matrix_inverse: - * @m: a #GtsMatrix. - * - * Returns: a pointer to a newly created #GtsMatrix inverse of @m or %NULL - * if @m is not invertible. - */ -GtsMatrix * gts_matrix_inverse (GtsMatrix * m) -{ - GtsMatrix * madj; - gdouble det; - gint i, j; - - g_return_val_if_fail (m != NULL, NULL); - - det = gts_matrix_determinant (m); - if (det == 0.) - return NULL; - - madj = adjoint (m); - for (i = 0; i < 4; i++) - for(j = 0; j < 4; j++) - madj[i][j] /= det; - - return madj; -} - -/** - * gts_matrix3_inverse: - * @m: a 3x3 #GtsMatrix. - * - * Returns: a pointer to a newly created 3x3 #GtsMatrix inverse of @m or %NULL - * if @m is not invertible. - */ -GtsMatrix * gts_matrix3_inverse (GtsMatrix * m) -{ - GtsMatrix * mi; - gdouble det; - - g_return_val_if_fail (m != NULL, NULL); - - det = (m[0][0]*(m[1][1]*m[2][2] - m[2][1]*m[1][2]) - - m[0][1]*(m[1][0]*m[2][2] - m[2][0]*m[1][2]) + - m[0][2]*(m[1][0]*m[2][1] - m[2][0]*m[1][1])); - if (det == 0.0) - return NULL; - - mi = g_malloc0 (4*sizeof (GtsVector)); - - mi[0][0] = (m[1][1]*m[2][2] - m[1][2]*m[2][1])/det; - mi[0][1] = (m[2][1]*m[0][2] - m[0][1]*m[2][2])/det; - mi[0][2] = (m[0][1]*m[1][2] - m[1][1]*m[0][2])/det; - mi[1][0] = (m[1][2]*m[2][0] - m[1][0]*m[2][2])/det; - mi[1][1] = (m[0][0]*m[2][2] - m[2][0]*m[0][2])/det; - mi[1][2] = (m[1][0]*m[0][2] - m[0][0]*m[1][2])/det; - mi[2][0] = (m[1][0]*m[2][1] - m[2][0]*m[1][1])/det; - mi[2][1] = (m[2][0]*m[0][1] - m[0][0]*m[2][1])/det; - mi[2][2] = (m[0][0]*m[1][1] - m[0][1]*m[1][0])/det; - - return mi; -} - -/** - * gts_matrix_print: - * @m: a #GtsMatrix. - * @fptr: a file descriptor. - * - * Print @m to file @fptr. - */ -void gts_matrix_print (GtsMatrix * m, FILE * fptr) -{ - g_return_if_fail (m != NULL); - g_return_if_fail (fptr != NULL); - - fprintf (fptr, - "[[%15.7g %15.7g %15.7g %15.7g]\n" - " [%15.7g %15.7g %15.7g %15.7g]\n" - " [%15.7g %15.7g %15.7g %15.7g]\n" - " [%15.7g %15.7g %15.7g %15.7g]]\n", - m[0][0], m[0][1], m[0][2], m[0][3], - m[1][0], m[1][1], m[1][2], m[1][3], - m[2][0], m[2][1], m[2][2], m[2][3], - m[3][0], m[3][1], m[3][2], m[3][3]); -} - -/** - * gts_vector_print: - * @v: a #GtsVector. - * @fptr: a file descriptor. - * - * Print @s to file @fptr. - */ -void gts_vector_print (GtsVector v, FILE * fptr) -{ - g_return_if_fail (fptr != NULL); - - fprintf (fptr, - "[%15.7g %15.7g %15.7g ]\n", - v[0], v[1], v[2]); -} - -/** - * gts_vector4_print: - * @v: a #GtsVector4. - * @fptr: a file descriptor. - * - * Print @v to file @fptr. - */ -void gts_vector4_print (GtsVector4 v, FILE * fptr) -{ - g_return_if_fail (fptr != NULL); - - fprintf (fptr, - "[%15.7g %15.7g %15.7g %15.7g]\n", - v[0], v[1], v[2], v[3]); -} - -/* [cos(alpha)]^2 */ -#define COSALPHA2 0.999695413509 /* alpha = 1 degree */ -/* [sin(alpha)]^2 */ -#define SINALPHA2 3.04586490453e-4 /* alpha = 1 degree */ - -/** - * gts_matrix_compatible_row: - * @A: a #GtsMatrix. - * @b: a #GtsVector. - * @n: the number of previous constraints of @A.x=@b. - * @A1: a #GtsMatrix. - * @b1: a #GtsVector. - * - * Given a system of @n constraints @A.x=@b adds to it the compatible - * constraints defined by @A1.x=@b1. The compatibility is determined - * by insuring that the resulting system is well-conditioned (see - * Lindstrom and Turk (1998, 1999)). - * - * Returns: the number of constraints of the resulting system. - */ -guint gts_matrix_compatible_row (GtsMatrix * A, - GtsVector b, - guint n, - GtsVector A1, - gdouble b1) -{ - gdouble na1; - - g_return_val_if_fail (A != NULL, 0); - - na1 = gts_vector_scalar (A1, A1); - if (na1 == 0.0) - return n; - - /* normalize row */ - na1 = sqrt (na1); - A1[0] /= na1; A1[1] /= na1; A1[2] /= na1; b1 /= na1; - - if (n == 1) { - gdouble a0a1 = gts_vector_scalar (A[0], A1); - if (a0a1*a0a1 >= COSALPHA2) - return 1; - } - else if (n == 2) { - GtsVector V; - gdouble s; - - gts_vector_cross (V, A[0], A[1]); - s = gts_vector_scalar (V, A1); - if (s*s <= gts_vector_scalar (V, V)*SINALPHA2) - return 2; - } - - A[n][0] = A1[0]; A[n][1] = A1[1]; A[n][2] = A1[2]; b[n] = b1; - return n + 1; -} - -/** - * gts_matrix_quadratic_optimization: - * @A: a #GtsMatrix. - * @b: a #GtsVector. - * @n: the number of constraints (must be smaller than 3). - * @H: a symmetric positive definite Hessian. - * @c: a #GtsVector. - * - * Solve a quadratic optimization problem: Given a quadratic objective function - * f which can be written as: f(x) = x^t.@H.x + @c^t.x + k, where @H is the - * symmetric positive definite Hessian of f and k is a constant, find the - * minimum of f subject to the set of @n prior linear constraints, defined by - * the first @n rows of @A and @b (@A.x = @b). The new constraints given by - * the minimization are added to @A and @b only if they are linearly - * independent as determined by gts_matrix_compatible_row(). - * - * Returns: the new number of constraints defined by @A and @b. - */ -guint gts_matrix_quadratic_optimization (GtsMatrix * A, - GtsVector b, - guint n, - GtsMatrix * H, - GtsVector c) -{ - g_return_val_if_fail (A != NULL, 0); - g_return_val_if_fail (b != NULL, 0); - g_return_val_if_fail (n < 3, 0); - g_return_val_if_fail (H != NULL, 0); - - switch (n) { - case 0: { - n = gts_matrix_compatible_row (A, b, n, H[0], - c[0]); - n = gts_matrix_compatible_row (A, b, n, H[1], - c[1]); - n = gts_matrix_compatible_row (A, b, n, H[2], - c[2]); - return n; - } - case 1: { - GtsVector Q0 = {0., 0., 0.}; - GtsVector Q1 = {0., 0., 0.}; - GtsVector A1; - gdouble max = A[0][0]*A[0][0]; - guint d = 0; - - /* build a vector orthogonal to the constraint */ - if (A[0][1]*A[0][1] > max) { max = A[0][1]*A[0][1]; d = 1; } - if (A[0][2]*A[0][2] > max) { max = A[0][2]*A[0][2]; d = 2; } - switch (d) { - case 0: Q0[0] = - A[0][2]/A[0][0]; Q0[2] = 1.0; break; - case 1: Q0[1] = - A[0][2]/A[0][1]; Q0[2] = 1.0; break; - case 2: Q0[2] = - A[0][0]/A[0][2]; Q0[0] = 1.0; break; - } - - /* build a second vector orthogonal to the first and to the constraint */ - gts_vector_cross (Q1, A[0], Q0); - - A1[0] = gts_vector_scalar (Q0, H[0]); - A1[1] = gts_vector_scalar (Q0, H[1]); - A1[2] = gts_vector_scalar (Q0, H[2]); - - n = gts_matrix_compatible_row (A, b, n, A1, - gts_vector_scalar (Q0, c)); - - A1[0] = gts_vector_scalar (Q1, H[0]); - A1[1] = gts_vector_scalar (Q1, H[1]); - A1[2] = gts_vector_scalar (Q1, H[2]); - - n = gts_matrix_compatible_row (A, b, n, A1, - gts_vector_scalar (Q1, c)); - - return n; - } - case 2: { - /* build a vector orthogonal to the two constraints */ - GtsVector A1, Q; - - gts_vector_cross (Q, A[0], A[1]); - A1[0] = gts_vector_scalar (Q, H[0]); - A1[1] = gts_vector_scalar (Q, H[1]); - A1[2] = gts_vector_scalar (Q, H[2]); - - n = gts_matrix_compatible_row (A, b, n, A1, - gts_vector_scalar (Q, c)); - - return n; - } - default: - g_assert_not_reached (); - } - return 0; -} - -/** - * gts_matrix_destroy: - * @m: a #GtsMatrix. - * - * Free all the memory allocated for @m. - */ -void gts_matrix_destroy (GtsMatrix * m) -{ - g_free (m); -} - -/** - * gts_matrix_product: - * @m1: a #GtsMatrix. - * @m2: another #GtsMatrix. - * - * Returns: a new #GtsMatrix, product of @m1 and @m2. - */ -GtsMatrix * gts_matrix_product (GtsMatrix * m1, GtsMatrix * m2) -{ - guint i, j; - GtsMatrix * m; - - g_return_val_if_fail (m1 != NULL, NULL); - g_return_val_if_fail (m2 != NULL, NULL); - g_return_val_if_fail (m1 != m2, NULL); - - m = g_malloc (4*sizeof (GtsVector4)); - - for (i = 0; i < 4; i++) - for (j = 0; j < 4; j++) - m[i][j] = m1[i][0]*m2[0][j] + m1[i][1]*m2[1][j] + - m1[i][2]*m2[2][j] + m1[i][3]*m2[3][j]; - return m; -} - -/** - * gts_matrix_zero: - * @m: a #GtsMatrix or $NULL. - * - * Initializes @m to zeros. Allocates a matrix if @m is %NULL. - * - * Returns: the zero'ed matrix. - */ -GtsMatrix * gts_matrix_zero (GtsMatrix * m) -{ - if (m == NULL) - m = g_malloc0 (4*sizeof (GtsVector4)); - else { - m[0][0] = m[1][0] = m[2][0] = m[3][0] = 0.; - m[0][1] = m[1][1] = m[2][1] = m[3][1] = 0.; - m[0][2] = m[1][2] = m[2][2] = m[3][2] = 0.; - m[0][3] = m[1][3] = m[2][3] = m[3][3] = 0.; - } - return m; -} - -/** - * gts_matrix_identity: - * @m: a #GtsMatrix or %NULL. - * - * Initializes @m to an identity matrix. Allocates a matrix if @m is %NULL. - * - * Returns: the identity matrix. - */ -GtsMatrix * gts_matrix_identity (GtsMatrix * m) -{ - m = gts_matrix_zero (m); - m[0][0] = m[1][1] = m[2][2] = m[3][3] = 1.; - return m; -} - -/** - * gts_matrix_scale: - * @m: a #GtsMatrix or %NULL. - * @s: the scaling vector. - * - * Initializes @m to a scaling matrix for @s. Allocates a matrix if @m - * is %NULL. - * - * Returns: the scaling matrix. - */ -GtsMatrix * gts_matrix_scale (GtsMatrix * m, GtsVector s) -{ - m = gts_matrix_zero (m); - m[0][0] = s[0]; - m[1][1] = s[1]; - m[2][2] = s[2]; - m[3][3] = 1.; - return m; -} - -/** - * gts_matrix_translate: - * @m: a #GtsMatrix or %NULL. - * @t: the translation vector. - * - * Initializes @m to a translation matrix for @t. Allocates a new - * matrix if @m is %NULL. - * - * Returns: the translation matix. - */ -GtsMatrix * gts_matrix_translate (GtsMatrix * m, GtsVector t) -{ - m = gts_matrix_zero (m); - m[0][3] = t[0]; - m[1][3] = t[1]; - m[2][3] = t[2]; - m[3][3] = 1.; - m[0][0] = m[1][1] = m[2][2] = 1.; - return m; -} - -/** - * gts_matrix_rotate: - * @m: a #GtsMatrix or %NULL. - * @r: the rotation axis. - * @angle: the angle (in radians) to rotate by. - * - * Initializes @m to a rotation matrix around @r by @angle. - * Allocates a new matrix if @m is %NULL. - * - * Returns: the rotation matrix. - */ -GtsMatrix * gts_matrix_rotate (GtsMatrix * m, - GtsVector r, - gdouble angle) -{ - gdouble c, c1, s; - - gts_vector_normalize (r); - - c = cos (angle); - c1 = 1. - c; - s = sin (angle); - - if (m == NULL) - m = g_malloc (4*sizeof (GtsVector4)); - - m[0][0] = r[0]*r[0]*c1 + c; - m[0][1] = r[0]*r[1]*c1 - r[2]*s; - m[0][2] = r[0]*r[2]*c1 + r[1]*s; - m[0][3] = 0.; - - m[1][0] = r[1]*r[0]*c1 + r[2]*s; - m[1][1] = r[1]*r[1]*c1 + c; - m[1][2] = r[1]*r[2]*c1 - r[0]*s; - m[1][3] = 0.; - - m[2][0] = r[2]*r[0]*c1 - r[1]*s; - m[2][1] = r[2]*r[1]*c1 + r[0]*s; - m[2][2] = r[2]*r[2]*c1 + c; - m[2][3] = 0.; - - m[3][0] = 0.; - m[3][1] = 0.; - m[3][2] = 0.; - m[3][3] = 1.; - - return m; -} Index: trunk/src/3rd/gts/psurface.c =================================================================== --- trunk/src/3rd/gts/psurface.c (revision 1064) +++ trunk/src/3rd/gts/psurface.c (nonexistent) @@ -1,471 +0,0 @@ -/* GTS - Library for the manipulation of triangulated surfaces - * Copyright (C) 1999 Stéphane Popinet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include -#include -#include "gts.h" - -#define HEAP_INSERT_OBJECT(h, e) (GTS_OBJECT (e)->reserved =\ - gts_eheap_insert (h, e)) -#define HEAP_REMOVE_OBJECT(h, e) (gts_eheap_remove (h, GTS_OBJECT (e)->reserved),\ - GTS_OBJECT (e)->reserved = NULL) - -static void psurface_destroy (GtsObject * object) -{ - GtsPSurface * ps = GTS_PSURFACE (object); - guint i; - - if (!GTS_PSURFACE_IS_CLOSED (ps)) - gts_psurface_close (ps); - - for (i = 0; i < ps->split->len; i++) - if (g_ptr_array_index (ps->split, i)) - gts_object_destroy (GTS_OBJECT (g_ptr_array_index (ps->split, i))); - g_ptr_array_free (ps->split, TRUE); - - (* GTS_OBJECT_CLASS (gts_psurface_class ())->parent_class->destroy) (object); -} - -static void psurface_class_init (GtsObjectClass * klass) -{ - klass->destroy = psurface_destroy; -} - -static void psurface_init (GtsPSurface * psurface) -{ - psurface->s = NULL; - psurface->split = g_ptr_array_new (); - psurface->split_class = gts_split_class (); - psurface->pos = psurface->min = 0; - psurface->vertices = psurface->faces = NULL; -} - -/** - * gts_psurface_class: - * - * Returns: the #GtsPSurfaceClass. - */ -GtsPSurfaceClass * gts_psurface_class (void) -{ - static GtsPSurfaceClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo psurface_info = { - "GtsPSurface", - sizeof (GtsPSurface), - sizeof (GtsPSurfaceClass), - (GtsObjectClassInitFunc) psurface_class_init, - (GtsObjectInitFunc) psurface_init, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (gts_object_class (), - &psurface_info); - } - - return klass; -} - -static GtsVertex * edge_collapse (GtsPSurface * ps, - GtsEdge * e, - GtsEHeap * heap, - GtsCoarsenFunc coarsen_func, - gpointer coarsen_data, - gdouble maxcosine2) -{ - GtsVertex * v1 = GTS_SEGMENT (e)->v1, * v2 = GTS_SEGMENT (e)->v2, * mid; - GtsSplit * vs; - GtsObject * o1, * o2; - - /* if the edge is degenerate (i.e. v1 == v2), destroy and return */ - if (v1 == v2) { - gts_object_destroy (GTS_OBJECT (e)); - return NULL; - } - - if (!gts_edge_collapse_is_valid (e) || - /* check that a non-manifold edge is not a contact edge */ - (g_slist_length (e->triangles) > 2 && gts_edge_is_contact (e) > 1)) { - GTS_OBJECT (e)->reserved = - gts_eheap_insert_with_key (heap, e, G_MAXDOUBLE); - return NULL; - } - - mid = (*coarsen_func) (e, ps->s->vertex_class, coarsen_data); - - if (gts_edge_collapse_creates_fold (e, mid, maxcosine2)) { - GTS_OBJECT (e)->reserved = - gts_eheap_insert_with_key (heap, e, G_MAXDOUBLE); - gts_object_destroy (GTS_OBJECT (mid)); - return NULL; - } - - if (GTS_OBJECT (v1)->reserved) - o1 = GTS_OBJECT (v1)->reserved; - else - o1 = GTS_OBJECT (v1); - if (GTS_OBJECT (v2)->reserved) - o2 = GTS_OBJECT (v2)->reserved; - else - o2 = GTS_OBJECT (v2); - vs = gts_split_new (ps->split_class, mid, o1, o2); - gts_split_collapse (vs, ps->s->edge_class, heap); - GTS_OBJECT (vs->v)->reserved = vs; - g_ptr_array_add (ps->split, vs); - - return mid; -} - -static void update_2nd_closest_neighbors (GtsVertex * v, GtsEHeap * heap) -{ - GSList * i = v->segments; - GSList * list = NULL; - - while (i) { - GtsSegment * s = i->data; - if (GTS_IS_EDGE (s)) { - GtsVertex * v1 = s->v1 == v ? s->v2 : s->v1; - GSList * j = v1->segments; - while (j) { - GtsSegment * s1 = j->data; - if (GTS_IS_EDGE (s1) && !g_slist_find (list, s1)) - list = g_slist_prepend (list, s1); - j = j->next; - } - } - i = i->next; - } - - i = list; - while (i) { - GtsEdge * e = i->data; - if (GTS_OBJECT (e)->reserved) - HEAP_REMOVE_OBJECT (heap, e); - HEAP_INSERT_OBJECT (heap, e); - i = i->next; - } - - g_slist_free (list); -} - -static gdouble edge_length2 (GtsEdge * e) -{ - return gts_point_distance2 (GTS_POINT (GTS_SEGMENT (e)->v1), - GTS_POINT (GTS_SEGMENT (e)->v2)); -} - -static void create_heap_coarsen (GtsEdge * e, GtsEHeap * heap) -{ - HEAP_INSERT_OBJECT (heap, e); -} - -/* #define DEBUG_FOLD */ -/* #define DEBUG_CONTACT_VERTEX */ - -#ifdef DEBUG_FOLD -static void check_fold (GtsTriangle * t, gdouble * maxcosine2) -{ - GtsEdge * e1 = t->e1, * e2 = t->e2, * e3 = t->e3; - - - if (gts_triangles_are_folded (e1->triangles, - GTS_SEGMENT (e1)->v1, - GTS_SEGMENT (e1)->v2, - *maxcosine2) || - gts_triangles_are_folded (e2->triangles, - GTS_SEGMENT (e2)->v1, - GTS_SEGMENT (e2)->v2, - *maxcosine2) || - gts_triangles_are_folded (e3->triangles, - GTS_SEGMENT (e3)->v1, - GTS_SEGMENT (e3)->v2, - *maxcosine2)) { - fprintf (stderr, "triangle %p:(%p,%p,%p) is folded\n", t, e1, e2, e3); - g_assert_not_reached (); - } -} -#endif - -/** - * gts_psurface_new: - * @klass: a #GtsPSurfaceClass. - * @surface: a #GtsSurface. - * @split_class: a #GtsSplitClass to use for the new progressive surface. - * @cost_func: cost function for the edge collapse algorithm. - * @cost_data: data to pass to @cost_func. - * @coarsen_func: the function returning the vertex replacement for the edge - * collapse. - * @coarsen_data: data to pass to @coarsen_func. - * @stop_func: the function to call to decide whether to stop the coarsening - * process. - * @stop_data: data to pass to @stop_func. - * @minangle: the minimum angle allowable between two neighboring triangles. - * This is used to avoid introducing folds in the mesh during simplification. - * - * This function works in exactly the same way as the - * gts_surface_coarsen() function, except that the history of edge - * collapse is saved in an array of #GtsSplit objects. This allows for - * dynamic continuous multiresolution control of the input @surface. - * - * Returns: a new progressive surface. - */ -GtsPSurface * gts_psurface_new (GtsPSurfaceClass * klass, - GtsSurface * surface, - GtsSplitClass * split_class, - GtsKeyFunc cost_func, - gpointer cost_data, - GtsCoarsenFunc coarsen_func, - gpointer coarsen_data, - GtsStopFunc stop_func, - gpointer stop_data, - gdouble minangle) -{ - GtsPSurface * psurface; - GtsEHeap * heap; - GtsEdge * e; - gdouble top_cost, maxcosine2; - guint i; - - g_return_val_if_fail (klass != NULL, NULL); - g_return_val_if_fail (surface != NULL, NULL); - g_return_val_if_fail (split_class != NULL, NULL); - g_return_val_if_fail (stop_func != NULL, NULL); - - psurface = GTS_PSURFACE (gts_object_new (GTS_OBJECT_CLASS (klass))); - psurface->s = surface; - psurface->split_class = split_class; - - if (cost_func == NULL) - cost_func = (GtsKeyFunc) edge_length2; - if (coarsen_func == NULL) - coarsen_func = (GtsCoarsenFunc) gts_segment_midvertex; - - heap = gts_eheap_new (cost_func, cost_data); - maxcosine2 = cos (minangle); maxcosine2 *= maxcosine2; - - gts_eheap_freeze (heap); - gts_surface_foreach_edge (surface, (GtsFunc) create_heap_coarsen, heap); - gts_eheap_thaw (heap); - /* we want to control edge destruction manually */ - gts_allow_floating_edges = TRUE; - while ((e = gts_eheap_remove_top (heap, &top_cost)) && - (top_cost < G_MAXDOUBLE) && - !(*stop_func) (top_cost, gts_eheap_size (heap) - - gts_edge_face_number (e, surface), stop_data)) { - GtsVertex * v = edge_collapse (psurface, e, heap, - coarsen_func, coarsen_data, maxcosine2); - if (v != NULL) { - update_2nd_closest_neighbors (v, heap); -#ifdef DEBUG_FOLD - { - GSList * triangles = gts_vertex_triangles (v, NULL), * i; - fprintf (stderr, "\n---- Check for folds ----\n%p: ", v); - i = triangles; - while (i) { - GtsTriangle * t = i->data; - fprintf (stderr, "%p:(%p,%p,%p) ", t, t->e1, t->e2, t->e3); - i = i->next; - } - fprintf (stderr, "\n"); - g_slist_free (triangles); - gts_surface_foreach_face (surface, (GtsFunc) check_fold, &maxcosine2); - } -#endif -#ifdef DEBUG_CONTACT_VERTEX - if (gts_vertex_is_contact (v, FALSE) != 1) { - FILE * fptr = fopen ("after", "wt"); - GSList * triangles = gts_vertex_triangles (v, NULL), * i; - - fprintf (stderr, "collapse of %p created a contact vertex\n", e); - - fprintf (fptr, - "(geometry \"sphere\" { = SPHERE 0.1 0. 0. 0. })\n" - "(normalization \"sphere\" none)\n"); - i = triangles; - while (i) { - gts_write_triangle (i->data, GTS_POINT (v), fptr); - i = i->next; - } - g_assert_not_reached (); - } -#endif - } - } - gts_allow_floating_edges = FALSE; - - /* set reserved field of remaining edges back to NULL */ - if (e) GTS_OBJECT (e)->reserved = NULL; - gts_eheap_foreach (heap, (GFunc) gts_object_reset_reserved, NULL); - - gts_eheap_destroy (heap); - - psurface->pos = psurface->split->len; - psurface->min = gts_surface_vertex_number (psurface->s); - - /* set reserved field of vertices (used to build the hierarchy) - back to NULL */ - for (i = 0; i < psurface->split->len; i++) { - GtsSplit * vs = g_ptr_array_index (psurface->split, i); - gts_object_reset_reserved (GTS_OBJECT (vs->v)); - } - - return psurface; -} - -/** - * gts_psurface_add_vertex: - * @ps: a #GtsPSurface. - * - * Adds a vertex to the progressive surface @ps by expanding the next - * available #GtsSplit. - * - * Returns: the expanded #GtsSplit or %NULL if all the #GtsSplit have already - * been expanded. - */ -GtsSplit * gts_psurface_add_vertex (GtsPSurface * ps) -{ - GtsSplit * vs; - - g_return_val_if_fail (ps != NULL, NULL); - g_return_val_if_fail (GTS_PSURFACE_IS_CLOSED (ps), NULL); - - if (ps->pos == 0) - return NULL; - - vs = g_ptr_array_index (ps->split, --ps->pos); - gts_split_expand (vs, ps->s, ps->s->edge_class); - - return vs; -} - -/** - * gts_psurface_remove_vertex: - * @ps: a #GtsPSurface. - * - * Removes one vertex from the progressive surface @ps by collapsing the first - * available #GtsSplit. - * - * Returns: the collapsed #GtsSplit or %NULL if all the #GtsSplit have already - * been collapsed. - */ -GtsSplit * gts_psurface_remove_vertex (GtsPSurface * ps) -{ - GtsSplit * vs; - - g_return_val_if_fail (ps != NULL, NULL); - g_return_val_if_fail (GTS_PSURFACE_IS_CLOSED (ps), NULL); - - if (ps->pos == ps->split->len) - return NULL; - - vs = g_ptr_array_index (ps->split, ps->pos++); - gts_split_collapse (vs, ps->s->edge_class, NULL); - - return vs; -} - -/** - * gts_psurface_max_vertex_number: - * @ps: a #GtsPSurface. - * - * Returns: the maximum number of vertices of @ps i.e. the number of vertices - * if all the #GtsSplit were expanded. - */ -guint gts_psurface_max_vertex_number (GtsPSurface * ps) -{ - g_return_val_if_fail (ps != NULL, 0); - - return ps->min + ps->split->len; -} - -/** - * gts_psurface_min_vertex_number: - * @ps: a #GtsPSurface. - * - * Returns: the minimum number of vertices of @ps i.e. the number of vertices - * if all the #GtsSplit were collapsed. - */ -guint gts_psurface_min_vertex_number (GtsPSurface * ps) -{ - g_return_val_if_fail (ps != NULL, 0); - - return ps->min; -} - -/** - * gts_psurface_set_vertex_number: - * @ps: a #GtsPSurface. - * @n: a number of vertices. - * - * Performs the required number of collapses or expansions to set the number - * of vertices of @ps to @n. - */ -void gts_psurface_set_vertex_number (GtsPSurface * ps, guint n) -{ - g_return_if_fail (ps != NULL); - g_return_if_fail (GTS_PSURFACE_IS_CLOSED (ps)); - - n = ps->min + ps->split->len - n; - while (ps->pos > n && gts_psurface_add_vertex (ps)) - ; - while (ps->pos < n && gts_psurface_remove_vertex (ps)) - ; -} - -/** - * gts_psurface_get_vertex_number: - * @ps: a #GtsPSurface. - * - * Returns: the current number of vertices of @ps. - */ -guint gts_psurface_get_vertex_number (GtsPSurface * ps) -{ - g_return_val_if_fail (ps != NULL, 0); - - if (!GTS_PSURFACE_IS_CLOSED (ps)) - return ps->min + ps->pos; - else - return ps->min + ps->split->len - ps->pos; -} - -/** - * gts_psurface_foreach_vertex: - * @ps: a #GtsPSurface. - * @func: a function to call for each vertex of @ps. - * @data: data to be passed to @func. - * - * Calls @func for each (potential) vertex of @ps, whether actually used - * or not. The vertices are called in the order they were created during the - * edge collapse operation. - */ -void gts_psurface_foreach_vertex (GtsPSurface * ps, - GtsFunc func, - gpointer data) -{ - guint i; - - g_return_if_fail (ps != NULL); - g_return_if_fail (func != NULL); - g_return_if_fail (GTS_PSURFACE_IS_CLOSED (ps)); - - for (i = 0; i < ps->split->len; i++) { - GtsSplit * vs = g_ptr_array_index (ps->split, i); - (*func) (vs->v, data); - } -} Index: trunk/src/3rd/gts/partition.c =================================================================== --- trunk/src/3rd/gts/partition.c (revision 1064) +++ trunk/src/3rd/gts/partition.c (nonexistent) @@ -1,1219 +0,0 @@ -/* GTS - Library for the manipulation of triangulated surfaces - * Copyright (C) 1999 Stéphane Popinet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include - -#include "gts.h" - -/* #define DEBUG */ - -/* Graph partition */ - -/** - * gts_graph_partition_edges_cut: - * @partition: a list of @GtsGraph representing a partition. - * - * Returns: the number of edges cut by the partition. - */ -guint gts_graph_partition_edges_cut (GSList * partition) -{ - guint cuts = 0; - - while (partition) { - cuts += gts_graph_edges_cut (partition->data); - partition = partition->next; - } - - return cuts/2; -} - -/** - * gts_graph_partition_edges_cut_weight: - * @partition: a list of @GtsGraph representing a partition. - * - * Returns: the total weight of the edges cut by the partition. - */ -gfloat gts_graph_partition_edges_cut_weight (GSList * partition) -{ - gfloat weight = 0.; - - while (partition) { - weight += gts_graph_edges_cut_weight (partition->data); - partition = partition->next; - } - - return weight/2.; -} - -/** - * gts_graph_partition_print_stats: - * @partition: a list of @GtsGraph representing a partition. - * @fp: a file pointer. - * - * Writes to @fp a summary of the properties of @partition. - */ -void gts_graph_partition_print_stats (GSList * partition, - FILE * fp) -{ - GtsRange weight; - GSList * i; - - g_return_if_fail (partition != NULL); - g_return_if_fail (fp != NULL); - - gts_range_init (&weight); - i = partition; - while (i) { - gts_range_add_value (&weight, gts_graph_weight (i->data)); - i = i->next; - } - gts_range_update (&weight); - - fprintf (fp, - "# parts: %d\n" - "# edge cuts: %5d edge cuts weight: %5g\n" - "# weight: ", - g_slist_length (partition), - gts_graph_partition_edges_cut (partition), - gts_graph_partition_edges_cut_weight (partition)); - gts_range_print (&weight, fp); - fputc ('\n', fp); -} - -/** - * gts_graph_partition_balance: - * @partition: a list of @GtsGraph representing a partition. - * - * Returns: the difference between the maximum and the minimum weight - * of the graphs in @partition. - */ -gfloat gts_graph_partition_balance (GSList * partition) -{ - gfloat wmin = G_MAXFLOAT; - gfloat wmax = - G_MAXFLOAT; - - g_return_val_if_fail (partition != NULL, 0.); - - while (partition) { - gfloat weight = gts_graph_weight (partition->data); - if (weight < wmin) - wmin = weight; - if (weight > wmax) - wmax = weight; - partition = partition->next; - } - return wmax - wmin; -} - -/** - * gts_graph_partition_clone: - * @partition: a list of @GtsGraph representing a partition. - * - * Returns: a new partition clone of @partition (i.e. a list of new - * graphs clones of the graphs in @partition). - */ -GSList * gts_graph_partition_clone (GSList * partition) -{ - GSList * cparts = NULL; - - while (partition) { - cparts = - g_slist_prepend (cparts, - gts_object_clone (GTS_OBJECT (partition->data))); - partition = partition->next; - } - return cparts; -} - -/** - * gts_graph_partition_destroy: - * @partition: a list of @GtsGraph representing a partition. - * - * Destroys all the graphs in @partition and frees @partition. - */ -void gts_graph_partition_destroy (GSList * partition) -{ - GSList * i = partition; - - while (i) { - gts_object_destroy (GTS_OBJECT (i->data)); - i = i->next; - } - g_slist_free (partition); -} - -static void find_smallest_degree (GtsGNode * n, gpointer * data) -{ - GtsGNode ** nmin = data[0]; - GtsGraph * g = data[1]; - guint * min = data[2]; - guint degree = gts_gnode_degree (n, g); - - if (degree < *min) { - *min = degree; - *nmin = n; - } -} - -static gint graph_comp_weight (GtsGraph * g1, GtsGraph * g2) -{ - if (gts_graph_weight (g1) > gts_graph_weight (g2)) - return 1; - return -1; -} - -static void partition_update (GSList * list, GtsGraph * g) -{ - GSList * i; - GtsGraph * g1; - GtsHeap * size_heap; - gboolean reinit = TRUE; - - /* initialize traversals */ - i = list; - while (i) { - GtsGNode * seed = GTS_OBJECT (i->data)->reserved; - GTS_OBJECT (seed)->reserved = - gts_graph_traverse_new (g, seed, GTS_BREADTH_FIRST, reinit); - reinit = FALSE; - i = i->next; - } - - size_heap = gts_heap_new ((GCompareFunc) graph_comp_weight); - i = list; - while (i) { - gts_heap_insert (size_heap, i->data); - i = i->next; - } - while ((g1 = gts_heap_remove_top (size_heap))) { - GtsGraphTraverse * t = GTS_OBJECT (GTS_OBJECT (g1)->reserved)->reserved; - GtsGNode * n = gts_graph_traverse_next (t); - if (n) { - gts_container_add (GTS_CONTAINER (g1), GTS_CONTAINEE (n)); - gts_heap_insert (size_heap, g1); - } - } - gts_heap_destroy (size_heap); - - /* destroy traversals */ - i = list; - while (i) { - GtsGNode * seed = GTS_OBJECT (i->data)->reserved; - gts_graph_traverse_destroy (GTS_OBJECT (seed)->reserved); - GTS_OBJECT (seed)->reserved = NULL; - i = i->next; - } -} - -static void better_seed (GtsGNode * n, gpointer * data) -{ - guint * sum = data[0]; - GtsGNode ** seed = data[1]; - GtsGraph * g = data[2]; - guint sum1 = gts_graph_distance_sum (g, n); - - if (sum1 < *sum) { - *sum = sum1; - *seed = n; - } -} - -static GtsGNode * graph_new_seed (GtsGraph * g, GtsGNode * seed) -{ - guint sum = gts_graph_distance_sum (g, seed); - gpointer data[3]; - GtsGNode * new_seed = seed; - - data[0] = ∑ - data[1] = &new_seed; - data[2] = g; - gts_gnode_foreach_neighbor (seed, g, (GtsFunc) better_seed, data); - - return new_seed; -} - -/** - * gts_graph_bubble_partition: - * @g: a #GtsGraph. - * @np: number of partitions. - * @niter: the maximum number of iterations. - * @step_info: a #GtsFunc or %NULL. - * @data: user data to pass to @step_info. - * - * An implementation of the "bubble partitioning algorithm" of - * Diekmann, Preis, Schlimbach and Walshaw (2000). The maximum number - * of iteration on the positions of the graph growing seeds is - * controlled by @niter. - * - * If not %NULL @step_info is called after each iteration on the seeds - * positions passing the partition (a GSList) as argument. - * - * Returns: a list of @np new #GtsGraph representing the partition. - */ -GSList * gts_graph_bubble_partition (GtsGraph * g, - guint np, - guint niter, - GtsFunc step_info, - gpointer data) -{ - GSList * list = NULL, * seeds = NULL; - GtsGNode * seed = NULL; - guint min = G_MAXINT/2 - 1; - gpointer info[3]; - GtsGraph * g1; - gboolean changed = TRUE; - - g_return_val_if_fail (g != NULL, NULL); - g_return_val_if_fail (np > 0, NULL); - - info[0] = &seed; - info[1] = g; - info[2] = &min; - gts_container_foreach (GTS_CONTAINER (g), - (GtsFunc) find_smallest_degree, - info); - if (seed == NULL) - return NULL; - - g1 = GTS_GRAPH (gts_object_new (GTS_OBJECT (g)->klass)); - gts_container_add (GTS_CONTAINER (g1), GTS_CONTAINEE (seed)); - list = g_slist_prepend (list, g1); - GTS_OBJECT (g1)->reserved = seed; - seeds = g_slist_prepend (seeds, seed); - - while (--np && seed) - if ((seed = gts_graph_farthest (g, seeds))) { - g1 = GTS_GRAPH (gts_object_new (GTS_OBJECT (g)->klass)); - gts_container_add (GTS_CONTAINER (g1), GTS_CONTAINEE (seed)); - list = g_slist_prepend (list, g1); - GTS_OBJECT (g1)->reserved = seed; - seeds = g_slist_prepend (seeds, seed); - } - g_slist_free (seeds); - - partition_update (list, g); - - while (changed && niter--) { - GSList * i; - - changed = FALSE; - i = list; - while (i) { - GtsGraph * g1 = i->data; - GtsGNode * seed = GTS_OBJECT (g1)->reserved; - GtsGNode * new_seed = graph_new_seed (g1, seed); - if (new_seed != seed) { - changed = TRUE; - GTS_OBJECT (g1)->reserved = new_seed; - } - i = i->next; - } - - if (changed) { - i = list; - while (i) { - GtsGraph * g1 = i->data; - GtsGNode * seed = GTS_OBJECT (g1)->reserved; - - gts_object_destroy (GTS_OBJECT (g1)); - i->data = g1 = GTS_GRAPH (gts_object_new (GTS_OBJECT (g)->klass)); - gts_container_add (GTS_CONTAINER (g1), GTS_CONTAINEE (seed)); - GTS_OBJECT (g1)->reserved = seed; - i = i->next; - } - partition_update (list, g); - if (step_info) - (* step_info) (list, data); - } - } - g_slist_foreach (list, (GFunc) gts_object_reset_reserved, NULL); - return list; -} - -/* Graph bisection */ - -static gdouble node_cost (GtsGNode * n, gpointer * data) -{ - GtsGraph * g = data[0]; - GtsGraph * g1 = data[1]; - GSList * i = GTS_SLIST_CONTAINER (n)->items; - gdouble cost = 0.; - - while (i) { - GtsGEdge * e = i->data; - GtsGNode * n1 = GTS_GNODE_NEIGHBOR (n, e); - - if (gts_containee_is_contained (GTS_CONTAINEE (n1), GTS_CONTAINER (g))) { - if (gts_containee_is_contained (GTS_CONTAINEE (n1), GTS_CONTAINER (g1))) - cost -= gts_gedge_weight (e); - else - cost += gts_gedge_weight (e); - } - i = i->next; - } - - return cost; -} - -static void add_neighbor (GtsGNode * n, GtsEHeap * heap) -{ - if (GTS_OBJECT (n)->reserved == n) - return; - if (GTS_OBJECT (n)->reserved) - gts_eheap_remove (heap, GTS_OBJECT (n)->reserved); - GTS_OBJECT (n)->reserved = gts_eheap_insert (heap, n); -} - -static void add_unused (GtsGNode * n, GtsGraph * g2) -{ - if (GTS_OBJECT (n)->reserved) - GTS_OBJECT (n)->reserved = NULL; - else - gts_container_add (GTS_CONTAINER (g2), GTS_CONTAINEE (n)); -} - -static gdouble degree_cost (GtsGNode * n, GtsGraph * g) -{ - return gts_gnode_degree (n, g); -} - -static void add_seed (GtsGNode * n, GtsEHeap * heap) -{ - gts_eheap_insert (heap, n); -} - -static void boundary_node1 (GtsGNode * n, GtsGraphBisection * bg) -{ - GSList * i = GTS_SLIST_CONTAINER (n)->items; - - while (i) { - GtsGNode * n1 = GTS_GNODE_NEIGHBOR (n, i->data); - if (gts_containee_is_contained (GTS_CONTAINEE (n1), - GTS_CONTAINER (bg->g2))) { - g_hash_table_insert (bg->bg1, n, n1); - return; - } - i = i->next; - } -} - -static void boundary_node2 (GtsGNode * n, GtsGraphBisection * bg) -{ - GSList * i = GTS_SLIST_CONTAINER (n)->items; - - while (i) { - GtsGNode * n1 = GTS_GNODE_NEIGHBOR (n, i->data); - if (gts_containee_is_contained (GTS_CONTAINEE (n1), - GTS_CONTAINER (bg->g1))) { - g_hash_table_insert (bg->bg2, n, n1); - return; - } - i = i->next; - } -} - -static void check_bg (GtsGNode * n, gpointer * data) -{ - GHashTable * bg = data[0]; - GtsGraph * g = data[1]; - gboolean * ok = data[2]; - guint * nb = data[3]; - guint nn = gts_gnode_degree (n, g); - - if (nn > 0) - (*nb)++; - if ((nn > 0 && !g_hash_table_lookup (bg, n)) || - (nn == 0 && g_hash_table_lookup (bg, n))) { - g_warning ("nn: %d lookup: %p\n", - nn, g_hash_table_lookup (bg, n)); - *ok = FALSE; - } -} - -/** - * gts_graph_bisection_check: - * @bg: a #GtsGraphBisection. - * - * Checks that the boundary of @bg is correctly defined (used for - * debugging purposes). - * - * Returns: %TRUE if @bg is ok, %FALSE otherwise. - */ -gboolean gts_graph_bisection_check (GtsGraphBisection * bg) -{ - gboolean ok = TRUE; - guint nb; - gpointer data[4]; - - g_return_val_if_fail (bg != NULL, FALSE); - - nb = 0; - data[0] = bg->bg1; - data[1] = bg->g2; - data[2] = &ok; - data[3] = &nb; - gts_container_foreach (GTS_CONTAINER (bg->g1), (GtsFunc) check_bg, data); - g_return_val_if_fail (g_hash_table_size (bg->bg1) == nb, FALSE); - - nb = 0; - data[0] = bg->bg2; - data[1] = bg->g1; - gts_container_foreach (GTS_CONTAINER (bg->g2), (GtsFunc) check_bg, data); - g_return_val_if_fail (g_hash_table_size (bg->bg2) == nb, FALSE); - - return ok; -} - -/** - * gts_graph_ggg_bisection: - * @g: a #GtsGraph. - * @ntry: the number of randomly selected initial seeds. - * - * An implementation of the "Greedy Graph Growing" algorithm of - * Karypis and Kumar (1997). - * - * @ntry randomly chosen seeds are used and the best partition is retained. - * - * Returns: a new #GtsGraphBisection of @g. - */ -GtsGraphBisection * gts_graph_ggg_bisection (GtsGraph * g, guint ntry) -{ - gfloat size, bestcost = G_MAXFLOAT, smin; - GtsGraph * bestg1 = NULL, * bestg2 = NULL; - gboolean balanced = FALSE; - GtsEHeap * degree_heap; - GtsGNode * seed; - GtsGraphBisection * bg; - - g_return_val_if_fail (g != NULL, NULL); - - bg = g_malloc (sizeof (GtsGraphBisection)); - bg->g = g; - - size = gts_graph_weight (g)/2.; - smin = 0.9*size; - - degree_heap = gts_eheap_new ((GtsKeyFunc) degree_cost, g); - gts_eheap_freeze (degree_heap); - gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) add_seed, degree_heap); - gts_eheap_thaw (degree_heap); - - while (ntry && ((seed = gts_eheap_remove_top (degree_heap, NULL)))) { - GtsGraph * g1, * g2; - GtsGNode * n; - gdouble cost; - gpointer data[2]; - GtsEHeap * heap; - - g1 = gts_graph_new (GTS_GRAPH_CLASS (GTS_OBJECT (g)->klass), - g->node_class, g->edge_class); - g2 = gts_graph_new (GTS_GRAPH_CLASS (GTS_OBJECT (g)->klass), - g->node_class, g->edge_class); - - data[0] = g; - data[1] = g1; - heap = gts_eheap_new ((GtsKeyFunc) node_cost, data); - - gts_container_add (GTS_CONTAINER (g1), GTS_CONTAINEE (seed)); - GTS_OBJECT (seed)->reserved = seed; - gts_gnode_foreach_neighbor (seed, g, (GtsFunc) add_neighbor, heap); - - while ((n = gts_eheap_remove_top (heap, &cost))) - if (gts_graph_weight (g1) + gts_gnode_weight (n) <= size) { - gts_container_add (GTS_CONTAINER (g1), GTS_CONTAINEE (n)); - GTS_OBJECT (n)->reserved = n; - gts_gnode_foreach_neighbor (n, g, (GtsFunc) add_neighbor, heap); - } - else - GTS_OBJECT (n)->reserved = NULL; - gts_eheap_destroy (heap); - - gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) add_unused, g2); - - cost = gts_graph_edges_cut_weight (g1); - if (!bestg1 || - (!balanced && gts_graph_weight (g1) >= smin) || - (cost < bestcost && gts_graph_weight (g1) >= smin)) { - if (bestg1) - bestcost = cost; - if (bestg1) - gts_object_destroy (GTS_OBJECT (bestg1)); - if (bestg2) - gts_object_destroy (GTS_OBJECT (bestg2)); - bestg1 = g1; - bestg2 = g2; - if (gts_graph_weight (g1) >= smin) - balanced = TRUE; - } - else { - gts_object_destroy (GTS_OBJECT (g1)); - gts_object_destroy (GTS_OBJECT (g2)); - } - - ntry--; - } - gts_eheap_destroy (degree_heap); - -#ifdef DEBUG - fprintf (stderr, "bestcost: %5g g1: %5g|%5d g2: %5g|%5d\n", - bestcost, - gts_graph_weight (bestg1), - gts_container_size (GTS_CONTAINER (bestg1)), - gts_graph_weight (bestg2), - gts_container_size (GTS_CONTAINER (bestg2))); -#endif - - g_assert (bestg1 != NULL); - bg->g1 = bestg1; - g_assert (bestg2 != NULL); - bg->g2 = bestg2; - - /* boundary nodes */ - bg->bg1 = g_hash_table_new (NULL, NULL); - gts_container_foreach (GTS_CONTAINER (bg->g1), (GtsFunc) boundary_node1, bg); - bg->bg2 = g_hash_table_new (NULL, NULL); - gts_container_foreach (GTS_CONTAINER (bg->g2), (GtsFunc) boundary_node2, bg); - - return bg; -} - -/** - * gts_graph_bfgg_bisection: - * @g: a #GtsGraph. - * @ntry: the number of randomly selected initial seeds. - * - * An implementation of a "Breadth-First Graph Growing" algorithm. - * - * @ntry randomly chosen seeds are used and the best partition is retained. - * - * Returns: a new #GtsGraphBisection of @g. - */ -GtsGraphBisection * gts_graph_bfgg_bisection (GtsGraph * g, guint ntry) -{ - gfloat size, bestcost = G_MAXFLOAT, smin; - GtsGraph * bestg1 = NULL, * bestg2 = NULL; - GtsEHeap * degree_heap; - GtsGNode * seed; - GtsGraphBisection * bg; - - g_return_val_if_fail (g != NULL, NULL); - - bg = g_malloc (sizeof (GtsGraphBisection)); - bg->g = g; - - size = gts_graph_weight (g)/2.; - smin = 0.9*size; - - degree_heap = gts_eheap_new ((GtsKeyFunc) degree_cost, g); - gts_eheap_freeze (degree_heap); - gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) add_seed, degree_heap); - gts_eheap_thaw (degree_heap); - - while (ntry && ((seed = gts_eheap_remove_top (degree_heap, NULL)))) { - GtsGraph * g1, * g2; - GtsGNode * n; - gdouble cost; - GtsGraphTraverse * t = gts_graph_traverse_new (g, seed, - GTS_BREADTH_FIRST, TRUE); - - g1 = gts_graph_new (GTS_GRAPH_CLASS (GTS_OBJECT (g)->klass), - g->node_class, g->edge_class); - g2 = gts_graph_new (GTS_GRAPH_CLASS (GTS_OBJECT (g)->klass), - g->node_class, g->edge_class); - - while ((n = gts_graph_traverse_next (t))) - if (gts_graph_weight (g1) + gts_gnode_weight (n) <= size) { - gts_container_add (GTS_CONTAINER (g1), GTS_CONTAINEE (n)); - GTS_OBJECT (n)->reserved = n; - } - gts_graph_traverse_destroy (t); - - gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) add_unused, g2); - - cost = gts_graph_edges_cut_weight (g1); - if (!bestg1 || (cost < bestcost && gts_graph_weight (g1) >= smin)) { - if (bestg1) - bestcost = cost; - if (bestg1) - gts_object_destroy (GTS_OBJECT (bestg1)); - if (bestg2) - gts_object_destroy (GTS_OBJECT (bestg2)); - bestg1 = g1; - bestg2 = g2; - } - else { - gts_object_destroy (GTS_OBJECT (g1)); - gts_object_destroy (GTS_OBJECT (g2)); - } - - ntry--; - } - gts_eheap_destroy (degree_heap); - -#ifdef DEBUG - fprintf (stderr, "bestcost: %5g g1: %5g|%5d g2: %5g|%5d\n", - bestcost, - gts_graph_weight (bestg1), - gts_container_size (GTS_CONTAINER (bestg1)), - gts_graph_weight (bestg2), - gts_container_size (GTS_CONTAINER (bestg2))); -#endif - - bg->g1 = bestg1; - bg->g2 = bestg2; - - /* boundary nodes */ - bg->bg1 = g_hash_table_new (NULL, NULL); - gts_container_foreach (GTS_CONTAINER (bg->g1), (GtsFunc) boundary_node1, bg); - bg->bg2 = g_hash_table_new (NULL, NULL); - gts_container_foreach (GTS_CONTAINER (bg->g2), (GtsFunc) boundary_node2, bg); - - return bg; -} - -static gdouble node_move_cost1 (GtsGNode * n, GtsGraphBisection * bg) -{ - return gts_gnode_move_cost (n, bg->g1, bg->g2); -} - -static gdouble node_move_cost2 (GtsGNode * n, GtsGraphBisection * bg) -{ - return gts_gnode_move_cost (n, bg->g2, bg->g1); -} - -static void build_heap (GtsGNode * n, GtsEHeap * heap) -{ - GTS_OBJECT (n)->reserved = gts_eheap_insert (heap, n); -} - -/** - * gts_graph_bisection_kl_refine: - * @bg: a #GtsGraphBisection. - * @mmax: the maximum number of unsuccessful successive moves. - * - * An implementation of the simplified Kernighan-Lin algorithm for - * graph bisection refinement as described in Karypis and Kumar - * (1997). - * - * The algorithm stops if @mmax consecutive modes do not lead to a - * decrease in the number of edges cut. This last @mmax moves are - * undone. - * - * Returns: the decrease in the weight of the edges cut by the bisection. - */ -gdouble gts_graph_bisection_kl_refine (GtsGraphBisection * bg, - guint mmax) -{ - GtsEHeap * h1, * h2; - GtsGNode * n; - guint nm = 0, i; - GtsGNode ** moves; - gdouble bestcost = 0., totalcost = 0., best_balance; - - g_return_val_if_fail (bg != NULL, 0.); - g_return_val_if_fail (mmax > 0, 0.); - - h1 = gts_eheap_new ((GtsKeyFunc) node_move_cost1, bg); - gts_eheap_freeze (h1); - gts_container_foreach (GTS_CONTAINER (bg->g1), (GtsFunc) build_heap, h1); - gts_eheap_thaw (h1); - - h2 = gts_eheap_new ((GtsKeyFunc) node_move_cost2, bg); - gts_eheap_freeze (h2); - gts_container_foreach (GTS_CONTAINER (bg->g2), (GtsFunc) build_heap, h2); - gts_eheap_thaw (h2); - - moves = g_malloc (sizeof (GtsGNode *)*mmax); - best_balance = fabs (gts_graph_weight (bg->g1) - gts_graph_weight (bg->g2)); - - do { - GtsGraph * g1, * g2; - gdouble cost; - - if (gts_graph_weight (bg->g1) > gts_graph_weight (bg->g2)) { - n = gts_eheap_remove_top (h1, &cost); - g1 = bg->g1; - g2 = bg->g2; - } - else { - n = gts_eheap_remove_top (h2, &cost); - g1 = bg->g2; - g2 = bg->g1; - } - if (n) { - GSList * i; - - GTS_OBJECT (n)->reserved = NULL; - gts_container_add (GTS_CONTAINER (g2), GTS_CONTAINEE (n)); - gts_container_remove (GTS_CONTAINER (g1), GTS_CONTAINEE (n)); - - totalcost += cost; - if (totalcost < bestcost) { - bestcost = totalcost; - nm = 0; - } - else if (totalcost == bestcost) { - gdouble balance = fabs (gts_graph_weight (g1) - gts_graph_weight (g2)); - - if (balance < best_balance) { - best_balance = balance; - nm = 0; - } - } - else - moves[nm++] = n; - - i = GTS_SLIST_CONTAINER (n)->items; - while (i) { - GtsGNode * n1 = GTS_GNODE_NEIGHBOR (n, i->data); - if (GTS_OBJECT (n1)->reserved && - gts_containee_is_contained (GTS_CONTAINEE (n1), - GTS_CONTAINER (bg->g))) { - GtsEHeap * h = - gts_containee_is_contained (GTS_CONTAINEE (n1), - GTS_CONTAINER (bg->g1)) ? h1 : h2; - gts_eheap_remove (h, GTS_OBJECT (n1)->reserved); - GTS_OBJECT (n1)->reserved = gts_eheap_insert (h, n1); - } - i = i->next; - } - } - } while (n && nm < mmax); - - gts_eheap_foreach (h1, (GFunc) gts_object_reset_reserved, NULL); - gts_eheap_foreach (h2, (GFunc) gts_object_reset_reserved, NULL); - gts_eheap_destroy (h1); - gts_eheap_destroy (h2); - - /* undo last nm moves */ - for (i = 0; i < nm; i++) { - GtsGNode * n = moves[i]; - GtsGraph * g1 = - gts_containee_is_contained (GTS_CONTAINEE (n), - GTS_CONTAINER (bg->g1)) ? bg->g1 : bg->g2; - GtsGraph * g2 = g1 == bg->g1 ? bg->g2 : bg->g1; - - gts_container_add (GTS_CONTAINER (g2), GTS_CONTAINEE (n)); - gts_container_remove (GTS_CONTAINER (g1), GTS_CONTAINEE (n)); - } - g_free (moves); - - return bestcost; -} - -static void build_bheap (GtsGNode * n, GtsGNode * n1, GtsEHeap * heap) -{ - GTS_OBJECT (n)->reserved = gts_eheap_insert (heap, n); -} - -static void update_neighbors (GtsGNode * n, GtsGraphBisection * bg, - GtsEHeap * h1, GtsEHeap * h2) -{ - GSList * i; - - i = GTS_SLIST_CONTAINER (n)->items; - while (i) { - GtsGNode * n1 = GTS_GNODE_NEIGHBOR (n, i->data); - if (gts_containee_is_contained (GTS_CONTAINEE (n1), - GTS_CONTAINER (bg->g))) { - GtsEHeap * h; - GtsGraph /* * g1,*/ * g2; - GHashTable * bg1; - - if (gts_containee_is_contained (GTS_CONTAINEE (n1), - GTS_CONTAINER (bg->g1))) { - h = h1; - //g1 = bg->g1; - g2 = bg->g2; - bg1 = bg->bg1; - } - else { - h = h2; - //g1 = bg->g2; - g2 = bg->g1; - bg1 = bg->bg2; - } - g_hash_table_remove (bg1, n1); - if (h && GTS_OBJECT (n1)->reserved && GTS_OBJECT (n1)->reserved != n1) { - gts_eheap_remove (h, GTS_OBJECT (n1)->reserved); - GTS_OBJECT (n1)->reserved = NULL; - } - if (gts_gnode_degree (n1, g2)) { - g_hash_table_insert (bg1, n1, n1); - if (h && GTS_OBJECT (n1)->reserved != n1) - GTS_OBJECT (n1)->reserved = gts_eheap_insert (h, n1); - } - } - i = i->next; - } -} - -/** - * gts_graph_bisection_bkl_refine: - * @bg: a #GtsGraphBisection. - * @mmax: the maximum number of unsuccessful successive moves. - * @imbalance: the maximum relative imbalance allowed between the - * weights of both halves of the partition. - * - * An implementation of the simplified boundary Kernighan-Lin - * algorithm for graph bisection refinement as described in Karypis - * and Kumar (1997). - * - * The algorithm stops if @mmax consecutive modes do not lead to a - * decrease in the number of edges cut. This last @mmax moves are - * undone. - * - * Returns: the decrease in the weight of the edges cut by the bisection. - */ -gdouble gts_graph_bisection_bkl_refine (GtsGraphBisection * bg, - guint mmax, - gfloat imbalance) -{ - GtsEHeap * h1, * h2; - GtsGNode * n; - guint nm = 0, i; - GtsGNode ** moves; - gdouble bestcost = 0., totalcost = 0., best_balance; - gboolean balanced = FALSE; - - g_return_val_if_fail (bg != NULL, 0.); - g_return_val_if_fail (mmax > 0, 0.); - g_return_val_if_fail (imbalance >= 0. && imbalance <= 1., 0.); - - h1 = gts_eheap_new ((GtsKeyFunc) node_move_cost1, bg); - gts_eheap_freeze (h1); - g_hash_table_foreach (bg->bg1, (GHFunc) build_bheap, h1); - gts_eheap_thaw (h1); - - h2 = gts_eheap_new ((GtsKeyFunc) node_move_cost2, bg); - gts_eheap_freeze (h2); - g_hash_table_foreach (bg->bg2, (GHFunc) build_bheap, h2); - gts_eheap_thaw (h2); - - moves = g_malloc (sizeof (GtsGNode *)*mmax); - imbalance *= gts_graph_weight (bg->g); - best_balance = fabs (gts_graph_weight (bg->g1) - gts_graph_weight (bg->g2)); - if (best_balance <= imbalance) - balanced = TRUE; - - do { - GtsGraph * g1, * g2; - GHashTable * bg1, * bg2; - gdouble cost; - - if (gts_graph_weight (bg->g1) > gts_graph_weight (bg->g2)) { - n = gts_eheap_remove_top (h1, &cost); - g1 = bg->g1; - g2 = bg->g2; - bg1 = bg->bg1; - bg2 = bg->bg2; - } - else { - n = gts_eheap_remove_top (h2, &cost); - g1 = bg->g2; - g2 = bg->g1; - bg1 = bg->bg2; - bg2 = bg->bg1; - } - if (n) { - gdouble balance; - - GTS_OBJECT (n)->reserved = n; - gts_container_add (GTS_CONTAINER (g2), GTS_CONTAINEE (n)); - gts_container_remove (GTS_CONTAINER (g1), GTS_CONTAINEE (n)); - g_hash_table_remove (bg1, n); - if (gts_gnode_degree (n, g1)) - g_hash_table_insert (bg2, n, n); - - update_neighbors (n, bg, h1, h2); - - totalcost += cost; - balance = fabs (gts_graph_weight (g1) - gts_graph_weight (g2)); - - if (!balanced && balance <= imbalance) { - bestcost = totalcost; - best_balance = balance; - balanced = TRUE; - nm = 0; - } - else if (totalcost < bestcost && - (balance < best_balance || balance <= imbalance)) { - bestcost = totalcost; - best_balance = balance; - nm = 0; - } - else if (totalcost == bestcost && balance < best_balance) { - best_balance = balance; - nm = 0; - } - else - moves[nm++] = n; - } - } while (n && nm < mmax); - - gts_container_foreach (GTS_CONTAINER (bg->g), - (GtsFunc) gts_object_reset_reserved, NULL); - gts_eheap_destroy (h1); - gts_eheap_destroy (h2); - - /* undo last nm moves */ - for (i = 0; i < nm; i++) { - GtsGNode * n = moves[i]; - GtsGraph * g1, * g2; - GHashTable * bg1, * bg2; - - if (gts_containee_is_contained (GTS_CONTAINEE (n), - GTS_CONTAINER (bg->g1))) { - g1 = bg->g1; - g2 = bg->g2; - bg1 = bg->bg1; - bg2 = bg->bg2; - } - else { - g1 = bg->g2; - g2 = bg->g1; - bg1 = bg->bg2; - bg2 = bg->bg1; - } - - gts_container_add (GTS_CONTAINER (g2), GTS_CONTAINEE (n)); - gts_container_remove (GTS_CONTAINER (g1), GTS_CONTAINEE (n)); - g_hash_table_remove (bg1, n); - if (gts_gnode_degree (n, g1)) - g_hash_table_insert (bg2, n, n); - - update_neighbors (n, bg, NULL, NULL); - } - g_free (moves); - - return bestcost; -} - -/* Multilevel partitioning */ - -static void bisection_children (GtsGNodeSplit * ns, GtsGraphBisection * bg) -{ - GtsGraph * g, * g1; - GHashTable * bbg; - GtsGNode * n1 = GTS_GNODE_SPLIT_N1 (ns); - GtsGNode * n2 = GTS_GNODE_SPLIT_N2 (ns); - - if (gts_containee_is_contained (GTS_CONTAINEE (ns->n), - GTS_CONTAINER (bg->g1))) { - g = bg->g1; - g1 = bg->g2; - bbg = bg->bg1; - } - else { - g = bg->g2; - g1 = bg->g1; - bbg = bg->bg2; - } - - gts_allow_floating_gnodes = TRUE; - gts_container_remove (GTS_CONTAINER (g), GTS_CONTAINEE (ns->n)); - gts_allow_floating_gnodes = FALSE; - gts_container_add (GTS_CONTAINER (g), GTS_CONTAINEE (n1)); - gts_container_add (GTS_CONTAINER (g), GTS_CONTAINEE (n2)); - - if (g_hash_table_lookup (bbg, ns->n)) { - g_hash_table_remove (bbg, ns->n); - if (gts_gnode_degree (n1, g1) > 0) - g_hash_table_insert (bbg, n1, n1); - if (gts_gnode_degree (n2, g1) > 0) - g_hash_table_insert (bbg, n2, n2); - } -} - -/** - * gts_graph_bisection_new: - * @wg: a #GtsWGraph. - * @ntry: the number of tries for the graph growing algorithm. - * @mmax: the number of unsucessful moves for the refinement algorithm. - * @nmin: the minimum number of nodes of the coarsest graph. - * @imbalance: the maximum relative imbalance allowed between the - * weights of both halves of the partition. - * - * An implementation of a multilevel bisection algorithm as presented - * in Karypis and Kumar (1997). A multilevel hierarchy of graphs is - * created using the #GtsPGraph object. The bisection of the coarsest - * graph is created using the gts_graph_ggg_bisection() function. The - * graph is then uncoarsened using gts_pgraph_down() and at each level - * the bisection is refined using gts_graph_bisection_bkl_refine(). - * - * Returns: a new #GtsGraphBisection of @wg. - */ -GtsGraphBisection * gts_graph_bisection_new (GtsWGraph * wg, - guint ntry, - guint mmax, - guint nmin, - gfloat imbalance) -{ - GtsGraph * g; - GtsPGraph * pg; - GtsGraphBisection * bg; - gdouble cost; - - g_return_val_if_fail (wg != NULL, NULL); - - g = GTS_GRAPH (wg); - pg = gts_pgraph_new (gts_pgraph_class (), g, - gts_gnode_split_class (), - gts_wgnode_class (), - gts_wgedge_class (), - nmin); - - bg = gts_graph_ggg_bisection (g, ntry); -#ifdef DEBUG - fprintf (stderr, "before size: %5d weight: %5g cuts: %5d cweight: %5g\n", - gts_container_size (GTS_CONTAINER (bg->g1)), - gts_graph_weight (bg->g1), - gts_graph_edges_cut (bg->g1), - gts_graph_edges_cut_weight (bg->g1)); - g_assert (gts_graph_bisection_check (bg)); -#endif - while ((cost = gts_graph_bisection_bkl_refine (bg, mmax, imbalance))) { -#ifdef DEBUG - fprintf (stderr, " cost: %g\n", cost); - g_assert (gts_graph_bisection_check (bg)); -#endif - } -#ifdef DEBUG - fprintf (stderr, "after size: %5d weight: %5g cuts: %5d cweight: %5g\n", - gts_container_size (GTS_CONTAINER (bg->g1)), - gts_graph_weight (bg->g1), - gts_graph_edges_cut (bg->g1), - gts_graph_edges_cut_weight (bg->g1)); -#endif - while (gts_pgraph_down (pg, (GtsFunc) bisection_children, bg)) { -#ifdef DEBUG - fprintf (stderr, "before size: %5d weight: %5g cuts: %5d cweight: %5g\n", - gts_container_size (GTS_CONTAINER (bg->g1)), - gts_graph_weight (bg->g1), - gts_graph_edges_cut (bg->g1), - gts_graph_edges_cut_weight (bg->g1)); -#endif - while ((cost = gts_graph_bisection_bkl_refine (bg, mmax, imbalance))) { -#ifdef DEBUG - fprintf (stderr, " cost: %g\n", cost); - g_assert (gts_graph_bisection_check (bg)); -#endif - } -#ifdef DEBUG - fprintf (stderr, "after size: %5d weight: %5g cuts: %5d cweight: %5g\n", - gts_container_size (GTS_CONTAINER (bg->g1)), - gts_graph_weight (bg->g1), - gts_graph_edges_cut (bg->g1), - gts_graph_edges_cut_weight (bg->g1)); -#endif - } - gts_object_destroy (GTS_OBJECT (pg)); - - return bg; -} - -/** - * gts_graph_bisection_destroy: - * @bg: a #GtsGraphBisection. - * @destroy_graphs: controls graph destruction. - * - * Frees all the memory allocated for @bg. If @destroy_graphs is %TRUE - * the graphs created by @bg are destroyed. - */ -void gts_graph_bisection_destroy (GtsGraphBisection * bg, - gboolean destroy_graphs) -{ - g_return_if_fail (bg != NULL); - - g_hash_table_destroy (bg->bg1); - g_hash_table_destroy (bg->bg2); - - if (destroy_graphs) { - gts_object_destroy (GTS_OBJECT (bg->g1)); - gts_object_destroy (GTS_OBJECT (bg->g2)); - } - - g_free (bg); -} - -static void recursive_bisection (GtsWGraph * wg, - guint np, - guint ntry, - guint mmax, - guint nmin, - gfloat imbalance, - GSList ** list) -{ - if (np == 0) - *list = g_slist_prepend (*list, wg); - else { - GtsGraphBisection * bg = - gts_graph_bisection_new (wg, ntry, mmax, nmin, imbalance); - GtsGraph * g1 = bg->g1; - GtsGraph * g2 = bg->g2; - - gts_object_destroy (GTS_OBJECT (wg)); - gts_graph_bisection_destroy (bg, FALSE); - recursive_bisection (GTS_WGRAPH (g1), np - 1, ntry, mmax, nmin, imbalance, - list); - recursive_bisection (GTS_WGRAPH (g2), np - 1, ntry, mmax, nmin, imbalance, - list); - } -} - -/** - * gts_graph_recursive_bisection: - * @wg: a #GtsWGraph. - * @n: the number of bisection levels. - * @ntry: the number of tries for the graph growing algorithm. - * @mmax: the number of unsucessful moves for the refinement algorithm. - * @nmin: the minimum number of nodes of the coarsest graph. - * @imbalance: the maximum relative imbalance allowed between the - * weights of both halves of the partition. - * - * Calls gts_graph_bisection_new() recursively in order to obtain a - * 2^@n partition of @wg. - * - * Returns: a list of 2^@n new #GtsGraph representing the partition. - */ -GSList * gts_graph_recursive_bisection (GtsWGraph * wg, - guint n, - guint ntry, - guint mmax, - guint nmin, - gfloat imbalance) -{ - GtsGraphBisection * bg; - GtsGraph * g1, * g2; - GSList * list = NULL; - - g_return_val_if_fail (wg != NULL, NULL); - g_return_val_if_fail (n > 0, NULL); - - bg = gts_graph_bisection_new (wg, ntry, mmax, nmin, imbalance); - g1 = bg->g1; - g2 = bg->g2; - gts_graph_bisection_destroy (bg, FALSE); - recursive_bisection (GTS_WGRAPH (g1), n - 1, ntry, mmax, nmin, imbalance, - &list); - recursive_bisection (GTS_WGRAPH (g2), n - 1, ntry, mmax, nmin, imbalance, - &list); - - return list; -} Index: trunk/src/3rd/gts/gts-private.h =================================================================== --- trunk/src/3rd/gts/gts-private.h (revision 1064) +++ trunk/src/3rd/gts/gts-private.h (nonexistent) @@ -1,37 +0,0 @@ -/* GTS - Library for the manipulation of triangulated surfaces - * Copyright (C) 1999 Stéphane Popinet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef __GTS_PRIVATE_H__ -#define __GTS_PRIVATE_H__ - -/* Debugging flags */ - -/* #define DEBUG_FUNCTIONS */ - -#ifdef DEBUG_FUNCTIONS -/* #define DEBUG_LEAKS */ -#define DEBUG_IDENTITY -guint id (gpointer p); -void id_insert (gpointer p); -void id_remove (gpointer p); -void gts_write_triangle (GtsTriangle * t, GtsPoint * o, FILE * fptr); -void gts_write_segment (GtsSegment * s, GtsPoint * o, FILE * fptr); -#endif /* DEBUG_FUNCTIONS */ - -#endif /* __GTS_PRIVATE_H__ */ Index: trunk/src/3rd/gts/boolean.c =================================================================== --- trunk/src/3rd/gts/boolean.c (revision 1064) +++ trunk/src/3rd/gts/boolean.c (nonexistent) @@ -1,2048 +0,0 @@ -/* GTS - Library for the manipulation of triangulated surfaces - * Copyright (C) 1999--2002 Stéphane Popinet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include -#include "gts.h" - -/*#define DEBUG*/ -/*#define DEBUG_BOOLEAN*/ -/*#define CHECK_ORIENTED*/ - -#ifdef DEBUG -# include "gts-private.h" -#endif /* DEBUG */ - -static void surface_inter_destroy (GtsObject * object) -{ - GtsSurfaceInter * si = GTS_SURFACE_INTER (object); - - gts_object_destroy (GTS_OBJECT (si->s1)); - gts_object_destroy (GTS_OBJECT (si->s2)); - g_slist_free (si->edges); - - (* GTS_OBJECT_CLASS (gts_surface_inter_class ())->parent_class->destroy) - (object); -} - -static void surface_inter_class_init (GtsObjectClass * klass) -{ - klass->destroy = surface_inter_destroy; -} - -static void surface_inter_init (GtsSurfaceInter * si) -{ - si->s1 = si->s2 = NULL; - si->edges = NULL; -} - -/** - * gts_surface_inter_class: - * - * Returns: the #GtsSurfaceInterClass. - */ -GtsSurfaceInterClass * gts_surface_inter_class (void) -{ - static GtsSurfaceInterClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo surface_inter_info = { - "GtsSurfaceInter", - sizeof (GtsSurfaceInter), - sizeof (GtsSurfaceInterClass), - (GtsObjectClassInitFunc) surface_inter_class_init, - (GtsObjectInitFunc) surface_inter_init, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (gts_object_class (), &surface_inter_info); - } - - return klass; -} - -/* EdgeInter: Header */ - -typedef struct _EdgeInter EdgeInter; - -struct _EdgeInter { - GtsEdge parent; - - GtsTriangle * t1, * t2; -}; - -#define EDGE_INTER(obj) GTS_OBJECT_CAST (obj,\ - EdgeInter,\ - edge_inter_class ()) -#define IS_EDGE_INTER(obj) (gts_object_is_from_class (obj,\ - edge_inter_class ())) - -static GtsEdgeClass * edge_inter_class (void); -static EdgeInter * edge_inter_new (GtsVertex * v1, GtsVertex * v2, - GtsTriangle * t1, GtsTriangle * t2); - -/* EdgeInter: Object */ - -static GtsEdgeClass * edge_inter_class (void) -{ - static GtsEdgeClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo edge_inter_info = { - "EdgeInter", - sizeof (EdgeInter), - sizeof (GtsEdgeClass), - (GtsObjectClassInitFunc) NULL, - (GtsObjectInitFunc) NULL, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_constraint_class ()), - &edge_inter_info); - } - - return klass; -} - -static EdgeInter * edge_inter_new (GtsVertex * v1, GtsVertex * v2, - GtsTriangle * t1, GtsTriangle * t2) -{ - EdgeInter * object; - - object = EDGE_INTER (gts_edge_new (GTS_EDGE_CLASS (edge_inter_class ()), - v1, v2)); - object->t1 = t1; - object->t2 = t2; - - return object; -} - -#ifdef DEBUG -static void write_surface_graph (GtsSurface * s, FILE * fp) -{ - GSList * l = NULL; - GtsGraph * g; - static void add_to_list (gpointer data, GSList ** l) { - *l = g_slist_prepend (*l, data); - } - - gts_surface_foreach_vertex (s, (GtsFunc) gts_object_reset_reserved, NULL); - gts_surface_foreach_edge (s, (GtsFunc) gts_object_reset_reserved, NULL); - gts_surface_foreach_edge (s, (GtsFunc) add_to_list, &l); - g = gts_segments_graph_new (gts_graph_class (), l); - gts_graph_write_dot (g, fp); - gts_object_destroy (GTS_OBJECT (g)); - g_slist_free (l); -} -#endif /* DEBUG */ - -static GtsPoint * segment_triangle_intersection (GtsSegment * s, - GtsTriangle * t, - GtsPointClass * klass) -{ - GtsPoint * A, * B, * C, * D, * E; - gint ABCE, ABCD, ADCE, ABDE, BCDE; - GtsEdge * AB, * BC, * CA; - gdouble a, b, c; - - g_return_val_if_fail (s != NULL, NULL); - g_return_val_if_fail (t != NULL, NULL); - g_return_val_if_fail (klass != NULL, NULL); - - gts_triangle_vertices_edges (t, NULL, - (GtsVertex **) &A, - (GtsVertex **) &B, - (GtsVertex **) &C, - &AB, &BC, &CA); - D = GTS_POINT (s->v1); - E = GTS_POINT (s->v2); - - ABCE = gts_point_orientation_3d_sos (A, B, C, E); - ABCD = gts_point_orientation_3d_sos (A, B, C, D); - if (ABCE < 0 || ABCD > 0) { - GtsPoint * tmpp; - gint tmp; - - tmpp = E; E = D; D = tmpp; - tmp = ABCE; ABCE = ABCD; ABCD = tmp; - } - if (ABCE < 0 || ABCD > 0) - return NULL; - ADCE = gts_point_orientation_3d_sos (A, D, C, E); - if (ADCE < 0) - return NULL; - ABDE = gts_point_orientation_3d_sos (A, B, D, E); - if (ABDE < 0) - return NULL; - BCDE = gts_point_orientation_3d_sos (B, C, D, E); - if (BCDE < 0) - return NULL; - a = gts_point_orientation_3d (A, B, C, E); - b = gts_point_orientation_3d (A, B, C, D); - if (a != b) { - c = a/(a - b); - return gts_point_new (klass, - E->x + c*(D->x - E->x), - E->y + c*(D->y - E->y), - E->z + c*(D->z - E->z)); - } - /* D and E are contained within ABC */ -#ifdef DEBUG - fprintf (stderr, - "segment: %p:%s triangle: %p:%s intersection\n" - "D and E contained in ABC\n", - s, GTS_NEDGE (s)->name, t, GTS_NFACE (t)->name); -#endif /* DEBUG */ - g_assert (a == 0.); - return gts_point_new (klass, - (E->x + D->x)/2., - (E->y + D->y)/2., - (E->z + D->z)/2.); -} - -static gint triangle_triangle_orientation (GtsPoint * p1, - GtsPoint * p2, GtsPoint * p3, - GtsPoint * p4, GtsPoint * p5, - GtsPoint * p6) -{ - gint o4 = 0, o5 = 0, o6 = 0; - - if (p4 != p1 && p4 != p2 && p4 != p3) - o4 = gts_point_orientation_3d_sos (p1, p2, p3, p4); - if (p5 != p1 && p5 != p2 && p5 != p3) - o5 = gts_point_orientation_3d_sos (p1, p2, p3, p5); - if (o4*o5 < 0) - return 0; - if (p6 != p1 && p6 != p2 && p6 != p3) - o6 = gts_point_orientation_3d_sos (p1, p2, p3, p6); - if (o4*o6 < 0 || o5*o6 < 0) - return 0; - if (o4) return o4; - if (o5) return o5; - g_assert (o6); - return o6; -} - -static gint triangle_point_orientation (GtsTriangle * t1, - GtsTriangle * t2, - gint o1, - GtsPoint * p) -{ - GtsPoint * p1 = GTS_POINT (GTS_SEGMENT (t1->e1)->v1); - GtsPoint * p2 = GTS_POINT (GTS_SEGMENT (t1->e1)->v2); - GtsPoint * p3 = GTS_POINT (gts_triangle_vertex (t1)); - GtsPoint * p4 = GTS_POINT (GTS_SEGMENT (t2->e1)->v1); - GtsPoint * p5 = GTS_POINT (GTS_SEGMENT (t2->e1)->v2); - GtsPoint * p6 = GTS_POINT (gts_triangle_vertex (t2)); - gint o = triangle_triangle_orientation (p1, p2, p3, p4, p5, p6); - - if (o != 0) - return o; - o = triangle_triangle_orientation (p4, p5, p6, p1, p2, p3); - if (o != 0) { - gint o2 = gts_point_orientation_3d_sos (p4, p5, p6, p); - - return - o*o1*o2; - } - return 0; -} - -static void add_edge_inter (GtsEdge * e, - GtsTriangle * t, - GtsVertex * v) -{ - GtsVertex * ev1 = GTS_SEGMENT (e)->v1, * ev2 = GTS_SEGMENT (e)->v2; - GList * i = GTS_OBJECT (e)->reserved; - - GTS_OBJECT (v)->reserved = t; - if (i == NULL) { - GTS_OBJECT (e)->reserved = g_list_prepend (NULL, v); -#ifdef DEBUG - fprintf (stderr, "add_edge_inter: inserting %p (%p,%p)\n", v, e, t); -#endif /* DEBUG */ - } - else { - GtsPoint * p1 = GTS_POINT (GTS_SEGMENT (t->e1)->v1); - GtsPoint * p2 = GTS_POINT (GTS_SEGMENT (t->e1)->v2); - GtsPoint * p3 = GTS_POINT (gts_triangle_vertex (t)); - gint o1, oref = gts_point_orientation_3d_sos (p1, p2, p3, GTS_POINT (ev1)); - - o1 = oref; - while (i) { - gint o2 = triangle_point_orientation (t, GTS_OBJECT (i->data)->reserved, - oref, GTS_POINT (ev1)); - - if (o2 == 0) { -#ifdef DEBUG - g_warning ("add_edge_inter: safe sign evaluation failed\n"); -#endif /* DEBUG */ - o2 = gts_point_orientation_3d_sos (p1, p2, p3, i->data); - } - - if (o1*o2 < 0) - break; - ev1 = i->data; - o1 = o2; - i = i->next; - } - if (i != NULL) { - GList * n = g_list_prepend (NULL, v); - - ev2 = i->data; - n->next = i; - n->prev = i->prev; - i->prev = n; - if (n->prev == NULL) - GTS_OBJECT (e)->reserved = n; - else - n->prev->next = n; - } - else { - g_assert (o1*gts_point_orientation_3d_sos (p1, p2, p3, GTS_POINT (ev2)) - < 0); - GTS_OBJECT (e)->reserved = g_list_append (GTS_OBJECT (e)->reserved, v); - } -#ifdef DEBUG - fprintf (stderr, - "add_edge_inter: inserting %p (%p,%p) between %p and %p\n", - v, e, t, ev1, ev2); - i = GTS_OBJECT (e)->reserved; - while (i) { - fprintf (stderr, " %p", i->data); - i = i->next; - } - fprintf (stderr, "\n"); -#endif /* DEBUG */ - } -} - -static GtsVertex * intersects (GtsEdge * e, - GtsTriangle * t, - GtsSurface * s) -{ - GList * i = GTS_OBJECT (e)->reserved; - GtsVertex * v; - - while (i) { - if (GTS_OBJECT (i->data)->reserved == t) - return i->data; - i = i->next; - } - - v = GTS_VERTEX (segment_triangle_intersection (GTS_SEGMENT (e), t, - GTS_POINT_CLASS (s->vertex_class))); - if (v != NULL) { -#ifdef DEBUG - if (GTS_IS_NVERTEX (v) && GTS_IS_NEDGE (e) && GTS_IS_NFACE (t) && - GTS_NVERTEX (v)->name[0] == '\0') - g_snprintf (GTS_NVERTEX (v)->name, GTS_NAME_LENGTH, "%s|%s", - GTS_NEDGE (e)->name, GTS_NFACE (t)->name); -#endif /* DEBUG */ - if (s->vertex_class->intersection_attributes) - (*s->vertex_class->intersection_attributes) - (v, GTS_OBJECT (e), GTS_OBJECT (t)); - add_edge_inter (e, t, v); - } - return v; -} - -/* see figure misc/orientation.fig */ -static gint intersection_orientation (GtsTriangle * t1, - GtsEdge * e, - GtsTriangle * t2) -{ - GtsVertex * v1, * v2, * v3; - GtsEdge * e2, * e3; - GtsVertex * v4, * v5, * v6; - - gts_triangle_vertices_edges (t1, e, &v1, &v2, &v3, &e, &e2, &e3); - gts_triangle_vertices (t2, &v4, &v5, &v6); - - return gts_point_orientation_3d_sos (GTS_POINT (v4), - GTS_POINT (v5), - GTS_POINT (v6), - GTS_POINT (v2)); -} - -#define UPDATE_ORIENTATION if (o > 0) { vi2 = v; /* e2 = e; */ } else { vi2 = vi1;\ - /* e2 = e1; */\ - vi1 = v;\ - /* e1 = e; */ } - -static void intersect_edges (GtsBBox * bb1, GtsBBox * bb2, - GtsSurfaceInter * si) -{ - GtsSurface * s1 = GTS_OBJECT (si->s1)->reserved; - GtsTriangle * t1 = GTS_TRIANGLE (bb1->bounded); - GtsTriangle * t2 = GTS_TRIANGLE (bb2->bounded); - GtsVertex * v, * vi1 = NULL, * vi2 = NULL; - //GtsEdge * e1 = NULL, * e2 = NULL, * e; - - vi1 = intersects (t2->e1, t1, s1); - //e1 = t2->e1; - v = intersects (t2->e2, t1, s1); - //e = t2->e2; - if (!vi1) { - vi1 = v; - //e1 = e; - } - else if (v) { - gint o = intersection_orientation (t2, t2->e2, t1); - UPDATE_ORIENTATION; - } - if (!vi2) { - v = intersects (t2->e3, t1, s1); - //e = t2->e3; - if (!vi1) { - vi1 = v; - //e1 = e; - } - else if (v) { - gint o = intersection_orientation (t2, t2->e3, t1); - UPDATE_ORIENTATION; - } - } - if (!vi2) { - v = intersects (t1->e1, t2, s1); - //e = t1->e1; - if (!vi1) { - vi1 = v; - //e1 = e; - } - else if (v) { - gint o = - intersection_orientation (t1, t1->e1, t2); - UPDATE_ORIENTATION; - } - } - if (!vi2) { - v = intersects (t1->e2, t2, s1); - //e = t1->e2; - if (!vi1) { - vi1 = v; - //e1 = e; - } - else if (v) { - gint o = - intersection_orientation (t1, t1->e2, t2); - UPDATE_ORIENTATION; - } - } - if (!vi2) { - v = intersects (t1->e3, t2, s1); - //e = t1->e3; - if (!vi1) { - vi1 = v; - //e1 = e; - } - else if (v) { - gint o = - intersection_orientation (t1, t1->e3, t2); - UPDATE_ORIENTATION; - } - } - - g_assert ((!vi1 && !vi2) || (vi1 && vi2)); - if (vi1) { - GtsEdge * e = GTS_EDGE (edge_inter_new (vi1, vi2, t1, t2)); - -#ifdef DEBUG - fprintf (stderr, "creating constraint %p: %p->%p: %p/%p\n", - e, vi1, vi2, t1, t2); -#endif /* DEBUG */ - gts_surface_add_face (si->s1, GTS_FACE (t1)); - gts_surface_add_face (si->s2, GTS_FACE (t2)); - si->edges = g_slist_prepend (si->edges, e); - GTS_OBJECT (t1)->reserved = g_slist_prepend (GTS_OBJECT (t1)->reserved, e); - GTS_OBJECT (t2)->reserved = g_slist_prepend (GTS_OBJECT (t2)->reserved, e); - } -} - -static GtsSurfaceInter * surface_inter_new (GtsSurfaceInterClass * klass, - GtsSurface * s1, - GtsSurface * s2, - GNode * faces_tree1, - GNode * faces_tree2) -{ - GtsSurfaceInter * si; - - si = GTS_SURFACE_INTER (gts_object_new (GTS_OBJECT_CLASS (klass))); - si->s1 = gts_surface_new (gts_surface_class (), - s1->face_class, - s1->edge_class, - s1->vertex_class); - GTS_OBJECT (si->s1)->reserved = s1; - si->s2 = gts_surface_new (gts_surface_class (), - s2->face_class, - s2->edge_class, - s2->vertex_class); - GTS_OBJECT (si->s2)->reserved = s2; - gts_bb_tree_traverse_overlapping (faces_tree1, faces_tree2, - (GtsBBTreeTraverseFunc) intersect_edges, - si); - - return si; -} - -static void free_slist (GtsObject * o) -{ - g_slist_free (o->reserved); - o->reserved = NULL; -} - -static void free_glist (GtsObject * o) -{ - g_list_foreach (o->reserved, (GFunc) gts_object_reset_reserved, NULL); - g_list_free (o->reserved); - o->reserved = NULL; -} - -/** - * gts_surface_intersection: - * @s1: a #GtsSurface. - * @s2: a #GtsSurface. - * @faces_tree1: a bounding box tree (see gts_bb_tree_new()) for - * the faces of @s1. - * @faces_tree2: a bounding box tree for the faces of @s2. - * - * Returns: a list of #GtsEdge defining the curve intersection of the - * two surfaces. - */ -GSList * gts_surface_intersection (GtsSurface * s1, - GtsSurface * s2, - GNode * faces_tree1, - GNode * faces_tree2) -{ - GtsSurfaceInter * si; - GSList * inter; - - g_return_val_if_fail (s1 != NULL, NULL); - g_return_val_if_fail (s2 != NULL, NULL); - g_return_val_if_fail (faces_tree1 != NULL, NULL); - g_return_val_if_fail (faces_tree2 != NULL, NULL); - - si = surface_inter_new (gts_surface_inter_class (), - s1, s2, faces_tree1, faces_tree2); - - gts_surface_foreach_face (si->s1, (GtsFunc) free_slist, NULL); - gts_surface_foreach_face (si->s2, (GtsFunc) free_slist, NULL); - gts_surface_foreach_edge (si->s1, (GtsFunc) free_glist, NULL); - gts_surface_foreach_edge (si->s2, (GtsFunc) free_glist, NULL); - inter = si->edges; - si->edges = NULL; - gts_object_destroy (GTS_OBJECT (si)); - - return inter; -} - -typedef enum { - INTERIOR = 1 << (GTS_USER_FLAG), - RELEVANT = 1 << (GTS_USER_FLAG + 1) -} CurveFlag; - -#define IS_SET(s, f) ((GTS_OBJECT_FLAGS (s) & (f)) != 0) -#define SET(s, f) (GTS_OBJECT_FLAGS (s) |= (f)) -#define UNSET(s, f) (GTS_OBJECT_FLAGS (s) &= ~(f)) -#define NEXT(s) (GTS_OBJECT (s)->reserved) - -#ifdef DEBUG -static void print_segment (GtsSegment * s) -{ - fprintf (stderr, "%p: %s->%s ", s, - GTS_NVERTEX (s->v1)->name, - GTS_NVERTEX (s->v2)->name); - if (NEXT (s)) { - GtsSegment * next = NEXT (s); - - fprintf (stderr, "next %p: %s->%s\n", next, - GTS_NVERTEX (next->v1)->name, - GTS_NVERTEX (next->v2)->name); - } - else - fprintf (stderr, "next NULL\n"); -} - -static void write_nodes (GSList * i, GHashTable * hash, guint * nn, - FILE * fp) -{ - while (i) { - GtsSegment * s = i->data; - - if (!g_hash_table_lookup (hash, s->v1)) { - fprintf (fp, " %u [ label = \"%p\" ];\n", *nn, s->v1); - g_hash_table_insert (hash, s->v1, GUINT_TO_POINTER ((*nn)++)); - } - if (!g_hash_table_lookup (hash, s->v2)) { - fprintf (fp, " %u [ label = \"%p\" ];\n", *nn, s->v2); - g_hash_table_insert (hash, s->v2, GUINT_TO_POINTER ((*nn)++)); - } - i = i->next; - } -} - -static void write_edges (GSList * i, GHashTable * hash, - GtsSurface * surface, - FILE * fp) -{ - while (i) { - GtsSegment * s = i->data; - - fprintf (fp, " %u -> %u [ label = \"%p:%d\" ];\n", - GPOINTER_TO_UINT (g_hash_table_lookup (hash, s->v1)), - GPOINTER_TO_UINT (g_hash_table_lookup (hash, s->v2)), - s, - gts_edge_face_number (GTS_EDGE (s), surface)); - i = i->next; - } -} - -static void write_graph (GSList * boundary, GSList * interior, - GtsSurface * surface, - FILE * fp) -{ - GHashTable * hash = g_hash_table_new (NULL, NULL); - guint nn = 1; - - fprintf (fp, "digraph oriented_curve {\n"); - write_nodes (boundary, hash, &nn, fp); - write_nodes (interior, hash, &nn, fp); - write_edges (boundary, hash, surface, fp); - fprintf (fp, " edge [ color = red ];\n"); - write_edges (interior, hash, surface, fp); - fprintf (fp, "}\n"); - g_hash_table_destroy (hash); -} - -static void write_graph1 (GtsSegment * start, GSList * i, - GtsSurface * surface, - FILE * fp) -{ - GSList * boundary = NULL, * interior = NULL; - GtsSegment * s = start; - - do { - boundary = g_slist_prepend (boundary, s); - s = NEXT (s); - } while (s != start); - while (i) { - if (IS_SET (i->data, INTERIOR)) - interior = g_slist_prepend (interior, i->data); - i = i->next; - } - write_graph (boundary, interior, surface, fp); - g_slist_free (boundary); - g_slist_free (interior); -} - -static void print_loop (GtsSegment * start, FILE * fp) -{ - GtsSegment * s = start; - - do { - fprintf (fp, " %p: %p:%s -> %p:%s\n", - s, - s->v1, GTS_NVERTEX (s->v1)->name, - s->v2, GTS_NVERTEX (s->v2)->name); - s = NEXT (s); - } while (s != start && s != NULL); -} - -static void draw_vector (GtsPoint * p1, GtsPoint * p2, FILE * fp) -{ - gdouble x = p2->x - p1->x; - gdouble y = p2->y - p1->y; - gdouble z = p2->z - p1->z; - - fprintf (fp, "VECT 1 3 0 3 0 %g %g %g %g %g %g %g %g %g\n", - p1->x + x - (x - y/2.)/5., - p1->y + y - (x/2. + y)/5., - p1->z + z - (x/2. + z)/5., - p1->x + x, - p1->y + y, - p1->z + z, - p1->x + x - (x + y/2.)/5., - p1->y + y + (x/2. - y)/5., - p1->z + z + (x/2. - z)/5.); - fprintf (fp, "VECT 1 2 0 2 0 %g %g %g %g %g %g\n", - p1->x, p1->y, p1->z, - p1->x + x, - p1->y + y, - p1->z + z); -} - -static void draw_vector1 (GtsPoint * p1, GtsPoint * p2, GtsPoint * o, - FILE * fp) -{ - gdouble x1 = o->x + 0.9*(p1->x - o->x); - gdouble y1 = o->y + 0.9*(p1->y - o->y); - gdouble z1 = o->z + 0.9*(p1->z - o->z); - gdouble x2 = o->x + 0.9*(p2->x - o->x); - gdouble y2 = o->y + 0.9*(p2->y - o->y); - gdouble z2 = o->z + 0.9*(p2->z - o->z); - gdouble x = x2 - x1; - gdouble y = y2 - y1; - gdouble z = z2 - z1; - - fprintf (fp, "VECT 1 3 0 3 0 %g %g %g %g %g %g %g %g %g\n", - x1 + x - (x - y/2.)/5., - y1 + y - (x/2. + y)/5., - z1 + z - (x/2. + z)/5., - x1 + x, - y1 + y, - z1 + z, - x1 + x - (x + y/2.)/5., - y1 + y + (x/2. - y)/5., - z1 + z + (x/2. - z)/5.); - fprintf (fp, "VECT 1 2 0 2 0 %g %g %g %g %g %g\n", - x1, y1, z1, - x1 + x, - y1 + y, - z1 + z); -} - -static void write_segments (GSList * boundary, GSList * interior, - FILE * fp) -{ - GSList * i = boundary; - - fprintf (fp, "LIST {\n"); - while (i) { - GSList * inext = i->next; - GtsSegment * s = i->data; - GtsSegment * next = inext ? inext->data : boundary->data; - GtsVertex * v1, * v2; - - if (s->v1 != next->v1 && s->v1 != next->v2) { - v1 = s->v1; - v2 = s->v2; - } - else { - v1 = s->v2; - v2 = s->v1; - } - draw_vector (GTS_POINT (v1), GTS_POINT (v2), fp); - i = inext; - } - i = interior; - while (i) { - GtsSegment * s = i->data; - - draw_vector (GTS_POINT (s->v1), GTS_POINT (s->v2), fp); - i = i->next; - } - fprintf (fp, "}\n"); -} - -static void write_loops (GSList * i, FILE * fp) -{ - guint nl = 0; - - while (i) { - GtsSegment * start = i->data, * s; - GtsPoint os; - guint n = 0; - - fprintf (fp, "(geometry \"loop%d\" = LIST {\n", nl++); - - os.x = os.y = os.z = 0.; - s = start; - do { - GtsSegment * next = NEXT (s); - GtsPoint * p; - - if (s->v1 != next->v1 && s->v1 != next->v2) - p = GTS_POINT (s->v1); - else - p = GTS_POINT (s->v2); - os.x += p->x; os.y += p->y; os.z += p->z; n++; - s = next; - } while (s != start); - os.x /= n; os.y /= n; os.z /= n; - - s = start; - do { - GtsSegment * next = NEXT (s); - - if (s->v1 != next->v1 && s->v1 != next->v2) - draw_vector1 (GTS_POINT (s->v1), GTS_POINT (s->v2), &os, fp); - else - draw_vector1 (GTS_POINT (s->v2), GTS_POINT (s->v1), &os, fp); - s = next; - } while (s != start); - - fprintf (fp, "})\n"); - - i = i->next; - } -} - -#define NAME(v) (GTS_IS_NVERTEX (v) ? GTS_NVERTEX (v)->name : "") -#endif /* DEBUG */ - -static GtsSegment * prev_flag (GtsSegment * s, CurveFlag flag) -{ - GSList * i = s->v1->segments; - - while (i) { - if (i->data != s && IS_SET (i->data, flag)) - return i->data; - i = i->next; - } - return NULL; -} - -static GtsSegment * next_flag (GtsSegment * s, CurveFlag flag) -{ - GSList * i = s->v2->segments; - - while (i) { - if (i->data != s && IS_SET (i->data, flag)) - return i->data; - i = i->next; - } - return NULL; -} - -static GtsSegment * next_interior (GtsVertex * v) -{ - GSList * i = v->segments; - - while (i) { - GtsSegment * s = i->data; - - if (s->v1 == v && IS_SET (s, INTERIOR)) - return s; - i = i->next; - } - return NULL; -} - -static GtsSegment * prev_interior (GtsVertex * v) -{ - GSList * i = v->segments; - - while (i) { - GtsSegment * s = i->data; - - if (s->v2 == v && IS_SET (s, INTERIOR)) - return s; - i = i->next; - } - return NULL; -} - -static GtsSegment * reverse (GtsSegment * start, - gboolean interior, - gboolean * isloop) -{ - GtsSegment * s = start, * prev = NULL, * rprev = NULL; - GtsSegment * rstart = NULL, * rstart1 = NULL; - - do { - GtsSegment * rs; - - g_assert (IS_EDGE_INTER (s)); - rs = GTS_SEGMENT (edge_inter_new (s->v2, s->v1, - EDGE_INTER (s)->t1, EDGE_INTER (s)->t2)); - - if (rstart == NULL) - rstart = rs; - else if (rstart1 == NULL) - rstart1 = rs; - if (interior) - SET (rs, INTERIOR); - NEXT (rs) = rprev; - rprev = rs; - prev = s; - s = NEXT (s); - } while (s != NULL && s != start); - if (s == start) { - NEXT (rstart) = rprev; - *isloop = TRUE; - } - else { - NEXT (rstart) = start; - NEXT (prev) = rprev; - *isloop = FALSE; - } - return rstart1; -} - -static GSList * interior_loops (GSList * interior) -{ - GSList * i = interior; - GSList * loops = NULL; - - i = interior; - while (i) { - GtsSegment * s = i->data; - - if (IS_SET (s, RELEVANT)) { - GtsSegment * start = s, * end; - - do { - GtsSegment * next = next_flag (s, INTERIOR); - - UNSET (s, RELEVANT); - end = s; - s = NEXT (s) = next; - } while (s != NULL && s != start); - - if (s == start) - loops = g_slist_prepend (loops, start); - else { - GtsSegment * next, * prev; - gboolean isloop; - - s = prev_flag (start, INTERIOR); - while (s) { - UNSET (s, RELEVANT); - NEXT (s) = start; - start = s; - s = prev_flag (s, INTERIOR); - } - next = next_flag (end, RELEVANT); - prev = prev_flag (start, RELEVANT); - if (prev != NULL) - SET (start->v1, INTERIOR); - if (next != NULL) - SET (end->v2, INTERIOR); - if (next == NULL && prev == NULL) - loops = g_slist_prepend (loops, start); - else - reverse (start, TRUE, &isloop); - } - } - i = i->next; - } - return loops; -} - -#define ORIENTATION(p1,p2,p3,o) (gts_point_orientation_3d (p1, p2, o, p3)) -#define ORIENTATION_SOS(p1,p2,p3,o) (gts_point_orientation_3d_sos (p1, p2, o, p3)) - -#define ORIENTED_VERTICES(s,next,w1,w2) {\ - if ((s)->v1 == (next)->v1 || (s)->v1 == (next)->v2) {\ - w1 = (s)->v2;\ - w2 = (s)->v1;\ - }\ - else {\ - w1 = (s)->v1;\ - w2 = (s)->v2;\ - }\ -} - -#if 0 -static GtsSegment * segment_intersects (GtsPoint * p1, GtsPoint * p2, - GSList * i, - GtsPoint * o) -{ - while (i) { - GtsSegment * s = i->data; - GtsPoint * p3 = GTS_POINT (s->v1); - GtsPoint * p4 = GTS_POINT (s->v2); - - if (p3 != p1 && p3 != p2 && p4 != p1 && p4 != p2) { - gdouble o1 = ORIENTATION (p3, p4, p1, o); - gdouble o2 = ORIENTATION (p3, p4, p2, o); - - if ((o1 < 0. && o2 > 0.) || (o1 > 0. && o2 < 0.)) { - o1 = ORIENTATION (p1, p2, p3, o); - o2 = ORIENTATION (p1, p2, p4, o); - - if ((o1 <= 0. && o2 >= 0.) || (o1 >= 0. && o2 <= 0.)) - return s; - } - } - i = i->next; - } - return NULL; -} -#else -static GtsSegment * segment_intersects (GtsPoint * p1, GtsPoint * p2, - GSList * i, - GtsPoint * o) -{ - while (i) { - GtsSegment * s = i->data; - GtsPoint * p3 = GTS_POINT (s->v1); - GtsPoint * p4 = GTS_POINT (s->v2); - - if (p3 != p1 && p3 != p2 && p4 != p1 && p4 != p2) { - gint o1 = ORIENTATION_SOS (p3, p4, p1, o); - gint o2 = ORIENTATION_SOS (p3, p4, p2, o); - - if (o1*o2 < 0) { - o1 = ORIENTATION_SOS (p1, p2, p3, o); - o2 = ORIENTATION_SOS (p1, p2, p4, o); - - if (o1*o2 < 0) - return s; - } - } - i = i->next; - } - return NULL; -} -#endif - -static gboolean is_inside_wedge (GtsSegment * s1, GtsSegment * s2, - GtsPoint * p, GtsPoint * o) -{ - GtsVertex * v1, * v2, * v3; - - ORIENTED_VERTICES (s1, s2, v1, v2); - v3 = s2->v1 != v2 ? s2->v1 : s2->v2; - - if (ORIENTATION (GTS_POINT (v1), GTS_POINT (v2), - GTS_POINT (v3), o) >= 0.) { - if (ORIENTATION (GTS_POINT (v1), GTS_POINT (v2), p, o) <= 0. || - ORIENTATION (GTS_POINT (v2), GTS_POINT (v3), p, o) <= 0.) - return FALSE; - } - else if (ORIENTATION (GTS_POINT (v1), GTS_POINT (v2), p, o) <= 0. && - ORIENTATION (GTS_POINT (v2), GTS_POINT (v3), p, o) <= 0.) - return FALSE; - return TRUE; -} - -static GtsSegment * connection (GtsPoint * p, - GSList * interior, - GSList * bloops, - GtsPoint * o) -{ - while (bloops) { - GtsSegment * start = bloops->data, * s = start; - - do { - GtsSegment * next = NEXT (s); - GtsVertex * v2 = s->v1 == next->v1 || s->v1 == next->v2 ? s->v1 : s->v2; - - if (is_inside_wedge (s, next, p, o) && - !segment_intersects (p, GTS_POINT (v2), interior, o)) - return s; - s = next; - } while (s != start); - bloops = bloops->next; - } - return NULL; -} - -static gdouble loop_orientation (GtsSegment * start, - GtsPoint * p, GtsPoint * o) -{ - GtsSegment * s = start; - gdouble or = 0.; - - do { - GtsSegment * next = NEXT (s); - GtsVertex * v1, * v2; - - ORIENTED_VERTICES (s, next, v1, v2); - or += ORIENTATION (p, GTS_POINT (v1), GTS_POINT (v2), o); - s = next; - } while (s != start); - -#ifdef DEBUG - fprintf (stderr, "loop orientation: %g\n", or); -#endif /* DEBUG */ - - return or; -} - -static void connect_interior_loop (GtsSegment * start, - GSList ** interior, - GSList ** bloops, - GtsSurface * surface, - GtsPoint * o) -{ - GtsSegment * s = start, * c = NULL, * next, * s1, * rs1, * rs; - GtsVertex * v, * cv; - gboolean isloop; - - do { - if (!(c = connection (GTS_POINT (s->v2), *interior, *bloops, o))) - s = NEXT (s); - } while (s != start && !c); - g_assert (c); - next = NEXT (c); - v = c->v1 == next->v1 || c->v1 == next->v2 ? c->v1 : c->v2; - cv = s->v2; -#ifdef DEBUG - fprintf (stderr, "connecting %p:%s with %p:%s\n", - cv, NAME (cv), v, NAME (v)); - fprintf (stderr, " c: %p: %p:%s %p:%s\n", c, - c->v1, NAME (c->v1), - c->v2, NAME (c->v2)); - fprintf (stderr, " next: %p: %p:%s %p:%s\n", next, - next->v1, NAME (next->v1), - next->v2, NAME (next->v2)); -#endif /* DEBUG */ - rs = reverse (s, FALSE, &isloop); - if (isloop) { - if (loop_orientation (rs, GTS_POINT (v), o) < 0.) { - GtsSegment * tmp = s; - s = rs; - rs = tmp; - } - *bloops = g_slist_prepend (*bloops, rs); - } - s1 = GTS_SEGMENT (gts_edge_new (surface->edge_class, v, cv)); - rs1 = GTS_SEGMENT (gts_edge_new (surface->edge_class, cv, v)); - NEXT (c) = s1; - NEXT (rs1) = next; - *interior = g_slist_prepend (*interior, s1); - NEXT (s1) = NEXT (s); - NEXT (s) = rs1; -} - -static GSList * boundary_loops (GSList * boundary) -{ - GSList * i = boundary; - GtsSegment * start = i->data; - GSList * loops = NULL; - - while (i) { - GtsSegment * s = i->data; - GSList * inext = i->next; - GtsSegment * next = inext ? inext->data : start; - GtsVertex * v = s->v1 == next->v1 || s->v1 == next->v2 ? s->v1 : s->v2; - - if (IS_SET (v, INTERIOR)) { - GtsSegment * intprev = prev_interior (v); - - NEXT (intprev) = next; - NEXT (s) = next_interior (v); - UNSET (v, INTERIOR); - } - else - NEXT (s) = next; - i = inext; - } - - i = boundary; - while (i) { - start = i->data; - - if (IS_SET (start, RELEVANT)) { - GtsSegment * s = start; - - do { - UNSET (s, RELEVANT); - UNSET (s, INTERIOR); - s = NEXT (s); - } while (s != start); - loops = g_slist_prepend (loops, start); - } - i = i->next; - } - - return loops; -} - -typedef struct _Ear Ear; - -struct _Ear { - GtsVertex * v1, * v2, * v3; - GtsSegment * s1, * s2, * s3; -}; - -static gboolean point_in_wedge (GtsPoint * p1, GtsPoint * p2, GtsPoint * p3, - GtsPoint * p, gboolean closed, GtsPoint * o) -{ - gdouble o1; - - if (p == p2 || p == p3) - return FALSE; - o1 = ORIENTATION (p1, p2, p, o); - if ((closed && o1 < 0.) || (!closed && o1 <= 0.)) return FALSE; - o1 = ORIENTATION (p3, p1, p, o); - if ((closed && o1 < 0.) || (!closed && o1 <= 0.)) return FALSE; - return TRUE; -} - -#if 0 -static gboolean segment_intersects1 (GtsPoint * p1, GtsPoint * p2, - GtsPoint * p3, GtsPoint * p4, - gboolean closed, GtsPoint * o) -{ - gdouble o1 = ORIENTATION (p3, p4, p1, o); - gdouble o2 = ORIENTATION (p3, p4, p2, o); - gdouble o3, o4; - - if ((closed && ((o1 > 0. && o2 > 0.) || (o1 < 0. && o2 < 0.))) || - (!closed && ((o1 >= 0. && o2 >= 0.) || (o1 <= 0. && o2 <= 0.)))) - return FALSE; - o3 = ORIENTATION (p1, p2, p3, o); - o4 = ORIENTATION (p1, p2, p4, o); - if ((o3 > 0. && o4 > 0.) || (o3 < 0. && o4 < 0.)) - return FALSE; - if (closed) return TRUE; - if ((o3 == 0. && o4 > 0.) || (o4 == 0. && o3 > 0.)) - return TRUE; - return FALSE; -} -#else -static gboolean segment_intersects1 (GtsPoint * p1, GtsPoint * p2, - GtsPoint * p3, GtsPoint * p4, - gboolean closed, GtsPoint * o) -{ - gint o1, o2; - - o1 = ORIENTATION_SOS (p3, p4, p1, o); - o2 = ORIENTATION_SOS (p3, p4, p2, o); - if (o1*o2 > 0) - return FALSE; - o1 = ORIENTATION_SOS (p1, p2, p3, o); - o2 = ORIENTATION_SOS (p1, p2, p4, o); - if (o1*o2 > 0) - return FALSE; - return TRUE; -} -#endif - -static GtsSegment * triangle_intersects_segments (GtsPoint * p1, - GtsPoint * p2, - GtsPoint * p3, - gboolean closed, - GtsSegment * start, - GtsPoint * o) -{ - GtsSegment * s = start; - - do { - GtsPoint * p4 = GTS_POINT (s->v1); - GtsPoint * p5 = GTS_POINT (s->v2); - - if (p4 == p1) { - if (point_in_wedge (p1, p2, p3, p5, closed, o)) - return s; - } - else if (p4 == p2) { - if (point_in_wedge (p2, p3, p1, p5, closed, o)) - return s; - } - else if (p4 == p3) { - if (point_in_wedge (p3, p1, p2, p5, closed, o)) - return s; - } - else if (p5 == p1) { - if (point_in_wedge (p1, p2, p3, p4, closed, o)) - return s; - } - else if (p5 == p2) { - if (point_in_wedge (p2, p3, p1, p4, closed, o)) - return s; - } - else if (p5 == p3) { - if (point_in_wedge (p3, p1, p2, p4, closed, o)) - return s; - } - else if (segment_intersects1 (p1, p2, p4, p5, closed, o) || - segment_intersects1 (p2, p3, p4, p5, closed, o) || - segment_intersects1 (p3, p1, p4, p5, closed, o)) - return s; - s = NEXT (s); - } while (s != start); - return NULL; -} - -static gboolean new_ear (GtsSegment * s, - Ear * e, - GtsSegment * start, - guint sloppy, - GtsPoint * o) -{ - gdouble or; - - e->s1 = s; - e->s2 = NEXT (s); - - g_return_val_if_fail (e->s2, FALSE); - g_return_val_if_fail (e->s2 != e->s1, FALSE); - - ORIENTED_VERTICES (e->s1, e->s2, e->v1, e->v2); - e->v3 = e->s2->v1 != e->v2 ? e->s2->v1 : e->s2->v2; - if (e->v3 == e->v1) - return FALSE; - e->s3 = NEXT (e->s2); - if (gts_segment_connect (e->s3, e->v1, e->v3)) { - if (NEXT (e->s3) != e->s1) - return FALSE; - } - else if (gts_vertices_are_connected (e->v1, e->v3)) - return FALSE; - else - e->s3 = NULL; - or = ORIENTATION (GTS_POINT (e->v1), GTS_POINT (e->v2), GTS_POINT (e->v3),o); - switch (sloppy) { - case 0: - if (or <= 0. || - triangle_intersects_segments (GTS_POINT (e->v1), GTS_POINT (e->v2), - GTS_POINT (e->v3), TRUE, start, o)) - return FALSE; - break; - case 1: - if (or < 0. || - (or > 0. && - triangle_intersects_segments (GTS_POINT (e->v1), GTS_POINT (e->v2), - GTS_POINT (e->v3), FALSE, start, o))) - return FALSE; - break; - case 2: - if ((or > 0. && - triangle_intersects_segments (GTS_POINT (e->v1), GTS_POINT (e->v2), - GTS_POINT (e->v3), FALSE, start, o)) || - (or < 0. && - triangle_intersects_segments (GTS_POINT (e->v2), GTS_POINT (e->v1), - GTS_POINT (e->v3), FALSE, start, o))) - return FALSE; - break; - case 3: - if (or < 0.) - return FALSE; - break; - } -#ifdef DEBUG - if (or <= 0.) - fprintf (stderr, "or: %g\n", or); -#endif /* DEBUG */ - g_assert (or > -1e-6); - return TRUE; -} - -static void triangulate_loop (GtsSegment * start, - GtsSurface * surface, - GtsPoint * o) -{ - GtsSegment * prev = start, * s; - guint sloppy = 0; -#ifdef DEBUG - guint nt = 0; -#endif /* DEBUG */ - - s = NEXT (start); - while (NEXT (s) != s) { - GtsSegment * next = NEXT (s); - Ear e; - -#ifdef DEBUG - fprintf (stderr, "prev: %p s: %p next: %p\n", prev, s, next); -#endif /* DEBUG */ - - if (!new_ear (s, &e, start, sloppy, o)) { - if (s == start) { - sloppy++; -#ifdef DEBUG - fprintf (stderr, "sloppy: %u\n", sloppy); -#endif /* DEBUG */ - } - prev = s; - s = next; - } - else { - GtsFace * f; - - if (!GTS_IS_EDGE (e.s3)) - e.s3 = GTS_SEGMENT (gts_edge_new (surface->edge_class, e.v1, e.v3)); - f = gts_face_new (surface->face_class, - GTS_EDGE (e.s1), GTS_EDGE (e.s2), GTS_EDGE (e.s3)); - gts_surface_add_face (surface, f); - UNSET (e.s1, RELEVANT); - UNSET (e.s1, INTERIOR); - UNSET (e.s2, RELEVANT); - UNSET (e.s2, INTERIOR); - NEXT (prev) = e.s3; - NEXT (e.s3) = NEXT (e.s2); - NEXT (e.s1) = NEXT (e.s2) = NULL; - start = prev; - s = NEXT (prev); - sloppy = 0; -#ifdef DEBUG - { - gchar name[80]; - FILE * fp; - - fprintf (stderr, " t.%u: (%p:%s,%p:%s,%p:%s)\n", - nt, - e.v1, NAME (e.v1), - e.v2, NAME (e.v2), - e.v3, NAME (e.v3)); - sprintf (name, "/tmp/t.%u", nt++); - fp = fopen (name, "wt"); - // gts_surface_write (surface, fp); - gts_write_triangle (GTS_TRIANGLE (f), NULL, fp); - // write_graph1 (start, interior, surface, fp); - fclose (fp); - print_loop (start, stderr); - } -#endif /* DEBUG */ - } - } - UNSET (s, RELEVANT); - UNSET (s, INTERIOR); - NEXT (s) = NULL; -} - -#ifdef CHECK_ORIENTED -static void check_object (GtsObject * o) -{ - g_assert (o->reserved == NULL); - g_assert (o->flags == 0); -} - -static void check_boundary (GtsEdge * e, GtsSurface * s) -{ - check_object (GTS_OBJECT (e)); - check_object (GTS_OBJECT (GTS_SEGMENT (e)->v1)); - check_object (GTS_OBJECT (GTS_SEGMENT (e)->v2)); - g_assert (gts_edge_face_number (e, s) == 1); -} - -static void check_interior (GtsEdge * e, GtsSurface * s) -{ - guint n; - check_object (GTS_OBJECT (e)); - check_object (GTS_OBJECT (GTS_SEGMENT (e)->v1)); - check_object (GTS_OBJECT (GTS_SEGMENT (e)->v2)); - - n = gts_edge_face_number (e, s); -#ifdef DEBUG - if (n != 2) - gts_surface_print_stats (s, stderr); -#endif /* DEBUG */ - g_assert (n == 2); -} - -static void check_boundary_interior_triangulation (GSList * boundary, - GSList * interior, - GtsSurface * surface) -{ - g_slist_foreach (boundary, (GFunc) check_boundary, surface); - g_slist_foreach (interior, (GFunc) check_interior, surface); -} -#endif /*ifdef CHECK_ORIENTED */ - -static void merge_duplicate (GtsEdge * e) -{ - GtsEdge * dup = gts_edge_is_duplicate (e); - - g_assert (dup); - gts_edge_replace (dup, e); - gts_object_destroy (GTS_OBJECT (dup)); -} - -static void triangulate_boundary_interior (GSList * boundary, - GSList * interior, - GtsSurface * s, - GtsPoint * o) -{ - GSList * iloops, * bloops, * i; - - i = boundary; - while (i) { - SET (i->data, RELEVANT); - i = i->next; - } - i = interior; - while (i) { - SET (i->data, RELEVANT); - SET (i->data, INTERIOR); - i = i->next; - } - - iloops = interior_loops (interior); - bloops = boundary_loops (boundary); - - i = iloops; - while (i) { -#ifdef DEBUG - fprintf (stderr, "--- interior loop ---\n"); - print_loop (i->data, stderr); -#endif /* DEBUG */ - connect_interior_loop (i->data, &interior, &bloops, s, o); - i = i->next; - } - -#ifdef DEBUG - { - FILE * fp = fopen ("/tmp/bloops", "w"); - write_loops (bloops, fp); - fclose (fp); - } -#endif /* DEBUG */ - - i = bloops; - while (i) { -#ifdef DEBUG - fprintf (stderr, "--- boundary loop ---\n"); - print_loop (i->data, stderr); -#endif /* DEBUG */ - triangulate_loop (i->data, s, o); - i = i->next; - } - - g_slist_foreach (interior, (GFunc) merge_duplicate, NULL); - g_slist_free (iloops); - g_slist_free (bloops); - -#ifdef CHECK_ORIENTED - check_boundary_interior_triangulation (boundary, interior, s); -#endif /* CHECK_ORIENTED */ -} - -static void create_edges (GtsSegment * s, GtsSurface * surface) -{ - if (GTS_OBJECT (s)->reserved) { - GList * i = GTS_OBJECT (s)->reserved; - GtsVertex * v1 = i->data; - - GTS_OBJECT (s)->reserved = g_list_prepend (i, - gts_edge_new (surface->edge_class, s->v1, v1)); - while (i) { - GList * next = i->next; - GtsVertex * v2 = next ? next->data : s->v2; - - GTS_OBJECT (i->data)->reserved = NULL; - i->data = gts_edge_new (surface->edge_class, v1, v2); - v1 = v2; - i = next; - } - } -} - -static void add_boundary (GtsSegment * s, GtsSegment * next, - GSList ** boundary) -{ - if (GTS_OBJECT (s)->reserved == NULL) - *boundary = g_slist_prepend (*boundary, s); - else { - if (s->v2 == next->v2 || s->v2 == next->v1) { - GList * i = g_list_last (GTS_OBJECT (s)->reserved); - - while (i) { - *boundary = g_slist_prepend (*boundary, i->data); - i = i->prev; - } - } - else { - GList * i = GTS_OBJECT (s)->reserved; - - while (i) { - *boundary = g_slist_prepend (*boundary, i->data); - i = i->next; - } - } - } -} - -static void triangulate_face (GtsTriangle * t, GtsSurface * surface) -{ - GSList * interior = GTS_OBJECT (t)->reserved; - GSList * boundary = NULL; - GtsSurface * s = gts_surface_new (gts_surface_class (), - surface->face_class, - surface->edge_class, - surface->vertex_class); - gdouble x, y, z; - GtsPoint * p = GTS_POINT (GTS_SEGMENT (t->e1)->v1); - GtsPoint * o; - - GTS_OBJECT (t)->reserved = NULL; - gts_triangle_normal (t, &x, &y, &z); - g_assert (x != 0. || y != 0. || z != 0.); - o = gts_point_new (gts_point_class (), p->x + x, p->y + y, p->z + z); - add_boundary (GTS_SEGMENT (t->e3), GTS_SEGMENT (t->e1), &boundary); - add_boundary (GTS_SEGMENT (t->e2), GTS_SEGMENT (t->e3), &boundary); - add_boundary (GTS_SEGMENT (t->e1), GTS_SEGMENT (t->e2), &boundary); -#ifdef DEBUG - { - static guint nt = 0; - char name[80]; - FILE * fp; - - fprintf (stderr, "%u: triangulating %p\n", nt, t); -if (nt == 28) - fprintf (stderr, "tintin!!!!\n"); - sprintf (name, "/tmp/oc.%u", nt++); - fp = fopen (name, "wt"); - // write_graph (boundary, interior, s, fp); - write_segments (boundary, interior, fp); - fclose (fp); - } -#endif /* DEBUG */ - triangulate_boundary_interior (boundary, interior, s, o); - g_slist_free (interior); - g_slist_free (boundary); - if (GTS_OBJECT (t)->klass->attributes) - gts_surface_foreach_face (s, (GtsFunc) gts_object_attributes, t); - gts_surface_merge (surface, s); - gts_object_destroy (GTS_OBJECT (s)); - gts_object_destroy (GTS_OBJECT (o)); -} - -static void free_edge_list (GtsObject * o) -{ - g_list_free (o->reserved); - o->reserved = NULL; -} - -/** - * gts_surface_inter_new: - * @klass: a #GtsSurfaceInterClass. - * @s1: a #GtsSurface. - * @s2: a #GtsSurface. - * @faces_tree1: a bounding box tree (see gts_bb_tree_new()) for - * the faces of @s1. - * @faces_tree2: a bounding box tree for the faces of @s2. - * @is_open1: whether @s1 is an "open" surface. - * @is_open2: whether @s2 is an "open" surface. - * - * When triangulating the cut faces, the new faces inherit the - * attributes of these original faces through their attributes() - * method. - * - * Returns: a new #GtsSurfaceInter describing the intersection of @s1 - * and @s2. - */ -GtsSurfaceInter * gts_surface_inter_new (GtsSurfaceInterClass * klass, - GtsSurface * s1, - GtsSurface * s2, - GNode * faces_tree1, - GNode * faces_tree2, - gboolean is_open1, - gboolean is_open2) -{ - GtsSurfaceInter * si; - GtsSurface * s; - - g_return_val_if_fail (klass != NULL, NULL); - g_return_val_if_fail (s1 != NULL, NULL); - g_return_val_if_fail (s2 != NULL, NULL); - g_return_val_if_fail (faces_tree1 != NULL, NULL); - g_return_val_if_fail (faces_tree2 != NULL, NULL); - - si = surface_inter_new (klass, s1, s2, faces_tree1, faces_tree2); - - gts_surface_foreach_edge (si->s1, (GtsFunc) create_edges, si->s1); - gts_surface_foreach_edge (si->s2, (GtsFunc) create_edges, si->s2); - -#ifdef DEBUG - fprintf (stderr, "====== triangulating s1 ======\n"); -#endif /* DEBUG */ - s = gts_surface_new (gts_surface_class (), - s1->face_class, - s1->edge_class, - s1->vertex_class); - gts_surface_foreach_face (si->s1, (GtsFunc) triangulate_face, s); - gts_surface_foreach_edge (si->s1, (GtsFunc) free_edge_list, NULL); - gts_object_destroy (GTS_OBJECT (si->s1)); - si->s1 = s; - GTS_OBJECT (si->s1)->reserved = s1; - -#ifdef DEBUG - fprintf (stderr, "====== triangulating s2 ======\n"); -#endif /* DEBUG */ - s = gts_surface_new (gts_surface_class (), - s2->face_class, - s2->edge_class, - s2->vertex_class); - gts_surface_foreach_face (si->s2, (GtsFunc) triangulate_face, s); - gts_surface_foreach_edge (si->s2, (GtsFunc) free_edge_list, NULL); - gts_object_destroy (GTS_OBJECT (si->s2)); - si->s2 = s; - GTS_OBJECT (si->s2)->reserved = s2; - - return si; -} - -static void check_surface_edge (GtsEdge * e, gpointer * data) -{ - gboolean * ok = data[0]; - GtsSurface * s = data[1]; - GtsSurface * bs = GTS_OBJECT (s)->reserved; - guint nf = gts_edge_face_number (e, s); - - if (nf < 1 || nf > 2) { - *ok = FALSE; - g_return_if_fail (nf >= 1 && nf <= 2); - } - if (nf == 1 && gts_edge_face_number (e, bs) == 0) { - *ok = FALSE; - g_return_if_fail (gts_edge_face_number (e, bs) > 0); - } -} - -static void mark_edge (GtsObject * o, gpointer data) -{ - o->reserved = data; -} - -static gint triangle_orientation (GtsTriangle * t, GtsEdge * e) -{ - GtsSegment * s = GTS_SEGMENT (t->e1 == e ? t->e2 - : - t->e2 == e ? t->e3 - : - t->e1); - GtsVertex * v2 = GTS_SEGMENT (e)->v2; - - if (s->v1 == v2 || s->v2 == v2) - return 1; - return -1; -} - -static gboolean check_orientation (GtsEdge * e, GtsSurface * s) -{ - GtsTriangle * t1 = NULL, * t2 = NULL; - GSList * i = e->triangles; - gint o1 = 0, o2 = 0; - - while (i) { - if (GTS_IS_FACE (i->data) && - gts_face_has_parent_surface (i->data, s)) { - if (t1 == NULL) { - t1 = i->data; - o1 = triangle_orientation (t1, e); - } - else if (t2 == NULL) { - t2 = i->data; - o2 = triangle_orientation (t2, e); - g_return_val_if_fail (o1*o2 < 0, FALSE); - } - else - g_assert_not_reached (); - } - i = i->next; - } - g_return_val_if_fail (t1 && t2, FALSE); - return TRUE; -} - -static void check_edge (GtsSegment * s, gpointer * data) -{ - gboolean * ok = data[0]; - GtsSurfaceInter * si = data[1]; - gboolean * closed = data[2]; - GSList * j; - guint nn = 0; - - j = s->v1->segments; - while (j && *ok) { - GtsSegment * s1 = j->data; - - if (s1 != s && GTS_OBJECT (s1)->reserved == si) { - if (s1->v2 != s->v1) - *ok = FALSE; - nn++; - } - j = j->next; - } - j = s->v2->segments; - while (j && *ok) { - GtsSegment * s1 = j->data; - - if (s1 != s && GTS_OBJECT (s1)->reserved == si) { - if (s1->v1 != s->v2) - *ok = FALSE; - nn++; - } - j = j->next; - } - if (nn != 2) - *closed = FALSE; - - if (!check_orientation (GTS_EDGE (s), si->s1)) - *ok = FALSE; - if (!check_orientation (GTS_EDGE (s), si->s2)) - *ok = FALSE; -} - -/** - * gts_surface_inter_check: - * @si: a #GtsSurfaceInter. - * @closed: is set to %TRUE if @si->edges is a closed curve, %FALSE - * otherwise. - * - * Returns: %TRUE if the curve described by @si is an orientable - * manifold, %FALSE otherwise. - */ -gboolean gts_surface_inter_check (GtsSurfaceInter * si, - gboolean * closed) -{ - gboolean ok = TRUE; - gpointer data[3]; - - g_return_val_if_fail (si != NULL, FALSE); - g_return_val_if_fail (closed != NULL, FALSE); - - *closed = si->edges ? TRUE : FALSE; - - /* mark edges as used by si */ - g_slist_foreach (si->edges, (GFunc) mark_edge, si); - - data[0] = &ok; - data[1] = si; - data[2] = closed; - g_slist_foreach (si->edges, (GFunc) check_edge, data); - g_slist_foreach (si->edges, (GFunc) gts_object_reset_reserved, NULL); - - /* check connectivity of the faces of @si */ - if (*closed) { - gpointer data[2]; - - data[0] = &ok; - data[1] = si->s1; - gts_surface_foreach_edge (si->s1, (GtsFunc) check_surface_edge, data); - data[1] = si->s2; - gts_surface_foreach_edge (si->s2, (GtsFunc) check_surface_edge, data); - } - - return ok; -} - -/* Given @e and @f returns a #GtsFace compatible with @f and belonging to - @s1 or @s2 */ -static GtsFace * next_compatible_face (GtsEdge * e, - GtsFace * f, - GtsSurface * s1, - GtsSurface * s2) -{ - GSList * i = e->triangles; - GtsFace * f2 = NULL, * f3 = NULL; - - while (i) { - GtsFace * f1 = i->data; - - if (f1 != f && GTS_IS_FACE (f1)) { - if (gts_face_has_parent_surface (f1, s1)) - return f1; - if (gts_face_has_parent_surface (f1, s2)) { - if (f2 == NULL) f2 = f1; - else if (f3 == NULL) f3 = f1; - else g_assert_not_reached (); /* s2 is a non-manifold surface */ - } - } - i = i->next; - } - if (f3 == NULL) { - if (gts_edge_is_boundary (e, s2)) - return NULL; - return f2; - } - g_assert (gts_face_has_parent_surface (f, s1)); - if (gts_triangles_are_compatible (GTS_TRIANGLE (f), GTS_TRIANGLE (f2), e)) - return f2; - return f3; -} - -static void walk_faces (GtsEdge * e, GtsFace * f, - GtsSurface * s1, - GtsSurface * s2, - GtsSurface * s) -{ - GtsFifo * faces = gts_fifo_new (); - GtsFifo * edges = gts_fifo_new (); - - gts_fifo_push (faces, f); - gts_fifo_push (edges, e); - while ((f = gts_fifo_pop (faces)) && (e = gts_fifo_pop (edges))) { - if (!GTS_OBJECT (f)->reserved) { - GtsTriangle * t = GTS_TRIANGLE (f); - GtsFace * f1; - - gts_surface_add_face (s, f); - GTS_OBJECT (f)->reserved = s; - if (t->e1 != e && !GTS_OBJECT (t->e1)->reserved && - (f1 = next_compatible_face (t->e1, f, s1, s2))) { - gts_fifo_push (faces, f1); - gts_fifo_push (edges, t->e1); - } - if (t->e2 != e && !GTS_OBJECT (t->e2)->reserved && - (f1 = next_compatible_face (t->e2, f, s1, s2))) { - gts_fifo_push (faces, f1); - gts_fifo_push (edges, t->e2); - } - if (t->e3 != e && !GTS_OBJECT (t->e3)->reserved && - (f1 = next_compatible_face (t->e3, f, s1, s2))) { - gts_fifo_push (faces, f1); - gts_fifo_push (edges, t->e3); - } - } - } - gts_fifo_destroy (faces); - gts_fifo_destroy (edges); -} - -/** - * gts_surface_inter_boolean: - * @si: a #GtsSurfaceInter. - * @surface: a #GtsSurface. - * @op: a #GtsBooleanOperation. - * - * Adds to @surface the part of the surface described by @si and @op. - */ -void gts_surface_inter_boolean (GtsSurfaceInter * si, - GtsSurface * surface, - GtsBooleanOperation op) -{ - GtsSurface * s = NULL; - gint orient = 1; - GSList * i; - - g_return_if_fail (si != NULL); - g_return_if_fail (surface != NULL); - - switch (op) { - case GTS_1_OUT_2: s = si->s1; orient = 1; break; - case GTS_1_IN_2: s = si->s1; orient = -1; break; - case GTS_2_OUT_1: s = si->s2; orient = -1; break; - case GTS_2_IN_1: s = si->s2; orient = 1; break; - default: g_assert_not_reached (); - } - - /* mark edges as belonging to intersection */ - g_slist_foreach (si->edges, (GFunc) mark_edge, si); - - i = si->edges; - while (i) { - GtsEdge * e = i->data; - GSList * j = e->triangles; - - while (j) { - if (gts_face_has_parent_surface (j->data, s) && - orient*triangle_orientation (j->data, e) > 0) { -#ifdef DEBUG_BOOLEAN - GtsFace * boundary = gts_edge_is_boundary (e, surface); - - g_assert (!boundary || boundary == j->data); -#endif /* DEBUG_BOOLEAN */ - walk_faces (e, j->data, s, GTS_OBJECT (s)->reserved, surface); - break; - } - j = j->next; - } - i = i->next; - } - g_slist_foreach (si->edges, (GFunc) gts_object_reset_reserved, NULL); - gts_surface_foreach_face (surface, - (GtsFunc) gts_object_reset_reserved, NULL); -} - -static void self_intersecting (GtsBBox * bb1, GtsBBox * bb2, - gpointer * d) -{ - GtsTriangle * t1 = bb1->bounded; - GtsTriangle * t2 = bb2->bounded; - - if (t1 != t2) { - GtsSegment * s1 = GTS_SEGMENT (t1->e1); - GtsSegment * s2 = GTS_SEGMENT (t1->e2); - GtsSegment * s3 = GTS_SEGMENT (t1->e3); - GtsSegment * s4 = GTS_SEGMENT (t2->e1); - GtsSegment * s5 = GTS_SEGMENT (t2->e2); - GtsSegment * s6 = GTS_SEGMENT (t2->e3); - GtsPoint * pi; - - if ((!gts_segments_touch (s4, s1) && - !gts_segments_touch (s4, s2) && - !gts_segments_touch (s4, s3) && - (pi = segment_triangle_intersection (s4, t1, gts_point_class ())) - != NULL) || - (!gts_segments_touch (s5, s1) && - !gts_segments_touch (s5, s2) && - !gts_segments_touch (s5, s3) && - (pi = segment_triangle_intersection (s5, t1, gts_point_class ())) - != NULL) || - (!gts_segments_touch (s6, s1) && - !gts_segments_touch (s6, s2) && - !gts_segments_touch (s6, s3) && - (pi = segment_triangle_intersection (s6, t1, gts_point_class ())) - != NULL)) { - GtsBBTreeTraverseFunc func = d[0]; - gpointer data = d[1]; - gboolean * self_inter = d[2]; - - gts_object_destroy (GTS_OBJECT (pi)); - *self_inter = TRUE; - (* func) (bb1, bb2, data); - } - } -} - -/** - * gts_surface_foreach_intersecting_face: - * @s: a #GtsSurface. - * @func: a #GtsBBTreeTraverseFunc. - * @data: user data to pass to @func. - * - * Calls @func for each intersecting pair of faces of @s. - * - * Returns: %TRUE if @func was called at least once, %FALSE otherwise. - */ -gboolean gts_surface_foreach_intersecting_face (GtsSurface * s, - GtsBBTreeTraverseFunc func, - gpointer data) -{ - GNode * tree; - gpointer d[3]; - gboolean self_inter = FALSE; - - g_return_val_if_fail (s != NULL, FALSE); - g_return_val_if_fail (func != NULL, FALSE); - - tree = gts_bb_tree_surface (s); - d[0] = func; - d[1] = data; - d[2] = &self_inter; - gts_bb_tree_traverse_overlapping (tree, tree, - (GtsBBTreeTraverseFunc) self_intersecting, - d); - gts_bb_tree_destroy (tree, TRUE); - - return self_inter; -} - -static void add_intersecting (GtsBBox * bb1, GtsBBox * bb2, - GtsSurface * intersected) -{ - gts_surface_add_face (intersected, bb1->bounded); - gts_surface_add_face (intersected, bb2->bounded); -} - -/** - * gts_surface_is_self_intersecting: - * @s: a #GtsSurface. - * - * Returns: a new #GtsSurface containing the faces of @s which are - * self-intersecting or %NULL if no faces of @s are self-intersecting. - */ -GtsSurface * gts_surface_is_self_intersecting (GtsSurface * s) -{ - GtsSurface * intersected; - - g_return_val_if_fail (s != NULL, NULL); - - intersected = gts_surface_new (GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass), - s->face_class, - s->edge_class, - s->vertex_class); - if (!gts_surface_foreach_intersecting_face (s, - (GtsBBTreeTraverseFunc) add_intersecting, intersected)) { - gts_object_destroy (GTS_OBJECT (intersected)); - intersected = NULL; - } - return intersected; -} Index: trunk/src/3rd/gts/container.c =================================================================== --- trunk/src/3rd/gts/container.c (revision 1064) +++ trunk/src/3rd/gts/container.c (nonexistent) @@ -1,493 +0,0 @@ -/* GTS - Library for the manipulation of triangulated surfaces - * Copyright (C) 1999 Stéphane Popinet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include "gts.h" - -/* GtsContainee */ - -static void containee_class_init (GtsContaineeClass * klass) -{ - klass->remove_container = NULL; - klass->add_container = NULL; - klass->foreach = NULL; - klass->is_contained = NULL; - klass->replace = NULL; -} - -GtsContaineeClass * gts_containee_class (void) -{ - static GtsContaineeClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo containee_info = { - "GtsContainee", - sizeof (GtsContainee), - sizeof (GtsContaineeClass), - (GtsObjectClassInitFunc) containee_class_init, - (GtsObjectInitFunc) NULL, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (gts_object_class (), - &containee_info); - } - - return klass; -} - -GtsContainee * gts_containee_new (GtsContaineeClass * klass) -{ - GtsContainee * object; - - object = GTS_CONTAINEE (gts_object_new (GTS_OBJECT_CLASS (klass))); - - return object; -} - -gboolean gts_containee_is_contained (GtsContainee * item, - GtsContainer * c) -{ - g_return_val_if_fail (item != NULL, FALSE); - g_return_val_if_fail (c != NULL, FALSE); - - if (GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->is_contained) - return - (* GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->is_contained) - (item, c); - return FALSE; -} - -void gts_containee_replace (GtsContainee * item, - GtsContainee * with) -{ - if (GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->replace) - (* GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->replace) (item, with); - if (GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->foreach) { - (* GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->foreach) - (item, (GtsFunc) gts_container_add, with); - (* GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->foreach) - (item, (GtsFunc) gts_container_remove, item); - } -} - -/* GtsSListContainee */ - -static void slist_containee_destroy (GtsObject * object) -{ - GtsSListContainee * item = GTS_SLIST_CONTAINEE (object); - GSList * i; - - i = item->containers; - while (i) { - GSList * next = i->next; - - gts_container_remove (i->data, GTS_CONTAINEE (item)); - i = next; - } - g_assert (item->containers == NULL); - - (* GTS_OBJECT_CLASS (gts_slist_containee_class ())->parent_class->destroy) - (object); -} - -static void slist_containee_remove_container (GtsContainee * i, - GtsContainer * c) -{ - GtsSListContainee * item = GTS_SLIST_CONTAINEE (i); - item->containers = g_slist_remove (item->containers, c); -} - -static void slist_containee_add_container (GtsContainee * i, - GtsContainer * c) -{ - GtsSListContainee * item = GTS_SLIST_CONTAINEE (i); - if (!g_slist_find (item->containers, c)) - item->containers = g_slist_prepend (item->containers, c); -} - -static void slist_containee_foreach (GtsContainee * c, - GtsFunc func, - gpointer data) -{ - GSList * i = GTS_SLIST_CONTAINEE (c)->containers; - - while (i) { - GSList * next = i->next; - - (* func) (i->data, data); - i = next; - } -} - -static gboolean slist_containee_is_contained (GtsContainee * i, - GtsContainer * c) -{ - return g_slist_find (GTS_SLIST_CONTAINEE (i)->containers, c) ? TRUE : FALSE; -} - -static void slist_containee_class_init (GtsSListContaineeClass * klass) -{ - GTS_CONTAINEE_CLASS (klass)->remove_container = - slist_containee_remove_container; - GTS_CONTAINEE_CLASS (klass)->add_container = - slist_containee_add_container; - GTS_CONTAINEE_CLASS (klass)->foreach = - slist_containee_foreach; - GTS_CONTAINEE_CLASS (klass)->is_contained = - slist_containee_is_contained; - - GTS_OBJECT_CLASS (klass)->destroy = slist_containee_destroy; -} - -static void slist_containee_init (GtsSListContainee * object) -{ - object->containers = NULL; -} - -GtsSListContaineeClass * gts_slist_containee_class (void) -{ - static GtsSListContaineeClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo slist_containee_info = { - "GtsSListContainee", - sizeof (GtsSListContainee), - sizeof (GtsSListContaineeClass), - (GtsObjectClassInitFunc) slist_containee_class_init, - (GtsObjectInitFunc) slist_containee_init, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_containee_class ()), - &slist_containee_info); - } - - return klass; -} - -/* GtsContainer */ - -static void remove_container (GtsContainee * item, GtsContainer * c) -{ - if (GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->remove_container) - (* GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->remove_container) - (item, c); -} - -static void container_destroy (GtsObject * object) -{ - GtsContainer * c = GTS_CONTAINER (object); - - gts_container_foreach (c, (GtsFunc) remove_container, c); - - (* GTS_OBJECT_CLASS (gts_container_class ())->parent_class->destroy) - (object); -} - -static void container_add (GtsContainer * c, GtsContainee * item) -{ - if (GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->add_container) - (* GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->add_container) - (item, c); -} - -static void container_remove (GtsContainer * c, GtsContainee * item) -{ - if (GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->remove_container) - (* GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->remove_container) - (item, c); -} - -static void container_clone_add (GtsContainee * item, GtsContainer * clone) -{ - gts_container_add (clone, item); -} - -static void container_clone (GtsObject * clone, GtsObject * object) -{ - gts_object_init (clone, object->klass); - gts_container_foreach (GTS_CONTAINER (object), - (GtsFunc) container_clone_add, clone); -} - -static void container_class_init (GtsContainerClass * klass) -{ - klass->add = container_add; - klass->remove = container_remove; - klass->foreach = NULL; - klass->size = NULL; - - GTS_OBJECT_CLASS (klass)->destroy = container_destroy; - GTS_OBJECT_CLASS (klass)->clone = container_clone; -} - -GtsContainerClass * gts_container_class (void) -{ - static GtsContainerClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo container_info = { - "GtsContainer", - sizeof (GtsContainer), - sizeof (GtsContainerClass), - (GtsObjectClassInitFunc) container_class_init, - (GtsObjectInitFunc) NULL, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = - gts_object_class_new (GTS_OBJECT_CLASS (gts_slist_containee_class ()), - &container_info); - } - - return klass; -} - -GtsContainer * gts_container_new (GtsContainerClass * klass) -{ - GtsContainer * object; - - object = GTS_CONTAINER (gts_object_new (GTS_OBJECT_CLASS (klass))); - - return object; -} - -void gts_container_add (GtsContainer * c, - GtsContainee * item) -{ - g_return_if_fail (c != NULL); - g_return_if_fail (item != NULL); - - g_assert (GTS_CONTAINER_CLASS (GTS_OBJECT (c)->klass)->add); - (* GTS_CONTAINER_CLASS (GTS_OBJECT (c)->klass)->add) (c, item); -} - -void gts_container_remove (GtsContainer * c, - GtsContainee * item) -{ - g_return_if_fail (c != NULL); - g_return_if_fail (item != NULL); - - g_assert (GTS_CONTAINER_CLASS (GTS_OBJECT (c)->klass)->remove); - (* GTS_CONTAINER_CLASS (GTS_OBJECT (c)->klass)->remove) (c, item); -} - -void gts_container_foreach (GtsContainer * c, - GtsFunc func, - gpointer data) -{ - g_return_if_fail (c != NULL); - g_return_if_fail (func != NULL); - - if (GTS_CONTAINER_CLASS (GTS_OBJECT (c)->klass)->foreach) - (* GTS_CONTAINER_CLASS (GTS_OBJECT (c)->klass)->foreach) (c, func, data); -} - -guint gts_container_size (GtsContainer * c) -{ - g_return_val_if_fail (c != NULL, 0); - - if (GTS_CONTAINER_CLASS (GTS_OBJECT (c)->klass)->size) - return (* GTS_CONTAINER_CLASS (GTS_OBJECT (c)->klass)->size) (c); - return 0; -} - -/* GtsHashContainer */ - -static void hash_container_destroy (GtsObject * object) -{ - GHashTable * items = GTS_HASH_CONTAINER (object)->items; - - (* GTS_OBJECT_CLASS (gts_hash_container_class ())->parent_class->destroy) - (object); - - g_hash_table_destroy (items); -} - -static void hash_container_add (GtsContainer * c, GtsContainee * item) -{ - g_return_if_fail (GTS_HASH_CONTAINER (c)->frozen == FALSE); - - g_hash_table_insert (GTS_HASH_CONTAINER (c)->items, item, NULL); - - (* GTS_CONTAINER_CLASS (GTS_OBJECT_CLASS (gts_hash_container_class ())->parent_class)->add) (c, item); -} - -static void hash_container_remove (GtsContainer * c, GtsContainee * item) -{ - g_return_if_fail (GTS_HASH_CONTAINER (c)->frozen == FALSE); - - g_hash_table_remove (GTS_HASH_CONTAINER (c)->items, item); - - (* GTS_CONTAINER_CLASS (GTS_OBJECT_CLASS (gts_hash_container_class ())->parent_class)->remove) (c, item); -} - -static void hash_foreach (GtsContainee * item, - gpointer item_data, - gpointer * info) -{ - (* ((GtsFunc) info[0])) (item, info[1]); -} - -static void hash_container_foreach (GtsContainer * c, - GtsFunc func, - gpointer data) -{ - gpointer info[2]; - - info[0] = func; - info[1] = data; - /* prevent removing or adding items */ - GTS_HASH_CONTAINER (c)->frozen = TRUE; - g_hash_table_foreach (GTS_HASH_CONTAINER (c)->items, - (GHFunc) hash_foreach, info); - GTS_HASH_CONTAINER (c)->frozen = FALSE; -} - -static guint hash_container_size (GtsContainer * c) -{ - return g_hash_table_size (GTS_HASH_CONTAINER (c)->items); -} - -static void hash_container_class_init (GtsHashContainerClass * klass) -{ - GTS_CONTAINER_CLASS (klass)->add = hash_container_add; - GTS_CONTAINER_CLASS (klass)->remove = hash_container_remove; - GTS_CONTAINER_CLASS (klass)->foreach = hash_container_foreach; - GTS_CONTAINER_CLASS (klass)->size = hash_container_size; - - GTS_OBJECT_CLASS (klass)->destroy = hash_container_destroy; -} - -static void hash_container_init (GtsHashContainer * object) -{ - object->items = g_hash_table_new (NULL, NULL); - object->frozen = FALSE; -} - -GtsHashContainerClass * gts_hash_container_class (void) -{ - static GtsHashContainerClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo hash_container_info = { - "GtsHashContainer", - sizeof (GtsHashContainer), - sizeof (GtsHashContainerClass), - (GtsObjectClassInitFunc) hash_container_class_init, - (GtsObjectInitFunc) hash_container_init, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_container_class ()), - &hash_container_info); - } - - return klass; -} - -/* GtsSListContainer */ - -static void slist_container_destroy (GtsObject * object) -{ - GSList * items = GTS_SLIST_CONTAINER (object)->items; - - (* GTS_OBJECT_CLASS (gts_slist_container_class ())->parent_class->destroy) - (object); - - g_slist_free (items); -} - -static void slist_container_add (GtsContainer * c, GtsContainee * item) -{ - g_return_if_fail (GTS_SLIST_CONTAINER (c)->frozen == FALSE); - - if (!g_slist_find (GTS_SLIST_CONTAINER (c)->items, item)) - GTS_SLIST_CONTAINER (c)->items = - g_slist_prepend (GTS_SLIST_CONTAINER (c)->items, item); - - (* GTS_CONTAINER_CLASS (GTS_OBJECT_CLASS (gts_slist_container_class ())->parent_class)->add) (c, item); -} - -static void slist_container_remove (GtsContainer * c, GtsContainee * item) -{ - g_return_if_fail (GTS_SLIST_CONTAINER (c)->frozen == FALSE); - - GTS_SLIST_CONTAINER (c)->items = - g_slist_remove (GTS_SLIST_CONTAINER (c)->items, item); - - (* GTS_CONTAINER_CLASS (GTS_OBJECT_CLASS (gts_slist_container_class ())->parent_class)->remove) (c, item); -} - -static void slist_container_foreach (GtsContainer * c, - GtsFunc func, - gpointer data) -{ - GSList * i; - - i = GTS_SLIST_CONTAINER (c)->items; - while (i) { - GSList * next = i->next; - - (* func) (i->data, data); - i = next; - } -} - -static guint slist_container_size (GtsContainer * c) -{ - return g_slist_length (GTS_SLIST_CONTAINER (c)->items); -} - -static void slist_container_class_init (GtsSListContainerClass * klass) -{ - GTS_CONTAINER_CLASS (klass)->add = slist_container_add; - GTS_CONTAINER_CLASS (klass)->remove = slist_container_remove; - GTS_CONTAINER_CLASS (klass)->foreach = slist_container_foreach; - GTS_CONTAINER_CLASS (klass)->size = slist_container_size; - - GTS_OBJECT_CLASS (klass)->destroy = slist_container_destroy; -} - -static void slist_container_init (GtsSListContainer * object) -{ - object->items = NULL; - object->frozen = FALSE; -} - -GtsSListContainerClass * gts_slist_container_class (void) -{ - static GtsSListContainerClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo slist_container_info = { - "GtsSListContainer", - sizeof (GtsSListContainer), - sizeof (GtsSListContainerClass), - (GtsObjectClassInitFunc) slist_container_class_init, - (GtsObjectInitFunc) slist_container_init, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_container_class ()), - &slist_container_info); - } - - return klass; -} Index: trunk/src/3rd/gts/eheap.c =================================================================== --- trunk/src/3rd/gts/eheap.c (revision 1064) +++ trunk/src/3rd/gts/eheap.c (nonexistent) @@ -1,461 +0,0 @@ -/* GTS - Library for the manipulation of triangulated surfaces - * Copyright (C) 1999 Stéphane Popinet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include -#include "gts.h" - -#define PARENT(i) ((i) >= 2 ? (i)/2 : 0) -#define LEFT_CHILD(i) (2*(i)) -#define RIGHT_CHILD(i) (2*(i) + 1) - - -/** - * gts_eheap_new: - * @key_func: a #GtsKeyFunc or %NULL. - * @data: user data to be passed to @key_func. - * - * Returns: a new #GtsEHeap using @key_func as key. - */ -GtsEHeap * gts_eheap_new (GtsKeyFunc key_func, - gpointer data) -{ - GtsEHeap * heap; - - heap = g_malloc (sizeof(GtsEHeap)); - heap->elts = g_ptr_array_new (); - heap->func = key_func; - heap->data = data; - heap->frozen = FALSE; - heap->randomized = FALSE; - return heap; -} - -static void sift_up (GtsEHeap * heap, guint i) -{ - GtsEHeapPair * parent, * child; - guint p; - gpointer * pdata = heap->elts->pdata; - gdouble key; - - child = pdata[i - 1]; - key = child->key; - while ((p = PARENT (i))) { - parent = pdata[p - 1]; - if (parent->key > key || - (heap->randomized && parent->key == key && rand () < RAND_MAX/2)) { - pdata[p - 1] = child; - pdata[i - 1] = parent; - child->pos = p; - parent->pos = i; - i = p; - } - else - i = 0; - } -} - -/** - * gts_eheap_insert: - * @heap: a #GtsEHeap. - * @p: a pointer to add to the heap. - * - * Inserts a new element @p in the heap. - * - * Returns: a #GtsEHeapPair describing the position of the element in the heap. - * This pointer is necessary for gts_eheap_remove() and - * gts_eheap_decrease_key(). - */ -GtsEHeapPair * gts_eheap_insert (GtsEHeap * heap, gpointer p) -{ - GtsEHeapPair * pair; - GPtrArray * elts; - - g_return_val_if_fail (heap != NULL, NULL); - g_return_val_if_fail (heap->func != NULL, NULL); - - elts = heap->elts; - pair = g_malloc (sizeof (GtsEHeapPair)); - g_ptr_array_add (elts, pair); - pair->data = p; - pair->pos = elts->len; - pair->key = (*heap->func) (p, heap->data); - if (!heap->frozen) - sift_up (heap, elts->len); - return pair; -} - -/** - * gts_eheap_insert_with_key: - * @heap: a #GtsEHeap. - * @p: a pointer to add to the heap. - * @key: the value of the key associated to @p. - * - * Inserts a new element @p in the heap. - * - * Returns: a #GtsEHeapPair describing the position of the element in the heap. - * This pointer is necessary for gts_eheap_remove() and - * gts_eheap_decrease_key(). - */ -GtsEHeapPair * gts_eheap_insert_with_key (GtsEHeap * heap, - gpointer p, - gdouble key) -{ - GtsEHeapPair * pair; - GPtrArray * elts; - - g_return_val_if_fail (heap != NULL, NULL); - - elts = heap->elts; - pair = g_malloc (sizeof (GtsEHeapPair)); - g_ptr_array_add (elts, pair); - pair->data = p; - pair->pos = elts->len; - pair->key = key; - if (!heap->frozen) - sift_up (heap, elts->len); - return pair; -} - -static void sift_down (GtsEHeap * heap, guint i) -{ - GtsEHeapPair * left_child, * right_child, * child, * parent; - guint lc, rc, c; - gpointer * pdata = heap->elts->pdata; - guint len = heap->elts->len; - gdouble key; - - lc = LEFT_CHILD (i); - rc = RIGHT_CHILD (i); - left_child = lc <= len ? pdata[lc - 1] : NULL; - right_child = rc <= len ? pdata[rc - 1] : NULL; - - parent = pdata[i - 1]; - key = parent->key; - while (left_child != NULL) { - if (right_child == NULL || left_child->key < right_child->key) { - child = left_child; - c = lc; - } - else { - child = right_child; - c = rc; - } - if (key > child->key) { - pdata[i - 1] = child; - child->pos = i; - pdata[c - 1] = parent; - parent->pos = c; - i = c; - lc = LEFT_CHILD (i); - rc = RIGHT_CHILD (i); - left_child = lc <= len ? pdata[lc - 1] : NULL; - right_child = rc <= len ? pdata[rc - 1] : NULL; - } - else - left_child = NULL; - } -} - -/** - * gts_eheap_remove_top: - * @heap: a #GtsEHeap. - * @key: a pointer on a gdouble or %NULL. - * - * Removes the element at the top of the heap and optionally (if @key is not - * %NULL) returns the value of its key. - * - * Returns: the element at the top of the heap. - */ -gpointer gts_eheap_remove_top (GtsEHeap * heap, gdouble * key) -{ - gpointer root; - GPtrArray * elts; - guint len; - GtsEHeapPair * pair; - - g_return_val_if_fail (heap != NULL, NULL); - - elts = heap->elts; - len = elts->len; - - if (len == 0) - return NULL; - if (len == 1) { - pair = g_ptr_array_remove_index (elts, 0); - root = pair->data; - if (key) - *key = pair->key; - g_free (pair); - return root; - } - - pair = elts->pdata[0]; - root = pair->data; - if (key) - *key = pair->key; - g_free (pair); - pair = g_ptr_array_remove_index (elts, len - 1); - elts->pdata[0] = pair; - pair->pos = 1; - sift_down (heap, 1); - return root; -} - -/** - * gts_eheap_top: - * @heap: a #GtsEHeap. - * @key: a pointer on a gdouble or %NULL. - * - * Returns: the element at the top of the heap and optionally (if @key is not - * %NULL) its key. - */ -gpointer gts_eheap_top (GtsEHeap * heap, gdouble * key) -{ - GtsEHeapPair * pair; - GPtrArray * elts; - - g_return_val_if_fail (heap != NULL, NULL); - - elts = heap->elts; - - if (elts->len == 0) - return NULL; - - pair = elts->pdata[0]; - if (key) - *key = pair->key; - return pair->data; -} - -/** - * gts_eheap_destroy: - * @heap: a #GtsEHeap. - * - * Free all the memory allocated for @heap. - */ -void gts_eheap_destroy (GtsEHeap * heap) -{ - guint i; - - g_return_if_fail (heap != NULL); - - for (i = 0; i < heap->elts->len; i++) - g_free (heap->elts->pdata[i]); - g_ptr_array_free (heap->elts, TRUE); - g_free (heap); -} - -/** - * gts_eheap_thaw: - * @heap: a #GtsEHeap. - * - * If @heap has been frozen previously using gts_eheap_freeze(), reorder it - * in O(n) time and unfreeze it. - */ -void gts_eheap_thaw (GtsEHeap * heap) -{ - guint i; - - g_return_if_fail (heap != NULL); - - if (!heap->frozen) - return; - - for (i = heap->elts->len/2; i > 0; i--) - sift_down (heap, i); - - heap->frozen = FALSE; -} - -/** - * gts_eheap_foreach: - * @heap: a #GtsEHeap. - * @func: the function to call for each element in the heap. - * @data: to pass to @func. - */ -void gts_eheap_foreach (GtsEHeap * heap, - GFunc func, - gpointer data) -{ - guint i; - GPtrArray * elts; - - g_return_if_fail (heap != NULL); - g_return_if_fail (func != NULL); - - elts = heap->elts; - for (i = 0; i < elts->len; i++) - (*func) (((GtsEHeapPair *) elts->pdata[i])->data, data); -} - -/** - * gts_eheap_remove: - * @heap: a #GtsEHeap. - * @p: a #GtsEHeapPair. - * - * Removes element corresponding to @p from @heap in O(log n). - * - * Returns: the element just removed from @heap. - */ -gpointer gts_eheap_remove (GtsEHeap * heap, GtsEHeapPair * p) -{ - GtsEHeapPair ** pdata; - GtsEHeapPair * parent; - guint i, par; - gpointer data; - - g_return_val_if_fail (heap != NULL, NULL); - g_return_val_if_fail (p != NULL, NULL); - - pdata = (GtsEHeapPair **)heap->elts->pdata; - i = p->pos; - data = p->data; - - g_return_val_if_fail (i > 0 && i <= heap->elts->len, NULL); - g_return_val_if_fail (p == pdata[i - 1], NULL); - - /* move element to the top */ - while ((par = PARENT (i))) { - parent = pdata[par - 1]; - pdata[par - 1] = p; - pdata[i - 1] = parent; - p->pos = par; - parent->pos = i; - i = par; - } - - gts_eheap_remove_top (heap, NULL); - - return data; -} - -/** - * gts_eheap_decrease_key: - * @heap: a #GtsEHeap. - * @p: a #GtsEHeapPair. - * @new_key: the new value of the key for this element. Must be smaller than - * the current key. - * - * Decreases the value of the key of the element at position @p. - */ -void gts_eheap_decrease_key (GtsEHeap * heap, - GtsEHeapPair * p, - gdouble new_key) -{ - guint i; - - g_return_if_fail (heap != NULL); - g_return_if_fail (p != NULL); - - i = p->pos; - g_return_if_fail (i > 0 && i <= heap->elts->len); - g_return_if_fail (p == heap->elts->pdata[i - 1]); - - g_return_if_fail (new_key <= p->key); - - p->key = new_key; - if (!heap->frozen) - sift_up (heap, i); -} - -/** - * gts_eheap_freeze: - * @heap: a #GtsEHeap. - * - * Freezes the heap. Any subsequent operation will not preserve the heap - * property. Used in conjunction with gts_eheap_insert() and gts_eheap_thaw() - * to create a heap in O(n) time. - */ -void gts_eheap_freeze (GtsEHeap * heap) -{ - g_return_if_fail (heap != NULL); - - heap->frozen = TRUE; -} - -/** - * gts_eheap_size: - * @heap: a #GtsEHeap. - * - * Returns: the number of items in @heap. - */ -guint gts_eheap_size (GtsEHeap * heap) -{ - g_return_val_if_fail (heap != NULL, 0); - - return heap->elts->len; -} - -/** - * gts_eheap_update: - * @heap: a #GtsEHeap. - * - * Updates the key of each element of @heap and reorders it. - */ -void gts_eheap_update (GtsEHeap * heap) -{ - guint i, len; - GtsEHeapPair ** pairs; - gpointer data; - GtsKeyFunc func; - - g_return_if_fail (heap != NULL); - g_return_if_fail (heap->func != NULL); - - heap->frozen = TRUE; - - len = heap->elts->len; - pairs = (GtsEHeapPair **) heap->elts->pdata; - data = heap->data; - func = heap->func; - - for (i = 0; i < len; i++) { - GtsEHeapPair * pair = pairs[i]; - pair->key = (*func) (pair->data, data); - } - - gts_eheap_thaw (heap); -} - -/** - * gts_eheap_key: - * @heap: a #GtsEHeap. - * @p: a pointer to be tested; - * - * Returns: the value of the key for pointer @p. - */ -gdouble gts_eheap_key (GtsEHeap * heap, gpointer p) -{ - g_return_val_if_fail (heap != NULL, 0.); - g_return_val_if_fail (heap->func != NULL, 0.); - - return (* heap->func) (p, heap->data); -} - -/** - * gts_eheap_randomized: - * @heap: a #GtsEHeap. - * @randomized: whether @heap should be randomized. - */ -void gts_eheap_randomized (GtsEHeap * heap, gboolean randomized) -{ - g_return_if_fail (heap != NULL); - - heap->randomized = randomized; -} Index: trunk/src/3rd/gts/fifo.c =================================================================== --- trunk/src/3rd/gts/fifo.c (revision 1064) +++ trunk/src/3rd/gts/fifo.c (nonexistent) @@ -1,192 +0,0 @@ -/* GTS - Library for the manipulation of triangulated surfaces - * Copyright (C) 1999 Stéphane Popinet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include "gts.h" - -struct _GtsFifo { - GList * head; - GList * tail; -}; - -/** - * gts_fifo_new: - * - * Returns: a new #GtsFifo. - */ -GtsFifo * gts_fifo_new () -{ - GtsFifo * fifo = g_malloc (sizeof (GtsFifo)); - - fifo->head = fifo->tail = NULL; - return fifo; -} - -/** - * gts_fifo_write: - * @fifo: a #GtsFifo. - * @fp: a file pointer. - * - * Writes the content of @fifo in @fp. - */ -void gts_fifo_write (GtsFifo * fifo, FILE * fp) -{ - GList * i; - - g_return_if_fail (fifo != NULL); - g_return_if_fail (fp != NULL); - - fprintf (fp, "["); - i = fifo->head; - while (i) { - fprintf (fp, "%p ", i->data); - i = i->next; - } - fprintf (fp, "]"); -} - -/** - * gts_fifo_push: - * @fifo: a #GtsFifo. - * @data: data to add to @fifo. - * - * Push @data into @fifo. - */ -void gts_fifo_push (GtsFifo * fifo, gpointer data) -{ - g_return_if_fail (fifo != NULL); - - fifo->head = g_list_prepend (fifo->head, data); - if (fifo->tail == NULL) - fifo->tail = fifo->head; -} - -/** - * gts_fifo_pop: - * @fifo: a #GtsFifo. - * - * Removes the first element from @fifo. - * - * Returns: the first element in @fifo or %NULL if @fifo is empty. - */ -gpointer gts_fifo_pop (GtsFifo * fifo) -{ - gpointer data; - GList * tail; - - g_return_val_if_fail (fifo != NULL, NULL); - - if (fifo->tail == NULL) - return NULL; - tail = fifo->tail->prev; - data = fifo->tail->data; - fifo->head = g_list_remove_link (fifo->head, fifo->tail); - g_list_free_1 (fifo->tail); - fifo->tail = tail; - return data; -} - -/** - * gts_fifo_top: - * @fifo: a #GtsFifo. - * - * Returns: the first element in @fifo or %NULL if @fifo is empty. - */ -gpointer gts_fifo_top (GtsFifo * fifo) -{ - g_return_val_if_fail (fifo != NULL, NULL); - - if (fifo->tail == NULL) - return NULL; - return fifo->tail->data; -} - -/** - * gts_fifo_size: - * @fifo: a #GtsFifo. - * - * Returns: the number of elements in @fifo. - */ -guint gts_fifo_size (GtsFifo * fifo) -{ - g_return_val_if_fail (fifo != NULL, 0); - - return g_list_length (fifo->head); -} - -/** - * gts_fifo_destroy: - * @fifo: a #GtsFifo. - * - * Frees all the memory allocated for @fifo. - */ -void gts_fifo_destroy (GtsFifo * fifo) -{ - g_return_if_fail (fifo != NULL); - g_list_free (fifo->head); - g_free (fifo); -} - -/** - * gts_fifo_is_empty: - * @fifo: a #GtsFifo. - * - * Returns: %TRUE if @fifo is empty, %FALSE otherwise. - */ -gboolean gts_fifo_is_empty (GtsFifo * fifo) -{ - g_return_val_if_fail (fifo != NULL, TRUE); - - return (fifo->head == NULL); -} - -/** - * gts_fifo_foreach: - * @fifo: a #GtsFifo. - * @func: a #GtsFunc. - * @data: user data to be passed to @func. - * - * Calls @func in order for each item in @fifo, passing @data. - */ -void gts_fifo_foreach (GtsFifo * fifo, GtsFunc func, gpointer data) -{ - GList * i; - - g_return_if_fail (fifo != NULL); - g_return_if_fail (func != NULL); - - i = fifo->tail; - while (i) { - (* func) (i->data, data); - i = i->prev; - } -} - -/** - * gts_fifo_reverse: - * @fifo: a #GtsFifo. - * - * Reverses the order of elements in @fifo. - */ -void gts_fifo_reverse (GtsFifo * fifo) -{ - g_return_if_fail (fifo != NULL); - - fifo->tail = fifo->head; - fifo->head = g_list_reverse (fifo->head); -} Index: trunk/src/3rd/gts/predicates.c =================================================================== --- trunk/src/3rd/gts/predicates.c (revision 1064) +++ trunk/src/3rd/gts/predicates.c (nonexistent) @@ -1,2742 +0,0 @@ -/*****************************************************************************/ -/* */ -/* Routines for Arbitrary Precision Floating-point Arithmetic */ -/* and Fast Robust Geometric Predicates */ -/* (predicates.c) */ -/* */ -/* May 18, 1996 */ -/* */ -/* Placed in the public domain by */ -/* Jonathan Richard Shewchuk */ -/* School of Computer Science */ -/* Carnegie Mellon University */ -/* 5000 Forbes Avenue */ -/* Pittsburgh, Pennsylvania 15213-3891 */ -/* jrs@cs.cmu.edu */ -/* */ -/* This file contains C implementation of algorithms for exact addition */ -/* and multiplication of floating-point numbers, and predicates for */ -/* robustly performing the orientation and incircle tests used in */ -/* computational geometry. The algorithms and underlying theory are */ -/* described in Jonathan Richard Shewchuk. "Adaptive Precision Floating- */ -/* Point Arithmetic and Fast Robust Geometric Predicates." Technical */ -/* Report CMU-CS-96-140, School of Computer Science, Carnegie Mellon */ -/* University, Pittsburgh, Pennsylvania, May 1996. (Submitted to */ -/* Discrete & Computational Geometry.) */ -/* */ -/* This file, the paper listed above, and other information are available */ -/* from the Web page http://www.cs.cmu.edu/~quake/robust.html . */ -/* */ -/*****************************************************************************/ - -/*****************************************************************************/ -/* */ -/* Using this code: */ -/* */ -/* First, read the short or long version of the paper (from the Web page */ -/* above). */ -/* */ -/* Be sure to call exactinit() once, before calling any of the arithmetic */ -/* functions or geometric predicates. Also be sure to turn on the */ -/* optimizer when compiling this file. */ -/* */ -/* */ -/* Several geometric predicates are defined. Their parameters are all */ -/* points. Each point is an array of two or three floating-point */ -/* numbers. The geometric predicates, described in the papers, are */ -/* */ -/* orient2d(pa, pb, pc) */ -/* orient2dfast(pa, pb, pc) */ -/* orient3d(pa, pb, pc, pd) */ -/* orient3dfast(pa, pb, pc, pd) */ -/* incircle(pa, pb, pc, pd) */ -/* incirclefast(pa, pb, pc, pd) */ -/* insphere(pa, pb, pc, pd, pe) */ -/* inspherefast(pa, pb, pc, pd, pe) */ -/* */ -/* Those with suffix "fast" are approximate, non-robust versions. Those */ -/* without the suffix are adaptive precision, robust versions. There */ -/* are also versions with the suffices "exact" and "slow", which are */ -/* non-adaptive, exact arithmetic versions, which I use only for timings */ -/* in my arithmetic papers. */ -/* */ -/* */ -/* An expansion is represented by an array of floating-point numbers, */ -/* sorted from smallest to largest magnitude (possibly with interspersed */ -/* zeros). The length of each expansion is stored as a separate integer, */ -/* and each arithmetic function returns an integer which is the length */ -/* of the expansion it created. */ -/* */ -/* Several arithmetic functions are defined. Their parameters are */ -/* */ -/* e, f Input expansions */ -/* elen, flen Lengths of input expansions (must be >= 1) */ -/* h Output expansion */ -/* b Input scalar */ -/* */ -/* The arithmetic functions are */ -/* */ -/* grow_expansion(elen, e, b, h) */ -/* grow_expansion_zeroelim(elen, e, b, h) */ -/* expansion_sum(elen, e, flen, f, h) */ -/* expansion_sum_zeroelim1(elen, e, flen, f, h) */ -/* expansion_sum_zeroelim2(elen, e, flen, f, h) */ -/* fast_expansion_sum(elen, e, flen, f, h) */ -/* fast_expansion_sum_zeroelim(elen, e, flen, f, h) */ -/* linear_expansion_sum(elen, e, flen, f, h) */ -/* linear_expansion_sum_zeroelim(elen, e, flen, f, h) */ -/* scale_expansion(elen, e, b, h) */ -/* scale_expansion_zeroelim(elen, e, b, h) */ -/* compress(elen, e, h) */ -/* */ -/* All of these are described in the long version of the paper; some are */ -/* described in the short version. All return an integer that is the */ -/* length of h. Those with suffix _zeroelim perform zero elimination, */ -/* and are recommended over their counterparts. The procedure */ -/* fast_expansion_sum_zeroelim() (or linear_expansion_sum_zeroelim() on */ -/* processors that do not use the round-to-even tiebreaking rule) is */ -/* recommended over expansion_sum_zeroelim(). Each procedure has a */ -/* little note next to it (in the code below) that tells you whether or */ -/* not the output expansion may be the same array as one of the input */ -/* expansions. */ -/* */ -/* */ -/* If you look around below, you'll also find macros for a bunch of */ -/* simple unrolled arithmetic operations, and procedures for printing */ -/* expansions (commented out because they don't work with all C */ -/* compilers) and for generating random floating-point numbers whose */ -/* significand bits are all random. Most of the macros have undocumented */ -/* requirements that certain of their parameters should not be the same */ -/* variable; for safety, better to make sure all the parameters are */ -/* distinct variables. Feel free to send email to jrs@cs.cmu.edu if you */ -/* have questions. */ -/* */ -/*****************************************************************************/ - -#include -#include -#include -#include "predicates.h" - -/* Use header file generated automatically by predicates_init. */ -//#define USE_PREDICATES_INIT - -#ifdef USE_PREDICATES_INIT -#include "predicates_init.h" -#endif /* USE_PREDICATES_INIT */ - -/* FPU control. We MUST have only double precision (not extended precision) */ -#include "rounding.h" - -/* On some machines, the exact arithmetic routines might be defeated by the */ -/* use of internal extended precision floating-point registers. Sometimes */ -/* this problem can be fixed by defining certain values to be volatile, */ -/* thus forcing them to be stored to memory and rounded off. This isn't */ -/* a great solution, though, as it slows the arithmetic down. */ -/* */ -/* To try this out, write "#define INEXACT volatile" below. Normally, */ -/* however, INEXACT should be defined to be nothing. ("#define INEXACT".) */ - -#define INEXACT /* Nothing */ -/* #define INEXACT volatile */ - -#define REAL double /* float or double */ -#define REALPRINT doubleprint -#define REALRAND doublerand -#define NARROWRAND narrowdoublerand -#define UNIFORMRAND uniformdoublerand - -/* Which of the following two methods of finding the absolute values is */ -/* fastest is compiler-dependent. A few compilers can inline and optimize */ -/* the fabs() call; but most will incur the overhead of a function call, */ -/* which is disastrously slow. A faster way on IEEE machines might be to */ -/* mask the appropriate bit, but that's difficult to do in C. */ - -#define Absolute(a) ((a) >= 0.0 ? (a) : -(a)) -/* #define Absolute(a) fabs(a) */ - -/* Many of the operations are broken up into two pieces, a main part that */ -/* performs an approximate operation, and a "tail" that computes the */ -/* roundoff error of that operation. */ -/* */ -/* The operations Fast_Two_Sum(), Fast_Two_Diff(), Two_Sum(), Two_Diff(), */ -/* Split(), and Two_Product() are all implemented as described in the */ -/* reference. Each of these macros requires certain variables to be */ -/* defined in the calling routine. The variables `bvirt', `c', `abig', */ -/* `_i', `_j', `_k', `_l', `_m', and `_n' are declared `INEXACT' because */ -/* they store the result of an operation that may incur roundoff error. */ -/* The input parameter `x' (or the highest numbered `x_' parameter) must */ -/* also be declared `INEXACT'. */ - -#define Fast_Two_Sum_Tail(a, b, x, y) \ - bvirt = x - a; \ - y = b - bvirt - -#define Fast_Two_Sum(a, b, x, y) \ - x = (REAL) (a + b); \ - Fast_Two_Sum_Tail(a, b, x, y) - -#define Fast_Two_Diff_Tail(a, b, x, y) \ - bvirt = a - x; \ - y = bvirt - b - -#define Fast_Two_Diff(a, b, x, y) \ - x = (REAL) (a - b); \ - Fast_Two_Diff_Tail(a, b, x, y) - -#define Two_Sum_Tail(a, b, x, y) \ - bvirt = (REAL) (x - a); \ - avirt = x - bvirt; \ - bround = b - bvirt; \ - around = a - avirt; \ - y = around + bround - -#define Two_Sum(a, b, x, y) \ - x = (REAL) (a + b); \ - Two_Sum_Tail(a, b, x, y) - -#define Two_Diff_Tail(a, b, x, y) \ - bvirt = (REAL) (a - x); \ - avirt = x + bvirt; \ - bround = bvirt - b; \ - around = a - avirt; \ - y = around + bround - -#define Two_Diff(a, b, x, y) \ - x = (REAL) (a - b); \ - Two_Diff_Tail(a, b, x, y) - -#define Split(a, ahi, alo) \ - c = (REAL) (splitter * a); \ - abig = (REAL) (c - a); \ - ahi = c - abig; \ - alo = a - ahi - -#define Two_Product_Tail(a, b, x, y) \ - Split(a, ahi, alo); \ - Split(b, bhi, blo); \ - err1 = x - (ahi * bhi); \ - err2 = err1 - (alo * bhi); \ - err3 = err2 - (ahi * blo); \ - y = (alo * blo) - err3 - -#define Two_Product(a, b, x, y) \ - x = (REAL) (a * b); \ - Two_Product_Tail(a, b, x, y) - -/* Two_Product_Presplit() is Two_Product() where one of the inputs has */ -/* already been split. Avoids redundant splitting. */ - -#define Two_Product_Presplit(a, b, bhi, blo, x, y) \ - x = (REAL) (a * b); \ - Split(a, ahi, alo); \ - err1 = x - (ahi * bhi); \ - err2 = err1 - (alo * bhi); \ - err3 = err2 - (ahi * blo); \ - y = (alo * blo) - err3 - -/* Two_Product_2Presplit() is Two_Product() where both of the inputs have */ -/* already been split. Avoids redundant splitting. */ - -#define Two_Product_2Presplit(a, ahi, alo, b, bhi, blo, x, y) \ - x = (REAL) (a * b); \ - err1 = x - (ahi * bhi); \ - err2 = err1 - (alo * bhi); \ - err3 = err2 - (ahi * blo); \ - y = (alo * blo) - err3 - -/* Square() can be done more quickly than Two_Product(). */ - -#define Square_Tail(a, x, y) \ - Split(a, ahi, alo); \ - err1 = x - (ahi * ahi); \ - err3 = err1 - ((ahi + ahi) * alo); \ - y = (alo * alo) - err3 - -#define Square(a, x, y) \ - x = (REAL) (a * a); \ - Square_Tail(a, x, y) - -/* Macros for summing expansions of various fixed lengths. These are all */ -/* unrolled versions of Expansion_Sum(). */ - -#define Two_One_Sum(a1, a0, b, x2, x1, x0) \ - Two_Sum(a0, b , _i, x0); \ - Two_Sum(a1, _i, x2, x1) - -#define Two_One_Diff(a1, a0, b, x2, x1, x0) \ - Two_Diff(a0, b , _i, x0); \ - Two_Sum( a1, _i, x2, x1) - -#define Two_Two_Sum(a1, a0, b1, b0, x3, x2, x1, x0) \ - Two_One_Sum(a1, a0, b0, _j, _0, x0); \ - Two_One_Sum(_j, _0, b1, x3, x2, x1) - -#define Two_Two_Diff(a1, a0, b1, b0, x3, x2, x1, x0) \ - Two_One_Diff(a1, a0, b0, _j, _0, x0); \ - Two_One_Diff(_j, _0, b1, x3, x2, x1) - -#define Four_One_Sum(a3, a2, a1, a0, b, x4, x3, x2, x1, x0) \ - Two_One_Sum(a1, a0, b , _j, x1, x0); \ - Two_One_Sum(a3, a2, _j, x4, x3, x2) - -#define Four_Two_Sum(a3, a2, a1, a0, b1, b0, x5, x4, x3, x2, x1, x0) \ - Four_One_Sum(a3, a2, a1, a0, b0, _k, _2, _1, _0, x0); \ - Four_One_Sum(_k, _2, _1, _0, b1, x5, x4, x3, x2, x1) - -#define Four_Four_Sum(a3, a2, a1, a0, b4, b3, b1, b0, x7, x6, x5, x4, x3, x2, \ - x1, x0) \ - Four_Two_Sum(a3, a2, a1, a0, b1, b0, _l, _2, _1, _0, x1, x0); \ - Four_Two_Sum(_l, _2, _1, _0, b4, b3, x7, x6, x5, x4, x3, x2) - -#define Eight_One_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b, x8, x7, x6, x5, x4, \ - x3, x2, x1, x0) \ - Four_One_Sum(a3, a2, a1, a0, b , _j, x3, x2, x1, x0); \ - Four_One_Sum(a7, a6, a5, a4, _j, x8, x7, x6, x5, x4) - -#define Eight_Two_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b1, b0, x9, x8, x7, \ - x6, x5, x4, x3, x2, x1, x0) \ - Eight_One_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b0, _k, _6, _5, _4, _3, _2, \ - _1, _0, x0); \ - Eight_One_Sum(_k, _6, _5, _4, _3, _2, _1, _0, b1, x9, x8, x7, x6, x5, x4, \ - x3, x2, x1) - -#define Eight_Four_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b4, b3, b1, b0, x11, \ - x10, x9, x8, x7, x6, x5, x4, x3, x2, x1, x0) \ - Eight_Two_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b1, b0, _l, _6, _5, _4, _3, \ - _2, _1, _0, x1, x0); \ - Eight_Two_Sum(_l, _6, _5, _4, _3, _2, _1, _0, b4, b3, x11, x10, x9, x8, \ - x7, x6, x5, x4, x3, x2) - -/* Macros for multiplying expansions of various fixed lengths. */ - -#define Two_One_Product(a1, a0, b, x3, x2, x1, x0) \ - Split(b, bhi, blo); \ - Two_Product_Presplit(a0, b, bhi, blo, _i, x0); \ - Two_Product_Presplit(a1, b, bhi, blo, _j, _0); \ - Two_Sum(_i, _0, _k, x1); \ - Fast_Two_Sum(_j, _k, x3, x2) - -#define Four_One_Product(a3, a2, a1, a0, b, x7, x6, x5, x4, x3, x2, x1, x0) \ - Split(b, bhi, blo); \ - Two_Product_Presplit(a0, b, bhi, blo, _i, x0); \ - Two_Product_Presplit(a1, b, bhi, blo, _j, _0); \ - Two_Sum(_i, _0, _k, x1); \ - Fast_Two_Sum(_j, _k, _i, x2); \ - Two_Product_Presplit(a2, b, bhi, blo, _j, _0); \ - Two_Sum(_i, _0, _k, x3); \ - Fast_Two_Sum(_j, _k, _i, x4); \ - Two_Product_Presplit(a3, b, bhi, blo, _j, _0); \ - Two_Sum(_i, _0, _k, x5); \ - Fast_Two_Sum(_j, _k, x7, x6) - -#define Two_Two_Product(a1, a0, b1, b0, x7, x6, x5, x4, x3, x2, x1, x0) \ - Split(a0, a0hi, a0lo); \ - Split(b0, bhi, blo); \ - Two_Product_2Presplit(a0, a0hi, a0lo, b0, bhi, blo, _i, x0); \ - Split(a1, a1hi, a1lo); \ - Two_Product_2Presplit(a1, a1hi, a1lo, b0, bhi, blo, _j, _0); \ - Two_Sum(_i, _0, _k, _1); \ - Fast_Two_Sum(_j, _k, _l, _2); \ - Split(b1, bhi, blo); \ - Two_Product_2Presplit(a0, a0hi, a0lo, b1, bhi, blo, _i, _0); \ - Two_Sum(_1, _0, _k, x1); \ - Two_Sum(_2, _k, _j, _1); \ - Two_Sum(_l, _j, _m, _2); \ - Two_Product_2Presplit(a1, a1hi, a1lo, b1, bhi, blo, _j, _0); \ - Two_Sum(_i, _0, _n, _0); \ - Two_Sum(_1, _0, _i, x2); \ - Two_Sum(_2, _i, _k, _1); \ - Two_Sum(_m, _k, _l, _2); \ - Two_Sum(_j, _n, _k, _0); \ - Two_Sum(_1, _0, _j, x3); \ - Two_Sum(_2, _j, _i, _1); \ - Two_Sum(_l, _i, _m, _2); \ - Two_Sum(_1, _k, _i, x4); \ - Two_Sum(_2, _i, _k, x5); \ - Two_Sum(_m, _k, x7, x6) - -/* An expansion of length two can be squared more quickly than finding the */ -/* product of two different expansions of length two, and the result is */ -/* guaranteed to have no more than six (rather than eight) components. */ - -#define Two_Square(a1, a0, x5, x4, x3, x2, x1, x0) \ - Square(a0, _j, x0); \ - _0 = a0 + a0; \ - Two_Product(a1, _0, _k, _1); \ - Two_One_Sum(_k, _1, _j, _l, _2, x1); \ - Square(a1, _j, _1); \ - Two_Two_Sum(_j, _1, _l, _2, x5, x4, x3, x2) - -#ifndef USE_PREDICATES_INIT - -static REAL splitter; /* = 2^ceiling(p / 2) + 1. Used to split floats in half. */ -/* A set of coefficients used to calculate maximum roundoff errors. */ -static REAL resulterrbound; -static REAL ccwerrboundA, ccwerrboundB, ccwerrboundC; -static REAL o3derrboundA, o3derrboundB, o3derrboundC; -static REAL iccerrboundA, iccerrboundB, iccerrboundC; -static REAL isperrboundA, isperrboundB, isperrboundC; - -void -gts_predicates_init() -{ - double half = 0.5; - double check = 1.0, lastcheck; - int every_other = 1; - /* epsilon = 2^(-p). Used to estimate roundoff errors. */ - double epsilon = 1.0; - - FPU_ROUND_DOUBLE; - - splitter = 1.; - - /* Repeatedly divide `epsilon' by two until it is too small to add to */ - /* one without causing roundoff. (Also check if the sum is equal to */ - /* the previous sum, for machines that round up instead of using exact */ - /* rounding. Not that this library will work on such machines anyway). */ - do { - lastcheck = check; - epsilon *= half; - if (every_other) { - splitter *= 2.0; - } - every_other = !every_other; - check = 1.0 + epsilon; - } while ((check != 1.0) && (check != lastcheck)); - splitter += 1.0; - /* Error bounds for orientation and incircle tests. */ - resulterrbound = (3.0 + 8.0 * epsilon) * epsilon; - ccwerrboundA = (3.0 + 16.0 * epsilon) * epsilon; - ccwerrboundB = (2.0 + 12.0 * epsilon) * epsilon; - ccwerrboundC = (9.0 + 64.0 * epsilon) * epsilon * epsilon; - o3derrboundA = (7.0 + 56.0 * epsilon) * epsilon; - o3derrboundB = (3.0 + 28.0 * epsilon) * epsilon; - o3derrboundC = (26.0 + 288.0 * epsilon) * epsilon * epsilon; - iccerrboundA = (10.0 + 96.0 * epsilon) * epsilon; - iccerrboundB = (4.0 + 48.0 * epsilon) * epsilon; - iccerrboundC = (44.0 + 576.0 * epsilon) * epsilon * epsilon; - isperrboundA = (16.0 + 224.0 * epsilon) * epsilon; - isperrboundB = (5.0 + 72.0 * epsilon) * epsilon; - isperrboundC = (71.0 + 1408.0 * epsilon) * epsilon * epsilon; - - - FPU_RESTORE; -} - -#endif /* USE_PREDICATES_INIT */ - -/*****************************************************************************/ -/* */ -/* doubleprint() Print the bit representation of a double. */ -/* */ -/* Useful for debugging exact arithmetic routines. */ -/* */ -/*****************************************************************************/ - -/* -void doubleprint(number) -double number; -{ - unsigned long long no; - unsigned long long sign, expo; - int exponent; - int i, bottomi; - - no = *(unsigned long long *) &number; - sign = no & 0x8000000000000000ll; - expo = (no >> 52) & 0x7ffll; - exponent = (int) expo; - exponent = exponent - 1023; - if (sign) { - printf("-"); - } else { - printf(" "); - } - if (exponent == -1023) { - printf( - "0.0000000000000000000000000000000000000000000000000000_ ( )"); - } else { - printf("1."); - bottomi = -1; - for (i = 0; i < 52; i++) { - if (no & 0x0008000000000000ll) { - printf("1"); - bottomi = i; - } else { - printf("0"); - } - no <<= 1; - } - printf("_%d (%d)", exponent, exponent - 1 - bottomi); - } -} -*/ - -/*****************************************************************************/ -/* */ -/* floatprint() Print the bit representation of a float. */ -/* */ -/* Useful for debugging exact arithmetic routines. */ -/* */ -/*****************************************************************************/ - -/* -void floatprint(number) -float number; -{ - unsigned no; - unsigned sign, expo; - int exponent; - int i, bottomi; - - no = *(unsigned *) &number; - sign = no & 0x80000000; - expo = (no >> 23) & 0xff; - exponent = (int) expo; - exponent = exponent - 127; - if (sign) { - printf("-"); - } else { - printf(" "); - } - if (exponent == -127) { - printf("0.00000000000000000000000_ ( )"); - } else { - printf("1."); - bottomi = -1; - for (i = 0; i < 23; i++) { - if (no & 0x00400000) { - printf("1"); - bottomi = i; - } else { - printf("0"); - } - no <<= 1; - } - printf("_%3d (%3d)", exponent, exponent - 1 - bottomi); - } -} -*/ - -/*****************************************************************************/ -/* */ -/* expansion_print() Print the bit representation of an expansion. */ -/* */ -/* Useful for debugging exact arithmetic routines. */ -/* */ -/*****************************************************************************/ - -/* -void expansion_print(elen, e) -int elen; -REAL *e; -{ - int i; - - for (i = elen - 1; i >= 0; i--) { - REALPRINT(e[i]); - if (i > 0) { - printf(" +\n"); - } else { - printf("\n"); - } - } -} -*/ - -/*****************************************************************************/ -/* */ -/* doublerand() Generate a double with random 53-bit significand and a */ -/* random exponent in [0, 511]. */ -/* */ -/*****************************************************************************/ - -/* -static double doublerand() -{ - double result; - double expo; - long a, b, c; - long i; - - a = random(); - b = random(); - c = random(); - result = (double) (a - 1073741824) * 8388608.0 + (double) (b >> 8); - for (i = 512, expo = 2; i <= 131072; i *= 2, expo = expo * expo) { - if (c & i) { - result *= expo; - } - } - return result; -} -*/ - -/*****************************************************************************/ -/* */ -/* narrowdoublerand() Generate a double with random 53-bit significand */ -/* and a random exponent in [0, 7]. */ -/* */ -/*****************************************************************************/ - -/* -static double narrowdoublerand() -{ - double result; - double expo; - long a, b, c; - long i; - - a = random(); - b = random(); - c = random(); - result = (double) (a - 1073741824) * 8388608.0 + (double) (b >> 8); - for (i = 512, expo = 2; i <= 2048; i *= 2, expo = expo * expo) { - if (c & i) { - result *= expo; - } - } - return result; -} -*/ - -/*****************************************************************************/ -/* */ -/* uniformdoublerand() Generate a double with random 53-bit significand. */ -/* */ -/*****************************************************************************/ - -/* -static double uniformdoublerand() -{ - double result; - long a, b; - - a = random(); - b = random(); - result = (double) (a - 1073741824) * 8388608.0 + (double) (b >> 8); - return result; -} -*/ - -/*****************************************************************************/ -/* */ -/* floatrand() Generate a float with random 24-bit significand and a */ -/* random exponent in [0, 63]. */ -/* */ -/*****************************************************************************/ - -/* -static float floatrand() -{ - float result; - float expo; - long a, c; - long i; - - a = random(); - c = random(); - result = (float) ((a - 1073741824) >> 6); - for (i = 512, expo = 2; i <= 16384; i *= 2, expo = expo * expo) { - if (c & i) { - result *= expo; - } - } - return result; -} -*/ - -/*****************************************************************************/ -/* */ -/* narrowfloatrand() Generate a float with random 24-bit significand and */ -/* a random exponent in [0, 7]. */ -/* */ -/*****************************************************************************/ - -/* -static float narrowfloatrand() -{ - float result; - float expo; - long a, c; - long i; - - a = random(); - c = random(); - result = (float) ((a - 1073741824) >> 6); - for (i = 512, expo = 2; i <= 2048; i *= 2, expo = expo * expo) { - if (c & i) { - result *= expo; - } - } - return result; -} -*/ - -/*****************************************************************************/ -/* */ -/* uniformfloatrand() Generate a float with random 24-bit significand. */ -/* */ -/*****************************************************************************/ - -/* -static float uniformfloatrand() -{ - float result; - long a; - - a = random(); - result = (float) ((a - 1073741824) >> 6); - return result; -} -*/ - -/*****************************************************************************/ -/* */ -/* fast_expansion_sum_zeroelim() Sum two expansions, eliminating zero */ -/* components from the output expansion. */ -/* */ -/* Sets h = e + f. See the long version of my paper for details. */ -/* */ -/* If round-to-even is used (as with IEEE 754), maintains the strongly */ -/* nonoverlapping property. (That is, if e is strongly nonoverlapping, h */ -/* will be also.) Does NOT maintain the nonoverlapping or nonadjacent */ -/* properties. */ -/* */ -/*****************************************************************************/ - -static int fast_expansion_sum_zeroelim(int elen, REAL *e, - int flen, REAL *f, REAL *h) - /* h cannot be e or f. */ -{ - REAL Q; - INEXACT REAL Qnew; - INEXACT REAL hh; - INEXACT REAL bvirt; - REAL avirt, bround, around; - int eindex, findex, hindex; - REAL enow, fnow; - - enow = e[0]; - fnow = f[0]; - eindex = findex = 0; - if ((fnow > enow) == (fnow > -enow)) { - Q = enow; - enow = e[++eindex]; - } else { - Q = fnow; - fnow = f[++findex]; - } - hindex = 0; - if ((eindex < elen) && (findex < flen)) { - if ((fnow > enow) == (fnow > -enow)) { - Fast_Two_Sum(enow, Q, Qnew, hh); - enow = e[++eindex]; - } else { - Fast_Two_Sum(fnow, Q, Qnew, hh); - fnow = f[++findex]; - } - Q = Qnew; - if (hh != 0.0) { - h[hindex++] = hh; - } - while ((eindex < elen) && (findex < flen)) { - if ((fnow > enow) == (fnow > -enow)) { - Two_Sum(Q, enow, Qnew, hh); - enow = e[++eindex]; - } else { - Two_Sum(Q, fnow, Qnew, hh); - fnow = f[++findex]; - } - Q = Qnew; - if (hh != 0.0) { - h[hindex++] = hh; - } - } - } - while (eindex < elen) { - Two_Sum(Q, enow, Qnew, hh); - enow = e[++eindex]; - Q = Qnew; - if (hh != 0.0) { - h[hindex++] = hh; - } - } - while (findex < flen) { - Two_Sum(Q, fnow, Qnew, hh); - fnow = f[++findex]; - Q = Qnew; - if (hh != 0.0) { - h[hindex++] = hh; - } - } - if ((Q != 0.0) || (hindex == 0)) { - h[hindex++] = Q; - } - return hindex; -} - -/*****************************************************************************/ -/* */ -/* scale_expansion_zeroelim() Multiply an expansion by a scalar, */ -/* eliminating zero components from the */ -/* output expansion. */ -/* */ -/* Sets h = be. See either version of my paper for details. */ -/* */ -/* Maintains the nonoverlapping property. If round-to-even is used (as */ -/* with IEEE 754), maintains the strongly nonoverlapping and nonadjacent */ -/* properties as well. (That is, if e has one of these properties, so */ -/* will h.) */ -/* */ -/*****************************************************************************/ - -static int scale_expansion_zeroelim(int elen, REAL *e, REAL b, REAL *h) - /* e and h cannot be the same. */ -{ - INEXACT REAL Q, sum; - REAL hh; - INEXACT REAL product1; - REAL product0; - int eindex, hindex; - REAL enow; - INEXACT REAL bvirt; - REAL avirt, bround, around; - INEXACT REAL c; - INEXACT REAL abig; - REAL ahi, alo, bhi, blo; - REAL err1, err2, err3; - - Split(b, bhi, blo); - Two_Product_Presplit(e[0], b, bhi, blo, Q, hh); - hindex = 0; - if (hh != 0) { - h[hindex++] = hh; - } - for (eindex = 1; eindex < elen; eindex++) { - enow = e[eindex]; - Two_Product_Presplit(enow, b, bhi, blo, product1, product0); - Two_Sum(Q, product0, sum, hh); - if (hh != 0) { - h[hindex++] = hh; - } - Fast_Two_Sum(product1, sum, Q, hh); - if (hh != 0) { - h[hindex++] = hh; - } - } - if ((Q != 0.0) || (hindex == 0)) { - h[hindex++] = Q; - } - return hindex; -} - -/*****************************************************************************/ -/* */ -/* estimate() Produce a one-word estimate of an expansion's value. */ -/* */ -/* See either version of my paper for details. */ -/* */ -/*****************************************************************************/ - -static REAL estimate(int elen, REAL *e) -{ - REAL Q; - int eindex; - - Q = e[0]; - for (eindex = 1; eindex < elen; eindex++) { - Q += e[eindex]; - } - return Q; -} - -/*****************************************************************************/ -/* */ -/* orient2dfast() Approximate 2D orientation test. Nonrobust. */ -/* orient2dexact() Exact 2D orientation test. Robust. */ -/* orient2dslow() Another exact 2D orientation test. Robust. */ -/* orient2d() Adaptive exact 2D orientation test. Robust. */ -/* */ -/* Return a positive value if the points pa, pb, and pc occur */ -/* in counterclockwise order; a negative value if they occur */ -/* in clockwise order; and zero if they are collinear. The */ -/* result is also a rough approximation of twice the signed */ -/* area of the triangle defined by the three points. */ -/* */ -/* Only the first and last routine should be used; the middle two are for */ -/* timings. */ -/* */ -/* The last three use exact arithmetic to ensure a correct answer. The */ -/* result returned is the determinant of a matrix. In orient2d() only, */ -/* this determinant is computed adaptively, in the sense that exact */ -/* arithmetic is used only to the degree it is needed to ensure that the */ -/* returned value has the correct sign. Hence, orient2d() is usually quite */ -/* fast, but will run more slowly when the input points are collinear or */ -/* nearly so. */ -/* */ -/*****************************************************************************/ - -static REAL orient2dadapt(REAL *pa, REAL *pb, REAL *pc, REAL detsum) -{ - INEXACT REAL acx, acy, bcx, bcy; - REAL acxtail, acytail, bcxtail, bcytail; - INEXACT REAL detleft, detright; - REAL detlefttail, detrighttail; - REAL det, errbound; - REAL B[4], C1[8], C2[12], D[16]; - INEXACT REAL B3; - int C1length, C2length, Dlength; - REAL u[4]; - INEXACT REAL u3; - INEXACT REAL s1, t1; - REAL s0, t0; - - INEXACT REAL bvirt; - REAL avirt, bround, around; - INEXACT REAL c; - INEXACT REAL abig; - REAL ahi, alo, bhi, blo; - REAL err1, err2, err3; - INEXACT REAL _i, _j; - REAL _0; - - acx = (REAL) (pa[0] - pc[0]); - bcx = (REAL) (pb[0] - pc[0]); - acy = (REAL) (pa[1] - pc[1]); - bcy = (REAL) (pb[1] - pc[1]); - - Two_Product(acx, bcy, detleft, detlefttail); - Two_Product(acy, bcx, detright, detrighttail); - - Two_Two_Diff(detleft, detlefttail, detright, detrighttail, - B3, B[2], B[1], B[0]); - B[3] = B3; - - det = estimate(4, B); - errbound = ccwerrboundB * detsum; - if ((det >= errbound) || (-det >= errbound)) { - return det; - } - - Two_Diff_Tail(pa[0], pc[0], acx, acxtail); - Two_Diff_Tail(pb[0], pc[0], bcx, bcxtail); - Two_Diff_Tail(pa[1], pc[1], acy, acytail); - Two_Diff_Tail(pb[1], pc[1], bcy, bcytail); - - if ((acxtail == 0.0) && (acytail == 0.0) - && (bcxtail == 0.0) && (bcytail == 0.0)) { - return det; - } - - errbound = ccwerrboundC * detsum + resulterrbound * Absolute(det); - det += (acx * bcytail + bcy * acxtail) - - (acy * bcxtail + bcx * acytail); - if ((det >= errbound) || (-det >= errbound)) { - return det; - } - - Two_Product(acxtail, bcy, s1, s0); - Two_Product(acytail, bcx, t1, t0); - Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]); - u[3] = u3; - C1length = fast_expansion_sum_zeroelim(4, B, 4, u, C1); - - Two_Product(acx, bcytail, s1, s0); - Two_Product(acy, bcxtail, t1, t0); - Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]); - u[3] = u3; - C2length = fast_expansion_sum_zeroelim(C1length, C1, 4, u, C2); - - Two_Product(acxtail, bcytail, s1, s0); - Two_Product(acytail, bcxtail, t1, t0); - Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]); - u[3] = u3; - Dlength = fast_expansion_sum_zeroelim(C2length, C2, 4, u, D); - - return(D[Dlength - 1]); -} - -REAL orient2d(pa, pb, pc) -REAL *pa; -REAL *pb; -REAL *pc; -{ - REAL detleft, detright, det; - REAL detsum, errbound; - REAL orient; - - FPU_ROUND_DOUBLE; - - detleft = (pa[0] - pc[0]) * (pb[1] - pc[1]); - detright = (pa[1] - pc[1]) * (pb[0] - pc[0]); - det = detleft - detright; - - if (detleft > 0.0) { - if (detright <= 0.0) { - FPU_RESTORE; - return det; - } else { - detsum = detleft + detright; - } - } else if (detleft < 0.0) { - if (detright >= 0.0) { - FPU_RESTORE; - return det; - } else { - detsum = -detleft - detright; - } - } else { - FPU_RESTORE; - return det; - } - - errbound = ccwerrboundA * detsum; - if ((det >= errbound) || (-det >= errbound)) { - FPU_RESTORE; - return det; - } - - orient = orient2dadapt(pa, pb, pc, detsum); - FPU_RESTORE; - return orient; -} - -/*****************************************************************************/ -/* */ -/* orient3dfast() Approximate 3D orientation test. Nonrobust. */ -/* orient3dexact() Exact 3D orientation test. Robust. */ -/* orient3dslow() Another exact 3D orientation test. Robust. */ -/* orient3d() Adaptive exact 3D orientation test. Robust. */ -/* */ -/* Return a positive value if the point pd lies below the */ -/* plane passing through pa, pb, and pc; "below" is defined so */ -/* that pa, pb, and pc appear in counterclockwise order when */ -/* viewed from above the plane. Returns a negative value if */ -/* pd lies above the plane. Returns zero if the points are */ -/* coplanar. The result is also a rough approximation of six */ -/* times the signed volume of the tetrahedron defined by the */ -/* four points. */ -/* */ -/* Only the first and last routine should be used; the middle two are for */ -/* timings. */ -/* */ -/* The last three use exact arithmetic to ensure a correct answer. The */ -/* result returned is the determinant of a matrix. In orient3d() only, */ -/* this determinant is computed adaptively, in the sense that exact */ -/* arithmetic is used only to the degree it is needed to ensure that the */ -/* returned value has the correct sign. Hence, orient3d() is usually quite */ -/* fast, but will run more slowly when the input points are coplanar or */ -/* nearly so. */ -/* */ -/*****************************************************************************/ - -static REAL orient3dadapt(REAL *pa, REAL *pb, REAL *pc, REAL *pd, - REAL permanent) -{ - INEXACT REAL adx, bdx, cdx, ady, bdy, cdy, adz, bdz, cdz; - REAL det, errbound; - - INEXACT REAL bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1; - REAL bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0; - REAL bc[4], ca[4], ab[4]; - INEXACT REAL bc3, ca3, ab3; - REAL adet[8], bdet[8], cdet[8]; - int alen, blen, clen; - REAL abdet[16]; - int ablen; - REAL *finnow, *finother, *finswap; - REAL fin1[192], fin2[192]; - int finlength; - - REAL adxtail, bdxtail, cdxtail; - REAL adytail, bdytail, cdytail; - REAL adztail, bdztail, cdztail; - INEXACT REAL at_blarge, at_clarge; - INEXACT REAL bt_clarge, bt_alarge; - INEXACT REAL ct_alarge, ct_blarge; - REAL at_b[4], at_c[4], bt_c[4], bt_a[4], ct_a[4], ct_b[4]; - int at_blen, at_clen, bt_clen, bt_alen, ct_alen, ct_blen; - INEXACT REAL bdxt_cdy1, cdxt_bdy1, cdxt_ady1; - INEXACT REAL adxt_cdy1, adxt_bdy1, bdxt_ady1; - REAL bdxt_cdy0, cdxt_bdy0, cdxt_ady0; - REAL adxt_cdy0, adxt_bdy0, bdxt_ady0; - INEXACT REAL bdyt_cdx1, cdyt_bdx1, cdyt_adx1; - INEXACT REAL adyt_cdx1, adyt_bdx1, bdyt_adx1; - REAL bdyt_cdx0, cdyt_bdx0, cdyt_adx0; - REAL adyt_cdx0, adyt_bdx0, bdyt_adx0; - REAL bct[8], cat[8], abt[8]; - int bctlen, catlen, abtlen; - INEXACT REAL bdxt_cdyt1, cdxt_bdyt1, cdxt_adyt1; - INEXACT REAL adxt_cdyt1, adxt_bdyt1, bdxt_adyt1; - REAL bdxt_cdyt0, cdxt_bdyt0, cdxt_adyt0; - REAL adxt_cdyt0, adxt_bdyt0, bdxt_adyt0; - REAL u[4], v[12], w[16]; - INEXACT REAL u3; - int vlength, wlength; - REAL negate; - - INEXACT REAL bvirt; - REAL avirt, bround, around; - INEXACT REAL c; - INEXACT REAL abig; - REAL ahi, alo, bhi, blo; - REAL err1, err2, err3; - INEXACT REAL _i, _j, _k; - REAL _0; - - adx = (REAL) (pa[0] - pd[0]); - bdx = (REAL) (pb[0] - pd[0]); - cdx = (REAL) (pc[0] - pd[0]); - ady = (REAL) (pa[1] - pd[1]); - bdy = (REAL) (pb[1] - pd[1]); - cdy = (REAL) (pc[1] - pd[1]); - adz = (REAL) (pa[2] - pd[2]); - bdz = (REAL) (pb[2] - pd[2]); - cdz = (REAL) (pc[2] - pd[2]); - - Two_Product(bdx, cdy, bdxcdy1, bdxcdy0); - Two_Product(cdx, bdy, cdxbdy1, cdxbdy0); - Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]); - bc[3] = bc3; - alen = scale_expansion_zeroelim(4, bc, adz, adet); - - Two_Product(cdx, ady, cdxady1, cdxady0); - Two_Product(adx, cdy, adxcdy1, adxcdy0); - Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]); - ca[3] = ca3; - blen = scale_expansion_zeroelim(4, ca, bdz, bdet); - - Two_Product(adx, bdy, adxbdy1, adxbdy0); - Two_Product(bdx, ady, bdxady1, bdxady0); - Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]); - ab[3] = ab3; - clen = scale_expansion_zeroelim(4, ab, cdz, cdet); - - ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet); - finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1); - - det = estimate(finlength, fin1); - errbound = o3derrboundB * permanent; - if ((det >= errbound) || (-det >= errbound)) { - return det; - } - - Two_Diff_Tail(pa[0], pd[0], adx, adxtail); - Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail); - Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail); - Two_Diff_Tail(pa[1], pd[1], ady, adytail); - Two_Diff_Tail(pb[1], pd[1], bdy, bdytail); - Two_Diff_Tail(pc[1], pd[1], cdy, cdytail); - Two_Diff_Tail(pa[2], pd[2], adz, adztail); - Two_Diff_Tail(pb[2], pd[2], bdz, bdztail); - Two_Diff_Tail(pc[2], pd[2], cdz, cdztail); - - if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0) - && (adytail == 0.0) && (bdytail == 0.0) && (cdytail == 0.0) - && (adztail == 0.0) && (bdztail == 0.0) && (cdztail == 0.0)) { - return det; - } - - errbound = o3derrboundC * permanent + resulterrbound * Absolute(det); - det += (adz * ((bdx * cdytail + cdy * bdxtail) - - (bdy * cdxtail + cdx * bdytail)) - + adztail * (bdx * cdy - bdy * cdx)) - + (bdz * ((cdx * adytail + ady * cdxtail) - - (cdy * adxtail + adx * cdytail)) - + bdztail * (cdx * ady - cdy * adx)) - + (cdz * ((adx * bdytail + bdy * adxtail) - - (ady * bdxtail + bdx * adytail)) - + cdztail * (adx * bdy - ady * bdx)); - if ((det >= errbound) || (-det >= errbound)) { - return det; - } - - finnow = fin1; - finother = fin2; - - if (adxtail == 0.0) { - if (adytail == 0.0) { - at_b[0] = 0.0; - at_blen = 1; - at_c[0] = 0.0; - at_clen = 1; - } else { - negate = -adytail; - Two_Product(negate, bdx, at_blarge, at_b[0]); - at_b[1] = at_blarge; - at_blen = 2; - Two_Product(adytail, cdx, at_clarge, at_c[0]); - at_c[1] = at_clarge; - at_clen = 2; - } - } else { - if (adytail == 0.0) { - Two_Product(adxtail, bdy, at_blarge, at_b[0]); - at_b[1] = at_blarge; - at_blen = 2; - negate = -adxtail; - Two_Product(negate, cdy, at_clarge, at_c[0]); - at_c[1] = at_clarge; - at_clen = 2; - } else { - Two_Product(adxtail, bdy, adxt_bdy1, adxt_bdy0); - Two_Product(adytail, bdx, adyt_bdx1, adyt_bdx0); - Two_Two_Diff(adxt_bdy1, adxt_bdy0, adyt_bdx1, adyt_bdx0, - at_blarge, at_b[2], at_b[1], at_b[0]); - at_b[3] = at_blarge; - at_blen = 4; - Two_Product(adytail, cdx, adyt_cdx1, adyt_cdx0); - Two_Product(adxtail, cdy, adxt_cdy1, adxt_cdy0); - Two_Two_Diff(adyt_cdx1, adyt_cdx0, adxt_cdy1, adxt_cdy0, - at_clarge, at_c[2], at_c[1], at_c[0]); - at_c[3] = at_clarge; - at_clen = 4; - } - } - if (bdxtail == 0.0) { - if (bdytail == 0.0) { - bt_c[0] = 0.0; - bt_clen = 1; - bt_a[0] = 0.0; - bt_alen = 1; - } else { - negate = -bdytail; - Two_Product(negate, cdx, bt_clarge, bt_c[0]); - bt_c[1] = bt_clarge; - bt_clen = 2; - Two_Product(bdytail, adx, bt_alarge, bt_a[0]); - bt_a[1] = bt_alarge; - bt_alen = 2; - } - } else { - if (bdytail == 0.0) { - Two_Product(bdxtail, cdy, bt_clarge, bt_c[0]); - bt_c[1] = bt_clarge; - bt_clen = 2; - negate = -bdxtail; - Two_Product(negate, ady, bt_alarge, bt_a[0]); - bt_a[1] = bt_alarge; - bt_alen = 2; - } else { - Two_Product(bdxtail, cdy, bdxt_cdy1, bdxt_cdy0); - Two_Product(bdytail, cdx, bdyt_cdx1, bdyt_cdx0); - Two_Two_Diff(bdxt_cdy1, bdxt_cdy0, bdyt_cdx1, bdyt_cdx0, - bt_clarge, bt_c[2], bt_c[1], bt_c[0]); - bt_c[3] = bt_clarge; - bt_clen = 4; - Two_Product(bdytail, adx, bdyt_adx1, bdyt_adx0); - Two_Product(bdxtail, ady, bdxt_ady1, bdxt_ady0); - Two_Two_Diff(bdyt_adx1, bdyt_adx0, bdxt_ady1, bdxt_ady0, - bt_alarge, bt_a[2], bt_a[1], bt_a[0]); - bt_a[3] = bt_alarge; - bt_alen = 4; - } - } - if (cdxtail == 0.0) { - if (cdytail == 0.0) { - ct_a[0] = 0.0; - ct_alen = 1; - ct_b[0] = 0.0; - ct_blen = 1; - } else { - negate = -cdytail; - Two_Product(negate, adx, ct_alarge, ct_a[0]); - ct_a[1] = ct_alarge; - ct_alen = 2; - Two_Product(cdytail, bdx, ct_blarge, ct_b[0]); - ct_b[1] = ct_blarge; - ct_blen = 2; - } - } else { - if (cdytail == 0.0) { - Two_Product(cdxtail, ady, ct_alarge, ct_a[0]); - ct_a[1] = ct_alarge; - ct_alen = 2; - negate = -cdxtail; - Two_Product(negate, bdy, ct_blarge, ct_b[0]); - ct_b[1] = ct_blarge; - ct_blen = 2; - } else { - Two_Product(cdxtail, ady, cdxt_ady1, cdxt_ady0); - Two_Product(cdytail, adx, cdyt_adx1, cdyt_adx0); - Two_Two_Diff(cdxt_ady1, cdxt_ady0, cdyt_adx1, cdyt_adx0, - ct_alarge, ct_a[2], ct_a[1], ct_a[0]); - ct_a[3] = ct_alarge; - ct_alen = 4; - Two_Product(cdytail, bdx, cdyt_bdx1, cdyt_bdx0); - Two_Product(cdxtail, bdy, cdxt_bdy1, cdxt_bdy0); - Two_Two_Diff(cdyt_bdx1, cdyt_bdx0, cdxt_bdy1, cdxt_bdy0, - ct_blarge, ct_b[2], ct_b[1], ct_b[0]); - ct_b[3] = ct_blarge; - ct_blen = 4; - } - } - - bctlen = fast_expansion_sum_zeroelim(bt_clen, bt_c, ct_blen, ct_b, bct); - wlength = scale_expansion_zeroelim(bctlen, bct, adz, w); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, - finother); - finswap = finnow; finnow = finother; finother = finswap; - - catlen = fast_expansion_sum_zeroelim(ct_alen, ct_a, at_clen, at_c, cat); - wlength = scale_expansion_zeroelim(catlen, cat, bdz, w); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, - finother); - finswap = finnow; finnow = finother; finother = finswap; - - abtlen = fast_expansion_sum_zeroelim(at_blen, at_b, bt_alen, bt_a, abt); - wlength = scale_expansion_zeroelim(abtlen, abt, cdz, w); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, - finother); - finswap = finnow; finnow = finother; finother = finswap; - - if (adztail != 0.0) { - vlength = scale_expansion_zeroelim(4, bc, adztail, v); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v, - finother); - finswap = finnow; finnow = finother; finother = finswap; - } - if (bdztail != 0.0) { - vlength = scale_expansion_zeroelim(4, ca, bdztail, v); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v, - finother); - finswap = finnow; finnow = finother; finother = finswap; - } - if (cdztail != 0.0) { - vlength = scale_expansion_zeroelim(4, ab, cdztail, v); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v, - finother); - finswap = finnow; finnow = finother; finother = finswap; - } - - if (adxtail != 0.0) { - if (bdytail != 0.0) { - Two_Product(adxtail, bdytail, adxt_bdyt1, adxt_bdyt0); - Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdz, u3, u[2], u[1], u[0]); - u[3] = u3; - finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, - finother); - finswap = finnow; finnow = finother; finother = finswap; - if (cdztail != 0.0) { - Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdztail, u3, u[2], u[1], u[0]); - u[3] = u3; - finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, - finother); - finswap = finnow; finnow = finother; finother = finswap; - } - } - if (cdytail != 0.0) { - negate = -adxtail; - Two_Product(negate, cdytail, adxt_cdyt1, adxt_cdyt0); - Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdz, u3, u[2], u[1], u[0]); - u[3] = u3; - finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, - finother); - finswap = finnow; finnow = finother; finother = finswap; - if (bdztail != 0.0) { - Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdztail, u3, u[2], u[1], u[0]); - u[3] = u3; - finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, - finother); - finswap = finnow; finnow = finother; finother = finswap; - } - } - } - if (bdxtail != 0.0) { - if (cdytail != 0.0) { - Two_Product(bdxtail, cdytail, bdxt_cdyt1, bdxt_cdyt0); - Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adz, u3, u[2], u[1], u[0]); - u[3] = u3; - finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, - finother); - finswap = finnow; finnow = finother; finother = finswap; - if (adztail != 0.0) { - Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adztail, u3, u[2], u[1], u[0]); - u[3] = u3; - finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, - finother); - finswap = finnow; finnow = finother; finother = finswap; - } - } - if (adytail != 0.0) { - negate = -bdxtail; - Two_Product(negate, adytail, bdxt_adyt1, bdxt_adyt0); - Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdz, u3, u[2], u[1], u[0]); - u[3] = u3; - finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, - finother); - finswap = finnow; finnow = finother; finother = finswap; - if (cdztail != 0.0) { - Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdztail, u3, u[2], u[1], u[0]); - u[3] = u3; - finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, - finother); - finswap = finnow; finnow = finother; finother = finswap; - } - } - } - if (cdxtail != 0.0) { - if (adytail != 0.0) { - Two_Product(cdxtail, adytail, cdxt_adyt1, cdxt_adyt0); - Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdz, u3, u[2], u[1], u[0]); - u[3] = u3; - finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, - finother); - finswap = finnow; finnow = finother; finother = finswap; - if (bdztail != 0.0) { - Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdztail, u3, u[2], u[1], u[0]); - u[3] = u3; - finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, - finother); - finswap = finnow; finnow = finother; finother = finswap; - } - } - if (bdytail != 0.0) { - negate = -cdxtail; - Two_Product(negate, bdytail, cdxt_bdyt1, cdxt_bdyt0); - Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adz, u3, u[2], u[1], u[0]); - u[3] = u3; - finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, - finother); - finswap = finnow; finnow = finother; finother = finswap; - if (adztail != 0.0) { - Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adztail, u3, u[2], u[1], u[0]); - u[3] = u3; - finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, - finother); - finswap = finnow; finnow = finother; finother = finswap; - } - } - } - - if (adztail != 0.0) { - wlength = scale_expansion_zeroelim(bctlen, bct, adztail, w); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, - finother); - finswap = finnow; finnow = finother; finother = finswap; - } - if (bdztail != 0.0) { - wlength = scale_expansion_zeroelim(catlen, cat, bdztail, w); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, - finother); - finswap = finnow; finnow = finother; finother = finswap; - } - if (cdztail != 0.0) { - wlength = scale_expansion_zeroelim(abtlen, abt, cdztail, w); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, - finother); - finswap = finnow; finnow = finother; finother = finswap; - } - - return finnow[finlength - 1]; -} - -REAL orient3d(pa, pb, pc, pd) -REAL *pa; -REAL *pb; -REAL *pc; -REAL *pd; -{ - REAL adx, bdx, cdx, ady, bdy, cdy, adz, bdz, cdz; - REAL bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady; - REAL det; - REAL permanent, errbound; - REAL orient; - - FPU_ROUND_DOUBLE; - - adx = pa[0] - pd[0]; - bdx = pb[0] - pd[0]; - cdx = pc[0] - pd[0]; - ady = pa[1] - pd[1]; - bdy = pb[1] - pd[1]; - cdy = pc[1] - pd[1]; - adz = pa[2] - pd[2]; - bdz = pb[2] - pd[2]; - cdz = pc[2] - pd[2]; - - bdxcdy = bdx * cdy; - cdxbdy = cdx * bdy; - - cdxady = cdx * ady; - adxcdy = adx * cdy; - - adxbdy = adx * bdy; - bdxady = bdx * ady; - - det = adz * (bdxcdy - cdxbdy) - + bdz * (cdxady - adxcdy) - + cdz * (adxbdy - bdxady); - - permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * Absolute(adz) - + (Absolute(cdxady) + Absolute(adxcdy)) * Absolute(bdz) - + (Absolute(adxbdy) + Absolute(bdxady)) * Absolute(cdz); - errbound = o3derrboundA * permanent; - if ((det > errbound) || (-det > errbound)) { - FPU_RESTORE; - return det; - } - - orient = orient3dadapt(pa, pb, pc, pd, permanent); - FPU_RESTORE; - return orient; -} - -/*****************************************************************************/ -/* */ -/* incirclefast() Approximate 2D incircle test. Nonrobust. */ -/* incircleexact() Exact 2D incircle test. Robust. */ -/* incircleslow() Another exact 2D incircle test. Robust. */ -/* incircle() Adaptive exact 2D incircle test. Robust. */ -/* */ -/* Return a positive value if the point pd lies inside the */ -/* circle passing through pa, pb, and pc; a negative value if */ -/* it lies outside; and zero if the four points are cocircular.*/ -/* The points pa, pb, and pc must be in counterclockwise */ -/* order, or the sign of the result will be reversed. */ -/* */ -/* Only the first and last routine should be used; the middle two are for */ -/* timings. */ -/* */ -/* The last three use exact arithmetic to ensure a correct answer. The */ -/* result returned is the determinant of a matrix. In incircle() only, */ -/* this determinant is computed adaptively, in the sense that exact */ -/* arithmetic is used only to the degree it is needed to ensure that the */ -/* returned value has the correct sign. Hence, incircle() is usually quite */ -/* fast, but will run more slowly when the input points are cocircular or */ -/* nearly so. */ -/* */ -/*****************************************************************************/ - -static REAL incircleadapt(REAL *pa, REAL *pb, REAL *pc, REAL *pd, - REAL permanent) -{ - INEXACT REAL adx, bdx, cdx, ady, bdy, cdy; - REAL det, errbound; - - INEXACT REAL bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1; - REAL bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0; - REAL bc[4], ca[4], ab[4]; - INEXACT REAL bc3, ca3, ab3; - REAL axbc[8], axxbc[16], aybc[8], ayybc[16], adet[32]; - int axbclen, axxbclen, aybclen, ayybclen, alen; - REAL bxca[8], bxxca[16], byca[8], byyca[16], bdet[32]; - int bxcalen, bxxcalen, bycalen, byycalen, blen; - REAL cxab[8], cxxab[16], cyab[8], cyyab[16], cdet[32]; - int cxablen, cxxablen, cyablen, cyyablen, clen; - REAL abdet[64]; - int ablen; - REAL fin1[1152], fin2[1152]; - REAL *finnow, *finother, *finswap; - int finlength; - - REAL adxtail, bdxtail, cdxtail, adytail, bdytail, cdytail; - INEXACT REAL adxadx1, adyady1, bdxbdx1, bdybdy1, cdxcdx1, cdycdy1; - REAL adxadx0, adyady0, bdxbdx0, bdybdy0, cdxcdx0, cdycdy0; - REAL aa[4], bb[4], cc[4]; - INEXACT REAL aa3, bb3, cc3; - INEXACT REAL ti1, tj1; - REAL ti0, tj0; - REAL u[4], v[4]; - INEXACT REAL u3, v3; - REAL temp8[8], temp16a[16], temp16b[16], temp16c[16]; - REAL temp32a[32], temp32b[32], temp48[48], temp64[64]; - int temp8len, temp16alen, temp16blen, temp16clen; - int temp32alen, temp32blen, temp48len, temp64len; - REAL axtbb[8], axtcc[8], aytbb[8], aytcc[8]; - int axtbblen, axtcclen, aytbblen, aytcclen; - REAL bxtaa[8], bxtcc[8], bytaa[8], bytcc[8]; - int bxtaalen, bxtcclen, bytaalen, bytcclen; - REAL cxtaa[8], cxtbb[8], cytaa[8], cytbb[8]; - int cxtaalen, cxtbblen, cytaalen, cytbblen; - REAL axtbc[8], aytbc[8], bxtca[8], bytca[8], cxtab[8], cytab[8]; - int axtbclen = 0, aytbclen = 0; - int bxtcalen = 0, bytcalen = 0; - int cxtablen = 0, cytablen = 0; - REAL axtbct[16], aytbct[16], bxtcat[16], bytcat[16], cxtabt[16], cytabt[16]; - int axtbctlen, aytbctlen, bxtcatlen, bytcatlen, cxtabtlen, cytabtlen; - REAL axtbctt[8], aytbctt[8], bxtcatt[8]; - REAL bytcatt[8], cxtabtt[8], cytabtt[8]; - int axtbcttlen, aytbcttlen, bxtcattlen, bytcattlen, cxtabttlen, cytabttlen; - REAL abt[8], bct[8], cat[8]; - int abtlen, bctlen, catlen; - REAL abtt[4], bctt[4], catt[4]; - int abttlen, bcttlen, cattlen; - INEXACT REAL abtt3, bctt3, catt3; - REAL negate; - - INEXACT REAL bvirt; - REAL avirt, bround, around; - INEXACT REAL c; - INEXACT REAL abig; - REAL ahi, alo, bhi, blo; - REAL err1, err2, err3; - INEXACT REAL _i, _j; - REAL _0; - - adx = (REAL) (pa[0] - pd[0]); - bdx = (REAL) (pb[0] - pd[0]); - cdx = (REAL) (pc[0] - pd[0]); - ady = (REAL) (pa[1] - pd[1]); - bdy = (REAL) (pb[1] - pd[1]); - cdy = (REAL) (pc[1] - pd[1]); - - Two_Product(bdx, cdy, bdxcdy1, bdxcdy0); - Two_Product(cdx, bdy, cdxbdy1, cdxbdy0); - Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]); - bc[3] = bc3; - axbclen = scale_expansion_zeroelim(4, bc, adx, axbc); - axxbclen = scale_expansion_zeroelim(axbclen, axbc, adx, axxbc); - aybclen = scale_expansion_zeroelim(4, bc, ady, aybc); - ayybclen = scale_expansion_zeroelim(aybclen, aybc, ady, ayybc); - alen = fast_expansion_sum_zeroelim(axxbclen, axxbc, ayybclen, ayybc, adet); - - Two_Product(cdx, ady, cdxady1, cdxady0); - Two_Product(adx, cdy, adxcdy1, adxcdy0); - Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]); - ca[3] = ca3; - bxcalen = scale_expansion_zeroelim(4, ca, bdx, bxca); - bxxcalen = scale_expansion_zeroelim(bxcalen, bxca, bdx, bxxca); - bycalen = scale_expansion_zeroelim(4, ca, bdy, byca); - byycalen = scale_expansion_zeroelim(bycalen, byca, bdy, byyca); - blen = fast_expansion_sum_zeroelim(bxxcalen, bxxca, byycalen, byyca, bdet); - - Two_Product(adx, bdy, adxbdy1, adxbdy0); - Two_Product(bdx, ady, bdxady1, bdxady0); - Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]); - ab[3] = ab3; - cxablen = scale_expansion_zeroelim(4, ab, cdx, cxab); - cxxablen = scale_expansion_zeroelim(cxablen, cxab, cdx, cxxab); - cyablen = scale_expansion_zeroelim(4, ab, cdy, cyab); - cyyablen = scale_expansion_zeroelim(cyablen, cyab, cdy, cyyab); - clen = fast_expansion_sum_zeroelim(cxxablen, cxxab, cyyablen, cyyab, cdet); - - ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet); - finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1); - - det = estimate(finlength, fin1); - errbound = iccerrboundB * permanent; - if ((det >= errbound) || (-det >= errbound)) { - return det; - } - - Two_Diff_Tail(pa[0], pd[0], adx, adxtail); - Two_Diff_Tail(pa[1], pd[1], ady, adytail); - Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail); - Two_Diff_Tail(pb[1], pd[1], bdy, bdytail); - Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail); - Two_Diff_Tail(pc[1], pd[1], cdy, cdytail); - if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0) - && (adytail == 0.0) && (bdytail == 0.0) && (cdytail == 0.0)) { - return det; - } - - errbound = iccerrboundC * permanent + resulterrbound * Absolute(det); - det += ((adx * adx + ady * ady) * ((bdx * cdytail + cdy * bdxtail) - - (bdy * cdxtail + cdx * bdytail)) - + 2.0 * (adx * adxtail + ady * adytail) * (bdx * cdy - bdy * cdx)) - + ((bdx * bdx + bdy * bdy) * ((cdx * adytail + ady * cdxtail) - - (cdy * adxtail + adx * cdytail)) - + 2.0 * (bdx * bdxtail + bdy * bdytail) * (cdx * ady - cdy * adx)) - + ((cdx * cdx + cdy * cdy) * ((adx * bdytail + bdy * adxtail) - - (ady * bdxtail + bdx * adytail)) - + 2.0 * (cdx * cdxtail + cdy * cdytail) * (adx * bdy - ady * bdx)); - if ((det >= errbound) || (-det >= errbound)) { - return det; - } - - finnow = fin1; - finother = fin2; - - if ((bdxtail != 0.0) || (bdytail != 0.0) - || (cdxtail != 0.0) || (cdytail != 0.0)) { - Square(adx, adxadx1, adxadx0); - Square(ady, adyady1, adyady0); - Two_Two_Sum(adxadx1, adxadx0, adyady1, adyady0, aa3, aa[2], aa[1], aa[0]); - aa[3] = aa3; - } - if ((cdxtail != 0.0) || (cdytail != 0.0) - || (adxtail != 0.0) || (adytail != 0.0)) { - Square(bdx, bdxbdx1, bdxbdx0); - Square(bdy, bdybdy1, bdybdy0); - Two_Two_Sum(bdxbdx1, bdxbdx0, bdybdy1, bdybdy0, bb3, bb[2], bb[1], bb[0]); - bb[3] = bb3; - } - if ((adxtail != 0.0) || (adytail != 0.0) - || (bdxtail != 0.0) || (bdytail != 0.0)) { - Square(cdx, cdxcdx1, cdxcdx0); - Square(cdy, cdycdy1, cdycdy0); - Two_Two_Sum(cdxcdx1, cdxcdx0, cdycdy1, cdycdy0, cc3, cc[2], cc[1], cc[0]); - cc[3] = cc3; - } - - if (adxtail != 0.0) { - axtbclen = scale_expansion_zeroelim(4, bc, adxtail, axtbc); - temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, 2.0 * adx, - temp16a); - - axtcclen = scale_expansion_zeroelim(4, cc, adxtail, axtcc); - temp16blen = scale_expansion_zeroelim(axtcclen, axtcc, bdy, temp16b); - - axtbblen = scale_expansion_zeroelim(4, bb, adxtail, axtbb); - temp16clen = scale_expansion_zeroelim(axtbblen, axtbb, -cdy, temp16c); - - temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, - temp16blen, temp16b, temp32a); - temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, - temp32alen, temp32a, temp48); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, - temp48, finother); - finswap = finnow; finnow = finother; finother = finswap; - } - if (adytail != 0.0) { - aytbclen = scale_expansion_zeroelim(4, bc, adytail, aytbc); - temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, 2.0 * ady, - temp16a); - - aytbblen = scale_expansion_zeroelim(4, bb, adytail, aytbb); - temp16blen = scale_expansion_zeroelim(aytbblen, aytbb, cdx, temp16b); - - aytcclen = scale_expansion_zeroelim(4, cc, adytail, aytcc); - temp16clen = scale_expansion_zeroelim(aytcclen, aytcc, -bdx, temp16c); - - temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, - temp16blen, temp16b, temp32a); - temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, - temp32alen, temp32a, temp48); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, - temp48, finother); - finswap = finnow; finnow = finother; finother = finswap; - } - if (bdxtail != 0.0) { - bxtcalen = scale_expansion_zeroelim(4, ca, bdxtail, bxtca); - temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, 2.0 * bdx, - temp16a); - - bxtaalen = scale_expansion_zeroelim(4, aa, bdxtail, bxtaa); - temp16blen = scale_expansion_zeroelim(bxtaalen, bxtaa, cdy, temp16b); - - bxtcclen = scale_expansion_zeroelim(4, cc, bdxtail, bxtcc); - temp16clen = scale_expansion_zeroelim(bxtcclen, bxtcc, -ady, temp16c); - - temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, - temp16blen, temp16b, temp32a); - temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, - temp32alen, temp32a, temp48); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, - temp48, finother); - finswap = finnow; finnow = finother; finother = finswap; - } - if (bdytail != 0.0) { - bytcalen = scale_expansion_zeroelim(4, ca, bdytail, bytca); - temp16alen = scale_expansion_zeroelim(bytcalen, bytca, 2.0 * bdy, - temp16a); - - bytcclen = scale_expansion_zeroelim(4, cc, bdytail, bytcc); - temp16blen = scale_expansion_zeroelim(bytcclen, bytcc, adx, temp16b); - - bytaalen = scale_expansion_zeroelim(4, aa, bdytail, bytaa); - temp16clen = scale_expansion_zeroelim(bytaalen, bytaa, -cdx, temp16c); - - temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, - temp16blen, temp16b, temp32a); - temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, - temp32alen, temp32a, temp48); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, - temp48, finother); - finswap = finnow; finnow = finother; finother = finswap; - } - if (cdxtail != 0.0) { - cxtablen = scale_expansion_zeroelim(4, ab, cdxtail, cxtab); - temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, 2.0 * cdx, - temp16a); - - cxtbblen = scale_expansion_zeroelim(4, bb, cdxtail, cxtbb); - temp16blen = scale_expansion_zeroelim(cxtbblen, cxtbb, ady, temp16b); - - cxtaalen = scale_expansion_zeroelim(4, aa, cdxtail, cxtaa); - temp16clen = scale_expansion_zeroelim(cxtaalen, cxtaa, -bdy, temp16c); - - temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, - temp16blen, temp16b, temp32a); - temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, - temp32alen, temp32a, temp48); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, - temp48, finother); - finswap = finnow; finnow = finother; finother = finswap; - } - if (cdytail != 0.0) { - cytablen = scale_expansion_zeroelim(4, ab, cdytail, cytab); - temp16alen = scale_expansion_zeroelim(cytablen, cytab, 2.0 * cdy, - temp16a); - - cytaalen = scale_expansion_zeroelim(4, aa, cdytail, cytaa); - temp16blen = scale_expansion_zeroelim(cytaalen, cytaa, bdx, temp16b); - - cytbblen = scale_expansion_zeroelim(4, bb, cdytail, cytbb); - temp16clen = scale_expansion_zeroelim(cytbblen, cytbb, -adx, temp16c); - - temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, - temp16blen, temp16b, temp32a); - temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, - temp32alen, temp32a, temp48); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, - temp48, finother); - finswap = finnow; finnow = finother; finother = finswap; - } - - if ((adxtail != 0.0) || (adytail != 0.0)) { - if ((bdxtail != 0.0) || (bdytail != 0.0) - || (cdxtail != 0.0) || (cdytail != 0.0)) { - Two_Product(bdxtail, cdy, ti1, ti0); - Two_Product(bdx, cdytail, tj1, tj0); - Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]); - u[3] = u3; - negate = -bdy; - Two_Product(cdxtail, negate, ti1, ti0); - negate = -bdytail; - Two_Product(cdx, negate, tj1, tj0); - Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]); - v[3] = v3; - bctlen = fast_expansion_sum_zeroelim(4, u, 4, v, bct); - - Two_Product(bdxtail, cdytail, ti1, ti0); - Two_Product(cdxtail, bdytail, tj1, tj0); - Two_Two_Diff(ti1, ti0, tj1, tj0, bctt3, bctt[2], bctt[1], bctt[0]); - bctt[3] = bctt3; - bcttlen = 4; - } else { - bct[0] = 0.0; - bctlen = 1; - bctt[0] = 0.0; - bcttlen = 1; - } - - if (adxtail != 0.0) { - temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, adxtail, temp16a); - axtbctlen = scale_expansion_zeroelim(bctlen, bct, adxtail, axtbct); - temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, 2.0 * adx, - temp32a); - temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, - temp32alen, temp32a, temp48); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, - temp48, finother); - finswap = finnow; finnow = finother; finother = finswap; - if (bdytail != 0.0) { - temp8len = scale_expansion_zeroelim(4, cc, adxtail, temp8); - temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail, - temp16a); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, - temp16a, finother); - finswap = finnow; finnow = finother; finother = finswap; - } - if (cdytail != 0.0) { - temp8len = scale_expansion_zeroelim(4, bb, -adxtail, temp8); - temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail, - temp16a); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, - temp16a, finother); - finswap = finnow; finnow = finother; finother = finswap; - } - - temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, adxtail, - temp32a); - axtbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adxtail, axtbctt); - temp16alen = scale_expansion_zeroelim(axtbcttlen, axtbctt, 2.0 * adx, - temp16a); - temp16blen = scale_expansion_zeroelim(axtbcttlen, axtbctt, adxtail, - temp16b); - temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, - temp16blen, temp16b, temp32b); - temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, - temp32blen, temp32b, temp64); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, - temp64, finother); - finswap = finnow; finnow = finother; finother = finswap; - } - if (adytail != 0.0) { - temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, adytail, temp16a); - aytbctlen = scale_expansion_zeroelim(bctlen, bct, adytail, aytbct); - temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, 2.0 * ady, - temp32a); - temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, - temp32alen, temp32a, temp48); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, - temp48, finother); - finswap = finnow; finnow = finother; finother = finswap; - - - temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, adytail, - temp32a); - aytbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adytail, aytbctt); - temp16alen = scale_expansion_zeroelim(aytbcttlen, aytbctt, 2.0 * ady, - temp16a); - temp16blen = scale_expansion_zeroelim(aytbcttlen, aytbctt, adytail, - temp16b); - temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, - temp16blen, temp16b, temp32b); - temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, - temp32blen, temp32b, temp64); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, - temp64, finother); - finswap = finnow; finnow = finother; finother = finswap; - } - } - if ((bdxtail != 0.0) || (bdytail != 0.0)) { - if ((cdxtail != 0.0) || (cdytail != 0.0) - || (adxtail != 0.0) || (adytail != 0.0)) { - Two_Product(cdxtail, ady, ti1, ti0); - Two_Product(cdx, adytail, tj1, tj0); - Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]); - u[3] = u3; - negate = -cdy; - Two_Product(adxtail, negate, ti1, ti0); - negate = -cdytail; - Two_Product(adx, negate, tj1, tj0); - Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]); - v[3] = v3; - catlen = fast_expansion_sum_zeroelim(4, u, 4, v, cat); - - Two_Product(cdxtail, adytail, ti1, ti0); - Two_Product(adxtail, cdytail, tj1, tj0); - Two_Two_Diff(ti1, ti0, tj1, tj0, catt3, catt[2], catt[1], catt[0]); - catt[3] = catt3; - cattlen = 4; - } else { - cat[0] = 0.0; - catlen = 1; - catt[0] = 0.0; - cattlen = 1; - } - - if (bdxtail != 0.0) { - temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, bdxtail, temp16a); - bxtcatlen = scale_expansion_zeroelim(catlen, cat, bdxtail, bxtcat); - temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, 2.0 * bdx, - temp32a); - temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, - temp32alen, temp32a, temp48); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, - temp48, finother); - finswap = finnow; finnow = finother; finother = finswap; - if (cdytail != 0.0) { - temp8len = scale_expansion_zeroelim(4, aa, bdxtail, temp8); - temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail, - temp16a); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, - temp16a, finother); - finswap = finnow; finnow = finother; finother = finswap; - } - if (adytail != 0.0) { - temp8len = scale_expansion_zeroelim(4, cc, -bdxtail, temp8); - temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail, - temp16a); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, - temp16a, finother); - finswap = finnow; finnow = finother; finother = finswap; - } - - temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, bdxtail, - temp32a); - bxtcattlen = scale_expansion_zeroelim(cattlen, catt, bdxtail, bxtcatt); - temp16alen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, 2.0 * bdx, - temp16a); - temp16blen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, bdxtail, - temp16b); - temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, - temp16blen, temp16b, temp32b); - temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, - temp32blen, temp32b, temp64); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, - temp64, finother); - finswap = finnow; finnow = finother; finother = finswap; - } - if (bdytail != 0.0) { - temp16alen = scale_expansion_zeroelim(bytcalen, bytca, bdytail, temp16a); - bytcatlen = scale_expansion_zeroelim(catlen, cat, bdytail, bytcat); - temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, 2.0 * bdy, - temp32a); - temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, - temp32alen, temp32a, temp48); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, - temp48, finother); - finswap = finnow; finnow = finother; finother = finswap; - - - temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, bdytail, - temp32a); - bytcattlen = scale_expansion_zeroelim(cattlen, catt, bdytail, bytcatt); - temp16alen = scale_expansion_zeroelim(bytcattlen, bytcatt, 2.0 * bdy, - temp16a); - temp16blen = scale_expansion_zeroelim(bytcattlen, bytcatt, bdytail, - temp16b); - temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, - temp16blen, temp16b, temp32b); - temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, - temp32blen, temp32b, temp64); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, - temp64, finother); - finswap = finnow; finnow = finother; finother = finswap; - } - } - if ((cdxtail != 0.0) || (cdytail != 0.0)) { - if ((adxtail != 0.0) || (adytail != 0.0) - || (bdxtail != 0.0) || (bdytail != 0.0)) { - Two_Product(adxtail, bdy, ti1, ti0); - Two_Product(adx, bdytail, tj1, tj0); - Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]); - u[3] = u3; - negate = -ady; - Two_Product(bdxtail, negate, ti1, ti0); - negate = -adytail; - Two_Product(bdx, negate, tj1, tj0); - Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]); - v[3] = v3; - abtlen = fast_expansion_sum_zeroelim(4, u, 4, v, abt); - - Two_Product(adxtail, bdytail, ti1, ti0); - Two_Product(bdxtail, adytail, tj1, tj0); - Two_Two_Diff(ti1, ti0, tj1, tj0, abtt3, abtt[2], abtt[1], abtt[0]); - abtt[3] = abtt3; - abttlen = 4; - } else { - abt[0] = 0.0; - abtlen = 1; - abtt[0] = 0.0; - abttlen = 1; - } - - if (cdxtail != 0.0) { - temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, cdxtail, temp16a); - cxtabtlen = scale_expansion_zeroelim(abtlen, abt, cdxtail, cxtabt); - temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, 2.0 * cdx, - temp32a); - temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, - temp32alen, temp32a, temp48); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, - temp48, finother); - finswap = finnow; finnow = finother; finother = finswap; - if (adytail != 0.0) { - temp8len = scale_expansion_zeroelim(4, bb, cdxtail, temp8); - temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail, - temp16a); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, - temp16a, finother); - finswap = finnow; finnow = finother; finother = finswap; - } - if (bdytail != 0.0) { - temp8len = scale_expansion_zeroelim(4, aa, -cdxtail, temp8); - temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail, - temp16a); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, - temp16a, finother); - finswap = finnow; finnow = finother; finother = finswap; - } - - temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, cdxtail, - temp32a); - cxtabttlen = scale_expansion_zeroelim(abttlen, abtt, cdxtail, cxtabtt); - temp16alen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, 2.0 * cdx, - temp16a); - temp16blen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, cdxtail, - temp16b); - temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, - temp16blen, temp16b, temp32b); - temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, - temp32blen, temp32b, temp64); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, - temp64, finother); - finswap = finnow; finnow = finother; finother = finswap; - } - if (cdytail != 0.0) { - temp16alen = scale_expansion_zeroelim(cytablen, cytab, cdytail, temp16a); - cytabtlen = scale_expansion_zeroelim(abtlen, abt, cdytail, cytabt); - temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, 2.0 * cdy, - temp32a); - temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, - temp32alen, temp32a, temp48); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, - temp48, finother); - finswap = finnow; finnow = finother; finother = finswap; - - - temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, cdytail, - temp32a); - cytabttlen = scale_expansion_zeroelim(abttlen, abtt, cdytail, cytabtt); - temp16alen = scale_expansion_zeroelim(cytabttlen, cytabtt, 2.0 * cdy, - temp16a); - temp16blen = scale_expansion_zeroelim(cytabttlen, cytabtt, cdytail, - temp16b); - temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, - temp16blen, temp16b, temp32b); - temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, - temp32blen, temp32b, temp64); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, - temp64, finother); - finswap = finnow; finnow = finother; finother = finswap; - } - } - - return finnow[finlength - 1]; -} - -REAL incircle(pa, pb, pc, pd) -REAL *pa; -REAL *pb; -REAL *pc; -REAL *pd; -{ - REAL adx, bdx, cdx, ady, bdy, cdy; - REAL bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady; - REAL alift, blift, clift; - REAL det; - REAL permanent, errbound; - REAL inc; - - FPU_ROUND_DOUBLE; - - adx = pa[0] - pd[0]; - bdx = pb[0] - pd[0]; - cdx = pc[0] - pd[0]; - ady = pa[1] - pd[1]; - bdy = pb[1] - pd[1]; - cdy = pc[1] - pd[1]; - - bdxcdy = bdx * cdy; - cdxbdy = cdx * bdy; - alift = adx * adx + ady * ady; - - cdxady = cdx * ady; - adxcdy = adx * cdy; - blift = bdx * bdx + bdy * bdy; - - adxbdy = adx * bdy; - bdxady = bdx * ady; - clift = cdx * cdx + cdy * cdy; - - det = alift * (bdxcdy - cdxbdy) - + blift * (cdxady - adxcdy) - + clift * (adxbdy - bdxady); - - permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * alift - + (Absolute(cdxady) + Absolute(adxcdy)) * blift - + (Absolute(adxbdy) + Absolute(bdxady)) * clift; - errbound = iccerrboundA * permanent; - if ((det > errbound) || (-det > errbound)) { - FPU_RESTORE; - return det; - } - - inc = incircleadapt(pa, pb, pc, pd, permanent); - FPU_RESTORE; - return inc; -} - -/*****************************************************************************/ -/* */ -/* inspherefast() Approximate 3D insphere test. Nonrobust. */ -/* insphereexact() Exact 3D insphere test. Robust. */ -/* insphereslow() Another exact 3D insphere test. Robust. */ -/* insphere() Adaptive exact 3D insphere test. Robust. */ -/* */ -/* Return a positive value if the point pe lies inside the */ -/* sphere passing through pa, pb, pc, and pd; a negative value */ -/* if it lies outside; and zero if the five points are */ -/* cospherical. The points pa, pb, pc, and pd must be ordered */ -/* so that they have a positive orientation (as defined by */ -/* orient3d()), or the sign of the result will be reversed. */ -/* */ -/* Only the first and last routine should be used; the middle two are for */ -/* timings. */ -/* */ -/* The last three use exact arithmetic to ensure a correct answer. The */ -/* result returned is the determinant of a matrix. In insphere() only, */ -/* this determinant is computed adaptively, in the sense that exact */ -/* arithmetic is used only to the degree it is needed to ensure that the */ -/* returned value has the correct sign. Hence, insphere() is usually quite */ -/* fast, but will run more slowly when the input points are cospherical or */ -/* nearly so. */ -/* */ -/*****************************************************************************/ - -static REAL insphereexact(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe) -{ - INEXACT REAL axby1, bxcy1, cxdy1, dxey1, exay1; - INEXACT REAL bxay1, cxby1, dxcy1, exdy1, axey1; - INEXACT REAL axcy1, bxdy1, cxey1, dxay1, exby1; - INEXACT REAL cxay1, dxby1, excy1, axdy1, bxey1; - REAL axby0, bxcy0, cxdy0, dxey0, exay0; - REAL bxay0, cxby0, dxcy0, exdy0, axey0; - REAL axcy0, bxdy0, cxey0, dxay0, exby0; - REAL cxay0, dxby0, excy0, axdy0, bxey0; - REAL ab[4], bc[4], cd[4], de[4], ea[4]; - REAL ac[4], bd[4], ce[4], da[4], eb[4]; - REAL temp8a[8], temp8b[8], temp16[16]; - int temp8alen, temp8blen, temp16len; - REAL abc[24], bcd[24], cde[24], dea[24], eab[24]; - REAL abd[24], bce[24], cda[24], deb[24], eac[24]; - int abclen, bcdlen, cdelen, dealen, eablen; - int abdlen, bcelen, cdalen, deblen, eaclen; - REAL temp48a[48], temp48b[48]; - int temp48alen, temp48blen; - REAL abcd[96], bcde[96], cdea[96], deab[96], eabc[96]; - int abcdlen, bcdelen, cdealen, deablen, eabclen; - REAL temp192[192]; - REAL det384x[384], det384y[384], det384z[384]; - int xlen, ylen, zlen; - REAL detxy[768]; - int xylen; - REAL adet[1152], bdet[1152], cdet[1152], ddet[1152], edet[1152]; - int alen, blen, clen, dlen, elen; - REAL abdet[2304], cddet[2304], cdedet[3456]; - int ablen, cdlen; - REAL deter[5760]; - int deterlen; - int i; - - INEXACT REAL bvirt; - REAL avirt, bround, around; - INEXACT REAL c; - INEXACT REAL abig; - REAL ahi, alo, bhi, blo; - REAL err1, err2, err3; - INEXACT REAL _i, _j; - REAL _0; - - Two_Product(pa[0], pb[1], axby1, axby0); - Two_Product(pb[0], pa[1], bxay1, bxay0); - Two_Two_Diff(axby1, axby0, bxay1, bxay0, ab[3], ab[2], ab[1], ab[0]); - - Two_Product(pb[0], pc[1], bxcy1, bxcy0); - Two_Product(pc[0], pb[1], cxby1, cxby0); - Two_Two_Diff(bxcy1, bxcy0, cxby1, cxby0, bc[3], bc[2], bc[1], bc[0]); - - Two_Product(pc[0], pd[1], cxdy1, cxdy0); - Two_Product(pd[0], pc[1], dxcy1, dxcy0); - Two_Two_Diff(cxdy1, cxdy0, dxcy1, dxcy0, cd[3], cd[2], cd[1], cd[0]); - - Two_Product(pd[0], pe[1], dxey1, dxey0); - Two_Product(pe[0], pd[1], exdy1, exdy0); - Two_Two_Diff(dxey1, dxey0, exdy1, exdy0, de[3], de[2], de[1], de[0]); - - Two_Product(pe[0], pa[1], exay1, exay0); - Two_Product(pa[0], pe[1], axey1, axey0); - Two_Two_Diff(exay1, exay0, axey1, axey0, ea[3], ea[2], ea[1], ea[0]); - - Two_Product(pa[0], pc[1], axcy1, axcy0); - Two_Product(pc[0], pa[1], cxay1, cxay0); - Two_Two_Diff(axcy1, axcy0, cxay1, cxay0, ac[3], ac[2], ac[1], ac[0]); - - Two_Product(pb[0], pd[1], bxdy1, bxdy0); - Two_Product(pd[0], pb[1], dxby1, dxby0); - Two_Two_Diff(bxdy1, bxdy0, dxby1, dxby0, bd[3], bd[2], bd[1], bd[0]); - - Two_Product(pc[0], pe[1], cxey1, cxey0); - Two_Product(pe[0], pc[1], excy1, excy0); - Two_Two_Diff(cxey1, cxey0, excy1, excy0, ce[3], ce[2], ce[1], ce[0]); - - Two_Product(pd[0], pa[1], dxay1, dxay0); - Two_Product(pa[0], pd[1], axdy1, axdy0); - Two_Two_Diff(dxay1, dxay0, axdy1, axdy0, da[3], da[2], da[1], da[0]); - - Two_Product(pe[0], pb[1], exby1, exby0); - Two_Product(pb[0], pe[1], bxey1, bxey0); - Two_Two_Diff(exby1, exby0, bxey1, bxey0, eb[3], eb[2], eb[1], eb[0]); - - temp8alen = scale_expansion_zeroelim(4, bc, pa[2], temp8a); - temp8blen = scale_expansion_zeroelim(4, ac, -pb[2], temp8b); - temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, - temp16); - temp8alen = scale_expansion_zeroelim(4, ab, pc[2], temp8a); - abclen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, - abc); - - temp8alen = scale_expansion_zeroelim(4, cd, pb[2], temp8a); - temp8blen = scale_expansion_zeroelim(4, bd, -pc[2], temp8b); - temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, - temp16); - temp8alen = scale_expansion_zeroelim(4, bc, pd[2], temp8a); - bcdlen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, - bcd); - - temp8alen = scale_expansion_zeroelim(4, de, pc[2], temp8a); - temp8blen = scale_expansion_zeroelim(4, ce, -pd[2], temp8b); - temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, - temp16); - temp8alen = scale_expansion_zeroelim(4, cd, pe[2], temp8a); - cdelen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, - cde); - - temp8alen = scale_expansion_zeroelim(4, ea, pd[2], temp8a); - temp8blen = scale_expansion_zeroelim(4, da, -pe[2], temp8b); - temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, - temp16); - temp8alen = scale_expansion_zeroelim(4, de, pa[2], temp8a); - dealen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, - dea); - - temp8alen = scale_expansion_zeroelim(4, ab, pe[2], temp8a); - temp8blen = scale_expansion_zeroelim(4, eb, -pa[2], temp8b); - temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, - temp16); - temp8alen = scale_expansion_zeroelim(4, ea, pb[2], temp8a); - eablen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, - eab); - - temp8alen = scale_expansion_zeroelim(4, bd, pa[2], temp8a); - temp8blen = scale_expansion_zeroelim(4, da, pb[2], temp8b); - temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, - temp16); - temp8alen = scale_expansion_zeroelim(4, ab, pd[2], temp8a); - abdlen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, - abd); - - temp8alen = scale_expansion_zeroelim(4, ce, pb[2], temp8a); - temp8blen = scale_expansion_zeroelim(4, eb, pc[2], temp8b); - temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, - temp16); - temp8alen = scale_expansion_zeroelim(4, bc, pe[2], temp8a); - bcelen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, - bce); - - temp8alen = scale_expansion_zeroelim(4, da, pc[2], temp8a); - temp8blen = scale_expansion_zeroelim(4, ac, pd[2], temp8b); - temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, - temp16); - temp8alen = scale_expansion_zeroelim(4, cd, pa[2], temp8a); - cdalen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, - cda); - - temp8alen = scale_expansion_zeroelim(4, eb, pd[2], temp8a); - temp8blen = scale_expansion_zeroelim(4, bd, pe[2], temp8b); - temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, - temp16); - temp8alen = scale_expansion_zeroelim(4, de, pb[2], temp8a); - deblen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, - deb); - - temp8alen = scale_expansion_zeroelim(4, ac, pe[2], temp8a); - temp8blen = scale_expansion_zeroelim(4, ce, pa[2], temp8b); - temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, - temp16); - temp8alen = scale_expansion_zeroelim(4, ea, pc[2], temp8a); - eaclen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, - eac); - - temp48alen = fast_expansion_sum_zeroelim(cdelen, cde, bcelen, bce, temp48a); - temp48blen = fast_expansion_sum_zeroelim(deblen, deb, bcdlen, bcd, temp48b); - for (i = 0; i < temp48blen; i++) { - temp48b[i] = -temp48b[i]; - } - bcdelen = fast_expansion_sum_zeroelim(temp48alen, temp48a, - temp48blen, temp48b, bcde); - xlen = scale_expansion_zeroelim(bcdelen, bcde, pa[0], temp192); - xlen = scale_expansion_zeroelim(xlen, temp192, pa[0], det384x); - ylen = scale_expansion_zeroelim(bcdelen, bcde, pa[1], temp192); - ylen = scale_expansion_zeroelim(ylen, temp192, pa[1], det384y); - zlen = scale_expansion_zeroelim(bcdelen, bcde, pa[2], temp192); - zlen = scale_expansion_zeroelim(zlen, temp192, pa[2], det384z); - xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy); - alen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, adet); - - temp48alen = fast_expansion_sum_zeroelim(dealen, dea, cdalen, cda, temp48a); - temp48blen = fast_expansion_sum_zeroelim(eaclen, eac, cdelen, cde, temp48b); - for (i = 0; i < temp48blen; i++) { - temp48b[i] = -temp48b[i]; - } - cdealen = fast_expansion_sum_zeroelim(temp48alen, temp48a, - temp48blen, temp48b, cdea); - xlen = scale_expansion_zeroelim(cdealen, cdea, pb[0], temp192); - xlen = scale_expansion_zeroelim(xlen, temp192, pb[0], det384x); - ylen = scale_expansion_zeroelim(cdealen, cdea, pb[1], temp192); - ylen = scale_expansion_zeroelim(ylen, temp192, pb[1], det384y); - zlen = scale_expansion_zeroelim(cdealen, cdea, pb[2], temp192); - zlen = scale_expansion_zeroelim(zlen, temp192, pb[2], det384z); - xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy); - blen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, bdet); - - temp48alen = fast_expansion_sum_zeroelim(eablen, eab, deblen, deb, temp48a); - temp48blen = fast_expansion_sum_zeroelim(abdlen, abd, dealen, dea, temp48b); - for (i = 0; i < temp48blen; i++) { - temp48b[i] = -temp48b[i]; - } - deablen = fast_expansion_sum_zeroelim(temp48alen, temp48a, - temp48blen, temp48b, deab); - xlen = scale_expansion_zeroelim(deablen, deab, pc[0], temp192); - xlen = scale_expansion_zeroelim(xlen, temp192, pc[0], det384x); - ylen = scale_expansion_zeroelim(deablen, deab, pc[1], temp192); - ylen = scale_expansion_zeroelim(ylen, temp192, pc[1], det384y); - zlen = scale_expansion_zeroelim(deablen, deab, pc[2], temp192); - zlen = scale_expansion_zeroelim(zlen, temp192, pc[2], det384z); - xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy); - clen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, cdet); - - temp48alen = fast_expansion_sum_zeroelim(abclen, abc, eaclen, eac, temp48a); - temp48blen = fast_expansion_sum_zeroelim(bcelen, bce, eablen, eab, temp48b); - for (i = 0; i < temp48blen; i++) { - temp48b[i] = -temp48b[i]; - } - eabclen = fast_expansion_sum_zeroelim(temp48alen, temp48a, - temp48blen, temp48b, eabc); - xlen = scale_expansion_zeroelim(eabclen, eabc, pd[0], temp192); - xlen = scale_expansion_zeroelim(xlen, temp192, pd[0], det384x); - ylen = scale_expansion_zeroelim(eabclen, eabc, pd[1], temp192); - ylen = scale_expansion_zeroelim(ylen, temp192, pd[1], det384y); - zlen = scale_expansion_zeroelim(eabclen, eabc, pd[2], temp192); - zlen = scale_expansion_zeroelim(zlen, temp192, pd[2], det384z); - xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy); - dlen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, ddet); - - temp48alen = fast_expansion_sum_zeroelim(bcdlen, bcd, abdlen, abd, temp48a); - temp48blen = fast_expansion_sum_zeroelim(cdalen, cda, abclen, abc, temp48b); - for (i = 0; i < temp48blen; i++) { - temp48b[i] = -temp48b[i]; - } - abcdlen = fast_expansion_sum_zeroelim(temp48alen, temp48a, - temp48blen, temp48b, abcd); - xlen = scale_expansion_zeroelim(abcdlen, abcd, pe[0], temp192); - xlen = scale_expansion_zeroelim(xlen, temp192, pe[0], det384x); - ylen = scale_expansion_zeroelim(abcdlen, abcd, pe[1], temp192); - ylen = scale_expansion_zeroelim(ylen, temp192, pe[1], det384y); - zlen = scale_expansion_zeroelim(abcdlen, abcd, pe[2], temp192); - zlen = scale_expansion_zeroelim(zlen, temp192, pe[2], det384z); - xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy); - elen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, edet); - - ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet); - cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet); - cdelen = fast_expansion_sum_zeroelim(cdlen, cddet, elen, edet, cdedet); - deterlen = fast_expansion_sum_zeroelim(ablen, abdet, cdelen, cdedet, deter); - - return deter[deterlen - 1]; -} - -static REAL insphereadapt(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe, - REAL permanent) -{ - INEXACT REAL aex, bex, cex, dex, aey, bey, cey, dey, aez, bez, cez, dez; - REAL det, errbound; - - INEXACT REAL aexbey1, bexaey1, bexcey1, cexbey1; - INEXACT REAL cexdey1, dexcey1, dexaey1, aexdey1; - INEXACT REAL aexcey1, cexaey1, bexdey1, dexbey1; - REAL aexbey0, bexaey0, bexcey0, cexbey0; - REAL cexdey0, dexcey0, dexaey0, aexdey0; - REAL aexcey0, cexaey0, bexdey0, dexbey0; - REAL ab[4], bc[4], cd[4], da[4], ac[4], bd[4]; - INEXACT REAL ab3, bc3, cd3, da3, ac3, bd3; - REAL abeps, bceps, cdeps, daeps, aceps, bdeps; - REAL temp8a[8], temp8b[8], temp8c[8], temp16[16], temp24[24], temp48[48]; - int temp8alen, temp8blen, temp8clen, temp16len, temp24len, temp48len; - REAL xdet[96], ydet[96], zdet[96], xydet[192]; - int xlen, ylen, zlen, xylen; - REAL adet[288], bdet[288], cdet[288], ddet[288]; - int alen, blen, clen, dlen; - REAL abdet[576], cddet[576]; - int ablen, cdlen; - REAL fin1[1152]; - int finlength; - - REAL aextail, bextail, cextail, dextail; - REAL aeytail, beytail, ceytail, deytail; - REAL aeztail, beztail, ceztail, deztail; - - INEXACT REAL bvirt; - REAL avirt, bround, around; - INEXACT REAL c; - INEXACT REAL abig; - REAL ahi, alo, bhi, blo; - REAL err1, err2, err3; - INEXACT REAL _i, _j; - REAL _0; - - aex = (REAL) (pa[0] - pe[0]); - bex = (REAL) (pb[0] - pe[0]); - cex = (REAL) (pc[0] - pe[0]); - dex = (REAL) (pd[0] - pe[0]); - aey = (REAL) (pa[1] - pe[1]); - bey = (REAL) (pb[1] - pe[1]); - cey = (REAL) (pc[1] - pe[1]); - dey = (REAL) (pd[1] - pe[1]); - aez = (REAL) (pa[2] - pe[2]); - bez = (REAL) (pb[2] - pe[2]); - cez = (REAL) (pc[2] - pe[2]); - dez = (REAL) (pd[2] - pe[2]); - - Two_Product(aex, bey, aexbey1, aexbey0); - Two_Product(bex, aey, bexaey1, bexaey0); - Two_Two_Diff(aexbey1, aexbey0, bexaey1, bexaey0, ab3, ab[2], ab[1], ab[0]); - ab[3] = ab3; - - Two_Product(bex, cey, bexcey1, bexcey0); - Two_Product(cex, bey, cexbey1, cexbey0); - Two_Two_Diff(bexcey1, bexcey0, cexbey1, cexbey0, bc3, bc[2], bc[1], bc[0]); - bc[3] = bc3; - - Two_Product(cex, dey, cexdey1, cexdey0); - Two_Product(dex, cey, dexcey1, dexcey0); - Two_Two_Diff(cexdey1, cexdey0, dexcey1, dexcey0, cd3, cd[2], cd[1], cd[0]); - cd[3] = cd3; - - Two_Product(dex, aey, dexaey1, dexaey0); - Two_Product(aex, dey, aexdey1, aexdey0); - Two_Two_Diff(dexaey1, dexaey0, aexdey1, aexdey0, da3, da[2], da[1], da[0]); - da[3] = da3; - - Two_Product(aex, cey, aexcey1, aexcey0); - Two_Product(cex, aey, cexaey1, cexaey0); - Two_Two_Diff(aexcey1, aexcey0, cexaey1, cexaey0, ac3, ac[2], ac[1], ac[0]); - ac[3] = ac3; - - Two_Product(bex, dey, bexdey1, bexdey0); - Two_Product(dex, bey, dexbey1, dexbey0); - Two_Two_Diff(bexdey1, bexdey0, dexbey1, dexbey0, bd3, bd[2], bd[1], bd[0]); - bd[3] = bd3; - - temp8alen = scale_expansion_zeroelim(4, cd, bez, temp8a); - temp8blen = scale_expansion_zeroelim(4, bd, -cez, temp8b); - temp8clen = scale_expansion_zeroelim(4, bc, dez, temp8c); - temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, - temp8blen, temp8b, temp16); - temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c, - temp16len, temp16, temp24); - temp48len = scale_expansion_zeroelim(temp24len, temp24, aex, temp48); - xlen = scale_expansion_zeroelim(temp48len, temp48, -aex, xdet); - temp48len = scale_expansion_zeroelim(temp24len, temp24, aey, temp48); - ylen = scale_expansion_zeroelim(temp48len, temp48, -aey, ydet); - temp48len = scale_expansion_zeroelim(temp24len, temp24, aez, temp48); - zlen = scale_expansion_zeroelim(temp48len, temp48, -aez, zdet); - xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet); - alen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, adet); - - temp8alen = scale_expansion_zeroelim(4, da, cez, temp8a); - temp8blen = scale_expansion_zeroelim(4, ac, dez, temp8b); - temp8clen = scale_expansion_zeroelim(4, cd, aez, temp8c); - temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, - temp8blen, temp8b, temp16); - temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c, - temp16len, temp16, temp24); - temp48len = scale_expansion_zeroelim(temp24len, temp24, bex, temp48); - xlen = scale_expansion_zeroelim(temp48len, temp48, bex, xdet); - temp48len = scale_expansion_zeroelim(temp24len, temp24, bey, temp48); - ylen = scale_expansion_zeroelim(temp48len, temp48, bey, ydet); - temp48len = scale_expansion_zeroelim(temp24len, temp24, bez, temp48); - zlen = scale_expansion_zeroelim(temp48len, temp48, bez, zdet); - xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet); - blen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, bdet); - - temp8alen = scale_expansion_zeroelim(4, ab, dez, temp8a); - temp8blen = scale_expansion_zeroelim(4, bd, aez, temp8b); - temp8clen = scale_expansion_zeroelim(4, da, bez, temp8c); - temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, - temp8blen, temp8b, temp16); - temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c, - temp16len, temp16, temp24); - temp48len = scale_expansion_zeroelim(temp24len, temp24, cex, temp48); - xlen = scale_expansion_zeroelim(temp48len, temp48, -cex, xdet); - temp48len = scale_expansion_zeroelim(temp24len, temp24, cey, temp48); - ylen = scale_expansion_zeroelim(temp48len, temp48, -cey, ydet); - temp48len = scale_expansion_zeroelim(temp24len, temp24, cez, temp48); - zlen = scale_expansion_zeroelim(temp48len, temp48, -cez, zdet); - xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet); - clen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, cdet); - - temp8alen = scale_expansion_zeroelim(4, bc, aez, temp8a); - temp8blen = scale_expansion_zeroelim(4, ac, -bez, temp8b); - temp8clen = scale_expansion_zeroelim(4, ab, cez, temp8c); - temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, - temp8blen, temp8b, temp16); - temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c, - temp16len, temp16, temp24); - temp48len = scale_expansion_zeroelim(temp24len, temp24, dex, temp48); - xlen = scale_expansion_zeroelim(temp48len, temp48, dex, xdet); - temp48len = scale_expansion_zeroelim(temp24len, temp24, dey, temp48); - ylen = scale_expansion_zeroelim(temp48len, temp48, dey, ydet); - temp48len = scale_expansion_zeroelim(temp24len, temp24, dez, temp48); - zlen = scale_expansion_zeroelim(temp48len, temp48, dez, zdet); - xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet); - dlen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, ddet); - - ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet); - cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet); - finlength = fast_expansion_sum_zeroelim(ablen, abdet, cdlen, cddet, fin1); - - det = estimate(finlength, fin1); - errbound = isperrboundB * permanent; - if ((det >= errbound) || (-det >= errbound)) { - return det; - } - - Two_Diff_Tail(pa[0], pe[0], aex, aextail); - Two_Diff_Tail(pa[1], pe[1], aey, aeytail); - Two_Diff_Tail(pa[2], pe[2], aez, aeztail); - Two_Diff_Tail(pb[0], pe[0], bex, bextail); - Two_Diff_Tail(pb[1], pe[1], bey, beytail); - Two_Diff_Tail(pb[2], pe[2], bez, beztail); - Two_Diff_Tail(pc[0], pe[0], cex, cextail); - Two_Diff_Tail(pc[1], pe[1], cey, ceytail); - Two_Diff_Tail(pc[2], pe[2], cez, ceztail); - Two_Diff_Tail(pd[0], pe[0], dex, dextail); - Two_Diff_Tail(pd[1], pe[1], dey, deytail); - Two_Diff_Tail(pd[2], pe[2], dez, deztail); - if ((aextail == 0.0) && (aeytail == 0.0) && (aeztail == 0.0) - && (bextail == 0.0) && (beytail == 0.0) && (beztail == 0.0) - && (cextail == 0.0) && (ceytail == 0.0) && (ceztail == 0.0) - && (dextail == 0.0) && (deytail == 0.0) && (deztail == 0.0)) { - return det; - } - - errbound = isperrboundC * permanent + resulterrbound * Absolute(det); - abeps = (aex * beytail + bey * aextail) - - (aey * bextail + bex * aeytail); - bceps = (bex * ceytail + cey * bextail) - - (bey * cextail + cex * beytail); - cdeps = (cex * deytail + dey * cextail) - - (cey * dextail + dex * ceytail); - daeps = (dex * aeytail + aey * dextail) - - (dey * aextail + aex * deytail); - aceps = (aex * ceytail + cey * aextail) - - (aey * cextail + cex * aeytail); - bdeps = (bex * deytail + dey * bextail) - - (bey * dextail + dex * beytail); - det += (((bex * bex + bey * bey + bez * bez) - * ((cez * daeps + dez * aceps + aez * cdeps) - + (ceztail * da3 + deztail * ac3 + aeztail * cd3)) - + (dex * dex + dey * dey + dez * dez) - * ((aez * bceps - bez * aceps + cez * abeps) - + (aeztail * bc3 - beztail * ac3 + ceztail * ab3))) - - ((aex * aex + aey * aey + aez * aez) - * ((bez * cdeps - cez * bdeps + dez * bceps) - + (beztail * cd3 - ceztail * bd3 + deztail * bc3)) - + (cex * cex + cey * cey + cez * cez) - * ((dez * abeps + aez * bdeps + bez * daeps) - + (deztail * ab3 + aeztail * bd3 + beztail * da3)))) - + 2.0 * (((bex * bextail + bey * beytail + bez * beztail) - * (cez * da3 + dez * ac3 + aez * cd3) - + (dex * dextail + dey * deytail + dez * deztail) - * (aez * bc3 - bez * ac3 + cez * ab3)) - - ((aex * aextail + aey * aeytail + aez * aeztail) - * (bez * cd3 - cez * bd3 + dez * bc3) - + (cex * cextail + cey * ceytail + cez * ceztail) - * (dez * ab3 + aez * bd3 + bez * da3))); - if ((det >= errbound) || (-det >= errbound)) { - return det; - } - - return insphereexact(pa, pb, pc, pd, pe); -} - -REAL insphere(pa, pb, pc, pd, pe) -REAL *pa; -REAL *pb; -REAL *pc; -REAL *pd; -REAL *pe; -{ - REAL aex, bex, cex, dex; - REAL aey, bey, cey, dey; - REAL aez, bez, cez, dez; - REAL aexbey, bexaey, bexcey, cexbey, cexdey, dexcey, dexaey, aexdey; - REAL aexcey, cexaey, bexdey, dexbey; - REAL alift, blift, clift, dlift; - REAL ab, bc, cd, da, ac, bd; - REAL abc, bcd, cda, dab; - REAL aezplus, bezplus, cezplus, dezplus; - REAL aexbeyplus, bexaeyplus, bexceyplus, cexbeyplus; - REAL cexdeyplus, dexceyplus, dexaeyplus, aexdeyplus; - REAL aexceyplus, cexaeyplus, bexdeyplus, dexbeyplus; - REAL det; - REAL permanent, errbound; - REAL ins; - - FPU_ROUND_DOUBLE; - - aex = pa[0] - pe[0]; - bex = pb[0] - pe[0]; - cex = pc[0] - pe[0]; - dex = pd[0] - pe[0]; - aey = pa[1] - pe[1]; - bey = pb[1] - pe[1]; - cey = pc[1] - pe[1]; - dey = pd[1] - pe[1]; - aez = pa[2] - pe[2]; - bez = pb[2] - pe[2]; - cez = pc[2] - pe[2]; - dez = pd[2] - pe[2]; - - aexbey = aex * bey; - bexaey = bex * aey; - ab = aexbey - bexaey; - bexcey = bex * cey; - cexbey = cex * bey; - bc = bexcey - cexbey; - cexdey = cex * dey; - dexcey = dex * cey; - cd = cexdey - dexcey; - dexaey = dex * aey; - aexdey = aex * dey; - da = dexaey - aexdey; - - aexcey = aex * cey; - cexaey = cex * aey; - ac = aexcey - cexaey; - bexdey = bex * dey; - dexbey = dex * bey; - bd = bexdey - dexbey; - - abc = aez * bc - bez * ac + cez * ab; - bcd = bez * cd - cez * bd + dez * bc; - cda = cez * da + dez * ac + aez * cd; - dab = dez * ab + aez * bd + bez * da; - - alift = aex * aex + aey * aey + aez * aez; - blift = bex * bex + bey * bey + bez * bez; - clift = cex * cex + cey * cey + cez * cez; - dlift = dex * dex + dey * dey + dez * dez; - - det = (dlift * abc - clift * dab) + (blift * cda - alift * bcd); - - aezplus = Absolute(aez); - bezplus = Absolute(bez); - cezplus = Absolute(cez); - dezplus = Absolute(dez); - aexbeyplus = Absolute(aexbey); - bexaeyplus = Absolute(bexaey); - bexceyplus = Absolute(bexcey); - cexbeyplus = Absolute(cexbey); - cexdeyplus = Absolute(cexdey); - dexceyplus = Absolute(dexcey); - dexaeyplus = Absolute(dexaey); - aexdeyplus = Absolute(aexdey); - aexceyplus = Absolute(aexcey); - cexaeyplus = Absolute(cexaey); - bexdeyplus = Absolute(bexdey); - dexbeyplus = Absolute(dexbey); - permanent = ((cexdeyplus + dexceyplus) * bezplus - + (dexbeyplus + bexdeyplus) * cezplus - + (bexceyplus + cexbeyplus) * dezplus) - * alift - + ((dexaeyplus + aexdeyplus) * cezplus - + (aexceyplus + cexaeyplus) * dezplus - + (cexdeyplus + dexceyplus) * aezplus) - * blift - + ((aexbeyplus + bexaeyplus) * dezplus - + (bexdeyplus + dexbeyplus) * aezplus - + (dexaeyplus + aexdeyplus) * bezplus) - * clift - + ((bexceyplus + cexbeyplus) * aezplus - + (cexaeyplus + aexceyplus) * bezplus - + (aexbeyplus + bexaeyplus) * cezplus) - * dlift; - errbound = isperrboundA * permanent; - if ((det > errbound) || (-det > errbound)) { - FPU_RESTORE; - return det; - } - - ins = insphereadapt(pa, pb, pc, pd, pe, permanent); - FPU_RESTORE; - return ins; -} Index: trunk/src/3rd/gts/surface.c =================================================================== --- trunk/src/3rd/gts/surface.c (revision 1064) +++ trunk/src/3rd/gts/surface.c (nonexistent) @@ -1,2743 +0,0 @@ -/* GTS - Library for the manipulation of triangulated surfaces - * Copyright (C) 1999 Stéphane Popinet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include -#include -#include -#include "gts.h" - -#include "gts-private.h" - -static void destroy_foreach_face (GtsFace * f, GtsSurface * s) -{ - f->surfaces = g_slist_remove (f->surfaces, s); - if (!GTS_OBJECT_DESTROYED (f) && - !gts_allow_floating_faces && f->surfaces == NULL) - gts_object_destroy (GTS_OBJECT (f)); -} - -static void surface_destroy (GtsObject * object) -{ - GtsSurface * surface = GTS_SURFACE (object); - - gts_surface_foreach_face (surface, (GtsFunc) destroy_foreach_face, surface); -#ifdef USE_SURFACE_BTREE - g_tree_destroy (surface->faces); -#else /* not USE_SURFACE_BTREE */ - g_hash_table_destroy (surface->faces); -#endif /* not USE_SURFACE_BTREE */ - - (* GTS_OBJECT_CLASS (gts_surface_class ())->parent_class->destroy) (object); -} - -static void surface_write (GtsObject * object, FILE * fptr) -{ - fprintf (fptr, " %s %s %s %s", - object->klass->info.name, - GTS_OBJECT_CLASS (GTS_SURFACE (object)->face_class)->info.name, - GTS_OBJECT_CLASS (GTS_SURFACE (object)->edge_class)->info.name, - GTS_POINT_CLASS (GTS_SURFACE (object)->vertex_class)->binary ? - "GtsVertexBinary" : - GTS_OBJECT_CLASS (GTS_SURFACE (object)->vertex_class)->info.name); -} - -static void surface_class_init (GtsSurfaceClass * klass) -{ - GTS_OBJECT_CLASS (klass)->destroy = surface_destroy; - GTS_OBJECT_CLASS (klass)->write = surface_write; - klass->add_face = NULL; - klass->remove_face = NULL; -} - -#ifdef USE_SURFACE_BTREE -static gint compare_pointers (gconstpointer a, gconstpointer b) -{ - if (GPOINTER_TO_UINT (a) < GPOINTER_TO_UINT (b)) - return -1; - if (GPOINTER_TO_UINT (a) > GPOINTER_TO_UINT (b)) - return 1; - return 0; -} -#endif /* USE_SURFACE_BTREE */ - -static void surface_init (GtsSurface * surface) -{ -#ifdef USE_SURFACE_BTREE - surface->faces = g_tree_new (compare_pointers); -#else /* not USE_SURFACE_BTREE */ - surface->faces = g_hash_table_new (NULL, NULL); -#endif /* not USE_SURFACE_BTREE */ - surface->vertex_class = gts_vertex_class (); - surface->edge_class = gts_edge_class (); - surface->face_class = gts_face_class (); - surface->keep_faces = FALSE; -} - -/** - * gts_surface_class: - * - * Returns: the #GtsSurfaceClass. - */ -GtsSurfaceClass * gts_surface_class (void) -{ - static GtsSurfaceClass * klass = NULL; - - if (klass == NULL) { - GtsObjectClassInfo surface_info = { - "GtsSurface", - sizeof (GtsSurface), - sizeof (GtsSurfaceClass), - (GtsObjectClassInitFunc) surface_class_init, - (GtsObjectInitFunc) surface_init, - (GtsArgSetFunc) NULL, - (GtsArgGetFunc) NULL - }; - klass = gts_object_class_new (gts_object_class (), &surface_info); - } - - return klass; -} - -/** - * gts_surface_new: - * @klass: a #GtsSurfaceClass. - * @face_class: a #GtsFaceClass. - * @edge_class: a #GtsEdgeClass. - * @vertex_class: a #GtsVertexClass. - * - * Returns: a new empty #GtsSurface. - */ -GtsSurface * gts_surface_new (GtsSurfaceClass * klass, - GtsFaceClass * face_class, - GtsEdgeClass * edge_class, - GtsVertexClass * vertex_class) -{ - GtsSurface * s; - - s = GTS_SURFACE (gts_object_new (GTS_OBJECT_CLASS (klass))); - s->vertex_class = vertex_class; - s->edge_class = edge_class; - s->face_class = face_class; - - return s; -} - -/** - * gts_surface_add_face: - * @s: a #GtsSurface. - * @f: a #GtsFace. - * - * Adds face @f to surface @s. - */ -void gts_surface_add_face (GtsSurface * s, GtsFace * f) -{ - g_return_if_fail (s != NULL); - g_return_if_fail (f != NULL); - - g_assert (s->keep_faces == FALSE); - -#ifdef USE_SURFACE_BTREE - if (!g_tree_lookup (s->faces, f)) { - f->surfaces = g_slist_prepend (f->surfaces, s); - g_tree_insert (s->faces, f, f); - } -#else /* not USE_SURFACE_BTREE */ - if (!g_hash_table_lookup (s->faces, f)) { - f->surfaces = g_slist_prepend (f->surfaces, s); - g_hash_table_insert (s->faces, f, f); - } -#endif /* not USE_SURFACE_BTREE */ - - if (GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass)->add_face) - (* GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass)->add_face) (s, f); -} - -/** - * gts_surface_remove_face: - * @s: a #GtsSurface. - * @f: a #GtsFace. - * - * Removes face @f from surface @s. - */ -void gts_surface_remove_face (GtsSurface * s, - GtsFace * f) -{ - g_return_if_fail (s != NULL); - g_return_if_fail (f != NULL); - - g_assert (s->keep_faces == FALSE); - -#ifdef USE_SURFACE_BTREE - g_tree_remove (s->faces, f); -#else /* not USE_SURFACE_BTREE */ - g_hash_table_remove (s->faces, f); -#endif /* not USE_SURFACE_BTREE */ - - f->surfaces = g_slist_remove (f->surfaces, s); - - if (GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass)->remove_face) - (* GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass)->remove_face) (s, f); - - if (!GTS_OBJECT_DESTROYED (f) && - !gts_allow_floating_faces && - f->surfaces == NULL) - gts_object_destroy (GTS_OBJECT (f)); -} - -/** - * gts_surface_read: - * @surface: a #GtsSurface. - * @f: a #GtsFile. - * - * Add to @surface the data read from @f. The format of the file pointed to - * by @f is as described in gts_surface_write(). - * - * Returns: 0 if successful or the line number at which the parsing - * stopped in case of error (in which case the @error field of @f is - * set to a description of the error which occured). - */ -/* Update split.c/surface_read() if modifying this function */ -guint gts_surface_read (GtsSurface * surface, GtsFile * f) -{ - GtsVertex ** vertices; - GtsEdge ** edges; - guint n, nv, ne, nf; - - g_return_val_if_fail (surface != NULL, 1); - g_return_val_if_fail (f != NULL, 1); - - if (f->type != GTS_INT) { - gts_file_error (f, "expecting an integer (number of vertices)"); - return f->line; - } - nv = atoi (f->token->str); - - gts_file_next_token (f); - if (f->type != GTS_INT) { - gts_file_error (f, "expecting an integer (number of edges)"); - return f->line; - } - ne = atoi (f->token->str); - - gts_file_next_token (f); - if (f->type != GTS_INT) { - gts_file_error (f, "expecting an integer (number of faces)"); - return f->line; - } - nf = atoi (f->token->str); - - gts_file_next_token (f); - if (f->type == GTS_STRING) { - if (f->type != GTS_STRING) { - gts_file_error (f, "expecting a string (GtsSurfaceClass)"); - return f->line; - } - gts_file_next_token (f); - if (f->type != GTS_STRING) { - gts_file_error (f, "expecting a string (GtsFaceClass)"); - return f->line; - } - gts_file_next_token (f); - if (f->type != GTS_STRING) { - gts_file_error (f, "expecting a string (GtsEdgeClass)"); - return f->line; - } - gts_file_next_token (f); - if (f->type != GTS_STRING) { - gts_file_error (f, "expecting a string (GtsVertexClass)"); - return f->line; - } - if (!strcmp (f->token->str, "GtsVertexBinary")) - GTS_POINT_CLASS (surface->vertex_class)->binary = TRUE; - else { - GTS_POINT_CLASS (surface->vertex_class)->binary = FALSE; - gts_file_first_token_after (f, '\n'); - } - } - else - gts_file_first_token_after (f, '\n'); - - if (nf <= 0) - return 0; - - /* allocate nv + 1 just in case nv == 0 */ - vertices = g_malloc ((nv + 1)*sizeof (GtsVertex *)); - edges = g_malloc ((ne + 1)*sizeof (GtsEdge *)); - - n = 0; - while (n < nv && f->type != GTS_ERROR) { - GtsObject * new_vertex = - gts_object_new (GTS_OBJECT_CLASS (surface->vertex_class)); - - (* GTS_OBJECT_CLASS (surface->vertex_class)->read) (&new_vertex, f); - if (f->type != GTS_ERROR) { - if (!GTS_POINT_CLASS (surface->vertex_class)->binary) - gts_file_first_token_after (f, '\n'); - vertices[n++] = GTS_VERTEX (new_vertex); - } - else - gts_object_destroy (new_vertex); - } - if (f->type == GTS_ERROR) - nv = n; - if (GTS_POINT_CLASS (surface->vertex_class)->binary) - gts_file_first_token_after (f, '\n'); - - n = 0; - while (n < ne && f->type != GTS_ERROR) { - guint p1, p2; - - if (f->type != GTS_INT) - gts_file_error (f, "expecting an integer (first vertex index)"); - else { - p1 = atoi (f->token->str); - if (p1 == 0 || p1 > nv) - gts_file_error (f, "vertex index `%d' is out of range `[1,%d]'", - p1, nv); - else { - gts_file_next_token (f); - if (f->type != GTS_INT) - gts_file_error (f, "expecting an integer (second vertex index)"); - else { - p2 = atoi (f->token->str); - if (p2 == 0 || p2 > nv) - gts_file_error (f, "vertex index `%d' is out of range `[1,%d]'", - p2, nv); - else { - GtsEdge * new_edge = - gts_edge_new (surface->edge_class, - vertices[p1 - 1], vertices[p2 - 1]); - - gts_file_next_token (f); - if (f->type != '\n') - if (GTS_OBJECT_CLASS (surface->edge_class)->read) - (*GTS_OBJECT_CLASS (surface->edge_class)->read) - ((GtsObject **) &new_edge, f); - gts_file_first_token_after (f, '\n'); - edges[n++] = new_edge; - } - } - } - } - } - if (f->type == GTS_ERROR) - ne = n; - - n = 0; - while (n < nf && f->type != GTS_ERROR) { - guint s1, s2, s3; - - if (f->type != GTS_INT) - gts_file_error (f, "expecting an integer (first edge index)"); - else { - s1 = atoi (f->token->str); - if (s1 == 0 || s1 > ne) - gts_file_error (f, "edge index `%d' is out of range `[1,%d]'", - s1, ne); - else { - gts_file_next_token (f); - if (f->type != GTS_INT) - gts_file_error (f, "expecting an integer (second edge index)"); - else { - s2 = atoi (f->token->str); - if (s2 == 0 || s2 > ne) - gts_file_error (f, "edge index `%d' is out of range `[1,%d]'", - s2, ne); - else { - gts_file_next_token (f); - if (f->type != GTS_INT) - gts_file_error (f, "expecting an integer (third edge index)"); - else { - s3 = atoi (f->token->str); - if (s3 == 0 || s3 > ne) - gts_file_error (f, "edge index `%d' is out of range `[1,%d]'", - s3, ne); - else { - GtsFace * new_face = gts_face_new (surface->face_class, - edges[s1 - 1], - edges[s2 - 1], - edges[s3 - 1]); - - gts_file_next_token (f); - if (f->type != '\n') - if (GTS_OBJECT_CLASS (surface->face_class)->read) - (*GTS_OBJECT_CLASS (surface->face_class)->read) - ((GtsObject **) &new_face, f); - gts_file_first_token_after (f, '\n'); - gts_surface_add_face (surface, new_face); - n++; - } - } - } - } - } - } - } - - if (f->type == GTS_ERROR) { - gts_allow_floating_vertices = TRUE; - while (nv) - gts_object_destroy (GTS_OBJECT (vertices[nv-- - 1])); - gts_allow_floating_vertices = FALSE; - } - - g_free (vertices); - g_free (edges); - - if (f->type == GTS_ERROR) - return f->line; - return 0; -} - -static void sum_area (GtsFace * f, gdouble * area) { - *area += gts_triangle_area (GTS_TRIANGLE (f)); -} - -/** - * gts_surface_area: - * @s: a #GtsSurface. - * - * Returns: the area of @s obtained as the sum of the signed areas of its - * faces. - */ -gdouble gts_surface_area (GtsSurface * s) -{ - gdouble area = 0.0; - gts_surface_foreach_face (s, (GtsFunc)sum_area, &area); - return area; -} - -/** - * gts_range_init: - * @r: a #GtsRange. - * - * Initializes a #GtsRange. - */ -void gts_range_init (GtsRange * r) -{ - g_return_if_fail (r != NULL); - - r->max = - G_MAXDOUBLE; - r->min = G_MAXDOUBLE; - r->sum = r->sum2 = 0.0; - r->n = 0; -} - -/** - * gts_range_reset: - * @r: a #GtsRange. - * - * Sets all the fields of @r to 0. - */ -void gts_range_reset (GtsRange * r) -{ - g_return_if_fail (r != NULL); - - r->max = 0.0; - r->min = 0.0; - r->sum = r->sum2 = 0.0; - r->n = 0; -} - -/** - * gts_range_add_value: - * @r: a #GtsRange. - * @val: a value to add to @r. - * - * Adds @val to @r. - */ -void gts_range_add_value (GtsRange * r, gdouble val) -{ - g_return_if_fail (r != NULL); - - if (val < r->min) r->min = val; - if (val > r->max) r->max = val; - r->sum += val; - r->sum2 += val*val; - r->n++; -} - -/** - * gts_range_update: - * @r: a #GtsRange. - * - * Updates the fields of @r. - */ -void gts_range_update (GtsRange * r) -{ - g_return_if_fail (r != NULL); - - if (r->n > 0) { - if (r->sum2 - r->sum*r->sum/(gdouble) r->n >= 0.) - r->stddev = sqrt ((r->sum2 - r->sum*r->sum/(gdouble) r->n) - /(gdouble) r->n); - else - r->stddev = 0.; - r->mean = r->sum/(gdouble) r->n; - } - else - r->min = r->max = r->mean = r->stddev = 0.; -} - -/** - * gts_range_print: - * @r: a #GtsRange. - * @fptr: a file pointer. - * - * Writes a text representation of @r in @fptr. - */ -void gts_range_print (GtsRange * r, FILE * fptr) -{ - g_return_if_fail (r != NULL); - g_return_if_fail (fptr != NULL); - fprintf (fptr, "min: %g mean: %g | %g max: %g", - r->min, r->mean, r->stddev, r->max); -} - -static void stats_foreach_vertex (GtsVertex * v, GtsSurfaceStats * stats) -{ - GSList * i = v->segments; - guint nedges = 0; - - while (i) { - if (GTS_IS_EDGE (i->data) && - gts_edge_has_parent_surface (i->data, stats->parent)) - nedges++; - i = i->next; - } - gts_range_add_value (&stats->edges_per_vertex, nedges); -} - -static void stats_foreach_edge (GtsEdge * e, GtsSurfaceStats * stats) -{ - guint nt = gts_edge_face_number (e, stats->parent); - - if (gts_segment_is_duplicate (GTS_SEGMENT (e))) - stats->n_duplicate_edges++; - if (nt == 1) - stats->n_boundary_edges++; - else if (nt > 2) - stats->n_non_manifold_edges++; - gts_range_add_value (&stats->faces_per_edge, nt); -} - -static void stats_foreach_face (GtsTriangle * t, GtsSurfaceStats * stats) -{ - if (!gts_face_is_compatible (GTS_FACE (t), stats->parent)) - stats->n_incompatible_faces++; - if (gts_triangle_is_duplicate (t)) - stats->n_duplicate_faces++; - stats->n_faces++; -} - -/** - * gts_surface_stats: - * @s: a #GtsSurface. - * @stats: a #GtsSurfaceStats. - * - * Fills @stats with the statistics relevant to surface @s. - */ -void gts_surface_stats (GtsSurface * s, GtsSurfaceStats * stats) -{ - g_return_if_fail (s != NULL); - g_return_if_fail (stats != NULL); - - stats->parent = s; - stats->n_faces = 0; - stats->n_incompatible_faces = 0; - stats->n_duplicate_faces = 0; - stats->n_duplicate_edges = 0; - stats->n_boundary_edges = 0; - stats->n_non_manifold_edges = 0; - gts_range_init (&stats->edges_per_vertex); - gts_range_init (&stats->faces_per_edge); - - gts_surface_foreach_vertex (s, (GtsFunc) stats_foreach_vertex, stats); - gts_surface_foreach_edge (s, (GtsFunc) stats_foreach_edge, stats); - gts_surface_foreach_face (s, (GtsFunc) stats_foreach_face, stats); - - gts_range_update (&stats->edges_per_vertex); - gts_range_update (&stats->faces_per_edge); -} - -static void quality_foreach_edge (GtsSegment * s, - GtsSurfaceQualityStats * stats) -{ - GSList * i = GTS_EDGE (s)->triangles; - - gts_range_add_value (&stats->edge_length, - gts_point_distance (GTS_POINT (s->v1), - GTS_POINT (s->v2))); - while (i) { - GSList * j = i->next; - while (j) { - gts_range_add_value (&stats->edge_angle, - fabs (gts_triangles_angle (i->data, j->data))); - j = j->next; - } - i = i->next; - } -} - -static void quality_foreach_face (GtsTriangle * t, - GtsSurfaceQualityStats * stats) -{ - gts_range_add_value (&stats->face_quality, gts_triangle_quality (t)); - gts_range_add_value (&stats->face_area, gts_triangle_area (t)); -} - -/** - * gts_surface_quality_stats: - * @s: a #GtsSurface. - * @stats: a #GtsSurfaceQualityStats. - * - * Fills @stats with quality statistics relevant to surface @s. - */ -void gts_surface_quality_stats (GtsSurface * s, GtsSurfaceQualityStats * stats) -{ - g_return_if_fail (s != NULL); - g_return_if_fail (stats != NULL); - - stats->parent = s; - gts_range_init (&stats->face_quality); - gts_range_init (&stats->face_area); - gts_range_init (&stats->edge_length); - gts_range_init (&stats->edge_angle); - - gts_surface_foreach_edge (s, (GtsFunc) quality_foreach_edge, stats); - gts_surface_foreach_face (s, (GtsFunc) quality_foreach_face, stats); - - gts_range_update (&stats->face_quality); - gts_range_update (&stats->face_area); - gts_range_update (&stats->edge_length); - gts_range_update (&stats->edge_angle); -} - -/** - * gts_surface_print_stats: - * @s: a #GtsSurface. - * @fptr: a file pointer. - * - * Writes in the file pointed to by @fptr the statistics for surface @s. - */ -void gts_surface_print_stats (GtsSurface * s, FILE * fptr) -{ - GtsSurfaceStats stats; - GtsSurfaceQualityStats qstats; - - g_return_if_fail (s != NULL); - g_return_if_fail (fptr != NULL); - - gts_surface_stats (s, &stats); - gts_surface_quality_stats (s, &qstats); - - fprintf (fptr, - "# vertices: %u edges: %u faces: %u\n" - "# Connectivity statistics\n" - "# incompatible faces: %u\n" - "# duplicate faces: %u\n" - "# boundary edges: %u\n" - "# duplicate edges: %u\n" - "# non-manifold edges: %u\n", - stats.edges_per_vertex.n, - stats.faces_per_edge.n, - stats.n_faces, - stats.n_incompatible_faces, - stats.n_duplicate_faces, - stats.n_boundary_edges, - stats.n_duplicate_edges, - stats.n_non_manifold_edges); - fputs ("# edges per vertex: ", fptr); - gts_range_print (&stats.edges_per_vertex, fptr); - fputs ("\n# faces per edge: ", fptr); - gts_range_print (&stats.faces_per_edge, fptr); - fputs ("\n# Geometric statistics\n# face quality: ", fptr); - gts_range_print (&qstats.face_quality, fptr); - fputs ("\n# face area : ", fptr); - gts_range_print (&qstats.face_area, fptr); - fputs ("\n# edge length : ", fptr); - gts_range_print (&qstats.edge_length, fptr); - fputc ('\n', fptr); -} - -static void write_vertex (GtsPoint * p, gpointer * data) -{ - (*GTS_OBJECT (p)->klass->write) (GTS_OBJECT (p), (FILE *) data[0]); - if (!GTS_POINT_CLASS (GTS_OBJECT (p)->klass)->binary) - fputc ('\n', (FILE *) data[0]); - g_hash_table_insert (data[2], p, - GUINT_TO_POINTER (++(*((guint *) data[1])))); -} - -static void write_edge (GtsSegment * s, gpointer * data) -{ - fprintf ((FILE *) data[0], "%u %u", - GPOINTER_TO_UINT (g_hash_table_lookup (data[2], s->v1)), - GPOINTER_TO_UINT (g_hash_table_lookup (data[2], s->v2))); - if (GTS_OBJECT (s)->klass->write) - (*GTS_OBJECT (s)->klass->write) (GTS_OBJECT (s), (FILE *) data[0]); - fputc ('\n', (FILE *) data[0]); - g_hash_table_insert (data[3], s, - GUINT_TO_POINTER (++(*((guint *) data[1])))); -} - -static void write_face (GtsTriangle * t, gpointer * data) -{ - fprintf (data[0], "%u %u %u", - GPOINTER_TO_UINT (g_hash_table_lookup (data[3], t->e1)), - GPOINTER_TO_UINT (g_hash_table_lookup (data[3], t->e2)), - GPOINTER_TO_UINT (g_hash_table_lookup (data[3], t->e3))); - if (GTS_OBJECT (t)->klass->write) - (*GTS_OBJECT (t)->klass->write) (GTS_OBJECT (t), data[0]); - fputc ('\n', data[0]); -} - -/** - * gts_surface_write: - * @s: a #GtsSurface. - * @fptr: a file pointer. - * - * Writes in the file @fptr an ASCII representation of @s. The file - * format is as follows. - * - * All the lines beginning with #GTS_COMMENTS are ignored. The first line - * contains three unsigned integers separated by spaces. The first - * integer is the number of vertices, nv, the second is the number of - * edges, ne and the third is the number of faces, nf. - * - * Follows nv lines containing the x, y and z coordinates of the - * vertices. Follows ne lines containing the two indices (starting - * from one) of the vertices of each edge. Follows nf lines containing - * the three ordered indices (also starting from one) of the edges of - * each face. - * - * The format described above is the least common denominator to all - * GTS files. Consistent with an object-oriented approach, the GTS - * file format is extensible. Each of the lines of the file can be - * extended with user-specific attributes accessible through the - * read() and write() virtual methods of each of the objects written - * (surface, vertices, edges or faces). When read with different - * object classes, these extra attributes are just ignored. - */ -void gts_surface_write (GtsSurface * s, FILE * fptr) -{ - guint n; - gpointer data[4]; - GHashTable * vindex, * eindex; - GtsSurfaceStats stats; - - g_return_if_fail (s != NULL); - g_return_if_fail (fptr != NULL); - - data[0] = fptr; - data[1] = &n; - data[2] = vindex = g_hash_table_new (NULL, NULL); - data[3] = eindex = g_hash_table_new (NULL, NULL); - - gts_surface_stats (s, &stats); - fprintf (fptr, "%u %u %u", - stats.edges_per_vertex.n, - stats.faces_per_edge.n, - stats.n_faces); - if (GTS_OBJECT (s)->klass->write) - (*GTS_OBJECT (s)->klass->write) (GTS_OBJECT (s), fptr); - fputc ('\n', fptr); - n = 0; - gts_surface_foreach_vertex (s, (GtsFunc) write_vertex, data); - n = 0; - if (GTS_POINT_CLASS (s->vertex_class)->binary) - fputc ('\n', fptr); - gts_surface_foreach_edge (s, (GtsFunc) write_edge, data); - gts_surface_foreach_face (s, (GtsFunc) write_face, data); - g_hash_table_destroy (vindex); - g_hash_table_destroy (eindex); -} - -static void write_vertex_oogl (GtsPoint * p, gpointer * data) -{ - FILE * fp = data[0]; - - fprintf (fp, "%g %g %g", p->x, p->y, p->z); - if (GTS_OBJECT (p)->klass->color) { - GtsColor c = (* GTS_OBJECT (p)->klass->color) (GTS_OBJECT (p)); - fprintf (fp, " %g %g %g 1.0\n", c.r, c.g, c.b); - } - else - fputc ('\n', fp); - GTS_OBJECT (p)->reserved = GUINT_TO_POINTER ((*((guint *) data[1]))++); -} - -static void write_face_oogl (GtsTriangle * t, FILE * fp) -{ - GtsVertex * v1, * v2, * v3; - gts_triangle_vertices (t, &v1, &v2, &v3); - fprintf (fp, "3 %u %u %u", - GPOINTER_TO_UINT (GTS_OBJECT (v1)->reserved), - GPOINTER_TO_UINT (GTS_OBJECT (v2)->reserved), - GPOINTER_TO_UINT (GTS_OBJECT (v3)->reserved)); - if (GTS_OBJECT (t)->klass->color) { - GtsColor c = (* GTS_OBJECT (t)->klass->color) (GTS_OBJECT (t)); - fprintf (fp, " %g %g %g\n", c.r, c.g, c.b); - } - else - fputc ('\n', fp); -} - -/** - * gts_surface_write_oogl: - * @s: a #GtsSurface. - * @fptr: a file pointer. - * - * Writes in the file @fptr an OOGL (Geomview) representation of @s. - */ -void gts_surface_write_oogl (GtsSurface * s, FILE * fptr) -{ - guint n = 0; - gpointer data[2]; - GtsSurfaceStats stats; - - g_return_if_fail (s != NULL); - g_return_if_fail (fptr != NULL); - - data[0] = fptr; - data[1] = &n; - - gts_surface_stats (s, &stats); - if (GTS_OBJECT_CLASS (s->vertex_class)->color) - fputs ("COFF ", fptr); - else - fputs ("OFF ", fptr); - fprintf (fptr, "%u %u %u\n", - stats.edges_per_vertex.n, - stats.n_faces, - stats.faces_per_edge.n); - gts_surface_foreach_vertex (s, (GtsFunc) write_vertex_oogl, data); - gts_surface_foreach_face (s, (GtsFunc) write_face_oogl, fptr); - gts_surface_foreach_vertex (s, (GtsFunc) gts_object_reset_reserved, NULL); -} - -static void write_vertex_vtk (GtsPoint * p, gpointer * data) -{ - FILE * fp = data[0]; - - fprintf (fp, "%g %g %g\n", p->x, p->y, p->z); - GTS_OBJECT (p)->reserved = GUINT_TO_POINTER ((*((guint *) data[1]))++); -} - -static void write_face_vtk (GtsTriangle * t, FILE * fp) -{ - GtsVertex * v1, * v2, * v3; - gts_triangle_vertices (t, &v1, &v2, &v3); - fprintf (fp, "3 %u %u %u\n", - GPOINTER_TO_UINT (GTS_OBJECT (v1)->reserved), - GPOINTER_TO_UINT (GTS_OBJECT (v2)->reserved), - GPOINTER_TO_UINT (GTS_OBJECT (v3)->reserved)); -} - -/** - * gts_surface_write_vtk: - * @s: a #GtsSurface. - * @fptr: a file pointer. - * - * Writes in the file @fptr a VTK representation of @s. - */ -void gts_surface_write_vtk (GtsSurface * s, FILE * fptr) -{ - guint n = 0; - gpointer data[2]; - GtsSurfaceStats stats; - - g_return_if_fail (s != NULL); - g_return_if_fail (fptr != NULL); - - data[0] = fptr; - data[1] = &n; - - gts_surface_stats (s, &stats); - fprintf (fptr, - "# vtk DataFile Version 2.0\n" - "Generated by GTS\n" - "ASCII\n" - "DATASET POLYDATA\n" - "POINTS %u float\n", - stats.edges_per_vertex.n); - gts_surface_foreach_vertex (s, (GtsFunc) write_vertex_vtk, data); - fprintf (fptr, - "POLYGONS %u %u\n", - stats.n_faces, stats.n_faces*4); - gts_surface_foreach_face (s, (GtsFunc) write_face_vtk, fptr); - gts_surface_foreach_vertex (s, (GtsFunc) gts_object_reset_reserved, NULL); -} - -static void write_edge_oogl_boundary (GtsSegment * s, gpointer * data) -{ - if (!gts_edge_is_boundary (GTS_EDGE (s), data[1])) - return; - - if (GTS_OBJECT (s)->klass->color) { - GtsColor c = (* GTS_OBJECT (s)->klass->color) (GTS_OBJECT (s)); - fprintf (data[0], "VECT 1 2 1 2 1 %g %g %g %g %g %g %g %g %g 1.\n", - GTS_POINT (s->v1)->x, GTS_POINT (s->v1)->y, GTS_POINT (s->v1)->z, - GTS_POINT (s->v2)->x, GTS_POINT (s->v2)->y, GTS_POINT (s->v2)->z, - c.r, c.g, c.b); - } - else - fprintf (data[0], "VECT 1 2 0 2 0 %g %g %g %g %g %g\n", - GTS_POINT (s->v1)->x, GTS_POINT (s->v1)->y, GTS_POINT (s->v1)->z, - GTS_POINT (s->v2)->x, GTS_POINT (s->v2)->y, GTS_POINT (s->v2)->z); -} - -/** - * gts_surface_write_oogl_boundary: - * @s: a #GtsSurface. - * @fptr: a file pointer. - * - * Writes in the file @fptr an OOGL (Geomview) representation of the - * boundary of @s. - */ -void gts_surface_write_oogl_boundary (GtsSurface * s, FILE * fptr) -{ - gpointer data[2]; - - g_return_if_fail (s != NULL); - g_return_if_fail (fptr != NULL); - - data[0] = fptr; - data[1] = s; - fputs ("LIST {\n", fptr); - gts_surface_foreach_edge (s, (GtsFunc) write_edge_oogl_boundary, data); - fputs ("}\n", fptr); -} - -#ifdef USE_SURFACE_BTREE -static gint vertex_foreach_face (GtsTriangle * t, - gpointer t_data, - gpointer * info) -#else /* not USE_SURFACE_BTREE */ -static void vertex_foreach_face (GtsTriangle * t, - gpointer t_data, - gpointer * info) -#endif /* not USE_SURFACE_BTREE */ -{ - GHashTable * hash = info[0]; - gpointer data = info[1]; - GtsFunc func = (GtsFunc) info[2]; - GtsSegment - * s1 = GTS_SEGMENT (t->e1); - - if (!g_hash_table_lookup (hash, s1->v1)) { - (*func) (s1->v1, data); - g_hash_table_insert (hash, s1->v1, GINT_TO_POINTER (-1)); - } - if (!g_hash_table_lookup (hash, s1->v2)) { - (*func) (s1->v2, data); - g_hash_table_insert (hash, s1->v2, GINT_TO_POINTER (-1)); - } - if (!g_hash_table_lookup (hash, gts_triangle_vertex (t))) { - (*func) (gts_triangle_vertex (t), data); - g_hash_table_insert (hash, gts_triangle_vertex (t), - GINT_TO_POINTER (-1)); - } -#ifdef USE_SURFACE_BTREE - return FALSE; -#endif /* USE_SURFACE_BTREE */ -} - -/** - * gts_surface_foreach_vertex: - * @s: a #GtsSurface. - * @func: a #GtsFunc. - * @data: user data to be passed to @func. - * - * Calls @func once for each vertex of @s. - */ -void gts_surface_foreach_vertex (GtsSurface * s, GtsFunc func, gpointer data) -{ - gpointer info[3]; - - g_return_if_fail (s != NULL); - g_return_if_fail (func != NULL); - - /* forbid removal of faces */ - s->keep_faces = TRUE; - info[0] = g_hash_table_new (NULL, NULL); - info[1] = data; - info[2] = func; -#ifdef USE_SURFACE_BTREE - g_tree_traverse (s->faces, (GTraverseFunc) vertex_foreach_face, G_IN_ORDER, - info); -#else /* not USE_SURFACE_BTREE */ - g_hash_table_foreach (s->faces, (GHFunc) vertex_foreach_face, info); -#endif /* not USE_SURFACE_BTREE */ - g_hash_table_destroy (info[0]); - /* allow removal of faces */ - s->keep_faces = FALSE; -} - -#ifdef USE_SURFACE_BTREE -static gint edge_foreach_face (GtsTriangle * t, - gpointer t_data, - gpointer * info) -#else /* not USE_SURFACE_BTREE */ -static void edge_foreach_face (GtsTriangle * t, - gpointer t_data, - gpointer * info) -#endif /* not USE_SURFACE_BTREE */ -{ - GHashTable * hash = info[0]; - gpointer data = info[1]; - GtsFunc func = (GtsFunc) info[2]; - - if (!g_hash_table_lookup (hash, t->e1)) { - (*func) (t->e1, data); - g_hash_table_insert (hash, t->e1, GINT_TO_POINTER (-1)); - } - if (!g_hash_table_lookup (hash, t->e2)) { - (*func) (t->e2, data); - g_hash_table_insert (hash, t->e2, GINT_TO_POINTER (-1)); - } - if (!g_hash_table_lookup (hash, t->e3)) { - (*func) (t->e3, data); - g_hash_table_insert (hash, t->e3, GINT_TO_POINTER (-1)); - } -#ifdef USE_SURFACE_BTREE - return FALSE; -#endif /* not USE_SURFACE_BTREE */ -} - -/** - * gts_surface_foreach_edge: - * @s: a #GtsSurface. - * @func: a #GtsFunc. - * @data: user data to be passed to @func. - * - * Calls @func once for each edge of @s. - */ -void gts_surface_foreach_edge (GtsSurface * s, GtsFunc func, gpointer data) -{ - gpointer info[3]; - - g_return_if_fail (s != NULL); - g_return_if_fail (func != NULL); - - /* forbid removal of faces */ - s->keep_faces = TRUE; - info[0] = g_hash_table_new (NULL, NULL); - info[1] = data; - info[2] = func; -#ifdef USE_SURFACE_BTREE - g_tree_traverse (s->faces, (GTraverseFunc) edge_foreach_face, G_IN_ORDER, - info); -#else /* not USE_SURFACE_BTREE */ - g_hash_table_foreach (s->faces, (GHFunc) edge_foreach_face, info); -#endif /* not USE_SURFACE_BTREE */ - g_hash_table_destroy (info[0]); - /* allow removal of faces */ - s->keep_faces = FALSE; -} - -#ifdef USE_SURFACE_BTREE -static gint foreach_face (GtsFace * f, - gpointer t_data, - gpointer * info) -#else /* not USE_SURFACE_BTREE */ -static void foreach_face (GtsFace * f, - gpointer t_data, - gpointer * info) -#endif /* not USE_SURFACE_BTREE */ -{ - (*((GtsFunc) info[0])) (f, info[1]); -#ifdef USE_SURFACE_BTREE - return FALSE; -#endif /* USE_SURFACE_BTREE */ -} - -/** - * gts_surface_foreach_face: - * @s: a #GtsSurface. - * @func: a #GtsFunc. - * @data: user data to be passed to @func. - * - * Calls @func once for each face of @s. - */ -void gts_surface_foreach_face (GtsSurface * s, - GtsFunc func, - gpointer data) -{ - gpointer info[2]; - - g_return_if_fail (s != NULL); - g_return_if_fail (func != NULL); - - /* forbid removal of faces */ - s->keep_faces = TRUE; - info[0] = func; - info[1] = data; -#ifdef USE_SURFACE_BTREE - g_tree_traverse (s->faces, (GTraverseFunc) foreach_face, G_IN_ORDER, - info); -#else /* not USE_SURFACE_BTREE */ - g_hash_table_foreach (s->faces, (GHFunc) foreach_face, info); -#endif /* not USE_SURFACE_BTREE */ - /* allow removal of faces */ - s->keep_faces = FALSE; -} - -#ifdef USE_SURFACE_BTREE -static gint foreach_face_remove (GtsFace * f, - gpointer t_data, - gpointer * info) -{ - if ((*((GtsFunc) info[0])) (f, info[1])) { - GtsSurface * s = info[2]; - guint * n = info[3]; - - f->surfaces = g_slist_remove (f->surfaces, s); - if (!GTS_OBJECT_DESTROYED (f) && - !gts_allow_floating_faces && - f->surfaces == NULL) - gts_object_destroy (GTS_OBJECT (f)); - - if (GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass)->remove_face) - (* GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass)->remove_face) (s, f); - - g_tree_remove (s->faces, f); - (*n)++; - } - return FALSE; -} -#else /* not USE_SURFACE_BTREE */ -static gboolean foreach_face_remove (GtsFace * f, - gpointer t_data, - gpointer * info) -{ - if ((*((GtsFunc) info[0])) (f, info[1])) { - GtsSurface * s = info[2]; - - f->surfaces = g_slist_remove (f->surfaces, s); - if (!GTS_OBJECT_DESTROYED (f) && - !gts_allow_floating_faces && - f->surfaces == NULL) - gts_object_destroy (GTS_OBJECT (f)); - - if (GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass)->remove_face) - (* GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass)->remove_face) (s, f); - - return TRUE; - } - return FALSE; -} -#endif /* not USE_SURFACE_BTREE */ - -/** - * gts_surface_foreach_face_remove: - * @s: a #GtsSurface. - * @func: a #GtsFunc. - * @data: user data to be passed to @func. - * - * Calls @func once for each face of @s. If @func returns %TRUE the - * corresponding face is removed from @s (and destroyed if it does not - * belong to any other surface and #gts_allow_floating_faces is set to - * %FALSE). - * - * Returns: the number of faces removed from @s. - */ -guint gts_surface_foreach_face_remove (GtsSurface * s, - GtsFunc func, - gpointer data) -{ - gpointer info[4]; - guint n = 0; - - g_return_val_if_fail (s != NULL, 0); - g_return_val_if_fail (func != NULL, 0); - - /* forbid removal of faces */ - s->keep_faces = TRUE; - info[0] = func; - info[1] = data; - info[2] = s; -#ifdef USE_SURFACE_BTREE - info[3] = &n; - g_tree_traverse (s->faces, (GTraverseFunc) foreach_face_remove, G_PRE_ORDER, - info); -#else /* not USE_SURFACE_BTREE */ - n = g_hash_table_foreach_remove (s->faces, - (GHRFunc) foreach_face_remove, - info); -#endif /* not USE_SURFACE_BTREE */ - /* allow removal of faces */ - s->keep_faces = FALSE; - - return n; -} - -static void midvertex_insertion (GtsEdge * e, - GtsSurface * surface, - GtsEHeap * heap, - GtsRefineFunc refine_func, - gpointer refine_data, - GtsVertexClass * vertex_class, - GtsEdgeClass * edge_class) -{ - GtsVertex * midvertex; - GtsEdge * e1, * e2; - GSList * i; - - midvertex = (*refine_func) (e, vertex_class, refine_data); - e1 = gts_edge_new (edge_class, GTS_SEGMENT (e)->v1, midvertex); - gts_eheap_insert (heap, e1); - e2 = gts_edge_new (edge_class, GTS_SEGMENT (e)->v2, midvertex); - gts_eheap_insert (heap, e2); - - /* creates new faces and modifies old ones */ - i = e->triangles; - while (i) { - GtsTriangle * t = i->data; - GtsVertex * v1, * v2, * v3; - GtsEdge * te2, * te3, * ne, * tmp; - - gts_triangle_vertices_edges (t, e, &v1, &v2, &v3, &e, &te2, &te3); - ne = gts_edge_new (edge_class, midvertex, v3); - gts_eheap_insert (heap, ne); - if (GTS_SEGMENT (e1)->v1 == v2) { - tmp = e1; e1 = e2; e2 = tmp; - } - e1->triangles = g_slist_prepend (e1->triangles, t); - ne->triangles = g_slist_prepend (ne->triangles, t); - te2->triangles = g_slist_remove (te2->triangles, t); - t->e1 = e1; t->e2 = ne; t->e3 = te3; - gts_surface_add_face (surface, - gts_face_new (surface->face_class, e2, te2, ne)); - i = i->next; - } - /* destroys edge */ - g_slist_free (e->triangles); - e->triangles = NULL; - gts_object_destroy (GTS_OBJECT (e)); -} - -static gdouble edge_length2_inverse (GtsSegment * s) -{ - return - gts_point_distance2 (GTS_POINT (s->v1), GTS_POINT (s->v2)); -} - -static void create_heap_refine (GtsEdge * e, GtsEHeap * heap) -{ - gts_eheap_insert (heap, e); -} - -/** - * gts_surface_refine: - * @surface: a #GtsSurface. - * @cost_func: a function returning the cost for a given edge. - * @cost_data: user data to be passed to @cost_func. - * @refine_func: a #GtsRefineFunc. - * @refine_data: user data to be passed to @refine_func. - * @stop_func: a #GtsStopFunc. - * @stop_data: user data to be passed to @stop_func. - * - * Refine @surface using a midvertex insertion technique. All the - * edges of @surface are ordered according to @cost_func. The edges - * are then processed in order until @stop_func returns %TRUE. Each - * edge is split in two and new edges and faces are created. - * - * If @cost_func is set to %NULL, the edges are sorted according - * to their length squared (the longest is on top). - * - * If @refine_func is set to %NULL gts_segment_midvertex() is used. - * - */ -void gts_surface_refine (GtsSurface * surface, - GtsKeyFunc cost_func, - gpointer cost_data, - GtsRefineFunc refine_func, - gpointer refine_data, - GtsStopFunc stop_func, - gpointer stop_data) -{ - GtsEHeap * heap; - GtsEdge * e; - gdouble top_cost; - - g_return_if_fail (surface != NULL); - g_return_if_fail (stop_func != NULL); - - if (cost_func == NULL) - cost_func = (GtsKeyFunc) edge_length2_inverse; - if (refine_func == NULL) - refine_func = (GtsRefineFunc) gts_segment_midvertex; - - heap = gts_eheap_new (cost_func, cost_data); - gts_eheap_freeze (heap); - gts_surface_foreach_edge (surface, (GtsFunc) create_heap_refine, heap); - gts_eheap_thaw (heap); - while ((e = gts_eheap_remove_top (heap, &top_cost)) && - !(*stop_func) (top_cost, - gts_eheap_size (heap) + - gts_edge_face_number (e, surface) + 2, - stop_data)) - midvertex_insertion (e, surface, heap, refine_func, refine_data, - surface->vertex_class, surface->edge_class); - gts_eheap_destroy (heap); -} - -static GSList * edge_triangles (GtsEdge * e1, GtsEdge * e) -{ - GSList * i = e1->triangles; - GSList * triangles = NULL; - - while (i) { - GtsTriangle * t = i->data; - if (t->e1 == e || t->e2 == e || t->e3 == e) { - GtsEdge * e2; - GSList * j; - if (t->e1 == e) { - if (t->e2 == e1) - e2 = t->e3; - else - e2 = t->e2; - } - else if (t->e2 == e) { - if (t->e3 == e1) - e2 = t->e1; - else - e2 = t->e3; - } - else { - if (t->e2 == e1) - e2 = t->e1; - else - e2 = t->e2; - } - j = e2->triangles; - while (j) { - GtsTriangle * t = j->data; - if (t->e1 != e && t->e2 != e && t->e3 != e) - triangles = g_slist_prepend (triangles, t); - j = j->next; - } - } - else - triangles = g_slist_prepend (triangles, t); - i = i->next; - } - return triangles; -} - -static void replace_vertex (GSList * i, GtsVertex * v1, GtsVertex * v) -{ - while (i) { - GtsSegment * s = i->data; - if (s->v1 == v1) - s->v1 = v; - else - s->v2 = v; - i = i->next; - } -} - -/** - * gts_edge_collapse_creates_fold: - * @e: a #GtsEdge. - * @v: a #GtsVertex. - * @max: the maximum value of the square of the cosine of the angle between - * two triangles. - * - * Returns: %TRUE if collapsing edge @e to vertex @v would create - * faces making an angle the cosine squared of which would be larger than max, - * %FALSE otherwise. - */ -gboolean gts_edge_collapse_creates_fold (GtsEdge * e, - GtsVertex * v, - gdouble max) -{ - GtsVertex * v1, * v2; - GtsSegment * s; - GSList * i; - gboolean folded = FALSE; - - g_return_val_if_fail (e != NULL, TRUE); - g_return_val_if_fail (v != NULL, TRUE); - - s = GTS_SEGMENT (e); - v1 = s->v1; - v2 = s->v2; - replace_vertex (v1->segments, v1, v); - replace_vertex (v2->segments, v2, v); - - i = v1->segments; - while (i && !folded) { - GtsSegment * s = i->data; - if (GTS_IS_EDGE (s)) { - GtsEdge * e1 = GTS_EDGE (s); - if (e1 != e) { - GSList * triangles = edge_triangles (e1, e); - folded = gts_triangles_are_folded (triangles, s->v1, s->v2, max); - g_slist_free (triangles); - } - } - i = i->next; - } - - i = v2->segments; - while (i && !folded) { - GtsSegment * s = i->data; - if (GTS_IS_EDGE (s)) { - GtsEdge * e1 = GTS_EDGE (s); - if (e1 != e) { - GSList * triangles = edge_triangles (e1, e); - folded = gts_triangles_are_folded (triangles, s->v1, s->v2, max); - g_slist_free (triangles); - } - } - i = i->next; - } -#if 1 - if (!folded) { - GSList * triangles = gts_vertex_triangles (v1, NULL); - i = triangles = gts_vertex_triangles (v2, triangles); - while (i && !folded) { - GtsTriangle * t = i->data; - if (t->e1 != e && t->e2 != e && t->e3 != e) { - GtsEdge * e1 = gts_triangle_edge_opposite (t, v); - g_assert (e1); - folded = gts_triangles_are_folded (e1->triangles, - GTS_SEGMENT (e1)->v1, - GTS_SEGMENT (e1)->v2, - max); - } - i = i->next; - } - g_slist_free (triangles); - } -#endif - replace_vertex (v1->segments, v, v1); - replace_vertex (v2->segments, v, v2); - return folded; -} - -/** - * gts_edge_collapse_is_valid: - * @e: a #GtsEdge. - * - * An implementation of the topological constraints described in the - * "Mesh Optimization" article of Hoppe et al (1993). - * - * Returns: %TRUE if @e can be collapsed without violation of the topological - * constraints, %FALSE otherwise. - */ -gboolean gts_edge_collapse_is_valid (GtsEdge * e) -{ - GSList * i; - - g_return_val_if_fail (e != NULL, FALSE); - - i = GTS_SEGMENT (e)->v1->segments; - while (i) { - GtsEdge * e1 = i->data; - if (e1 != e && GTS_IS_EDGE (e1)) { - GtsEdge * e2 = NULL; - GSList * j = GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e)->v1 ? - GTS_SEGMENT (e1)->v2->segments : GTS_SEGMENT (e1)->v1->segments; - while (j && !e2) { - GtsEdge * e1 = j->data; - if (GTS_IS_EDGE (e1) && - (GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e)->v2 || - GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e)->v2)) - e2 = e1; - j = j->next; - } - if (e2 && !gts_triangle_use_edges (e, e1, e2)) - return FALSE; - } - i = i->next; - } - - if (gts_edge_is_boundary (e, NULL)) { - GtsTriangle * t = e->triangles->data; - if (gts_edge_is_boundary (t->e1, NULL) && - gts_edge_is_boundary (t->e2, NULL) && - gts_edge_is_boundary (t->e3, NULL)) - return FALSE; - } - else { - if (gts_vertex_is_boundary (GTS_SEGMENT (e)->v1, NULL) && - gts_vertex_is_boundary (GTS_SEGMENT (e)->v2, NULL)) - return FALSE; - if (gts_edge_belongs_to_tetrahedron (e)) - return FALSE; - } - - return TRUE; -} - -#define HEAP_INSERT_EDGE(h, e) (GTS_OBJECT (e)->reserved = gts_eheap_insert (h, e)) -#define HEAP_REMOVE_EDGE(h, e) (gts_eheap_remove (h, GTS_OBJECT (e)->reserved),\ - GTS_OBJECT (e)->reserved = NULL) - -static GtsVertex * edge_collapse (GtsEdge * e, - GtsEHeap * heap, - GtsCoarsenFunc coarsen_func, - gpointer coarsen_data, - GtsVertexClass * klass, - gdouble maxcosine2) -{ - GSList * i; - GtsVertex * v1 = GTS_SEGMENT (e)->v1, * v2 = GTS_SEGMENT (e)->v2, * mid; - - /* if the edge is degenerate (i.e. v1 == v2), destroy and return */ - if (v1 == v2) { - gts_object_destroy (GTS_OBJECT (e)); - return NULL; - } - - if (!gts_edge_collapse_is_valid (e)) { - GTS_OBJECT (e)->reserved = - gts_eheap_insert_with_key (heap, e, G_MAXDOUBLE); - return NULL; - } - - mid = (*coarsen_func) (e, klass, coarsen_data); - - if (gts_edge_collapse_creates_fold (e, mid, maxcosine2)) { - GTS_OBJECT (e)->reserved = - gts_eheap_insert_with_key (heap, e, G_MAXDOUBLE); - gts_object_destroy (GTS_OBJECT (mid)); - return NULL; - } - - gts_object_destroy (GTS_OBJECT (e)); - - gts_vertex_replace (v1, mid); - gts_object_destroy (GTS_OBJECT (v1)); - gts_vertex_replace (v2, mid); - gts_object_destroy (GTS_OBJECT (v2)); - - /* destroy duplicate edges */ - i = mid->segments; - while (i) { - GtsEdge * e1 = i->data; - GtsEdge * duplicate; - while ((duplicate = gts_edge_is_duplicate (e1))) { - gts_edge_replace (duplicate, GTS_EDGE (e1)); - HEAP_REMOVE_EDGE (heap, duplicate); - gts_object_destroy (GTS_OBJECT (duplicate)); - } - i = i->next; - if (!e1->triangles) { - /* e1 is the result of the collapse of one edge of a pair of identical - faces (it should not happen unless duplicate triangles are present in - the initial surface) */ - g_warning ("file %s: line %d (%s): probably duplicate triangle.", - __FILE__, __LINE__, G_GNUC_PRETTY_FUNCTION); - HEAP_REMOVE_EDGE (heap, e1); - gts_object_destroy (GTS_OBJECT (e1)); - if (i == NULL) /* mid has been destroyed */ - mid = NULL; - } - } - - return mid; -} - -/* - * I don't see where this code is ever used, but keep it for a bit - * in case it is needed for debugging - */ -#ifdef GTS_NEED_UPDATE_CLOSEST_NEIGHBORS -static void update_closest_neighbors (GtsVertex * v, GtsEHeap * heap) -{ - GSList * i = v->segments; - - while (i) { - GtsSegment * s = i->data; - if (GTS_IS_EDGE (s)) { - HEAP_REMOVE_EDGE (heap, GTS_EDGE (s)); - HEAP_INSERT_EDGE (heap, GTS_EDGE (s)); - } - i = i->next; - } -} -#endif - -static void update_2nd_closest_neighbors (GtsVertex * v, GtsEHeap * heap) -{ - GSList * i = v->segments; - GSList * list = NULL; - - while (i) { - GtsSegment * s = i->data; - if (GTS_IS_EDGE (s)) { - GtsVertex * v1 = s->v1 == v ? s->v2 : s->v1; - GSList * j = v1->segments; - while (j) { - GtsSegment * s1 = j->data; - if (GTS_IS_EDGE (s1) && !g_slist_find (list, s1)) - list = g_slist_prepend (list, s1); - j = j->next; - } - } - i = i->next; - } - - i = list; - while (i) { - GtsEdge * e = i->data; - HEAP_REMOVE_EDGE (heap, e); - HEAP_INSERT_EDGE (heap, e); - i = i->next; - } - - g_slist_free (list); -} - -static gdouble edge_length2 (GtsEdge * e) -{ - return gts_point_distance2 (GTS_POINT (GTS_SEGMENT (e)->v1), - GTS_POINT (GTS_SEGMENT (e)->v2)); -} - -static void create_heap_coarsen (GtsEdge * e, GtsEHeap * heap) -{ - HEAP_INSERT_EDGE (heap, e); -} - -/** - * gts_surface_coarsen: - * @surface: a #GtsSurface. - * @cost_func: a function returning the cost for a given edge. - * @cost_data: user data to be passed to @cost_func. - * @coarsen_func: a #GtsCoarsenVertexFunc. - * @coarsen_data: user data to be passed to @coarsen_func. - * @stop_func: a #GtsStopFunc. - * @stop_data: user data to be passed to @stop_func. - * @minangle: minimum angle between two neighboring triangles. - * - * The edges of @surface are sorted according to @cost_func to - * create a priority heap (a #GtsEHeap). The edges are extracted in - * turn from the top of the heap and collapsed (i.e. the vertices are - * replaced by the vertex returned by the @coarsen_func function) - * until the @stop_func functions returns %TRUE. - * - * If @cost_func is set to %NULL, the edges are sorted according - * to their length squared (the shortest is on top). - * - * If @coarsen_func is set to %NULL gts_segment_midvertex() is used. - * - * The minimum angle is used to avoid introducing faces which would be folded. - */ -void gts_surface_coarsen (GtsSurface * surface, - GtsKeyFunc cost_func, - gpointer cost_data, - GtsCoarsenFunc coarsen_func, - gpointer coarsen_data, - GtsStopFunc stop_func, - gpointer stop_data, - gdouble minangle) -{ - GtsEHeap * heap; - GtsEdge * e; - gdouble top_cost; - gdouble maxcosine2; - - g_return_if_fail (surface != NULL); - g_return_if_fail (stop_func != NULL); - - if (cost_func == NULL) - cost_func = (GtsKeyFunc) edge_length2; - if (coarsen_func == NULL) - coarsen_func = (GtsCoarsenFunc) gts_segment_midvertex; - - heap = gts_eheap_new (cost_func, cost_data); - maxcosine2 = cos (minangle); maxcosine2 *= maxcosine2; - - gts_eheap_freeze (heap); - gts_surface_foreach_edge (surface, (GtsFunc) create_heap_coarsen, heap); - gts_eheap_thaw (heap); - /* we want to control edge destruction manually */ - gts_allow_floating_edges = TRUE; - while ((e = gts_eheap_remove_top (heap, &top_cost)) && - (top_cost < G_MAXDOUBLE) && - !(*stop_func) (top_cost, gts_eheap_size (heap) - - gts_edge_face_number (e, surface), stop_data)) - { - GtsVertex * v = edge_collapse (e, heap, coarsen_func, coarsen_data, - surface->vertex_class, maxcosine2); - if (v != NULL) - update_2nd_closest_neighbors (v, heap); - } - gts_allow_floating_edges = FALSE; - - /* set reserved field of remaining edges back to NULL */ - if (e) GTS_OBJECT (e)->reserved = NULL; - gts_eheap_foreach (heap, (GFunc) gts_object_reset_reserved, NULL); - - gts_eheap_destroy (heap); -} - -/** - * gts_coarsen_stop_number: - * @cost: the cost of the edge collapse considered. - * @nedge: the current number of edges of the surface being simplified. - * @min_number: a pointer to the minimum number of edges desired for the - * surface being simplified. - * - * This function is to be used as the @stop_func argument of - * gts_surface_coarsen() or gts_psurface_new(). - * - * Returns: %TRUE if the edge collapse would create a surface with a smaller - * number of edges than given by @min_number, %FALSE otherwise. - */ -gboolean gts_coarsen_stop_number (gdouble cost, - guint nedge, - guint * min_number) -{ - g_return_val_if_fail (min_number != NULL, TRUE); - - if (nedge < *min_number) - return TRUE; - return FALSE; -} - -/** - * gts_coarsen_stop_cost: - * @cost: the cost of the edge collapse considered. - * @nedge: the current number of edges of the surface being simplified. - * @max_cost: a pointer to the maximum cost allowed for an edge collapse. - * - * This function is to be used as the @stop_func argument of - * gts_surface_coarsen() or gts_psurface_new(). - * - * Returns: %TRUE if the cost of the edge collapse considered is larger than - * given by @max_cost, %FALSE otherwise. - */ -gboolean gts_coarsen_stop_cost (gdouble cost, - guint nedge, - gdouble * max_cost) -{ - g_return_val_if_fail (max_cost != NULL, TRUE); - - if (cost > *max_cost) - return TRUE; - return FALSE; -} - -#define GTS_M_ICOSAHEDRON_X /* sqrt(sqrt(5)+1)/sqrt(2*sqrt(5)) */ \ - 0.850650808352039932181540497063011072240401406 -#define GTS_M_ICOSAHEDRON_Y /* sqrt(2)/sqrt(5+sqrt(5)) */ \ - 0.525731112119133606025669084847876607285497935 -#define GTS_M_ICOSAHEDRON_Z 0.0 - -static guint generate_icosahedron (GtsSurface * s) -{ - GtsVertex * v01 = gts_vertex_new (s->vertex_class, - +GTS_M_ICOSAHEDRON_Z, +GTS_M_ICOSAHEDRON_X, -GTS_M_ICOSAHEDRON_Y); - GtsVertex * v02 = gts_vertex_new (s->vertex_class, - +GTS_M_ICOSAHEDRON_X, +GTS_M_ICOSAHEDRON_Y, +GTS_M_ICOSAHEDRON_Z); - GtsVertex * v03 = gts_vertex_new (s->vertex_class, - +GTS_M_ICOSAHEDRON_Y, +GTS_M_ICOSAHEDRON_Z, -GTS_M_ICOSAHEDRON_X); - GtsVertex * v04 = gts_vertex_new (s->vertex_class, - +GTS_M_ICOSAHEDRON_Y, +GTS_M_ICOSAHEDRON_Z, +GTS_M_ICOSAHEDRON_X); - GtsVertex * v05 = gts_vertex_new (s->vertex_class, - +GTS_M_ICOSAHEDRON_X, -GTS_M_ICOSAHEDRON_Y, +GTS_M_ICOSAHEDRON_Z); - GtsVertex * v06 = gts_vertex_new (s->vertex_class, - +GTS_M_ICOSAHEDRON_Z, +GTS_M_ICOSAHEDRON_X, +GTS_M_ICOSAHEDRON_Y); - GtsVertex * v07 = gts_vertex_new (s->vertex_class, - -GTS_M_ICOSAHEDRON_Y, +GTS_M_ICOSAHEDRON_Z, +GTS_M_ICOSAHEDRON_X); - GtsVertex * v08 = gts_vertex_new (s->vertex_class, - +GTS_M_ICOSAHEDRON_Z, -GTS_M_ICOSAHEDRON_X, -GTS_M_ICOSAHEDRON_Y); - GtsVertex * v09 = gts_vertex_new (s->vertex_class, - -GTS_M_ICOSAHEDRON_X, +GTS_M_ICOSAHEDRON_Y, +GTS_M_ICOSAHEDRON_Z); - GtsVertex * v10 = gts_vertex_new (s->vertex_class, - -GTS_M_ICOSAHEDRON_Y, +GTS_M_ICOSAHEDRON_Z, -GTS_M_ICOSAHEDRON_X); - GtsVertex * v11 = gts_vertex_new (s->vertex_class, - -GTS_M_ICOSAHEDRON_X, -GTS_M_ICOSAHEDRON_Y, +GTS_M_ICOSAHEDRON_Z); - GtsVertex * v12 = gts_vertex_new (s->vertex_class, - +GTS_M_ICOSAHEDRON_Z, -GTS_M_ICOSAHEDRON_X, +GTS_M_ICOSAHEDRON_Y); - - GtsEdge * e01 = gts_edge_new (s->edge_class, v01, v02); - GtsEdge * e02 = gts_edge_new (s->edge_class, v03, v02); - GtsEdge * e03 = gts_edge_new (s->edge_class, v01, v03); - GtsEdge * e04 = gts_edge_new (s->edge_class, v04, v05); - GtsEdge * e05 = gts_edge_new (s->edge_class, v02, v05); - GtsEdge * e06 = gts_edge_new (s->edge_class, v04, v02); - GtsEdge * e07 = gts_edge_new (s->edge_class, v06, v07); - GtsEdge * e08 = gts_edge_new (s->edge_class, v04, v07); - GtsEdge * e09 = gts_edge_new (s->edge_class, v06, v04); - GtsEdge * e10 = gts_edge_new (s->edge_class, v08, v03); - GtsEdge * e11 = gts_edge_new (s->edge_class, v03, v05); - GtsEdge * e12 = gts_edge_new (s->edge_class, v08, v05); - GtsEdge * e13 = gts_edge_new (s->edge_class, v06, v09); - GtsEdge * e14 = gts_edge_new (s->edge_class, v07, v09); - GtsEdge * e15 = gts_edge_new (s->edge_class, v08, v10); - GtsEdge * e16 = gts_edge_new (s->edge_class, v03, v10); - GtsEdge * e17 = gts_edge_new (s->edge_class, v06, v01); - GtsEdge * e18 = gts_edge_new (s->edge_class, v01, v09); - GtsEdge * e19 = gts_edge_new (s->edge_class, v08, v11); - GtsEdge * e20 = gts_edge_new (s->edge_class, v10, v11); - GtsEdge * e21 = gts_edge_new (s->edge_class, v06, v02); - GtsEdge * e22 = gts_edge_new (s->edge_class, v12, v11); - GtsEdge * e23 = gts_edge_new (s->edge_class, v12, v08); - GtsEdge * e24 = gts_edge_new (s->edge_class, v12, v07); - GtsEdge * e25 = gts_edge_new (s->edge_class, v07, v11); - GtsEdge * e26 = gts_edge_new (s->edge_class, v12, v04); - GtsEdge * e27 = gts_edge_new (s->edge_class, v09, v11); - GtsEdge * e28 = gts_edge_new (s->edge_class, v10, v09); - GtsEdge * e29 = gts_edge_new (s->edge_class, v12, v05); - GtsEdge * e30 = gts_edge_new (s->edge_class, v01, v10); - - gts_surface_add_face (s, gts_face_new (s->face_class, e01, e02, e03)); - gts_surface_add_face (s, gts_face_new (s->face_class, e04, e05, e06)); - gts_surface_add_face (s, gts_face_new (s->face_class, e07, e08, e09)); - gts_surface_add_face (s, gts_face_new (s->face_class, e10, e11, e12)); - gts_surface_add_face (s, gts_face_new (s->face_class, e13, e14, e07)); - gts_surface_add_face (s, gts_face_new (s->face_class, e15, e16, e10)); - gts_surface_add_face (s, gts_face_new (s->face_class, e17, e18, e13)); - gts_surface_add_face (s, gts_face_new (s->face_class, e19, e20, e15)); - gts_surface_add_face (s, gts_face_new (s->face_class, e21, e01, e17)); - gts_surface_add_face (s, gts_face_new (s->face_class, e22, e19, e23)); - gts_surface_add_face (s, gts_face_new (s->face_class, e09, e06, e21)); - gts_surface_add_face (s, gts_face_new (s->face_class, e24, e25, e22)); - gts_surface_add_face (s, gts_face_new (s->face_class, e26, e08, e24)); - gts_surface_add_face (s, gts_face_new (s->face_class, e20, e27, e28)); - gts_surface_add_face (s, gts_face_new (s->face_class, e29, e04, e26)); - gts_surface_add_face (s, gts_face_new (s->face_class, e14, e27, e25)); - gts_surface_add_face (s, gts_face_new (s->face_class, e23, e12, e29)); - gts_surface_add_face (s, gts_face_new (s->face_class, e02, e05, e11)); - gts_surface_add_face (s, gts_face_new (s->face_class, e30, e28, e18)); - gts_surface_add_face (s, gts_face_new (s->face_class, e03, e16, e30)); - - return 0; -} - -static GtsVertex * unit_sphere_arc_midvertex (GtsSegment * s, - GtsVertexClass * vertex_class) -{ - GtsPoint * p1, * p2; - gdouble x, y, z, norm; - - p1 = GTS_POINT (s->v1); p2 = GTS_POINT (s->v2); - - x = 0.5*(p1->x + p2->x); - y = 0.5*(p1->y + p2->y); - z = 0.5*(p1->z + p2->z); - - norm = x*x + y*y + z*z; - norm = sqrt (norm); - - x /= norm; y /= norm; z /= norm; - - return gts_vertex_new (vertex_class, x, y, z); -} - -static void tessellate_face (GtsFace * f, - GtsSurface * s, - GtsRefineFunc refine_func, - gpointer refine_data, - GtsVertexClass * vertex_class, - GtsEdgeClass * edge_class) -{ - GtsTriangle * t; - GtsEdge * e1, * e2, * e3; /* former edges */ - GtsVertex * v1, * v2, * v3; /* initial vertices */ - GtsVertex * v4, * v5, * v6; /* new vertices */ - GtsEdge * e56, * e64, * e45; /* new inside edges */ - GtsEdge * e24, * e34, * e35, * e15, * e16, * e26; /* new border edges */ - GSList * dum; - GtsEdge * edum; - - t = GTS_TRIANGLE (f); - e1 = t->e1; e2 = t->e2; e3 = t->e3; - - if (GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e2)->v1) { - v1 = GTS_SEGMENT (e2)->v2; - v2 = GTS_SEGMENT (e1)->v1; - v3 = GTS_SEGMENT (e1)->v2; - } - else if (GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e2)->v2) { - v1 = GTS_SEGMENT (e2)->v1; - v2 = GTS_SEGMENT (e1)->v1; - v3 = GTS_SEGMENT (e1)->v2; - } - else if (GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e2)->v1) { - v1 = GTS_SEGMENT (e2)->v2; - v2 = GTS_SEGMENT (e1)->v2; - v3 = GTS_SEGMENT (e1)->v1; - } - else if (GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e2)->v2) { - v1 = GTS_SEGMENT (e2)->v1; - v2 = GTS_SEGMENT (e1)->v2; - v3 = GTS_SEGMENT (e1)->v1; - } - else { - v1 = v2 = v3 = NULL; - g_assert_not_reached (); - } - - e1->triangles = g_slist_remove (e1->triangles, t); - e2->triangles = g_slist_remove (e2->triangles, t); - e3->triangles = g_slist_remove (e3->triangles, t); - - if (GTS_OBJECT (e1)->reserved) { - dum = (GTS_OBJECT (e1)->reserved); - e24 = dum->data; - e34 = dum->next->data; - v4 = GTS_SEGMENT (e24)->v2; - if (GTS_SEGMENT (e24)->v1 == v3) { - edum = e34; e34 = e24; e24 = edum; - } - } - else { - v4 = (*refine_func) (e1, vertex_class, refine_data); - e24 = gts_edge_new (edge_class, v2, v4); - e34 = gts_edge_new (edge_class, v3, v4); - dum = g_slist_append (NULL, e24); - dum = g_slist_append (dum, e34); - GTS_OBJECT (e1)->reserved = dum; - } - if (GTS_OBJECT (e2)->reserved) { - dum = (GTS_OBJECT (e2)->reserved); - e35 = dum->data; - e15 = dum->next->data; - v5 = GTS_SEGMENT (e35)->v2; - if (GTS_SEGMENT (e35)->v1 == v1) { - edum = e15; e15 = e35; e35 = edum; - } - } - else { - v5 = (*refine_func) (e2, vertex_class, refine_data); - e35 = gts_edge_new (edge_class, v3, v5); - e15 = gts_edge_new (edge_class, v1, v5); - dum = g_slist_append (NULL, e35); - dum = g_slist_append (dum, e15); - GTS_OBJECT (e2)->reserved = dum; - } - if (GTS_OBJECT (e3)->reserved) { - dum = (GTS_OBJECT (e3)->reserved); - e16 = dum->data; - e26 = dum->next->data; - v6 = GTS_SEGMENT (e16)->v2; - if (GTS_SEGMENT (e16)->v1 == v2) { - edum = e16; e16 = e26; e26 = edum; - } - } - else { - v6 = (*refine_func) (e3, vertex_class, refine_data); - e16 = gts_edge_new (edge_class, v1, v6); - e26 = gts_edge_new (edge_class, v2, v6); - dum = g_slist_append (NULL, e16); - dum = g_slist_append (dum, e26); - GTS_OBJECT (e3)->reserved = dum; - } - - if (e1->triangles == NULL) { - g_slist_free (GTS_OBJECT (e1)->reserved); - GTS_OBJECT (e1)->reserved = NULL; - gts_object_destroy (GTS_OBJECT (e1)); - e1 = NULL; - } - if (e2->triangles == NULL) { - g_slist_free (GTS_OBJECT (e2)->reserved); - GTS_OBJECT (e2)->reserved = NULL; - gts_object_destroy (GTS_OBJECT (e2)); - e2 = NULL; - } - if (e3->triangles == NULL) { - g_slist_free (GTS_OBJECT (e3)->reserved); - GTS_OBJECT (e3)->reserved = NULL; - gts_object_destroy (GTS_OBJECT (e3)); - e3 = NULL; - } - - e56 = gts_edge_new (edge_class, v5, v6); - e64 = gts_edge_new (edge_class, v6, v4); - e45 = gts_edge_new (edge_class, v4, v5); - t->e1 = e56; e56->triangles = g_slist_prepend (e56->triangles, t); - t->e2 = e64; e64->triangles = g_slist_prepend (e64->triangles, t); - t->e3 = e45; e45->triangles = g_slist_prepend (e45->triangles, t); - - gts_surface_add_face (s, gts_face_new (s->face_class, e16, e56, e15)); - gts_surface_add_face (s, gts_face_new (s->face_class, e26, e24, e64)); - gts_surface_add_face (s, gts_face_new (s->face_class, e45, e34, e35)); -} - -static void create_array_tessellate (GtsFace * f, GPtrArray * array) -{ - g_ptr_array_add (array, f); -} - -/** - * gts_surface_tessellate: - * @s: a #GtsSurface. - * @refine_func: a #GtsRefineFunc. - * @refine_data: user data to be passed to @refine_func. - * - * Tessellate each triangle of @s with 4 triangles: - * the number of triangles is increased by a factor of 4. - * http://mathworld.wolfram.com/GeodesicDome.html - * - * If @refine_func is set to %NULL a mid arc function is used: if - * the surface is a polyhedron with the unit sphere as circum sphere, - * then gts_surface_tessellate() corresponds to a geodesation step - * (see gts_surface_generate_sphere()). - * - */ -void gts_surface_tessellate (GtsSurface * s, - GtsRefineFunc refine_func, - gpointer refine_data) -{ - GPtrArray * array; - guint i; - - g_return_if_fail (s != NULL); - - if (refine_func == NULL) /* tessellate_surface == geodesate_surface */ - refine_func = (GtsRefineFunc) unit_sphere_arc_midvertex; - - array = g_ptr_array_new (); - gts_surface_foreach_face (s, (GtsFunc) create_array_tessellate, array); - for(i = 0; i < array->len; i++) - tessellate_face (g_ptr_array_index (array, i), - s, refine_func, refine_data, - s->vertex_class, s->edge_class); - g_ptr_array_free (array, TRUE); -} - -/** - * gts_surface_generate_sphere: - * @s: a #GtsSurface. - * @geodesation_order: a #guint. - * - * Add a triangulated unit sphere generated by recursive subdivision to @s. - * First approximation is an isocahedron; each level of refinement - * (@geodesation_order) increases the number of triangles by a factor of 4. - * http://mathworld.wolfram.com/GeodesicDome.html - * - * Returns: @s. - */ -GtsSurface * gts_surface_generate_sphere (GtsSurface * s, - guint geodesation_order) -{ - guint cgo; - - g_return_val_if_fail (s != NULL, NULL); - g_return_val_if_fail (geodesation_order != 0, NULL); - - generate_icosahedron (s); - - for (cgo = 1; cgo < geodesation_order; cgo++) - gts_surface_tessellate (s, NULL, NULL); - - return s; -} - -static void foreach_vertex_copy (GtsPoint * p, GtsVertexClass * klass) -{ - GTS_OBJECT (p)->reserved = gts_vertex_new (klass, p->x, p->y, p->z); -} - -static void foreach_edge_copy (GtsSegment * s, GtsEdgeClass * klass) -{ - GTS_OBJECT (s)->reserved = gts_edge_new (klass, - GTS_OBJECT (s->v1)->reserved, - GTS_OBJECT (s->v2)->reserved); -} - -static void foreach_face_copy (GtsTriangle * t, - GtsSurface * s) -{ - gts_surface_add_face (s, gts_face_new (s->face_class, - GTS_OBJECT (t->e1)->reserved, - GTS_OBJECT (t->e2)->reserved, - GTS_OBJECT (t->e3)->reserved)); -} - -/** - * gts_surface_copy: - * @s1: a #GtsSurface. - * @s2: a #GtsSurface. - * - * Add a copy of all the faces, edges and vertices of @s2 to @s1. - * - * Returns: @s1. - */ -GtsSurface * gts_surface_copy (GtsSurface * s1, GtsSurface * s2) -{ - g_return_val_if_fail (s1 != NULL, NULL); - g_return_val_if_fail (s2 != NULL, NULL); - - gts_surface_foreach_vertex (s2, (GtsFunc) foreach_vertex_copy, - s1->vertex_class); - gts_surface_foreach_edge (s2, (GtsFunc) foreach_edge_copy, s1->edge_class); - gts_surface_foreach_face (s2, (GtsFunc) foreach_face_copy, s1); - - gts_surface_foreach_vertex (s2, (GtsFunc) gts_object_reset_reserved, NULL); - gts_surface_foreach_edge (s2, (GtsFunc) gts_object_reset_reserved, NULL); - - return s1; -} - -static void merge_foreach_face (GtsFace * f, - GtsSurface * s) -{ - gts_surface_add_face (s, f); -} - -/** - * gts_surface_merge: - * @s: a #GtsSurface. - * @with: another #GtsSurface. - * - * Adds all the faces of @with which do not already belong to @s - * to @s. - */ -void gts_surface_merge (GtsSurface * s, GtsSurface * with) -{ - g_return_if_fail (s != NULL); - g_return_if_fail (with != NULL); - - gts_surface_foreach_face (with, (GtsFunc) merge_foreach_face, s); -} - -static void manifold_foreach_edge (GtsEdge * e, gpointer * data) -{ - gboolean * is_manifold = data[0]; - - if (*is_manifold) { - if (gts_edge_face_number (e, data[1]) > 2) - *is_manifold = FALSE; - } -} - -/** - * gts_surface_is_manifold: - * @s: a #GtsSurface. - * - * Returns: %TRUE if the surface is a manifold, %FALSE otherwise. - */ -gboolean gts_surface_is_manifold (GtsSurface * s) -{ - gboolean is_manifold = TRUE; - gpointer data[2]; - - g_return_val_if_fail (s != NULL, FALSE); - - data[0] = &is_manifold; - data[1] = s; - gts_surface_foreach_edge (s, (GtsFunc) manifold_foreach_edge, data); - return is_manifold; -} - -static void closed_foreach_edge (GtsEdge * e, gpointer * data) -{ - gboolean * is_closed = data[0]; - - if (*is_closed) { - if (gts_edge_face_number (e, data[1]) != 2) - *is_closed = FALSE; - } -} - -/** - * gts_surface_is_closed: - * @s: a #GtsSurface. - * - * Returns: %TRUE if @s is a closed surface, %FALSE otherwise. Note that a - * closed surface is also a manifold. - */ -gboolean gts_surface_is_closed (GtsSurface * s) -{ - gboolean is_closed = TRUE; - gpointer data[2]; - - g_return_val_if_fail (s != NULL, FALSE); - - data[0] = &is_closed; - data[1] = s; - gts_surface_foreach_edge (s, (GtsFunc) closed_foreach_edge, data); - return is_closed; -} - -static void orientable_foreach_edge (GtsEdge * e, gpointer * data) -{ - gboolean * is_orientable = data[0]; - - if (*is_orientable) { - GtsSurface * surface = data[1]; - GtsFace * f1 = NULL, * f2 = NULL; - GSList * i = e->triangles; - while (i && *is_orientable) { - GtsFace * f = i->data; - if (GTS_IS_FACE (f) && gts_face_has_parent_surface (f, surface)) { - if (!f1) f1 = f; - else if (!f2) f2 = f; - else *is_orientable = FALSE; - } - i = i->next; - } - if (f1 && f2 && !gts_triangles_are_compatible (GTS_TRIANGLE (f1), - GTS_TRIANGLE (f2), e)) - *is_orientable = FALSE; - } -} - -/** - * gts_surface_is_orientable: - * @s: a #GtsSurface. - * - * Returns: %TRUE if all the faces of @s have compatible orientation - * as checked by gts_faces_are_compatible(), %FALSE otherwise. Note that - * an orientable surface is also a manifold. - */ -gboolean gts_surface_is_orientable (GtsSurface * s) -{ - gboolean is_orientable = TRUE; - gpointer data[2]; - - g_return_val_if_fail (s != NULL, FALSE); - - data[0] = &is_orientable; - data[1] = s; - gts_surface_foreach_edge (s, (GtsFunc) orientable_foreach_edge, data); - return is_orientable; -} - -static void volume_foreach_face (GtsTriangle * t, - gdouble * volume) -{ - GtsVertex * va, * vb, * vc; - GtsPoint * pa, * pb, * pc; - - gts_triangle_vertices (t, &va, &vb, &vc); - pa = GTS_POINT (va); - pb = GTS_POINT (vb); - pc = GTS_POINT (vc); - - *volume += (pa->x * (pb->y * pc->z - pb->z * pc->y) + - pb->x * (pc->y * pa->z - pc->z * pa->y) + - pc->x * (pa->y * pb->z - pa->z * pb->y)); -} - -/** - * gts_surface_volume: - * @s: a #GtsSurface. - * - * Returns: the signed volume of the domain bounded by the surface @s. It - * makes sense only if @s is a closed and orientable manifold. - */ -gdouble gts_surface_volume (GtsSurface * s) -{ - gdouble volume = 0.0; - - g_return_val_if_fail (s != NULL, 0.0); - - gts_surface_foreach_face (s, (GtsFunc) volume_foreach_face, &volume); - - return volume/6.; -} - -static void center_of_mass_foreach_face (GtsTriangle * t, - gpointer * data) -{ - GtsVertex * v1, * v2, * v3; - GtsPoint * p1, * p2, * p3; - gdouble x1, y1, z1, x2, y2, z2, nx, ny, nz; - gdouble * volume = data[0]; - gdouble * cm = data[1]; - - gts_triangle_vertices (t, &v1, &v2, &v3); - p1 = GTS_POINT (v1); - p2 = GTS_POINT (v2); - p3 = GTS_POINT (v3); - - x1 = p2->x - p1->x; - y1 = p2->y - p1->y; - z1 = p2->z - p1->z; - - x2 = p3->x - p1->x; - y2 = p3->y - p1->y; - z2 = p3->z - p1->z; - - nx = y1*z2 - z1*y2; - ny = z1*x2 - x1*z2; - nz = x1*y2 - y1*x2; - - cm[0] += nx*(p1->x*p1->x + p2->x*p2->x + p3->x*p3->x + - p1->x*p2->x + p1->x*p3->x + p2->x*p3->x); - cm[1] += ny*(p1->y*p1->y + p2->y*p2->y + p3->y*p3->y + - p1->y*p2->y + p1->y*p3->y + p2->y*p3->y); - cm[2] += nz*(p1->z*p1->z + p2->z*p2->z + p3->z*p3->z + - p1->z*p2->z + p1->z*p3->z + p2->z*p3->z); - - *volume += nx*(p1->x + p2->x + p3->x); -} - - -/** - * gts_surface_center_of_mass: - * @s: a #GtsSurface. - * @cm: a #GtsVector. - * - * Fills @cm with the coordinates of the center of mass of @s. - * - * Returns: the signed volume of the domain bounded by the surface @s. - */ -gdouble gts_surface_center_of_mass (GtsSurface * s, - GtsVector cm) -{ - gdouble volume = 0.; - gpointer data[2]; - - g_return_val_if_fail (s != NULL, 0.0); - - data[0] = &volume; - data[1] = &(cm[0]); - cm[0] = cm[1] = cm[2] = 0.; - gts_surface_foreach_face (s, (GtsFunc) center_of_mass_foreach_face, data); - - if (volume != 0.) { - cm[0] /= 4.*volume; - cm[1] /= 4.*volume; - cm[2] /= 4.*volume; - } - - return volume/6.; -} - -static void center_of_area_foreach_face (GtsTriangle * t, - gpointer * data) -{ - GtsVertex * v1, * v2, * v3; - GtsPoint * p1, * p2, * p3; - gdouble a; - gdouble * area = data[0]; - gdouble * cm = data[1]; - - gts_triangle_vertices (t, &v1, &v2, &v3); - p1 = GTS_POINT (v1); - p2 = GTS_POINT (v2); - p3 = GTS_POINT (v3); - - a = gts_triangle_area (t); - cm[0] += a*(p1->x + p2->x + p3->x); - cm[1] += a*(p1->y + p2->y + p3->y); - cm[2] += a*(p1->z + p2->z + p3->z); - *area += a; -} - - -/** - * gts_surface_center_of_area: - * @s: a #GtsSurface. - * @cm: a #GtsVector. - * - * Fills @cm with the coordinates of the center of area of @s. - * - * Returns: the area of surface @s. - */ -gdouble gts_surface_center_of_area (GtsSurface * s, - GtsVector cm) -{ - gdouble area = 0.; - gpointer data[2]; - - g_return_val_if_fail (s != NULL, 0.0); - - data[0] = &area; - data[1] = &(cm[0]); - cm[0] = cm[1] = cm[2] = 0.; - gts_surface_foreach_face (s, (GtsFunc) center_of_area_foreach_face, data); - - if (area != 0.) { - cm[0] /= 3.*area; - cm[1] /= 3.*area; - cm[2] /= 3.*area; - } - - return area; -} - -static void number_foreach (gpointer data, guint * n) -{ - (*n)++; -} - -/** - * gts_surface_vertex_number: - * @s: a #GtsSurface. - * - * Returns: the number of vertices of @s. - */ -guint gts_surface_vertex_number (GtsSurface * s) -{ - guint n = 0; - - g_return_val_if_fail (s != NULL, 0); - - gts_surface_foreach_vertex (s, (GtsFunc) number_foreach, &n); - - return n; -} - -/** - * gts_surface_edge_number: - * @s: a #GtsSurface. - * - * Returns: the number of edges of @s. - */ -guint gts_surface_edge_number (GtsSurface * s) -{ - guint n = 0; - - g_return_val_if_fail (s != NULL, 0); - - gts_surface_foreach_edge (s, (GtsFunc) number_foreach, &n); - - return n; -} - -/** - * gts_surface_face_number: - * @s: a #GtsSurface. - * - * Returns: the number of faces of @s - */ -guint gts_surface_face_number (GtsSurface * s) -{ - g_return_val_if_fail (s != NULL, 0); - -#ifdef USE_SURFACE_BTREE - return g_tree_nnodes (s->faces); -#else /* not USE_SURFACE_BTREE */ - return g_hash_table_size (s->faces); -#endif /* not USE_SURFACE_BTREE */ -} - -static void build_list_face (GtsTriangle * t, GSList ** list) -{ - *list = g_slist_prepend (*list, gts_bbox_triangle (gts_bbox_class (), t)); -} - -static void build_list_boundary (GtsEdge * e, GSList ** list) -{ - if (gts_edge_is_boundary (e, NULL)) - *list = g_slist_prepend (*list, gts_bbox_segment (gts_bbox_class (), - GTS_SEGMENT (e))); -} - -/** - * gts_surface_distance: - * @s1: a #GtsSurface. - * @s2: a #GtsSurface. - * @delta: a spatial increment defined as the percentage of the diagonal - * of the bounding box of @s2. - * @face_range: a #GtsRange. - * @boundary_range: a #GtsRange. - * - * Using the gts_bb_tree_surface_distance() and - * gts_bb_tree_surface_boundary_distance() functions fills @face_range - * and @boundary_range with the min, max and average Euclidean - * (minimum) distances between the faces of @s1 and the faces of @s2 - * and between the boundary edges of @s1 and @s2. - */ -void gts_surface_distance (GtsSurface * s1, GtsSurface * s2, gdouble delta, - GtsRange * face_range, GtsRange * boundary_range) -{ - GNode * face_tree, * boundary_tree; - GSList * bboxes; - - g_return_if_fail (s1 != NULL); - g_return_if_fail (s2 != NULL); - g_return_if_fail (delta > 0. && delta < 1.); - g_return_if_fail (face_range != NULL); - g_return_if_fail (boundary_range != NULL); - - bboxes = NULL; - gts_surface_foreach_face (s2, (GtsFunc) build_list_face, &bboxes); - if (bboxes != NULL) { - face_tree = gts_bb_tree_new (bboxes); - g_slist_free (bboxes); - - gts_bb_tree_surface_distance (face_tree, s1, - (GtsBBoxDistFunc) gts_point_triangle_distance, - delta, face_range); - gts_bb_tree_destroy (face_tree, TRUE); - - bboxes = NULL; - gts_surface_foreach_edge (s2, (GtsFunc) build_list_boundary, &bboxes); - if (bboxes != NULL) { - boundary_tree = gts_bb_tree_new (bboxes); - g_slist_free (bboxes); - - gts_bb_tree_surface_boundary_distance (boundary_tree, - s1, - (GtsBBoxDistFunc) gts_point_segment_distance, - delta, boundary_range); - gts_bb_tree_destroy (boundary_tree, TRUE); - } - else - gts_range_reset (boundary_range); - } - else { - gts_range_reset (face_range); - gts_range_reset (boundary_range); - } -} - -static void surface_boundary (GtsEdge * e, gpointer * data) -{ - GSList ** list = data[0]; - - if (gts_edge_is_boundary (e, data[1])) - *list = g_slist_prepend (*list, e); -} - -/** - * gts_surface_boundary: - * @surface: a #GtsSurface. - * - * Returns: a list of #GtsEdge boundary of @surface. - */ -GSList * gts_surface_boundary (GtsSurface * surface) -{ - GSList * list = NULL; - gpointer data[2]; - - g_return_val_if_fail (surface != NULL, NULL); - - data[0] = &list; - data[1] = surface; - gts_surface_foreach_edge (surface, (GtsFunc) surface_boundary, data); - - return list; -} - -struct _GtsSurfaceTraverse { - GtsFifo * q; - GtsSurface * s; -}; - -/** - * gts_surface_traverse_new: - * @s: a #GtsSurface. - * @f: a #GtsFace belonging to @s. - * - * Returns: a new #GtsSurfaceTraverse, initialized to start traversing - * from face @f of surface @s. - */ -GtsSurfaceTraverse * gts_surface_traverse_new (GtsSurface * s, - GtsFace * f) -{ - GtsSurfaceTraverse * t; - - g_return_val_if_fail (s != NULL, NULL); - g_return_val_if_fail (f != NULL, NULL); - g_return_val_if_fail (gts_face_has_parent_surface (f, s), NULL); - - t = g_malloc (sizeof (GtsSurfaceTraverse)); - t->q = gts_fifo_new (); - t->s = s; - GTS_OBJECT (f)->reserved = GUINT_TO_POINTER (1); - gts_fifo_push (t->q, f); - return t; -} - -static void push_neighbor (GtsFace * v, gpointer * data) -{ - if (!GTS_OBJECT (v)->reserved) { - GTS_OBJECT (v)->reserved = - GUINT_TO_POINTER (GPOINTER_TO_UINT (GTS_OBJECT (data[1])->reserved) + 1); - gts_fifo_push (data[0], v); - } -} - -/** - * gts_surface_traverse_next: - * @t: a #GtsSurfaceTraverse. - * @level: a pointer to a guint or %NULL. - * - * Returns: the next face of the traversal in breadth-first order or - * %NULL if no faces are left. If @level if not %NULL, it is filled - * with the level of the returned face (0 for the initial face, 1 for - * its neighbors and so on). - */ -GtsFace * gts_surface_traverse_next (GtsSurfaceTraverse * t, - guint * level) -{ - GtsFace * u; - - g_return_val_if_fail (t != NULL, NULL); - - u = gts_fifo_pop (t->q); - if (u) { - gpointer data[2]; - - if (level) - *level = GPOINTER_TO_UINT (GTS_OBJECT (u)->reserved); - data[0] = t->q; - data[1] = u; - gts_face_foreach_neighbor (u, t->s, (GtsFunc) push_neighbor, data); - } - return u; -} - -/** - * gts_surface_traverse_destroy: - * @t: a #GtsSurfaceTraverse. - * - * Frees all the memory allocated for @t. - */ -void gts_surface_traverse_destroy (GtsSurfaceTraverse * t) -{ - g_return_if_fail (t != NULL); - - gts_surface_foreach_face (t->s, (GtsFunc) gts_object_reset_reserved, NULL); - gts_fifo_destroy (t->q); - g_free (t); -} - -static void traverse_manifold (GtsTriangle * t, GtsSurface * s) -{ - if (g_slist_length (GTS_FACE (t)->surfaces) > 1) - return; - - gts_surface_add_face (s, GTS_FACE (t)); - if (g_slist_length (t->e1->triangles) == 2) { - if (t->e1->triangles->data != t) - traverse_manifold (t->e1->triangles->data, s); - else - traverse_manifold (t->e1->triangles->next->data, s); - } - if (g_slist_length (t->e2->triangles) == 2) { - if (t->e2->triangles->data != t) - traverse_manifold (t->e2->triangles->data, s); - else - traverse_manifold (t->e2->triangles->next->data, s); - } - if (g_slist_length (t->e3->triangles) == 2) { - if (t->e3->triangles->data != t) - traverse_manifold (t->e3->triangles->data, s); - else - traverse_manifold (t->e3->triangles->next->data, s); - } -} - -static void non_manifold_edges (GtsEdge * e, gpointer * data) -{ - GtsSurface * s = data[0]; - GSList ** non_manifold = data[1]; - - if (gts_edge_face_number (e, s) > 2) { - GSList * i = e->triangles; - - while (i) { - if (gts_face_has_parent_surface (i->data, s) && - !g_slist_find (*non_manifold, i->data)) - *non_manifold = g_slist_prepend (*non_manifold, i->data); - i = i->next; - } - } -} - -static void traverse_boundary (GtsEdge * e, gpointer * data) -{ - GtsSurface * orig = data[0]; - GSList ** components = data[1]; - GtsFace * f = gts_edge_is_boundary (e, orig); - - if (f != NULL && g_slist_length (f->surfaces) == 1) { - GtsSurface * s = - gts_surface_new (GTS_SURFACE_CLASS (GTS_OBJECT (orig)->klass), - orig->face_class, - orig->edge_class, - orig->vertex_class); - GSList * non_manifold = NULL, * i; - gpointer data[2]; - - *components = g_slist_prepend (*components, s); - data[0] = s; - data[1] = &non_manifold; - traverse_manifold (GTS_TRIANGLE (f), s); - - gts_surface_foreach_edge (s, (GtsFunc) non_manifold_edges, data); - i = non_manifold; - while (i) { - gts_surface_remove_face (s, i->data); - i = i->next; - } - g_slist_free (non_manifold); - } -} - -static void traverse_remaining (GtsFace * f, gpointer * data) -{ - GtsSurface * orig = data[0]; - GSList ** components = data[1]; - - if (g_slist_length (f->surfaces) == 1) { - GtsSurface * s = - gts_surface_new (GTS_SURFACE_CLASS (GTS_OBJECT (orig)->klass), - orig->face_class, - orig->edge_class, - orig->vertex_class); - GSList * non_manifold = NULL, * i; - gpointer data[2]; - - *components = g_slist_prepend (*components, s); - data[0] = s; - data[1] = &non_manifold; - traverse_manifold (GTS_TRIANGLE (f), s); - - gts_surface_foreach_edge (s, (GtsFunc) non_manifold_edges, data); - i = non_manifold; - while (i) { - gts_surface_remove_face (s, i->data); - i = i->next; - } - g_slist_free (non_manifold); - } -} - -/** - * gts_surface_split: - * @s: a #GtsSurface. - * - * Splits a surface into connected and manifold components. - * - * Returns: a list of new #GtsSurface. - */ -GSList * gts_surface_split (GtsSurface * s) -{ - gpointer data[2]; - GSList * components = NULL; - - g_return_val_if_fail (s != NULL, NULL); - - data[0] = s; - data[1] = &components; - - /* boundary components */ - gts_surface_foreach_edge (s, (GtsFunc) traverse_boundary, data); - - /* remaining components */ - gts_surface_foreach_face (s, (GtsFunc) traverse_remaining, data); - - return components; -} Index: trunk/src/3rd/gts/predicates.h =================================================================== --- trunk/src/3rd/gts/predicates.h (revision 1064) +++ trunk/src/3rd/gts/predicates.h (nonexistent) @@ -1,41 +0,0 @@ -/* GTS - Library for the manipulation of triangulated surfaces - * Copyright (C) 1999 Stéphane Popinet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ -/* Header file for robust predicates by Jonathan Richard Shewchuk */ - -#ifndef __PREDICATES_H__ -#define __PREDICATES_H__ - -double orient2d (double * pa, - double * pb, - double * pc); -double orient3d (double * pa, - double * pb, - double * pc, - double * pd); -double incircle (double * pa, - double * pb, - double * pc, - double * pd); -double insphere (double * pa, - double * pb, - double * pc, - double * pd, - double * pe); - -#endif /* __PREDICATES_H__ */ Index: trunk/src/3rd/gts/iso.c =================================================================== --- trunk/src/3rd/gts/iso.c (revision 1064) +++ trunk/src/3rd/gts/iso.c (nonexistent) @@ -1,455 +0,0 @@ -/* GTS - Library for the manipulation of triangulated surfaces - * Copyright (C) 1999 Stéphane Popinet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include "gts.h" - -typedef enum { LEFT = 0, RIGHT = 1 } Orientation; - -typedef struct { - GtsVertex * v; - Orientation orientation; -} OrientedVertex; - -struct _GtsIsoSlice { - OrientedVertex *** vertices; - guint nx, ny; -}; - -/* coordinates of the edges of the cube (see doc/isocube.fig) */ -static guint c[12][4] = { - {0, 0, 0, 0}, {0, 0, 0, 1}, {0, 0, 1, 1}, {0, 0, 1, 0}, - {1, 0, 0, 0}, {1, 0, 0, 1}, {1, 1, 0, 1}, {1, 1, 0, 0}, - {2, 0, 0, 0}, {2, 1, 0, 0}, {2, 1, 1, 0}, {2, 0, 1, 0}}; - -/* first index is the edge number, second index is the edge orientation - (RIGHT or LEFT), third index are the edges which this edge may connect to - in order */ -static guint edge[12][2][3] = { - {{9, 1, 8}, {4, 3, 7}}, /* 0 */ - {{6, 2, 5}, {8, 0, 9}}, /* 1 */ - {{10, 3, 11}, {5, 1, 6}}, /* 2 */ - {{7, 0, 4}, {11, 2, 10}}, /* 3 */ - {{3, 7, 0}, {8, 5, 11}}, /* 4 */ - {{11, 4, 8}, {1, 6, 2}}, /* 5 */ - {{2, 5, 1}, {9, 7, 10}}, /* 6 */ - {{10, 6, 9}, {0, 4, 3}}, /* 7 */ - {{5, 11, 4}, {0, 9, 1}}, /* 8 */ - {{1, 8, 0}, {7, 10, 6}}, /* 9 */ - {{6, 9, 7}, {3, 11, 2}}, /* 10 */ - {{2, 10, 3}, {4, 8, 5}} /* 11 */ -}; - -static void ** malloc2D (guint nx, guint ny, gulong size) -{ - void ** m = g_malloc (nx*sizeof (void *)); - guint i; - - for (i = 0; i < nx; i++) - m[i] = g_malloc0 (ny*size); - - return m; -} - -static void free2D (void ** m, guint nx) -{ - guint i; - - g_return_if_fail (m != NULL); - - for (i = 0; i < nx; i++) - g_free (m[i]); - g_free (m); -} - -/** - * gts_grid_plane_new: - * @nx: - * @ny: - * - * Returns: - */ -GtsGridPlane * gts_grid_plane_new (guint nx, guint ny) -{ - GtsGridPlane * g = g_malloc (sizeof (GtsGridPlane)); - - g->p = (GtsPoint **) malloc2D (nx, ny, sizeof (GtsPoint)); - g->nx = nx; - g->ny = ny; - - return g; -} - -/** - * gts_grid_plane_destroy: - * @g: - * - */ -void gts_grid_plane_destroy (GtsGridPlane * g) -{ - g_return_if_fail (g != NULL); - - free2D ((void **) g->p, g->nx); - g_free (g); -} - -/** - * gts_iso_slice_new: - * @nx: number of vertices in the x direction. - * @ny: number of vertices in the y direction. - * - * Returns: a new #GtsIsoSlice. - */ -GtsIsoSlice * gts_iso_slice_new (guint nx, guint ny) -{ - GtsIsoSlice * slice; - - g_return_val_if_fail (nx > 1, NULL); - g_return_val_if_fail (ny > 1, NULL); - - slice = g_malloc (sizeof (GtsIsoSlice)); - - slice->vertices = g_malloc (3*sizeof (OrientedVertex **)); - slice->vertices[0] = - (OrientedVertex **) malloc2D (nx, ny, sizeof (OrientedVertex)); - slice->vertices[1] = - (OrientedVertex **) malloc2D (nx - 1, ny, sizeof (OrientedVertex)); - slice->vertices[2] = - (OrientedVertex **) malloc2D (nx, ny - 1, sizeof (OrientedVertex)); - slice->nx = nx; - slice->ny = ny; - - return slice; -} - -/** - * gts_iso_slice_fill: - * @slice: a #GtsIsoSlice. - * @plane1: a #GtsGridPlane. - * @plane2: another #GtsGridPlane. - * @f1: values of the function corresponding to @plane1. - * @f2: values of the function corresponding to @plane2. - * @iso: isosurface value. - * @klass: a #GtsVertexClass or one of its descendant to be used for the - * new vertices. - * - * Fill @slice with the coordinates of the vertices defined by - * f1 (x,y,z) = @iso and f2 (x, y, z) = @iso. - */ -void gts_iso_slice_fill (GtsIsoSlice * slice, - GtsGridPlane * plane1, - GtsGridPlane * plane2, - gdouble ** f1, - gdouble ** f2, - gdouble iso, - GtsVertexClass * klass) -{ - OrientedVertex *** vertices; - GtsPoint ** p1, ** p2 = NULL; - guint i, j, nx, ny; - - g_return_if_fail (slice != NULL); - g_return_if_fail (plane1 != NULL); - g_return_if_fail (f1 != NULL); - g_return_if_fail (f2 == NULL || plane2 != NULL); - - p1 = plane1->p; - if (plane2) - p2 = plane2->p; - vertices = slice->vertices; - nx = slice->nx; - ny = slice->ny; - - if (f2) - for (i = 0; i < nx; i++) - for (j = 0; j < ny; j++) { - gdouble v1 = f1[i][j] - iso; - gdouble v2 = f2[i][j] - iso; - if ((v1 >= 0. && v2 < 0.) || (v1 < 0. && v2 >= 0.)) { - gdouble c2 = v1/(v1 - v2), c1 = 1. - c2; - vertices[0][i][j].v = - gts_vertex_new (klass, - c1*p1[i][j].x + c2*p2[i][j].x, - c1*p1[i][j].y + c2*p2[i][j].y, - c1*p1[i][j].z + c2*p2[i][j].z); - vertices[0][i][j].orientation = v2 >= 0. ? RIGHT : LEFT; - } - else - vertices[0][i][j].v = NULL; - } - for (i = 0; i < nx - 1; i++) - for (j = 0; j < ny; j++) { - gdouble v1 = f1[i][j] - iso; - gdouble v2 = f1[i+1][j] - iso; - if ((v1 >= 0. && v2 < 0.) || (v1 < 0. && v2 >= 0.)) { - gdouble c2 = v1/(v1 - v2), c1 = 1. - c2; - vertices[1][i][j].v = - gts_vertex_new (klass, - c1*p1[i][j].x + c2*p1[i+1][j].x, - c1*p1[i][j].y + c2*p1[i+1][j].y, - c1*p1[i][j].z + c2*p1[i+1][j].z); - vertices[1][i][j].orientation = v2 >= 0. ? RIGHT : LEFT; - } - else - vertices[1][i][j].v = NULL; - } - for (i = 0; i < nx; i++) - for (j = 0; j < ny - 1; j++) { - gdouble v1 = f1[i][j] - iso; - gdouble v2 = f1[i][j+1] - iso; - if ((v1 >= 0. && v2 < 0.) || (v1 < 0. && v2 >= 0.)) { - gdouble c2 = v1/(v1 - v2), c1 = 1. - c2; - vertices[2][i][j].v = - gts_vertex_new (klass, - c1*p1[i][j].x + c2*p1[i][j+1].x, - c1*p1[i][j].y + c2*p1[i][j+1].y, - c1*p1[i][j].z + c2*p1[i][j+1].z); - vertices[2][i][j].orientation = v2 >= 0. ? RIGHT : LEFT; - } - else - vertices[2][i][j].v = NULL; - } -} - -/** - * gts_iso_slice_fill_cartesian: - * @slice: a #GtsIsoSlice. - * @g: a #GtsCartesianGrid. - * @f1: values of the function for plane z = @g.z. - * @f2: values of the function for plane z = @g.z + @g.dz. - * @iso: isosurface value. - * @klass: a #GtsVertexClass. - * - * Fill @slice with the coordinates of the vertices defined by - * f1 (x,y,z) = @iso and f2 (x, y, z) = @iso. - */ -void gts_iso_slice_fill_cartesian (GtsIsoSlice * slice, - GtsCartesianGrid g, - gdouble ** f1, - gdouble ** f2, - gdouble iso, - GtsVertexClass * klass) -{ - OrientedVertex *** vertices; - guint i, j; - gdouble x, y; - - g_return_if_fail (slice != NULL); - g_return_if_fail (f1 != NULL); - - vertices = slice->vertices; - - if (f2) - for (i = 0, x = g.x; i < g.nx; i++, x += g.dx) - for (j = 0, y = g.y; j < g.ny; j++, y += g.dy) { - gdouble v1 = f1[i][j] - iso; - gdouble v2 = f2[i][j] - iso; - if ((v1 >= 0. && v2 < 0.) || (v1 < 0. && v2 >= 0.)) { - vertices[0][i][j].v = - gts_vertex_new (klass, - x, y, g.z + g.dz*v1/(v1 - v2)); - vertices[0][i][j].orientation = v2 >= 0. ? RIGHT : LEFT; - } - else - vertices[0][i][j].v = NULL; - } - for (i = 0, x = g.x; i < g.nx - 1; i++, x += g.dx) - for (j = 0, y = g.y; j < g.ny; j++, y += g.dy) { - gdouble v1 = f1[i][j] - iso; - gdouble v2 = f1[i+1][j] - iso; - if ((v1 >= 0. && v2 < 0.) || (v1 < 0. && v2 >= 0.)) { - vertices[1][i][j].v = - gts_vertex_new (klass, x + g.dx*v1/(v1 - v2), y, g.z); - vertices[1][i][j].orientation = v2 >= 0. ? RIGHT : LEFT; - } - else - vertices[1][i][j].v = NULL; - } - for (i = 0, x = g.x; i < g.nx; i++, x += g.dx) - for (j = 0, y = g.y; j < g.ny - 1; j++, y += g.dy) { - gdouble v1 = f1[i][j] - iso; - gdouble v2 = f1[i][j+1] - iso; - if ((v1 >= 0. && v2 < 0.) || (v1 < 0. && v2 >= 0.)) { - vertices[2][i][j].v = - gts_vertex_new (klass, x, y + g.dy*v1/(v1 - v2), g.z); - vertices[2][i][j].orientation = v2 >= 0. ? RIGHT : LEFT; - } - else - vertices[2][i][j].v = NULL; - } -} - -/** - * gts_iso_slice_destroy: - * @slice: a #GtsIsoSlice. - * - * Free all memory allocated for @slice. - */ -void gts_iso_slice_destroy (GtsIsoSlice * slice) -{ - g_return_if_fail (slice != NULL); - - free2D ((void **) slice->vertices[0], slice->nx); - free2D ((void **) slice->vertices[1], slice->nx - 1); - free2D ((void **) slice->vertices[2], slice->nx); - g_free (slice->vertices); - g_free (slice); -} - -/** - * gts_isosurface_slice: - * @slice1: a #GtsIsoSlice. - * @slice2: another #GtsIsoSlice. - * @surface: a #GtsSurface. - * - * Given two successive slices @slice1 and @slice2 link their vertices with - * segments and triangles which are added to @surface. - */ -void gts_isosurface_slice (GtsIsoSlice * slice1, - GtsIsoSlice * slice2, - GtsSurface * surface) -{ - guint j, k, l, nx, ny; - OrientedVertex *** vertices[2]; - GtsVertex * va[12]; - - g_return_if_fail (slice1 != NULL); - g_return_if_fail (slice2 != NULL); - g_return_if_fail (surface != NULL); - g_return_if_fail (slice1->nx == slice2->nx && slice1->ny == slice2->ny); - - vertices[0] = slice1->vertices; - vertices[1] = slice2->vertices; - nx = slice1->nx; - ny = slice1->ny; - - /* link vertices with segments and triangles */ - for (j = 0; j < nx - 1; j++) - for (k = 0; k < ny - 1; k++) { - gboolean cube_is_cut = FALSE; - for (l = 0; l < 12; l++) { - guint nv = 0, e = l; - OrientedVertex ov = - vertices[c[e][1]][c[e][0]][j + c[e][2]][k + c[e][3]]; - while (ov.v && !GTS_OBJECT (ov.v)->reserved) { - guint m = 0, * ne = edge[e][ov.orientation]; - va[nv++] = ov.v; - GTS_OBJECT (ov.v)->reserved = surface; - ov.v = NULL; - while (m < 3 && !ov.v) { - e = ne[m++]; - ov = vertices[c[e][1]][c[e][0]][j + c[e][2]][k + c[e][3]]; - } - } - /* create edges and faces */ - if (nv > 2) { - GtsEdge * e1, * e2, * e3; - guint m; - if (!(e1 = GTS_EDGE (gts_vertices_are_connected (va[0], va[1])))) - e1 = gts_edge_new (surface->edge_class, va[0], va[1]); - for (m = 1; m < nv - 1; m++) { - if (!(e2 = GTS_EDGE (gts_vertices_are_connected (va[m], va[m+1])))) - e2 = gts_edge_new (surface->edge_class, va[m], va[m+1]); - if (!(e3 = GTS_EDGE (gts_vertices_are_connected (va[m+1], va[0])))) - e3 = gts_edge_new (surface->edge_class, va[m+1], va[0]); - gts_surface_add_face (surface, - gts_face_new (surface->face_class, - e1, e2, e3)); - e1 = e3; - } - } - if (nv > 0) - cube_is_cut = TRUE; - } - if (cube_is_cut) - for (l = 0; l < 12; l++) { - GtsVertex * v = - vertices[c[l][1]][c[l][0]][j + c[l][2]][k + c[l][3]].v; - if (v) - GTS_OBJECT (v)->reserved = NULL; - } - } -} - -#define SWAP(s1, s2, tmp) (tmp = s1, s1 = s2, s2 = tmp) - -/** - * gts_isosurface_cartesian: - * @surface: a #GtsSurface. - * @g: a #GtsCartesianGrid. - * @f: a #GtsIsoCartesianFunc. - * @data: user data to be passed to @f. - * @iso: isosurface value. - * - * Adds to @surface new faces defining the isosurface f(x,y,z) = @iso. By - * convention, the normals to the surface are pointing toward the positive - * values of f(x,y,z) - @iso. - * - * The user function @f is called successively for each value of the z - * coordinate defined by @g. It must fill the corresponding (x,y) plane with - * the values of the function for which the isosurface is to be computed. - */ -void gts_isosurface_cartesian (GtsSurface * surface, - GtsCartesianGrid g, - GtsIsoCartesianFunc f, - gpointer data, - gdouble iso) -{ - void * tmp; - gdouble ** f1, ** f2; - GtsIsoSlice * slice1, * slice2; - guint i; - - g_return_if_fail (surface != NULL); - g_return_if_fail (f != NULL); - g_return_if_fail (g.nx > 1); - g_return_if_fail (g.ny > 1); - g_return_if_fail (g.nz > 1); - - slice1 = gts_iso_slice_new (g.nx, g.ny); - slice2 = gts_iso_slice_new (g.nx, g.ny); - f1 = (gdouble **) malloc2D (g.nx, g.ny, sizeof (gdouble)); - f2 = (gdouble **) malloc2D (g.nx, g.ny, sizeof (gdouble)); - - (*f) (f1, g, 0, data); - g.z += g.dz; - (*f) (f2, g, 1, data); - g.z -= g.dz; - gts_iso_slice_fill_cartesian (slice1, g, f1, f2, iso, - surface->vertex_class); - g.z += g.dz; - for (i = 2; i < g.nz; i++) { - g.z += g.dz; - (*f) (f1, g, i, data); - SWAP (f1, f2, tmp); - g.z -= g.dz; - gts_iso_slice_fill_cartesian (slice2, g, f1, f2, iso, - surface->vertex_class); - g.z += g.dz; - gts_isosurface_slice (slice1, slice2, surface); - SWAP (slice1, slice2, tmp); - } - gts_iso_slice_fill_cartesian (slice2, g, f2, NULL, iso, - surface->vertex_class); - gts_isosurface_slice (slice1, slice2, surface); - - gts_iso_slice_destroy (slice1); - gts_iso_slice_destroy (slice2); - free2D ((void **) f1, g.nx); - free2D ((void **) f2, g.nx); -} Index: trunk/src/3rd/gts/misc.c =================================================================== --- trunk/src/3rd/gts/misc.c (revision 1064) +++ trunk/src/3rd/gts/misc.c (nonexistent) @@ -1,692 +0,0 @@ -/* GTS - Library for the manipulation of triangulated surfaces - * Copyright (C) 1999 Stéphane Popinet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include -#include - -#include "gts.h" -#include "gts-private.h" -#include "config.h" - -const guint gts_major_version = GTS_MAJOR_VERSION; -const guint gts_minor_version = GTS_MINOR_VERSION; -const guint gts_micro_version = GTS_MICRO_VERSION; -const guint gts_interface_age = 1; -const guint gts_binary_age = 1; - -static gboolean char_in_string (char c, const char * s) -{ - while (*s != '\0') - if (*(s++) == c) - return TRUE; - return FALSE; -} - -static GtsFile * file_new (void) -{ - GtsFile * f; - - f = g_malloc (sizeof (GtsFile)); - f->fp = NULL; - f->s = f->s1 = NULL; - f->curline = 1; - f->curpos = 1; - f->token = g_string_new (""); - f->type = '\0'; - f->error = NULL; - f->next_token = '\0'; - - f->scope = f->scope_max = 0; - f->delimiters = g_strdup (" \t"); - f->comments = g_strdup (GTS_COMMENTS); - f->tokens = g_strdup ("\n{}()="); - - return f; -} - -/** - * gts_file_new: - * @fp: a file pointer. - * - * Returns: a new #GtsFile. - */ -GtsFile * gts_file_new (FILE * fp) -{ - GtsFile * f; - - g_return_val_if_fail (fp != NULL, NULL); - - f = file_new (); - f->fp = fp; - gts_file_next_token (f); - - return f; -} - -/** - * gts_file_new_from_string: - * @s: a string. - * - * Returns: a new #GtsFile. - */ -GtsFile * gts_file_new_from_string (const gchar * s) -{ - GtsFile * f; - - g_return_val_if_fail (s != NULL, NULL); - - f = file_new (); - f->s1 = f->s = g_strdup (s); - gts_file_next_token (f); - - return f; -} - -/** - * gts_file_destroy: - * @f: a #GtsFile. - * - * Frees all the memory allocated for @f. - */ -void gts_file_destroy (GtsFile * f) -{ - g_return_if_fail (f != NULL); - - g_free (f->delimiters); - g_free (f->comments); - g_free (f->tokens); - if (f->error) - g_free (f->error); - if (f->s1) - g_free (f->s1); - g_string_free (f->token, TRUE); - g_free (f); -} - -/** - * gts_file_verror: - * @f: a @GtsFile. - * @format: the standard sprintf() format string. - * @args: the list of parameters to insert into the format string. - * - * Sets the @error field of @f using g_strdup_vprintf(). - * - * This function can be called only once and disables any other - * operation on @f (gts_file_close() excepted). - */ -void gts_file_verror (GtsFile * f, - const gchar * format, - va_list args) -{ - g_return_if_fail (f != NULL); - g_return_if_fail (format != NULL); - - g_assert (f->type != GTS_ERROR); - f->error = g_strdup_vprintf (format, args); - f->type = GTS_ERROR; -} - -/** - * gts_file_error: - * @f: a @GtsFile. - * @format: the standard sprintf() format string. - * @...: the parameters to insert into the format string. - * - * Sets the @error field of @f using gts_file_verror(). - * - * This function can be called only once and disables any other - * operation on @f (gts_file_close() excepted). - */ -void gts_file_error (GtsFile * f, - const gchar * format, - ...) -{ - va_list args; - - g_return_if_fail (f != NULL); - g_return_if_fail (format != NULL); - - va_start (args, format); - gts_file_verror (f, format, args); - va_end (args); -} - -static gint next_char (GtsFile * f) -{ - if (f->fp) - return fgetc (f->fp); - else if (*f->s == '\0') - return EOF; - return *(f->s++); -} - -/** - * gts_file_getc : - * @f: a #GtsFile. - * - * Returns: the next character in @f or EOF if the end of the file is - * reached or if an error occured. - */ -gint gts_file_getc (GtsFile * f) -{ - gint c; - - g_return_val_if_fail (f != NULL, EOF); - - if (f->type == GTS_ERROR) - return EOF; - - c = next_char (f); - f->curpos++; - while (char_in_string (c, f->comments)) { - while (c != EOF && c != '\n') - c = next_char (f); - if (c == '\n') { - f->curline++; - f->curpos = 1; - c = next_char (f); - } - } - switch (c) { - case '\n': - f->curline++; - f->curpos = 1; - break; - case '{': - f->scope++; - break; - case '}': - if (f->scope == 0) { - f->line = f->curline; - f->pos = f->curpos - 1; - gts_file_error (f, "no matching opening brace"); - c = EOF; - } - else - f->scope--; - } - return c; -} - -/** - * gts_file_read: - * @f: a #GtsFile. - * @ptr: a pointer. - * @size: size of an element. - * @nmemb: number of elements. - * - * Reads @nmemb elements of data, each @size bytes long, from @f, - * storing them at the location given by @ptr. - * - * Returns: the number of elements read. - */ -guint gts_file_read (GtsFile * f, gpointer ptr, guint size, guint nmemb) -{ - guint i, n; - gchar * p; - - g_return_val_if_fail (f != NULL, 0); - g_return_val_if_fail (ptr != NULL, 0); - g_return_val_if_fail (f->fp != NULL, 0); - - if (f->type == GTS_ERROR) - return 0; - - n = fread (ptr, size, nmemb, f->fp); - for (i = 0, p = ptr; i < n*size; i++, p++) { - f->curpos++; - if (*p == '\n') { - f->curline++; - f->curpos = 1; - } - } - return n; -} - -/** - * gts_file_getc_scope : - * @f: a #GtsFile. - * - * Returns: the next character in @f in the scope defined by - * @f->scope_max or EOF if the end of the file is reached or if an - * error occured. - */ -gint gts_file_getc_scope (GtsFile * f) -{ - gint c; - - g_return_val_if_fail (f != NULL, EOF); - - if (f->type == GTS_ERROR) - return EOF; - - if (f->scope <= f->scope_max) - c = gts_file_getc (f); - else { - c = gts_file_getc (f); - while (c != EOF && f->scope > f->scope_max) - c = gts_file_getc (f); - } - return c; -} - -/** - * gts_file_next_token: - * @f: a #GtsFile. - * - * Reads next token from @f and updates its @token and @delim fields. - */ -void gts_file_next_token (GtsFile * f) -{ - gint c; - gboolean in_string = FALSE; - - g_return_if_fail (f != NULL); - - if (f->type == GTS_ERROR) - return; - f->token->str[0] = '\0'; - f->token->len = 0; - if (f->next_token != '\0') { - if (char_in_string (f->next_token, f->tokens)) { - f->line = f->curline; - f->pos = f->curpos - 1; - g_string_append_c (f->token, f->next_token); - f->type = f->next_token; - f->next_token = '\0'; - return; - } - else { - c = f->next_token; - f->next_token = '\0'; - } - } - else - c = gts_file_getc_scope (f); - f->type = GTS_NONE; - while (c != EOF && (!in_string || !char_in_string (c, f->delimiters))) { - if (in_string) { - if (char_in_string (c, f->tokens)) { - f->next_token = c; - break; - } - g_string_append_c (f->token, c); - } - else if (!char_in_string (c, f->delimiters)) { - in_string = TRUE; - f->line = f->curline; - f->pos = f->curpos - 1; - g_string_append_c (f->token, c); - if (char_in_string (c, f->tokens)) { - f->type = c; - break; - } - } - c = gts_file_getc_scope (f); - } - if (f->type == GTS_NONE && f->token->len > 0) { - gchar * a; - - a = f->token->str; - while (*a != '\0' && char_in_string (*a, "+-")) a++; - if (*a == '\0') { - f->type = GTS_STRING; - return; - } - a = f->token->str; - while (*a != '\0' && char_in_string (*a, "+-0123456789")) a++; - if (*a == '\0') { - f->type = GTS_INT; - return; - } - a = f->token->str; - while (*a != '\0' && char_in_string (*a, "+-eE.")) a++; - if (*a == '\0') { - f->type = GTS_STRING; - return; - } - a = f->token->str; - while (*a != '\0' && char_in_string (*a, "+-0123456789eE.")) a++; - if (*a == '\0') { - f->type = GTS_FLOAT; - return; - } - a = f->token->str; - if (!strncmp (a, "0x", 2) || - !strncmp (a, "-0x", 3) || - !strncmp (a, "+0x", 3)) { - while (*a != '\0' && char_in_string (*a, "+-0123456789abcdefx")) a++; - if (*a == '\0') { - f->type = GTS_INT; - return; - } - a = f->token->str; - while (*a != '\0' && char_in_string (*a, "+-0123456789abcdefx.p")) a++; - if (*a == '\0') { - f->type = GTS_FLOAT; - return; - } - } - f->type = GTS_STRING; - } -} - -/** - * gts_file_first_token_after: - * @f: a #GtsFile. - * @type: a #GtsTokenType. - * - * Finds and sets the first token of a type different from @type - * occuring after a token of type @type. - */ -void gts_file_first_token_after (GtsFile * f, GtsTokenType type) -{ - g_return_if_fail (f != NULL); - - while (f->type != GTS_ERROR && - f->type != GTS_NONE && - f->type != type) - gts_file_next_token (f); - while (f->type == type) - gts_file_next_token (f); -} - -/** - * gts_file_assign_start: - * @f: a #GtsFile. - * @vars: a %GTS_NONE terminated array of #GtsFileVariable. - * - * Opens a block delimited by braces to read a list of optional - * arguments specified by @vars. - * - * If an error is encountered the @error field of @f is set. - */ -void gts_file_assign_start (GtsFile * f, GtsFileVariable * vars) -{ - GtsFileVariable * var; - - g_return_if_fail (f != NULL); - g_return_if_fail (vars != NULL); - - var = vars; - while (var->type != GTS_NONE) - (var++)->set = FALSE; - - if (f->type != '{') { - gts_file_error (f, "expecting an opening brace"); - return; - } - - f->scope_max++; - gts_file_next_token (f); -} - -/** - * gts_file_assign_next: - * @f: a #GtsFile. - * @vars: a %GTS_NONE terminated array of #GtsFileVariable. - * - * Assigns the next optional argument of @vars read from @f. - * - * Returns: the variable of @vars which has been assigned or %NULL if - * no variable has been assigned (if an error has been encountered the - * @error field of @f is set). - */ -GtsFileVariable * gts_file_assign_next (GtsFile * f, GtsFileVariable * vars) -{ - GtsFileVariable * var; - gboolean found = FALSE; - - g_return_val_if_fail (f != NULL, NULL); - g_return_val_if_fail (vars != NULL, NULL); - - while (f->type == '\n') - gts_file_next_token (f); - if (f->type == '}') { - f->scope_max--; - gts_file_next_token (f); - return NULL; - } - if (f->type == GTS_ERROR) - return NULL; - - var = vars; - while (f->type != GTS_ERROR && var->type != GTS_NONE && !found) { - if (!strcmp (var->name, f->token->str)) { - found = TRUE; - if (var->unique && var->set) - gts_file_error (f, "variable `%s' was already set at line %d:%d", - var->name, var->line, var->pos); - else { - var->line = f->line; - var->pos = f->pos; - gts_file_next_token (f); - if (f->type != '=') - gts_file_error (f, "expecting `='"); - else { - var->set = TRUE; - switch (var->type) { - case GTS_FILE: - break; - case GTS_INT: - gts_file_next_token (f); - if (f->type != GTS_INT) { - gts_file_error (f, "expecting an integer"); - var->set = FALSE; - } - else if (var->data) - *((gint *) var->data) = atoi (f->token->str); - break; - case GTS_UINT: - gts_file_next_token (f); - if (f->type != GTS_INT) { - gts_file_error (f, "expecting an integer"); - var->set = FALSE; - } - else if (var->data) - *((guint *) var->data) = atoi (f->token->str); - break; - case GTS_FLOAT: - gts_file_next_token (f); - if (f->type != GTS_INT && f->type != GTS_FLOAT) { - gts_file_error (f, "expecting a number"); - var->set = FALSE; - } - else if (var->data) - *((gfloat *) var->data) = atof (f->token->str); - break; - case GTS_DOUBLE: - gts_file_next_token (f); - if (f->type != GTS_INT && f->type != GTS_FLOAT) { - gts_file_error (f, "expecting a number"); - var->set = FALSE; - } - else if (var->data) - *((gdouble *) var->data) = atof (f->token->str); - break; - case GTS_STRING: - gts_file_next_token (f); - if (f->type != GTS_INT && - f->type != GTS_FLOAT && - f->type != GTS_STRING) { - gts_file_error (f, "expecting a string"); - var->set = FALSE; - } - else if (var->data) - *((gchar **) var->data) = g_strdup (f->token->str); - break; - default: - g_assert_not_reached (); - } - } - } - } - else - var++; - } - if (!found) - gts_file_error (f, "unknown identifier `%s'", f->token->str); - else if (f->type != GTS_ERROR) { - g_assert (var->set); - gts_file_next_token (f); - return var; - } - return NULL; -} - -/** - * gts_file_assign_variables: - * @f: a #GtsFile. - * @vars: an array of #GtsFileVariable. - * - * Assigns all the variables belonging to @vars found in @f. - * - * If an error is encountered the @error field of @f is set. - */ -void gts_file_assign_variables (GtsFile * f, GtsFileVariable * vars) -{ - g_return_if_fail (f != NULL); - g_return_if_fail (vars != NULL); - - gts_file_assign_start (f, vars); - while (gts_file_assign_next (f, vars)) - ; -} - -/** - * gts_file_variable_error: - * @f: a #GtsFile. - * @vars: an array of #GtsFileVariable. - * @name: the name of a variable in @vars. - * @format: the standard sprintf() format string. - * @...: the parameters to insert into the format string. - * - * Sets the @error field of @f using gts_file_verror(). - * - * String @name must match one of the variable names in @vars. - * - * If variable @name has been assigned (using gts_file_assign_variables()) - * sets the @line and @pos fields of @f to the line and position where - * it has been assigned. - */ -void gts_file_variable_error (GtsFile * f, - GtsFileVariable * vars, - const gchar * name, - const gchar * format, - ...) -{ - va_list args; - GtsFileVariable * var; - - g_return_if_fail (f != NULL); - g_return_if_fail (vars != NULL); - g_return_if_fail (name != NULL); - g_return_if_fail (format != NULL); - - var = vars; - while (var->type != GTS_NONE && strcmp (var->name, name)) - var++; - - g_return_if_fail (var->type != GTS_NONE); /* @name not found in @vars */ - - if (var->set) { - f->line = var->line; - f->pos = var->pos; - } - - va_start (args, format); - gts_file_verror (f, format, args); - va_end (args); -} - -#ifdef DEBUG_FUNCTIONS -static GHashTable * ids = NULL; -static guint next_id = 1; - -guint id (gpointer p) -{ - g_return_val_if_fail (p != NULL, 0); - g_return_val_if_fail (ids != NULL, 0); - g_assert (g_hash_table_lookup (ids, p)); - return GPOINTER_TO_UINT (g_hash_table_lookup (ids, p)); -} - -void id_insert (gpointer p) -{ - g_return_if_fail (p != NULL); - if (ids == NULL) ids = g_hash_table_new (NULL, NULL); - g_assert (g_hash_table_lookup (ids, p) == NULL); - g_hash_table_insert (ids, p, GUINT_TO_POINTER (next_id++)); -} - -void id_remove (gpointer p) -{ - g_assert (g_hash_table_lookup (ids, p)); - g_hash_table_remove (ids, p); -} - -void gts_write_triangle (GtsTriangle * t, - GtsPoint * o, - FILE * fptr) -{ - gdouble xo = o ? o->x : 0.0; - gdouble yo = o ? o->y : 0.0; - gdouble zo = o ? o->z : 0.0; - - g_return_if_fail (t != NULL && fptr != NULL); - - fprintf (fptr, "(hdefine geometry \"t%d\" { =\n", id (t)); - fprintf (fptr, "OFF 3 1 0\n" - "%g %g %g\n%g %g %g\n%g %g %g\n3 0 1 2\n})\n" - "(geometry \"t%d\" { : \"t%d\"})\n" - "(normalization \"t%d\" none)\n", - GTS_POINT (GTS_SEGMENT (t->e1)->v1)->x - xo, - GTS_POINT (GTS_SEGMENT (t->e1)->v1)->y - yo, - GTS_POINT (GTS_SEGMENT (t->e1)->v1)->z - zo, - GTS_POINT (GTS_SEGMENT (t->e1)->v2)->x - xo, - GTS_POINT (GTS_SEGMENT (t->e1)->v2)->y - yo, - GTS_POINT (GTS_SEGMENT (t->e1)->v2)->z - zo, - GTS_POINT (gts_triangle_vertex (t))->x - xo, - GTS_POINT (gts_triangle_vertex (t))->y - yo, - GTS_POINT (gts_triangle_vertex (t))->z - zo, - id (t), id (t), id (t)); -} - -void gts_write_segment (GtsSegment * s, - GtsPoint * o, - FILE * fptr) -{ - gdouble xo = o ? o->x : 0.0; - gdouble yo = o ? o->y : 0.0; - gdouble zo = o ? o->z : 0.0; - - g_return_if_fail (s != NULL && fptr != NULL); - - fprintf (fptr, "(geometry \"s%d\" { =\n", id (s)); - fprintf (fptr, "VECT 1 2 0 2 0 %g %g %g %g %g %g })\n" - "(normalization \"s%d\" none)\n", - GTS_POINT (s->v1)->x - xo, - GTS_POINT (s->v1)->y - yo, - GTS_POINT (s->v1)->z - zo, - GTS_POINT (s->v2)->x - xo, - GTS_POINT (s->v2)->y - yo, - GTS_POINT (s->v2)->z - zo, - id (s)); -} -#endif /* DEBUG_FUNCTIONS */ Index: trunk/src/3rd =================================================================== --- trunk/src/3rd (revision 1064) +++ trunk/src/3rd (nonexistent) Property changes on: trunk/src/3rd ___________________________________________________________________ Deleted: svn:externals ## -1 +0,0 ## -genht svn://repo.hu/genht/trunk/src Index: trunk/src/Makefile.dep =================================================================== --- trunk/src/Makefile.dep (revision 1064) +++ trunk/src/Makefile.dep (revision 1065) @@ -2,15 +2,11 @@ action.o: action.c ../config.h ../config.manual.h ../config.auto.h \ global.h const.h ../globalconst.h ../config.h macro.h hid.h polyarea.h \ - libpcb_fp.h action.h buffer.h change.h command.h \ - copy.h create.h crosshair.h data.h draw.h error.h file.h find.h insert.h \ - line.h mymem.h misc.h ds.h mirror.h move.h polygon.h rats.h remove.h \ - report.h rotate.h rubberband.h search.h select.h set.h thermal.h undo.h \ - rtree.h pcb-printf.h plugins.h rats_patch.h -autoplace.o: autoplace.c ../config.h ../config.manual.h ../config.auto.h \ - global.h const.h ../globalconst.h ../config.h macro.h hid.h polyarea.h \ - libpcb_fp.h box.h misc.h ds.h mymem.h compat.h data.h draw.h \ - error.h intersect.h rtree.h mirror.h move.h rats.h remove.h rotate.h + libpcb_fp.h action.h buffer.h change.h command.h copy.h create.h \ + crosshair.h data.h draw.h error.h file.h find.h insert.h line.h mymem.h \ + misc.h ds.h mirror.h move.h polygon.h rats.h remove.h report.h rotate.h \ + rubberband.h search.h select.h set.h thermal.h undo.h rtree.h \ + pcb-printf.h plugins.h rats_patch.h buffer.o: buffer.c ../config.h ../config.manual.h ../config.auto.h \ global.h const.h ../globalconst.h ../config.h macro.h hid.h polyarea.h \ libpcb_fp.h buffer.h copy.h create.h crosshair.h data.h error.h mymem.h \ @@ -26,8 +22,8 @@ libpcb_fp.h data.h draw.h mymem.h command.o: command.c ../config.h ../config.manual.h ../config.auto.h \ global.h const.h ../globalconst.h ../config.h macro.h hid.h polyarea.h \ - libpcb_fp.h action.h buffer.h command.h data.h error.h file.h \ - mymem.h misc.h ds.h rats.h set.h + libpcb_fp.h action.h buffer.h command.h data.h error.h file.h mymem.h \ + misc.h ds.h rats.h set.h compat.o: compat.c ../config.h ../config.manual.h ../config.auto.h \ compat.h global.h const.h ../globalconst.h ../config.h macro.h hid.h \ polyarea.h libpcb_fp.h @@ -38,7 +34,8 @@ create.o: create.c ../config.h ../config.manual.h ../config.auto.h \ global.h const.h ../globalconst.h ../config.h macro.h hid.h polyarea.h \ libpcb_fp.h create.h data.h draw.h error.h mymem.h misc.h ds.h parse_l.h \ - pcb-printf.h polygon.h rtree.h search.h set.h undo.h file.h + pcb-printf.h polygon.h rtree.h search.h set.h undo.h file.h \ + stub_vendor.h crosshair.o: crosshair.c ../config.h ../config.manual.h ../config.auto.h \ global.h const.h ../globalconst.h ../config.h macro.h hid.h polyarea.h \ libpcb_fp.h box.h misc.h ds.h mymem.h crosshair.h data.h draw.h error.h \ @@ -46,10 +43,6 @@ data.o: data.c ../config.h ../config.manual.h ../config.auto.h data.h \ global.h const.h ../globalconst.h ../config.h macro.h hid.h polyarea.h \ libpcb_fp.h -djopt.o: djopt.c ../config.h ../config.manual.h ../config.auto.h global.h \ - const.h ../globalconst.h ../config.h macro.h hid.h polyarea.h \ - libpcb_fp.h data.h create.h remove.h move.h draw.h undo.h strflags.h \ - find.h pcb-printf.h draw.o: draw.c ../config.h ../config.manual.h ../config.auto.h global.h \ const.h ../globalconst.h ../config.h macro.h hid.h polyarea.h \ libpcb_fp.h compat.h crosshair.h data.h draw.h error.h mymem.h misc.h \ @@ -63,10 +56,9 @@ event.o: event.c event.h error.h file.o: file.c ../config.h ../config.manual.h ../config.auto.h global.h \ const.h ../globalconst.h ../config.h macro.h hid.h polyarea.h \ - libpcb_fp.h buffer.h change.h create.h crosshair.h data.h \ - error.h file.h misc.h ds.h mymem.h move.h parse_l.h pcb-printf.h \ - polygon.h rats.h remove.h set.h strflags.h portability.h paths.h \ - rats_patch.h + libpcb_fp.h buffer.h change.h create.h crosshair.h data.h error.h file.h \ + misc.h ds.h mymem.h move.h parse_l.h pcb-printf.h polygon.h rats.h \ + remove.h set.h strflags.h portability.h paths.h rats_patch.h stub_edif.h find.o: find.c ../config.h ../config.manual.h ../config.auto.h global.h \ const.h ../globalconst.h ../config.h macro.h hid.h polyarea.h \ libpcb_fp.h crosshair.h data.h draw.h error.h find.h mymem.h misc.h ds.h \ @@ -114,16 +106,13 @@ const.h ../globalconst.h ../config.h macro.h hid.h polyarea.h \ libpcb_fp.h create.h crosshair.h data.h draw.h error.h misc.h ds.h \ mymem.h move.h polygon.h rtree.h search.h select.h thermal.h undo.h -mtspace.o: mtspace.c ../config.h ../config.manual.h ../config.auto.h \ - global.h const.h ../globalconst.h ../config.h macro.h hid.h polyarea.h \ - libpcb_fp.h box.h misc.h ds.h mymem.h heap.h rtree.h mtspace.h vector.h mymem.o: mymem.c ../config.h ../config.manual.h ../config.auto.h global.h \ const.h ../globalconst.h ../config.h macro.h hid.h polyarea.h \ libpcb_fp.h data.h error.h mymem.h misc.h ds.h rats.h rtree.h netlist.o: netlist.c ../config.h ../config.manual.h ../config.auto.h \ global.h const.h ../globalconst.h ../config.h macro.h hid.h polyarea.h \ - libpcb_fp.h action.h buffer.h command.h data.h error.h file.h \ - find.h mymem.h misc.h ds.h rats.h set.h create.h rats_patch.h + libpcb_fp.h action.h buffer.h command.h data.h error.h file.h find.h \ + mymem.h misc.h ds.h rats.h set.h create.h rats_patch.h paths.o: paths.c paths.h error.h pcb-printf.o: pcb-printf.c ../config.h ../config.manual.h \ ../config.auto.h global.h const.h ../globalconst.h ../config.h macro.h \ @@ -137,10 +126,6 @@ polygon1.o: polygon1.c global.h ../config.h ../config.manual.h \ ../config.auto.h const.h ../globalconst.h ../config.h macro.h hid.h \ polyarea.h libpcb_fp.h rtree.h heap.h -puller.o: puller.c ../config.h ../config.manual.h ../config.auto.h \ - global.h const.h ../globalconst.h ../config.h macro.h hid.h polyarea.h \ - libpcb_fp.h create.h data.h draw.h misc.h ds.h mymem.h move.h \ - pcb-printf.h remove.h rtree.h strflags.h undo.h print.o: print.c ../config.h ../config.manual.h ../config.auto.h global.h \ const.h ../globalconst.h ../config.h macro.h hid.h polyarea.h \ libpcb_fp.h data.h draw.h drill.h file.h find.h error.h misc.h ds.h \ @@ -148,15 +133,12 @@ rats.o: rats.c ../config.h ../config.manual.h ../config.auto.h global.h \ const.h ../globalconst.h ../config.h macro.h hid.h polyarea.h \ libpcb_fp.h create.h data.h draw.h error.h file.h find.h misc.h ds.h \ - mymem.h polygon.h rats.h search.h set.h undo.h -rats_mincut.o: rats_mincut.c ../config.h ../config.manual.h \ - ../config.auto.h global.h const.h ../globalconst.h ../config.h macro.h \ - hid.h polyarea.h libpcb_fp.h create.h data.h draw.h error.h file.h \ - find.h misc.h ds.h mymem.h polygon.h rats.h search.h set.h undo.h \ - pcb-mincut/graph.h pcb-mincut/solve.h pcb-mincut/graph.h + mymem.h polygon.h rats.h search.h set.h undo.h stub_mincut.h rats_patch.o: rats_patch.c rats_patch.h global.h ../config.h \ ../config.manual.h ../config.auto.h const.h ../globalconst.h ../config.h \ - macro.h hid.h polyarea.h libpcb_fp.h create.h + macro.h hid.h polyarea.h libpcb_fp.h ../src_3rd/genht/htsp.h \ + ../src_3rd/genht/ht.h ../src_3rd/genht/ht_inlines.h \ + ../src_3rd/genht/hash.h create.h remove.o: remove.c ../config.h ../config.manual.h ../config.auto.h \ global.h const.h ../globalconst.h ../config.h macro.h hid.h polyarea.h \ libpcb_fp.h data.h draw.h error.h misc.h ds.h mymem.h move.h polygon.h \ @@ -192,6 +174,15 @@ strflags.o: strflags.c ../config.h ../config.manual.h ../config.auto.h \ ../globalconst.h ../config.h global.h const.h macro.h hid.h polyarea.h \ libpcb_fp.h compat.h strflags.h +stub_edif.o: stub_edif.c ../config.h ../config.manual.h ../config.auto.h \ + global.h const.h ../globalconst.h ../config.h macro.h hid.h polyarea.h \ + libpcb_fp.h error.h stub_edif.h +stub_mincut.o: stub_mincut.c ../config.h ../config.manual.h \ + ../config.auto.h global.h const.h ../globalconst.h ../config.h macro.h \ + hid.h polyarea.h libpcb_fp.h error.h data.h stub_mincut.h +stub_vendor.o: stub_vendor.c stub_vendor.h ../config.h ../config.manual.h \ + ../config.auto.h global.h const.h ../globalconst.h ../config.h macro.h \ + hid.h polyarea.h libpcb_fp.h thermal.o: thermal.c ../config.h ../config.manual.h ../config.auto.h \ global.h const.h ../globalconst.h ../config.h macro.h hid.h polyarea.h \ libpcb_fp.h create.h data.h draw.h error.h misc.h ds.h mymem.h move.h \ @@ -205,9 +196,10 @@ global.h const.h ../globalconst.h ../config.h macro.h hid.h polyarea.h \ libpcb_fp.h vector.h actions.o: hid/common/actions.c ../config.h ../config.manual.h \ - ../config.auto.h hid/common/../../3rd/genht/hash.h \ - hid/common/../../3rd/genht/htsp.h hid/common/../../3rd/genht/ht.h \ - hid/common/../../3rd/genht/ht_inlines.h global.h const.h \ + ../config.auto.h hid/common/../../../src_3rd/genht/hash.h \ + hid/common/../../../src_3rd/genht/htsp.h \ + hid/common/../../../src_3rd/genht/ht.h \ + hid/common/../../../src_3rd/genht/ht_inlines.h global.h const.h \ ../globalconst.h ../config.h macro.h hid.h polyarea.h libpcb_fp.h data.h \ global.h error.h event.h hid.h hid/common/../hidint.h flags.o: hid/common/flags.c ../config.h ../config.manual.h \ @@ -233,8 +225,6 @@ ../config.manual.h ../config.auto.h const.h ../globalconst.h ../config.h \ macro.h hid.h polyarea.h libpcb_fp.h hid.h resource.h \ hid/common/hid_resource.h -graph.o: pcb-mincut/graph.c pcb-mincut/graph.h -solve.o: pcb-mincut/solve.c pcb-mincut/solve.h pcb-mincut/graph.h res_parse.o: res_parse.c ../config.h ../config.manual.h ../config.auto.h \ global.h const.h ../globalconst.h ../config.h macro.h hid.h polyarea.h \ libpcb_fp.h resource.h res_parse.h @@ -241,9 +231,6 @@ res_lex.o: res_lex.c ../config.h ../config.manual.h ../config.auto.h \ global.h const.h ../globalconst.h ../config.h macro.h hid.h polyarea.h \ libpcb_fp.h resource.h res_parse.h -edif.o: edif.c global.h ../config.h ../config.manual.h ../config.auto.h \ - const.h ../globalconst.h ../config.h macro.h hid.h polyarea.h \ - libpcb_fp.h data.h portability.o: portability.c ../config.h ../config.manual.h \ ../config.auto.h ds.h global.h const.h ../globalconst.h ../config.h \ macro.h hid.h polyarea.h libpcb_fp.h error.h mymem.h @@ -251,7 +238,8 @@ libpcb_fp.o: libpcb_fp.c ds.h ../config.h ../config.manual.h \ ../config.auto.h global.h const.h ../globalconst.h ../config.h macro.h \ hid.h polyarea.h libpcb_fp.h error.h portability.h paths.h \ - 3rd/genht/htsp.h 3rd/genht/ht.h 3rd/genht/ht_inlines.h 3rd/genht/hash.h + ../src_3rd/genht/htsp.h ../src_3rd/genht/ht.h \ + ../src_3rd/genht/ht_inlines.h ../src_3rd/genht/hash.h parse_y.o: parse_y.c ../config.h ../config.manual.h ../config.auto.h \ global.h const.h ../globalconst.h ../config.h macro.h hid.h polyarea.h \ libpcb_fp.h create.h data.h error.h file.h mymem.h misc.h ds.h parse_l.h \ @@ -261,9 +249,10 @@ libpcb_fp.h crosshair.h data.h error.h file.h mymem.h misc.h ds.h \ strflags.h parse_l.h parse_y.h create.h buildin.o: buildin.c plugins.h buildin.h -hash.o: 3rd/genht/hash.c -htsp.o: 3rd/genht/htsp.c 3rd/genht/htsp.h 3rd/genht/ht.h \ - 3rd/genht/ht_inlines.h 3rd/genht/ht.c +hash.o: ../src_3rd/genht/hash.c +htsp.o: ../src_3rd/genht/htsp.c ../src_3rd/genht/htsp.h \ + ../src_3rd/genht/ht.h ../src_3rd/genht/ht_inlines.h \ + ../src_3rd/genht/ht.c ghid-cell-renderer-visibility.o: hid/gtk/ghid-cell-renderer-visibility.c \ hid/gtk/gtkhid.h hid/gtk/gui.h global.h ../config.h ../config.manual.h \ ../config.auto.h const.h ../globalconst.h ../config.h macro.h hid.h \ @@ -411,12 +400,12 @@ ../config.h macro.h hid.h polyarea.h libpcb_fp.h hid/gtk/gtkhid.h \ hid/gtk/gui.h hid.h hid/common/hid_resource.h resource.h data.h global.h \ misc.h ds.h mymem.h hid/gtk/ghid-coord-entry.h hid/gtk/ghid-main-menu.h \ - hid/gtk/gui-pinout-preview.h hid/gtk/../hidint.h action.h \ - autoroute.h buffer.h change.h command.h copy.h create.h crosshair.h \ - draw.h error.h file.h find.h gpcb-menu.h insert.h line.h mymem.h move.h \ - pcb-printf.h polygon.h rats.h remove.h report.h rotate.h rubberband.h \ - search.h select.h set.h undo.h event.h free_atexit.h paths.h \ - hid/gtk/gui-icons-mode-buttons.data hid/gtk/gui-icons-misc.data + hid/gtk/gui-pinout-preview.h hid/gtk/../hidint.h action.h buffer.h \ + change.h command.h copy.h create.h crosshair.h draw.h error.h file.h \ + find.h gpcb-menu.h insert.h line.h mymem.h move.h pcb-printf.h polygon.h \ + rats.h remove.h report.h rotate.h rubberband.h search.h select.h set.h \ + undo.h event.h free_atexit.h paths.h hid/gtk/gui-icons-mode-buttons.data \ + hid/gtk/gui-icons-misc.data gui-utils.o: hid/gtk/gui-utils.c ../config.h ../config.manual.h \ ../config.auto.h hid/gtk/gui.h global.h const.h ../globalconst.h \ ../config.h macro.h hid.h polyarea.h libpcb_fp.h hid.h \ @@ -455,9 +444,10 @@ global.h error.h misc.h ds.h mymem.h pcb-printf.h hid.h \ hid/lesstif/../hidint.h hid/common/hid_resource.h resource.h \ hid/lesstif/lesstif.h mymem.h paths.h pcb-menu.h \ - hid/lesstif/../../3rd/genht/htsp.h hid/lesstif/../../3rd/genht/ht.h \ - hid/lesstif/../../3rd/genht/ht_inlines.h \ - hid/lesstif/../../3rd/genht/hash.h + hid/lesstif/../../../src_3rd/genht/htsp.h \ + hid/lesstif/../../../src_3rd/genht/ht.h \ + hid/lesstif/../../../src_3rd/genht/ht_inlines.h \ + hid/lesstif/../../../src_3rd/genht/hash.h netlist.o: hid/lesstif/netlist.c ../config.h ../config.manual.h \ ../config.auto.h hid/lesstif/xincludes.h compat.h global.h const.h \ ../globalconst.h ../config.h macro.h hid.h polyarea.h libpcb_fp.h data.h \ @@ -468,11 +458,6 @@ ../globalconst.h ../config.h macro.h hid.h polyarea.h libpcb_fp.h data.h \ global.h set.h misc.h ds.h mymem.h mymem.h pcb-printf.h hid.h \ hid/lesstif/../hidint.h hid/lesstif/lesstif.h -toporouter.o: toporouter.c toporouter.h data.h global.h ../config.h \ - ../config.manual.h ../config.auto.h const.h ../globalconst.h ../config.h \ - macro.h hid.h polyarea.h libpcb_fp.h autoroute.h box.h misc.h ds.h \ - mymem.h create.h draw.h error.h find.h heap.h rtree.h polygon.h rats.h \ - remove.h thermal.h undo.h pcb-printf.h batch.o: hid/batch/batch.c ../config.h ../config.manual.h \ ../config.auto.h global.h const.h ../globalconst.h ../config.h macro.h \ hid.h polyarea.h libpcb_fp.h hid.h data.h global.h misc.h ds.h mymem.h \ @@ -533,3 +518,22 @@ libpcb_fp.h data.h global.h misc.h ds.h mymem.h hid.h \ hid/lpr/../hidint.h hid/lpr/../ps/ps.h hid/common/hidnogui.h \ hid/common/hidinit.h +autoplace.o: ../src_plugins/autoplace/autoplace.c ../config.h \ + ../config.manual.h ../config.auto.h global.h const.h ../globalconst.h \ + ../config.h macro.h hid.h polyarea.h libpcb_fp.h \ + ../src_plugins/autoplace/autoplace.h box.h global.h misc.h ds.h mymem.h \ + compat.h data.h draw.h error.h intersect.h rtree.h macro.h mirror.h \ + misc.h move.h mymem.h rats.h remove.h rotate.h +action.o: ../src_plugins/autoplace/action.c ../config.h \ + ../config.manual.h ../config.auto.h global.h const.h ../globalconst.h \ + ../config.h macro.h hid.h polyarea.h libpcb_fp.h \ + ../src_plugins/autoplace/autoplace.h dolists.h +vendor.o: ../src_plugins/vendordrill/vendor.c ../config.h \ + ../config.manual.h ../config.auto.h change.h global.h const.h \ + ../globalconst.h ../config.h macro.h hid.h polyarea.h libpcb_fp.h data.h \ + draw.h error.h global.h resource.h set.h undo.h \ + ../src_plugins/vendordrill/vendor.h stub_vendor.h dolists.h +puller.o: ../src_plugins/puller/puller.c ../config.h ../config.manual.h \ + ../config.auto.h global.h const.h ../globalconst.h ../config.h macro.h \ + hid.h polyarea.h libpcb_fp.h create.h global.h data.h draw.h misc.h ds.h \ + mymem.h move.h pcb-printf.h remove.h rtree.h strflags.h undo.h dolists.h Index: trunk/src/Makefile.in =================================================================== --- trunk/src/Makefile.in (revision 1064) +++ trunk/src/Makefile.in (revision 1065) @@ -4,7 +4,7 @@ # TODO default compiler flags put /local/pcb/CFLAGS cc/rdynamic append /local/pcb/CFLAGS cc/cflags -append /local/pcb/CFLAGS {-I. -I.. -I3rd -Iicons -DHAVE_CONFIG_H -g -DNDEBUG} +append /local/pcb/CFLAGS {-I. -I.. -I../src_3rd -Iicons -DHAVE_CONFIG_H -g -DNDEBUG} append /local/pcb/LDFLAGS cc/ldflags append /local/pcb/LDFLAGS cc/rdynamic @@ -79,8 +79,8 @@ parse_y.o parse_l.o buildin.o - 3rd/genht/hash.o - 3rd/genht/htsp.o + ../src_3rd/genht/hash.o + ../src_3rd/genht/htsp.o @] # main: language generator files Index: trunk/src/Makefile.in.mod/toporouter =================================================================== --- trunk/src/Makefile.in.mod/toporouter (revision 1064) +++ trunk/src/Makefile.in.mod/toporouter (revision 1065) @@ -1,6 +1,6 @@ if /local/pcb/toporouter/enable then append /local/pcb/OBJS {toporouter.o} -append /local/pcb/LIBS {3rd/gts/libgts.a} -append /local/pcb/CFLAGS {-I3rd/gts} +append /local/pcb/LIBS {../src_3rd/gts/libgts.a} +append /local/pcb/CFLAGS {-I../src_3rd/gts} append /local/pcb/ACTION_REG_SRC { toporouter.c } end Index: trunk/src/command.c =================================================================== --- trunk/src/command.c (revision 1064) +++ trunk/src/command.c (revision 1065) @@ -42,7 +42,6 @@ #include "buffer.h" #include "command.h" #include "data.h" -#include "djopt.h" #include "error.h" #include "file.h" #include "mymem.h" Index: trunk/src/hid/common/actions.c =================================================================== --- trunk/src/hid/common/actions.c (revision 1064) +++ trunk/src/hid/common/actions.c (revision 1065) @@ -7,8 +7,8 @@ #include #include -#include "../../3rd/genht/hash.h" -#include "../../3rd/genht/htsp.h" +#include "../../../src_3rd/genht/hash.h" +#include "../../../src_3rd/genht/htsp.h" #include "global.h" #include "data.h" Index: trunk/src/hid/lesstif/menu.c =================================================================== --- trunk/src/hid/lesstif/menu.c (revision 1064) +++ trunk/src/hid/lesstif/menu.c (revision 1065) @@ -25,8 +25,8 @@ #include "paths.h" #include "pcb-menu.h" -#include "../../3rd/genht/htsp.h" -#include "../../3rd/genht/hash.h" +#include "../../../src_3rd/genht/htsp.h" +#include "../../../src_3rd/genht/hash.h" RCSID("$Id$"); Index: trunk/src/libpcb_fp.c =================================================================== --- trunk/src/libpcb_fp.c (revision 1064) +++ trunk/src/libpcb_fp.c (revision 1065) @@ -83,8 +83,8 @@ #include "paths.h" -#include "3rd/genht/htsp.h" -#include "3rd/genht/hash.h" +#include "../src_3rd/genht/htsp.h" +#include "../src_3rd/genht/hash.h" RCSID("$Id$"); Index: trunk/src/netlist.c =================================================================== --- trunk/src/netlist.c (revision 1064) +++ trunk/src/netlist.c (revision 1065) @@ -46,7 +46,6 @@ #include "buffer.h" #include "command.h" #include "data.h" -#include "djopt.h" #include "error.h" #include "file.h" #include "find.h" Index: trunk/src_3rd/README =================================================================== --- trunk/src_3rd/README (nonexistent) +++ trunk/src_3rd/README (revision 1065) @@ -0,0 +1 @@ +3rd party software libs Index: trunk/src_3rd/gts/Makefile.dep =================================================================== --- trunk/src_3rd/gts/Makefile.dep (nonexistent) +++ trunk/src_3rd/gts/Makefile.dep (revision 1065) @@ -0,0 +1,39 @@ +### Generated file, do not edit, run make dep ### + +object.o: object.c gts.h gts-private.h +point.o: point.c gts.h gts-private.h predicates.h +vertex.o: vertex.c gts.h +segment.o: segment.c gts.h +edge.o: edge.c gts.h +triangle.o: triangle.c gts.h +face.o: face.c gts.h +kdtree.o: kdtree.c gts.h +bbtree.o: bbtree.c gts.h +misc.o: misc.c gts.h gts-private.h ../../config.h ../../config.manual.h \ + ../../config.auto.h +predicates.o: predicates.c predicates.h rounding.h ../../config.h \ + ../../config.manual.h ../../config.auto.h +heap.o: heap.c gts.h +eheap.o: eheap.c gts.h +fifo.o: fifo.c gts.h +matrix.o: matrix.c gts.h +surface.o: surface.c gts.h gts-private.h +stripe.o: stripe.c gts.h +vopt.o: vopt.c gts.h +refine.o: refine.c gts.h +iso.o: iso.c gts.h +isotetra.o: isotetra.c gts.h +split.o: split.c gts.h +psurface.o: psurface.c gts.h +hsurface.o: hsurface.c gts.h +cdt.o: cdt.c ../../config.h ../../config.manual.h ../../config.auto.h \ + gts.h +boolean.o: boolean.c gts.h +named.o: named.c gts.h +oocs.o: oocs.c gts.h +container.o: container.c gts.h +graph.o: graph.c gts.h +pgraph.o: pgraph.c gts.h +partition.o: partition.c gts.h +curvature.o: curvature.c gts.h +tribox3.o: tribox3.c Index: trunk/src_3rd/gts/Makefile.in =================================================================== --- trunk/src_3rd/gts/Makefile.in (nonexistent) +++ trunk/src_3rd/gts/Makefile.in (revision 1065) @@ -0,0 +1,63 @@ +put /local/gts/CFLAGS [@-I. -I.. -I../.. -DG_LOG_DOMAIN=\"Gts\" @libs/sul/glib/cflags@@] +put /local/gts/OBJS [@ + object.o + point.o + vertex.o + segment.o + edge.o + triangle.o + face.o + kdtree.o + bbtree.o + misc.o + predicates.o + heap.o + eheap.o + fifo.o + matrix.o + surface.o + stripe.o + vopt.o + refine.o + iso.o + isotetra.o + split.o + psurface.o + hsurface.o + cdt.o + boolean.o + named.o + oocs.o + container.o + graph.o + pgraph.o + partition.o + curvature.o + tribox3.o +@] + +put /tmpasm/OFS { } +uniq /local/gts/OBJS +put /local/gts/SRCS /local/gts/OBJS +gsub /local/gts/SRCS {.o } {.c } + +print [@ +CFLAGS = @/local/gts/CFLAGS@ +OBJS = @/local/gts/OBJS@ +CC=@cc/cc@ + +libgts.a: $(OBJS) + @fstools/ar@ rvu libgts.a $(OBJS) + +clean: + -@fstools/rm@ $(OBJS) libgts.a +@] + +# generate explicit rules for .c -> .o +put /local/comp/OBJS /local/gts/OBJS +include {../../scconfig/Makefile.comp.inc} + +# generate deps +put /local/dep/CFLAGS /local/gts/CFLAGS +put /local/dep/SRCS /local/gts/SRCS +include {../../scconfig/Makefile.dep.inc} Index: trunk/src_3rd/gts/bbtree.c =================================================================== --- trunk/src_3rd/gts/bbtree.c (nonexistent) +++ trunk/src_3rd/gts/bbtree.c (revision 1065) @@ -0,0 +1,1289 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "gts.h" + +static void bbox_init (GtsBBox * bbox) +{ + bbox->bounded = NULL; +} + +/** + * gts_bbox_class: + * + * Returns: the #GtsBBoxClass. + */ +GtsBBoxClass * gts_bbox_class (void) +{ + static GtsBBoxClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo bbox_info = { + "GtsBBox", + sizeof (GtsBBox), + sizeof (GtsBBoxClass), + (GtsObjectClassInitFunc) NULL, + (GtsObjectInitFunc) bbox_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (gts_object_class (), &bbox_info); + } + + return klass; +} + +/** + * gts_bbox_set: + * @bbox: a #GtsBBox. + * @bounded: the object to be bounded. + * @x1: x-coordinate of the lower left corner. + * @y1: y-coordinate of the lower left corner. + * @z1: z-coordinate of the lower left corner. + * @x2: x-coordinate of the upper right corner. + * @y2: y-coordinate of the upper right corner. + * @z2: z-coordinate of the upper right corner. + * + * Sets fields of @bbox. + */ +void gts_bbox_set (GtsBBox * bbox, + gpointer bounded, + gdouble x1, gdouble y1, gdouble z1, + gdouble x2, gdouble y2, gdouble z2) +{ + g_return_if_fail (bbox != NULL); + g_return_if_fail (x2 >= x1 && y2 >= y1 && z2 >= z1); + + bbox->x1 = x1; bbox->y1 = y1; bbox->z1 = z1; + bbox->x2 = x2; bbox->y2 = y2; bbox->z2 = z2; + bbox->bounded = bounded; +} + +/** + * gts_bbox_new: + * @klass: a #GtsBBoxClass. + * @bounded: the object to be bounded. + * @x1: x-coordinate of the lower left corner. + * @y1: y-coordinate of the lower left corner. + * @z1: z-coordinate of the lower left corner. + * @x2: x-coordinate of the upper right corner. + * @y2: y-coordinate of the upper right corner. + * @z2: z-coordinate of the upper right corner. + * + * Returns: a new #GtsBBox. + */ +GtsBBox * gts_bbox_new (GtsBBoxClass * klass, + gpointer bounded, + gdouble x1, gdouble y1, gdouble z1, + gdouble x2, gdouble y2, gdouble z2) +{ + GtsBBox * bbox; + + g_return_val_if_fail (klass != NULL, NULL); + + bbox = GTS_BBOX (gts_object_new (GTS_OBJECT_CLASS (klass))); + gts_bbox_set (bbox, bounded, x1, y1, z1, x2, y2, z2); + return bbox; +} + +/** + * gts_bbox_triangle: + * @klass: a #GtsBBoxClass. + * @t: a #GtsTriangle. + * + * Returns: a new #GtsBBox bounding box of @t. + */ +GtsBBox * gts_bbox_triangle (GtsBBoxClass * klass, + GtsTriangle * t) +{ + GtsBBox * bbox; + GtsPoint * p; + + g_return_val_if_fail (t != NULL, NULL); + g_return_val_if_fail (klass != NULL, NULL); + + p = GTS_POINT (GTS_SEGMENT (t->e1)->v1); + bbox = gts_bbox_new (klass, t, p->x, p->y, p->z, p->x, p->y, p->z); + + p = GTS_POINT (GTS_SEGMENT (t->e1)->v2); + if (p->x > bbox->x2) bbox->x2 = p->x; + if (p->x < bbox->x1) bbox->x1 = p->x; + if (p->y > bbox->y2) bbox->y2 = p->y; + if (p->y < bbox->y1) bbox->y1 = p->y; + if (p->z > bbox->z2) bbox->z2 = p->z; + if (p->z < bbox->z1) bbox->z1 = p->z; + p = GTS_POINT (gts_triangle_vertex (t)); + if (p->x > bbox->x2) bbox->x2 = p->x; + if (p->x < bbox->x1) bbox->x1 = p->x; + if (p->y > bbox->y2) bbox->y2 = p->y; + if (p->y < bbox->y1) bbox->y1 = p->y; + if (p->z > bbox->z2) bbox->z2 = p->z; + if (p->z < bbox->z1) bbox->z1 = p->z; + + return bbox; +} + +/** + * gts_bbox_segment: + * @klass: a #GtsBBoxClass. + * @s: a #GtsSegment. + * + * Returns: a new #GtsBBox bounding box of @s. + */ +GtsBBox * gts_bbox_segment (GtsBBoxClass * klass, GtsSegment * s) +{ + GtsBBox * bbox; + GtsPoint * p1, * p2; + + g_return_val_if_fail (s != NULL, NULL); + g_return_val_if_fail (klass != NULL, NULL); + + bbox = gts_bbox_new (klass, s, 0., 0., 0., 0., 0., 0.); + + p1 = GTS_POINT (s->v1); + p2 = GTS_POINT (s->v2); + if (p1->x > p2->x) { + bbox->x2 = p1->x; bbox->x1 = p2->x; + } + else { + bbox->x1 = p1->x; bbox->x2 = p2->x; + } + if (p1->y > p2->y) { + bbox->y2 = p1->y; bbox->y1 = p2->y; + } + else { + bbox->y1 = p1->y; bbox->y2 = p2->y; + } + if (p1->z > p2->z) { + bbox->z2 = p1->z; bbox->z1 = p2->z; + } + else { + bbox->z1 = p1->z; bbox->z2 = p2->z; + } + + return bbox; +} + +static void bbox_foreach_vertex (GtsPoint * p, GtsBBox * bb) +{ + if (p->x < bb->x1) bb->x1 = p->x; + if (p->y < bb->y1) bb->y1 = p->y; + if (p->z < bb->z1) bb->z1 = p->z; + if (p->x > bb->x2) bb->x2 = p->x; + if (p->y > bb->y2) bb->y2 = p->y; + if (p->z > bb->z2) bb->z2 = p->z; +} + +/** + * gts_bbox_surface: + * @klass: a #GtsBBoxClass. + * @surface: a #GtsSurface. + * + * Returns: a new #GtsBBox bounding box of @surface. + */ +GtsBBox * gts_bbox_surface (GtsBBoxClass * klass, GtsSurface * surface) +{ + GtsBBox * bbox; + + g_return_val_if_fail (klass != NULL, NULL); + g_return_val_if_fail (surface != NULL, NULL); + + bbox = gts_bbox_new (klass, surface, 0., 0., 0., 0., 0., 0.); + bbox->x1 = bbox->y1 = bbox->z1 = G_MAXDOUBLE; + bbox->x2 = bbox->y2 = bbox->z2 = -G_MAXDOUBLE; + + gts_surface_foreach_vertex (surface, (GtsFunc) bbox_foreach_vertex, bbox); + + return bbox; +} + +/** + * gts_bbox_bboxes: + * @klass: a #GtsBBoxClass. + * @bboxes: a list of #GtsBBox. + * + * Returns: a new #GtsBBox bounding box of all the bounding boxes in + * @bboxes. + */ +GtsBBox * gts_bbox_bboxes (GtsBBoxClass * klass, GSList * bboxes) +{ + GtsBBox * bbox; + GtsBBox * bb; + + g_return_val_if_fail (bboxes != NULL, NULL); + g_return_val_if_fail (klass != NULL, NULL); + + bb = bboxes->data; + bbox = gts_bbox_new (klass, bboxes, + bb->x1, bb->y1, bb->z1, bb->x2, bb->y2, bb->z2); + bboxes = bboxes->next; + while (bboxes) { + bb = bboxes->data; + if (bb->x1 < bbox->x1) bbox->x1 = bb->x1; + if (bb->y1 < bbox->y1) bbox->y1 = bb->y1; + if (bb->z1 < bbox->z1) bbox->z1 = bb->z1; + if (bb->x2 > bbox->x2) bbox->x2 = bb->x2; + if (bb->y2 > bbox->y2) bbox->y2 = bb->y2; + if (bb->z2 > bbox->z2) bbox->z2 = bb->z2; + bboxes = bboxes->next; + } + + return bbox; +} + +/** + * gts_bbox_points: + * @klass: a #GtsBBoxClass. + * @points: a list of #GtsPoint. + * + * Returns: a new #GtsBBox bounding box of @points. + */ +GtsBBox * gts_bbox_points (GtsBBoxClass * klass, GSList * points) +{ + GtsPoint * p; + GtsBBox * bbox; + GSList * i; + + if (points == NULL) + return NULL; + + p = points->data; + bbox = gts_bbox_new (klass, points, p->x, p->y, p->z, p->x, p->y, p->z); + + i = points->next; + while (i) { + p = i->data; + if (p->x > bbox->x2) + bbox->x2 = p->x; + else if (p->x < bbox->x1) + bbox->x1 = p->x; + if (p->y > bbox->y2) + bbox->y2 = p->y; + else if (p->y < bbox->y1) + bbox->y1 = p->y; + if (p->z > bbox->z2) + bbox->z2 = p->z; + else if (p->z < bbox->z1) + bbox->z1 = p->z; + i = i->next; + } + + return bbox; +} + +/** + * gts_bboxes_are_overlapping: + * @bb1: a #GtsBBox. + * @bb2: a #GtsBBox. + * + * Returns: %TRUE if the bounding boxes @bb1 and @bb2 are overlapping + * (including just touching), %FALSE otherwise. + */ +gboolean gts_bboxes_are_overlapping (GtsBBox * bb1, GtsBBox * bb2) +{ + if (bb1 == bb2) + return TRUE; + if (bb1->x1 > bb2->x2) + return FALSE; + if (bb2->x1 > bb1->x2) + return FALSE; + if (bb1->y1 > bb2->y2) + return FALSE; + if (bb2->y1 > bb1->y2) + return FALSE; + if (bb1->z1 > bb2->z2) + return FALSE; + if (bb2->z1 > bb1->z2) + return FALSE; + return TRUE; +} + +#define bbox_volume(bb) (((bb)->x2 -\ + (bb)->x1)*\ + ((bb)->y2 -\ + (bb)->y1)*\ + ((bb)->z2 -\ + (bb)->z1)) + +/** + * gts_bbox_diagonal2: + * @bb: a #GtsBBox. + * + * Returns: the squared length of the diagonal of @bb. + */ +gdouble gts_bbox_diagonal2 (GtsBBox * bb) +{ + gdouble x, y, z; + + g_return_val_if_fail (bb != NULL, 0.); + + x = bb->x2 - bb->x1; + y = bb->y2 - bb->y1; + z = bb->z2 - bb->z1; + + return x*x + y*y + z*z; +} + +/** + * gts_bbox_draw: + * @bb: a #GtsBBox. + * @fptr: a file pointer. + * + * Writes in file @fptr an OOGL (Geomview) description of @bb. + */ +void gts_bbox_draw (GtsBBox * bb, FILE * fptr) +{ + g_return_if_fail (bb != NULL); + + fprintf (fptr, "OFF 8 6 12\n"); + fprintf (fptr, "%g %g %g\n", + bb->x1, bb->y1, bb->z1); + fprintf (fptr, "%g %g %g\n", + bb->x2, bb->y1, bb->z1); + fprintf (fptr, "%g %g %g\n", + bb->x2, bb->y2, bb->z1); + fprintf (fptr, "%g %g %g\n", + bb->x1, bb->y2, bb->z1); + fprintf (fptr, "%g %g %g\n", + bb->x1, bb->y1, bb->z2); + fprintf (fptr, "%g %g %g\n", + bb->x2, bb->y1, bb->z2); + fprintf (fptr, "%g %g %g\n", + bb->x2, bb->y2, bb->z2); + fprintf (fptr, "%g %g %g\n", + bb->x1, bb->y2, bb->z2); + fputs ("4 3 2 1 0\n" + "4 4 5 6 7\n" + "4 2 3 7 6\n" + "4 0 1 5 4\n" + "4 0 4 7 3\n" + "4 1 2 6 5\n", + fptr); +} + +#define MINMAX(x1, x2, xmin, xmax) { if (x1 < x2) { xmin = x1; xmax = x2; }\ + else { xmin = x2; xmax = x1; } } + +/** + * gts_bbox_point_distance2: + * @bb: a #GtsBBox. + * @p: a #GtsPoint. + * @min: a pointer on a gdouble. + * @max: a pointer on a gdouble. + * + * Sets @min and @max to lower and upper bounds for the square of the + * Euclidean distance between the object contained in @bb and @p. For these + * bounds to make any sense the bounding box must be "tight" i.e. each of the + * 6 faces of the box must at least be touched by one point of the bounded + * object. + */ +void gts_bbox_point_distance2 (GtsBBox * bb, GtsPoint * p, + gdouble * min, gdouble * max) +{ + gdouble x1, y1, z1, x2, y2, z2, x, y, z; + gdouble dmin, dmax, xd1, xd2, yd1, yd2, zd1, zd2; + gdouble mx, Mx, my, My, mz, Mz; + + g_return_if_fail (bb != NULL); + g_return_if_fail (p != NULL); + g_return_if_fail (min != NULL); + g_return_if_fail (max != NULL); + + x1 = bb->x1; y1 = bb->y1; z1 = bb->z1; + x2 = bb->x2; y2 = bb->y2; z2 = bb->z2; + x = p->x; y = p->y; z = p->z; + + xd1 = (x1 - x)*(x1 - x); + xd2 = (x - x2)*(x - x2); + yd1 = (y1 - y)*(y1 - y); + yd2 = (y - y2)*(y - y2); + zd1 = (z1 - z)*(z1 - z); + zd2 = (z - z2)*(z - z2); + + dmin = x < x1 ? xd1 : x > x2 ? xd2 : 0.0; + dmin += y < y1 ? yd1 : y > y2 ? yd2 : 0.0; + dmin += z < z1 ? zd1 : z > z2 ? zd2 : 0.0; + + MINMAX (xd1, xd2, mx, Mx); + MINMAX (yd1, yd2, my, My); + MINMAX (zd1, zd2, mz, Mz); + + dmax = mx + My + Mz; + dmax = MIN (dmax, Mx + my + Mz); + dmax = MIN (dmax, Mx + My + mz); + + *min = dmin; + *max = dmax; +} + +/** + * gts_bbox_is_stabbed: + * @bb: a #GtsBBox. + * @p: a #GtsPoint. + * + * Returns: %TRUE if the ray starting at @p and ending at (+infty, + * @p->y, @p->z) intersects with @bb, %FALSE otherwise. + */ +gboolean gts_bbox_is_stabbed (GtsBBox * bb, GtsPoint * p) +{ + g_return_val_if_fail (bb != NULL, FALSE); + g_return_val_if_fail (p != NULL, FALSE); + + if (p->x > bb->x2 || + p->y < bb->y1 || p->y > bb->y2 || + p->z < bb->z1 || p->z > bb->z2) + return FALSE; + return TRUE; +} + +extern int triBoxOverlap (double boxcenter[3], + double boxhalfsize[3], + double triverts[3][3]); + +/** + * gts_bbox_overlaps_triangle: + * @bb: a #GtsBBox. + * @t: a #GtsTriangle. + * + * This is a wrapper around the fast overlap test of Tomas + * Akenine-Moller (http://www.cs.lth.se/home/Tomas_Akenine_Moller/). + * + * Returns: %TRUE if @bb overlaps with @t, %FALSE otherwise. + */ +gboolean gts_bbox_overlaps_triangle (GtsBBox * bb, GtsTriangle * t) +{ + double bc[3], bh[3], tv[3][3]; + GtsPoint * p1, * p2, * p3; + + g_return_val_if_fail (bb != NULL, FALSE); + g_return_val_if_fail (t != NULL, FALSE); + + bc[0] = (bb->x2 + bb->x1)/2.; + bh[0] = (bb->x2 - bb->x1)/2.; + bc[1] = (bb->y2 + bb->y1)/2.; + bh[1] = (bb->y2 - bb->y1)/2.; + bc[2] = (bb->z2 + bb->z1)/2.; + bh[2] = (bb->z2 - bb->z1)/2.; + p1 = GTS_POINT (GTS_SEGMENT (t->e1)->v1); + p2 = GTS_POINT (GTS_SEGMENT (t->e1)->v2); + p3 = GTS_POINT (gts_triangle_vertex (t)); + tv[0][0] = p1->x; tv[0][1] = p1->y; tv[0][2] = p1->z; + tv[1][0] = p2->x; tv[1][1] = p2->y; tv[1][2] = p2->z; + tv[2][0] = p3->x; tv[2][1] = p3->y; tv[2][2] = p3->z; + + return triBoxOverlap (bc, bh, tv); +} + +/** + * gts_bbox_overlaps_segment: + * @bb: a #GtsBBox. + * @s: a #GtsSegment. + * + * This functions uses gts_bbox_overlaps_triangle() with a degenerate + * triangle. + * + * Returns: %TRUE if @bb overlaps with @s, %FALSE otherwise. + */ +gboolean gts_bbox_overlaps_segment (GtsBBox * bb, GtsSegment * s) +{ + double bc[3], bh[3], tv[3][3]; + GtsPoint * p1, * p2, * p3; + + g_return_val_if_fail (bb != NULL, FALSE); + g_return_val_if_fail (s != NULL, FALSE); + + bc[0] = (bb->x2 + bb->x1)/2.; + bh[0] = (bb->x2 - bb->x1)/2.; + bc[1] = (bb->y2 + bb->y1)/2.; + bh[1] = (bb->y2 - bb->y1)/2.; + bc[2] = (bb->z2 + bb->z1)/2.; + bh[2] = (bb->z2 - bb->z1)/2.; + p1 = GTS_POINT (s->v1); + p2 = GTS_POINT (s->v2); + p3 = p1; + tv[0][0] = p1->x; tv[0][1] = p1->y; tv[0][2] = p1->z; + tv[1][0] = p2->x; tv[1][1] = p2->y; tv[1][2] = p2->z; + tv[2][0] = p3->x; tv[2][1] = p3->y; tv[2][2] = p3->z; + + return triBoxOverlap (bc, bh, tv); +} + +/** + * gts_bb_tree_new: + * @bboxes: a list of #GtsBBox. + * + * Builds a new hierarchy of bounding boxes for @bboxes. At each + * level, the GNode->data field contains a #GtsBBox bounding box of + * all the children. The tree is binary and is built by repeatedly + * cutting in two approximately equal halves the bounding boxes at + * each level until a leaf node (i.e. a bounding box given in @bboxes) + * is reached. In order to minimize the depth of the tree, the cutting + * direction is always chosen as perpendicular to the longest + * dimension of the bounding box. + * + * Returns: a new hierarchy of bounding boxes. + */ +GNode * gts_bb_tree_new (GSList * bboxes) +{ + GSList * i, * positive = NULL, * negative = NULL; + GNode * node; + GtsBBox * bbox; + guint dir, np = 0, nn = 0; + gdouble * p1, * p2; + gdouble cut; + + g_return_val_if_fail (bboxes != NULL, NULL); + + if (bboxes->next == NULL) /* leaf node */ + return g_node_new (bboxes->data); + + bbox = gts_bbox_bboxes (gts_bbox_class (), bboxes); + node = g_node_new (bbox); + + if (bbox->x2 - bbox->x1 > bbox->y2 - bbox->y1) { + if (bbox->z2 - bbox->z1 > bbox->x2 - bbox->x1) + dir = 2; + else + dir = 0; + } + else if (bbox->z2 - bbox->z1 > bbox->y2 - bbox->y1) + dir = 2; + else + dir = 1; + + p1 = (gdouble *) &bbox->x1; + p2 = (gdouble *) &bbox->x2; + cut = (p1[dir] + p2[dir])/2.; + i = bboxes; + while (i) { + bbox = i->data; + p1 = (gdouble *) &bbox->x1; + p2 = (gdouble *) &bbox->x2; + if ((p1[dir] + p2[dir])/2. > cut) { + positive = g_slist_prepend (positive, bbox); + np++; + } + else { + negative = g_slist_prepend (negative, bbox); + nn++; + } + i = i->next; + } + if (!positive) { + GSList * last = g_slist_nth (negative, (nn - 1)/2); + positive = last->next; + last->next = NULL; + } + else if (!negative) { + GSList * last = g_slist_nth (positive, (np - 1)/2); + negative = last->next; + last->next = NULL; + } + g_node_prepend (node, gts_bb_tree_new (positive)); + g_slist_free (positive); + g_node_prepend (node, gts_bb_tree_new (negative)); + g_slist_free (negative); + + return node; +} + +static void prepend_triangle_bbox (GtsTriangle * t, GSList ** bboxes) +{ + *bboxes = g_slist_prepend (*bboxes, + gts_bbox_triangle (gts_bbox_class (), t)); +} + +/** + * gts_bb_tree_surface: + * @s: a #GtsSurface. + * + * Returns: a new hierarchy of bounding boxes bounding the faces of @s. + */ +GNode * gts_bb_tree_surface (GtsSurface * s) +{ + GSList * bboxes = NULL; + GNode * tree; + + g_return_val_if_fail (s != NULL, NULL); + + gts_surface_foreach_face (s, (GtsFunc) prepend_triangle_bbox, &bboxes); + tree = gts_bb_tree_new (bboxes); + g_slist_free (bboxes); + + return tree; +} + +/** + * gts_bb_tree_stabbed: + * @tree: a bounding box tree. + * @p: a #GtsPoint. + * + * Returns: a list of bounding boxes, leaves of @tree which are + * stabbed by the ray defined by @p (see gts_bbox_is_stabbed()). + */ +GSList * gts_bb_tree_stabbed (GNode * tree, GtsPoint * p) +{ + GSList * list = NULL; + GtsBBox * bb; + GNode * i; + + g_return_val_if_fail (tree != NULL, NULL); + g_return_val_if_fail (p != NULL, NULL); + + bb = tree->data; + if (!gts_bbox_is_stabbed (bb, p)) + return NULL; + if (tree->children == NULL) /* leaf node */ + return g_slist_prepend (NULL, bb); + i = tree->children; + while (i) { + list = g_slist_concat (list, gts_bb_tree_stabbed (i, p)); + i = i->next; + } + return list; +} + +/** + * gts_bb_tree_overlap: + * @tree: a bounding box tree. + * @bbox: a #GtsBBox. + * + * Returns: a list of bounding boxes, leaves of @tree which overlap @bbox. + */ +GSList * gts_bb_tree_overlap (GNode * tree, GtsBBox * bbox) +{ + GSList * list = NULL; + GtsBBox * bb; + GNode * i; + + g_return_val_if_fail (tree != NULL, NULL); + g_return_val_if_fail (bbox != NULL, NULL); + + bb = tree->data; + if (!gts_bboxes_are_overlapping (bbox, bb)) + return NULL; + if (tree->children == NULL) /* leaf node */ + return g_slist_prepend (NULL, bb); + i = tree->children; + while (i) { + list = g_slist_concat (list, gts_bb_tree_overlap (i, bbox)); + i = i->next; + } + return list; +} + +/** + * gts_bb_tree_is_overlapping: + * @tree: a bounding box tree. + * @bbox: a #GtsBBox. + * + * Returns: %TRUE if any leaf of @tree overlaps @bbox, %FALSE otherwise. + */ +gboolean gts_bb_tree_is_overlapping (GNode * tree, GtsBBox * bbox) +{ + GtsBBox * bb; + GNode * i; + + g_return_val_if_fail (tree != NULL, FALSE); + g_return_val_if_fail (bbox != NULL, FALSE); + + bb = tree->data; + if (!gts_bboxes_are_overlapping (bbox, bb)) + return FALSE; + if (tree->children == NULL) /* leaf node */ + return TRUE; + i = tree->children; + while (i) { + if (gts_bb_tree_is_overlapping (i, bbox)) + return TRUE; + i = i->next; + } + return FALSE; +} + +/** + * gts_bb_tree_traverse_overlapping: + * @tree1: a bounding box tree. + * @tree2: a bounding box tree. + * @func: a #GtsBBTreeTraverseFunc. + * @data: user data to be passed to @func. + * + * Calls @func for each overlapping pair of leaves of @tree1 and @tree2. + */ +void gts_bb_tree_traverse_overlapping (GNode * tree1, GNode * tree2, + GtsBBTreeTraverseFunc func, + gpointer data) +{ + GtsBBox * bb1, * bb2; + + g_return_if_fail (tree1 != NULL && tree2 != NULL); + + bb1 = tree1->data; bb2 = tree2->data; + if (!gts_bboxes_are_overlapping (bb1, bb2)) + return; + + if (tree1->children == NULL && tree2->children == NULL) + (*func) (tree1->data, tree2->data, data); + else if (tree2->children == NULL || + (tree1->children != NULL && + bbox_volume (bb1) > bbox_volume (bb2))) { + GNode * i = tree1->children; + while (i) { + gts_bb_tree_traverse_overlapping (i, tree2, func, data); + i = i->next; + } + } + else { + GNode * i = tree2->children; + while (i) { + gts_bb_tree_traverse_overlapping (tree1, i, func, data); + i = i->next; + } + } +} + +/** + * gts_bb_tree_draw: + * @tree: a bounding box tree. + * @depth: a specified depth. + * @fptr: a file pointer. + * + * Write in @fptr an OOGL (Geomview) description of @tree for the + * depth specified by @depth. + */ +void gts_bb_tree_draw (GNode * tree, guint depth, FILE * fptr) +{ + guint d; + + g_return_if_fail (tree != NULL); + g_return_if_fail (fptr != NULL); + + d = g_node_depth (tree); + + if (d == 1) + fprintf (fptr, "{ LIST"); + + if (d == depth) + gts_bbox_draw (tree->data, fptr); + else if (d < depth) { + GNode * i = tree->children; + while (i) { + gts_bb_tree_draw (i, depth, fptr); + i = i->next; + } + } + + if (d == 1) + fprintf (fptr, "}\n"); +} + +static void bb_tree_free (GNode * tree, gboolean free_leaves) +{ + GNode * i; + + g_return_if_fail (tree != NULL); + + if (!free_leaves && tree->children == NULL) /* leaf node */ + return; + + gts_object_destroy (tree->data); + + i = tree->children; + while (i) { + bb_tree_free (i, free_leaves); + i = i->next; + } +} + +/** + * gts_bb_tree_destroy: + * @tree: a bounding box tree. + * @free_leaves: if %TRUE the bounding boxes given by the user are freed. + * + * Destroys all the bounding boxes created by @tree and destroys the + * tree itself. If @free_leaves is set to %TRUE, destroys boxes given + * by the user when creating the tree (i.e. leaves of the tree). + */ +void gts_bb_tree_destroy (GNode * tree, gboolean free_leaves) +{ + g_return_if_fail (tree != NULL); + + bb_tree_free (tree, free_leaves); + g_node_destroy (tree); +} + +static gdouble bb_tree_min_max (GNode * tree, + GtsPoint * p, + gdouble min_max, + GSList ** list) +{ + GNode * tree1, * tree2; + gdouble min1, max1, min2, max2; + + if (tree->children == NULL) { + *list = g_slist_prepend (*list, tree->data); + return min_max; + } + tree1 = tree->children; + gts_bbox_point_distance2 (tree1->data, p, &min1, &max1); + if (max1 < min_max) + min_max = max1; + + tree2 = tree1->next; + gts_bbox_point_distance2 (tree2->data, p, &min2, &max2); + if (max2 < min_max) + min_max = max2; + + if (min1 < min2) { + if (min1 <= min_max) { + min_max = bb_tree_min_max (tree1, p, min_max, list); + if (min2 <= min_max) + min_max = bb_tree_min_max (tree2, p, min_max, list); + } + } + else { + if (min2 <= min_max) { + min_max = bb_tree_min_max (tree2, p, min_max, list); + if (min1 <= min_max) + min_max = bb_tree_min_max (tree1, p, min_max, list); + } + } + + return min_max; +} + +/** + * gts_bb_tree_point_closest_bboxes: + * @tree: a bounding box tree. + * @p: a #GtsPoint. + * + * Returns: a list of #GtsBBox. One of the bounding boxes is assured to contain + * the object of @tree closest to @p. + */ +GSList * gts_bb_tree_point_closest_bboxes (GNode * tree, + GtsPoint * p) +{ + gdouble min, min_max; + GSList * list = NULL, * i, * prev = NULL; + + g_return_val_if_fail (tree != NULL, NULL); + g_return_val_if_fail (p != NULL, NULL); + + gts_bbox_point_distance2 (tree->data, p, &min, &min_max); + min_max = bb_tree_min_max (tree, p, min_max, &list); + + i = list; + while (i) { + GSList * next = i->next; + gdouble min, max; + + gts_bbox_point_distance2 (i->data, p, &min, &max); + + if (min > min_max) { + if (prev == NULL) + list = next; + else + prev->next = next; + g_slist_free_1 (i); + } + else + prev = i; + i = next; + } + + return list; +} + +/** + * gts_bb_tree_point_distance: + * @tree: a bounding box tree. + * @p: a #GtsPoint. + * @distance: a #GtsBBoxDistFunc. + * @bbox: if not %NULL is set to the bounding box containing the closest + * object. + * + * Returns: the distance as evaluated by @distance between @p and the closest + * object in @tree. + */ +gdouble gts_bb_tree_point_distance (GNode * tree, + GtsPoint * p, + GtsBBoxDistFunc distance, + GtsBBox ** bbox) +{ + GSList * list, * i; + gdouble dmin = G_MAXDOUBLE; + + g_return_val_if_fail (tree != NULL, dmin); + g_return_val_if_fail (p != NULL, dmin); + g_return_val_if_fail (distance != NULL, dmin); + + i = list = gts_bb_tree_point_closest_bboxes (tree, p); + while (i) { + gdouble d = (*distance) (p, GTS_BBOX (i->data)->bounded); + + if (fabs (d) < fabs (dmin)) { + dmin = d; + if (bbox) + *bbox = i->data; + } + i = i->next; + } + g_slist_free (list); + + return dmin; +} + +/** + * gts_bb_tree_point_closest: + * @tree: a bounding box tree. + * @p: a #GtsPoint. + * @closest: a #GtsBBoxClosestFunc. + * @distance: if not %NULL is set to the distance between @p and the + * new #GtsPoint. + * + * Returns: a new #GtsPoint, closest point to @p and belonging to an object of + * @tree. + */ +GtsPoint * gts_bb_tree_point_closest (GNode * tree, + GtsPoint * p, + GtsBBoxClosestFunc closest, + gdouble * distance) +{ + GSList * list, * i; + gdouble dmin = G_MAXDOUBLE; + GtsPoint * np = NULL; + + g_return_val_if_fail (tree != NULL, NULL); + g_return_val_if_fail (p != NULL, NULL); + g_return_val_if_fail (closest != NULL, NULL); + + i = list = gts_bb_tree_point_closest_bboxes (tree, p); + while (i) { + GtsPoint * tp = (*closest) (p, GTS_BBOX (i->data)->bounded); + gdouble d = gts_point_distance2 (tp, p); + + if (d < dmin) { + if (np) + gts_object_destroy (GTS_OBJECT (np)); + np = tp; + dmin = d; + } + else + gts_object_destroy (GTS_OBJECT (tp)); + i = i->next; + } + g_slist_free (list); + + if (distance) + *distance = dmin; + + return np; +} + +/** + * gts_bb_tree_triangle_distance: + * @tree: a bounding box tree. + * @t: a #GtsTriangle. + * @distance: a #GtsBBoxDistFunc. + * @delta: spatial scale of the sampling to be used. + * @range: a #GtsRange to be filled with the results. + * + * Given a triangle @t, points are sampled regularly on its surface + * using @delta as increment. The distance from each of these points + * to the closest object of @tree is computed using @distance and the + * gts_bb_tree_point_distance() function. The fields of @range are + * filled with the number of points sampled, the minimum, average and + * maximum value and the standard deviation. + */ +void gts_bb_tree_triangle_distance (GNode * tree, + GtsTriangle * t, + GtsBBoxDistFunc distance, + gdouble delta, + GtsRange * range) +{ + GtsPoint * p1, * p2, * p3, * p; + GtsVector p1p2, p1p3; + gdouble l1, t1, dt1; + guint i, n1; + + g_return_if_fail (tree != NULL); + g_return_if_fail (t != NULL); + g_return_if_fail (distance != NULL); + g_return_if_fail (delta > 0.); + g_return_if_fail (range != NULL); + + gts_triangle_vertices (t, + (GtsVertex **) &p1, + (GtsVertex **) &p2, + (GtsVertex **) &p3); + + gts_vector_init (p1p2, p1, p2); + gts_vector_init (p1p3, p1, p3); + gts_range_init (range); + p = GTS_POINT (gts_object_new (GTS_OBJECT_CLASS (gts_point_class ()))); + + l1 = sqrt (gts_vector_scalar (p1p2, p1p2)); + n1 = l1/delta + 1; + dt1 = 1.0/(gdouble) n1; + t1 = 0.0; + for (i = 0; i <= n1; i++, t1 += dt1) { + gdouble t2 = 1. - t1; + gdouble x = t2*p1p3[0]; + gdouble y = t2*p1p3[1]; + gdouble z = t2*p1p3[2]; + gdouble l2 = sqrt (x*x + y*y + z*z); + guint j, n2 = (guint) (l2/delta + 1); + gdouble dt2 = t2/(gdouble) n2; + + x = t2*p1->x + t1*p2->x; + y = t2*p1->y + t1*p2->y; + z = t2*p1->z + t1*p2->z; + + t2 = 0.0; + for (j = 0; j <= n2; j++, t2 += dt2) { + p->x = x + t2*p1p3[0]; + p->y = y + t2*p1p3[1]; + p->z = z + t2*p1p3[2]; + + gts_range_add_value (range, + gts_bb_tree_point_distance (tree, p, distance, NULL)); + } + } + + gts_object_destroy (GTS_OBJECT (p)); + gts_range_update (range); +} + +/** + * gts_bb_tree_segment_distance: + * @tree: a bounding box tree. + * @s: a #GtsSegment. + * @distance: a #GtsBBoxDistFunc. + * @delta: spatial scale of the sampling to be used. + * @range: a #GtsRange to be filled with the results. + * + * Given a segment @s, points are sampled regularly on its length + * using @delta as increment. The distance from each of these points + * to the closest object of @tree is computed using @distance and the + * gts_bb_tree_point_distance() function. The fields of @range are + * filled with the number of points sampled, the minimum, average and + * maximum value and the standard deviation. + */ +void gts_bb_tree_segment_distance (GNode * tree, + GtsSegment * s, + gdouble (*distance) (GtsPoint *, + gpointer), + gdouble delta, + GtsRange * range) +{ + GtsPoint * p1, * p2, * p; + GtsVector p1p2; + gdouble l, t, dt; + guint i, n; + + g_return_if_fail (tree != NULL); + g_return_if_fail (s != NULL); + g_return_if_fail (distance != NULL); + g_return_if_fail (delta > 0.); + g_return_if_fail (range != NULL); + + p1 = GTS_POINT (s->v1); + p2 = GTS_POINT (s->v2); + + gts_vector_init (p1p2, p1, p2); + gts_range_init (range); + p = GTS_POINT (gts_object_new (GTS_OBJECT_CLASS (gts_point_class()))); + + l = sqrt (gts_vector_scalar (p1p2, p1p2)); + n = (guint) (l/delta + 1); + dt = 1.0/(gdouble) n; + t = 0.0; + for (i = 0; i <= n; i++, t += dt) { + p->x = p1->x + t*p1p2[0]; + p->y = p1->y + t*p1p2[1]; + p->z = p1->z + t*p1p2[2]; + + gts_range_add_value (range, + gts_bb_tree_point_distance (tree, p, distance, NULL)); + } + + gts_object_destroy (GTS_OBJECT (p)); + gts_range_update (range); +} + +static void surface_distance_foreach_triangle (GtsTriangle * t, + gpointer * data) +{ + gdouble * delta = data[1]; + GtsRange * range = data[2]; + gdouble * total_area = data[3], area; + GtsRange range_triangle; + + gts_bb_tree_triangle_distance (data[0], t, data[4], *delta, &range_triangle); + + if (range_triangle.min < range->min) + range->min = range_triangle.min; + if (range_triangle.max > range->max) + range->max = range_triangle.max; + range->n += range_triangle.n; + + area = gts_triangle_area (t); + *total_area += area; + range->sum += area*range_triangle.mean; + range->sum2 += area*range_triangle.mean*range_triangle.mean; +} + +/** + * gts_bb_tree_surface_distance: + * @tree: a bounding box tree. + * @s: a #GtsSurface. + * @distance: a #GtsBBoxDistFunc. + * @delta: a sampling increment defined as the percentage of the diagonal + * of the root bounding box of @tree. + * @range: a #GtsRange to be filled with the results. + * + * Calls gts_bb_tree_triangle_distance() for each face of @s. The + * fields of @range are filled with the minimum, maximum and average + * distance. The average distance is defined as the sum of the average + * distances for each triangle weighthed by their area and divided by + * the total area of the surface. The standard deviation is defined + * accordingly. The @n field of @range is filled with the number of + * sampled points used. + */ +void gts_bb_tree_surface_distance (GNode * tree, + GtsSurface * s, + GtsBBoxDistFunc distance, + gdouble delta, + GtsRange * range) +{ + gpointer data[5]; + gdouble total_area = 0.; + + g_return_if_fail (tree != NULL); + g_return_if_fail (s != NULL); + g_return_if_fail (delta > 0. && delta < 1.); + g_return_if_fail (range != NULL); + + gts_range_init (range); + delta *= sqrt (gts_bbox_diagonal2 (tree->data)); + data[0] = tree; + data[1] = δ + data[2] = range; + data[3] = &total_area; + data[4] = distance; + + gts_surface_foreach_face (s, + (GtsFunc) surface_distance_foreach_triangle, + data); + + if (total_area > 0.) { + if (range->sum2 - range->sum*range->sum/total_area >= 0.) + range->stddev = sqrt ((range->sum2 - range->sum*range->sum/total_area) + /total_area); + else + range->stddev = 0.; + range->mean = range->sum/total_area; + } + else + range->min = range->max = range->mean = range->stddev = 0.; +} + +static void surface_distance_foreach_boundary (GtsEdge * e, + gpointer * data) +{ + gdouble * delta = data[1]; + GtsRange * range = data[2]; + gdouble * total_length = data[3], length; + GtsRange range_edge; + + if (gts_edge_is_boundary (e, NULL)) { + GtsSegment * s = GTS_SEGMENT (e); + + gts_bb_tree_segment_distance (data[0], s, data[4], *delta, &range_edge); + + if (range_edge.min < range->min) + range->min = range_edge.min; + if (range_edge.max > range->max) + range->max = range_edge.max; + range->n += range_edge.n; + + length = gts_point_distance (GTS_POINT (s->v1), GTS_POINT (s->v2)); + *total_length += length; + range->sum += length*range_edge.mean; + range->sum2 += length*range_edge.mean*range_edge.mean; + } +} + +/** + * gts_bb_tree_surface_boundary_distance: + * @tree: a bounding box tree. + * @s: a #GtsSurface. + * @distance: a #GtsBBoxDistFunc. + * @delta: a sampling increment defined as the percentage of the diagonal + * of the root bounding box of @tree. + * @range: a #GtsRange to be filled with the results. + * + * Calls gts_bb_tree_segment_distance() for each edge boundary of @s. + * The fields of @range are filled with the minimum, maximum and + * average distance. The average distance is defined as the sum of the + * average distances for each boundary edge weighthed by their length + * and divided by the total length of the boundaries. The standard + * deviation is defined accordingly. The @n field of @range is filled + * with the number of sampled points used. + */ +void gts_bb_tree_surface_boundary_distance (GNode * tree, + GtsSurface * s, + gdouble (*distance) (GtsPoint *, + gpointer), + gdouble delta, + GtsRange * range) +{ + gpointer data[5]; + gdouble total_length = 0.; + + g_return_if_fail (tree != NULL); + g_return_if_fail (s != NULL); + g_return_if_fail (delta > 0. && delta < 1.); + g_return_if_fail (range != NULL); + + gts_range_init (range); + delta *= sqrt (gts_bbox_diagonal2 (tree->data)); + data[0] = tree; + data[1] = δ + data[2] = range; + data[3] = &total_length; + data[4] = distance; + + gts_surface_foreach_edge (s, + (GtsFunc) surface_distance_foreach_boundary, + data); + + if (total_length > 0.) { + if (range->sum2 - range->sum*range->sum/total_length >= 0.) + range->stddev = sqrt ((range->sum2 - + range->sum*range->sum/total_length) + /total_length); + else + range->stddev = 0.; + range->mean = range->sum/total_length; + } + else + range->min = range->max = range->mean = range->stddev = 0.; +} Index: trunk/src_3rd/gts/boolean.c =================================================================== --- trunk/src_3rd/gts/boolean.c (nonexistent) +++ trunk/src_3rd/gts/boolean.c (revision 1065) @@ -0,0 +1,2048 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999--2002 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "gts.h" + +/*#define DEBUG*/ +/*#define DEBUG_BOOLEAN*/ +/*#define CHECK_ORIENTED*/ + +#ifdef DEBUG +# include "gts-private.h" +#endif /* DEBUG */ + +static void surface_inter_destroy (GtsObject * object) +{ + GtsSurfaceInter * si = GTS_SURFACE_INTER (object); + + gts_object_destroy (GTS_OBJECT (si->s1)); + gts_object_destroy (GTS_OBJECT (si->s2)); + g_slist_free (si->edges); + + (* GTS_OBJECT_CLASS (gts_surface_inter_class ())->parent_class->destroy) + (object); +} + +static void surface_inter_class_init (GtsObjectClass * klass) +{ + klass->destroy = surface_inter_destroy; +} + +static void surface_inter_init (GtsSurfaceInter * si) +{ + si->s1 = si->s2 = NULL; + si->edges = NULL; +} + +/** + * gts_surface_inter_class: + * + * Returns: the #GtsSurfaceInterClass. + */ +GtsSurfaceInterClass * gts_surface_inter_class (void) +{ + static GtsSurfaceInterClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo surface_inter_info = { + "GtsSurfaceInter", + sizeof (GtsSurfaceInter), + sizeof (GtsSurfaceInterClass), + (GtsObjectClassInitFunc) surface_inter_class_init, + (GtsObjectInitFunc) surface_inter_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (gts_object_class (), &surface_inter_info); + } + + return klass; +} + +/* EdgeInter: Header */ + +typedef struct _EdgeInter EdgeInter; + +struct _EdgeInter { + GtsEdge parent; + + GtsTriangle * t1, * t2; +}; + +#define EDGE_INTER(obj) GTS_OBJECT_CAST (obj,\ + EdgeInter,\ + edge_inter_class ()) +#define IS_EDGE_INTER(obj) (gts_object_is_from_class (obj,\ + edge_inter_class ())) + +static GtsEdgeClass * edge_inter_class (void); +static EdgeInter * edge_inter_new (GtsVertex * v1, GtsVertex * v2, + GtsTriangle * t1, GtsTriangle * t2); + +/* EdgeInter: Object */ + +static GtsEdgeClass * edge_inter_class (void) +{ + static GtsEdgeClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo edge_inter_info = { + "EdgeInter", + sizeof (EdgeInter), + sizeof (GtsEdgeClass), + (GtsObjectClassInitFunc) NULL, + (GtsObjectInitFunc) NULL, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_constraint_class ()), + &edge_inter_info); + } + + return klass; +} + +static EdgeInter * edge_inter_new (GtsVertex * v1, GtsVertex * v2, + GtsTriangle * t1, GtsTriangle * t2) +{ + EdgeInter * object; + + object = EDGE_INTER (gts_edge_new (GTS_EDGE_CLASS (edge_inter_class ()), + v1, v2)); + object->t1 = t1; + object->t2 = t2; + + return object; +} + +#ifdef DEBUG +static void write_surface_graph (GtsSurface * s, FILE * fp) +{ + GSList * l = NULL; + GtsGraph * g; + static void add_to_list (gpointer data, GSList ** l) { + *l = g_slist_prepend (*l, data); + } + + gts_surface_foreach_vertex (s, (GtsFunc) gts_object_reset_reserved, NULL); + gts_surface_foreach_edge (s, (GtsFunc) gts_object_reset_reserved, NULL); + gts_surface_foreach_edge (s, (GtsFunc) add_to_list, &l); + g = gts_segments_graph_new (gts_graph_class (), l); + gts_graph_write_dot (g, fp); + gts_object_destroy (GTS_OBJECT (g)); + g_slist_free (l); +} +#endif /* DEBUG */ + +static GtsPoint * segment_triangle_intersection (GtsSegment * s, + GtsTriangle * t, + GtsPointClass * klass) +{ + GtsPoint * A, * B, * C, * D, * E; + gint ABCE, ABCD, ADCE, ABDE, BCDE; + GtsEdge * AB, * BC, * CA; + gdouble a, b, c; + + g_return_val_if_fail (s != NULL, NULL); + g_return_val_if_fail (t != NULL, NULL); + g_return_val_if_fail (klass != NULL, NULL); + + gts_triangle_vertices_edges (t, NULL, + (GtsVertex **) &A, + (GtsVertex **) &B, + (GtsVertex **) &C, + &AB, &BC, &CA); + D = GTS_POINT (s->v1); + E = GTS_POINT (s->v2); + + ABCE = gts_point_orientation_3d_sos (A, B, C, E); + ABCD = gts_point_orientation_3d_sos (A, B, C, D); + if (ABCE < 0 || ABCD > 0) { + GtsPoint * tmpp; + gint tmp; + + tmpp = E; E = D; D = tmpp; + tmp = ABCE; ABCE = ABCD; ABCD = tmp; + } + if (ABCE < 0 || ABCD > 0) + return NULL; + ADCE = gts_point_orientation_3d_sos (A, D, C, E); + if (ADCE < 0) + return NULL; + ABDE = gts_point_orientation_3d_sos (A, B, D, E); + if (ABDE < 0) + return NULL; + BCDE = gts_point_orientation_3d_sos (B, C, D, E); + if (BCDE < 0) + return NULL; + a = gts_point_orientation_3d (A, B, C, E); + b = gts_point_orientation_3d (A, B, C, D); + if (a != b) { + c = a/(a - b); + return gts_point_new (klass, + E->x + c*(D->x - E->x), + E->y + c*(D->y - E->y), + E->z + c*(D->z - E->z)); + } + /* D and E are contained within ABC */ +#ifdef DEBUG + fprintf (stderr, + "segment: %p:%s triangle: %p:%s intersection\n" + "D and E contained in ABC\n", + s, GTS_NEDGE (s)->name, t, GTS_NFACE (t)->name); +#endif /* DEBUG */ + g_assert (a == 0.); + return gts_point_new (klass, + (E->x + D->x)/2., + (E->y + D->y)/2., + (E->z + D->z)/2.); +} + +static gint triangle_triangle_orientation (GtsPoint * p1, + GtsPoint * p2, GtsPoint * p3, + GtsPoint * p4, GtsPoint * p5, + GtsPoint * p6) +{ + gint o4 = 0, o5 = 0, o6 = 0; + + if (p4 != p1 && p4 != p2 && p4 != p3) + o4 = gts_point_orientation_3d_sos (p1, p2, p3, p4); + if (p5 != p1 && p5 != p2 && p5 != p3) + o5 = gts_point_orientation_3d_sos (p1, p2, p3, p5); + if (o4*o5 < 0) + return 0; + if (p6 != p1 && p6 != p2 && p6 != p3) + o6 = gts_point_orientation_3d_sos (p1, p2, p3, p6); + if (o4*o6 < 0 || o5*o6 < 0) + return 0; + if (o4) return o4; + if (o5) return o5; + g_assert (o6); + return o6; +} + +static gint triangle_point_orientation (GtsTriangle * t1, + GtsTriangle * t2, + gint o1, + GtsPoint * p) +{ + GtsPoint * p1 = GTS_POINT (GTS_SEGMENT (t1->e1)->v1); + GtsPoint * p2 = GTS_POINT (GTS_SEGMENT (t1->e1)->v2); + GtsPoint * p3 = GTS_POINT (gts_triangle_vertex (t1)); + GtsPoint * p4 = GTS_POINT (GTS_SEGMENT (t2->e1)->v1); + GtsPoint * p5 = GTS_POINT (GTS_SEGMENT (t2->e1)->v2); + GtsPoint * p6 = GTS_POINT (gts_triangle_vertex (t2)); + gint o = triangle_triangle_orientation (p1, p2, p3, p4, p5, p6); + + if (o != 0) + return o; + o = triangle_triangle_orientation (p4, p5, p6, p1, p2, p3); + if (o != 0) { + gint o2 = gts_point_orientation_3d_sos (p4, p5, p6, p); + + return - o*o1*o2; + } + return 0; +} + +static void add_edge_inter (GtsEdge * e, + GtsTriangle * t, + GtsVertex * v) +{ + GtsVertex * ev1 = GTS_SEGMENT (e)->v1, * ev2 = GTS_SEGMENT (e)->v2; + GList * i = GTS_OBJECT (e)->reserved; + + GTS_OBJECT (v)->reserved = t; + if (i == NULL) { + GTS_OBJECT (e)->reserved = g_list_prepend (NULL, v); +#ifdef DEBUG + fprintf (stderr, "add_edge_inter: inserting %p (%p,%p)\n", v, e, t); +#endif /* DEBUG */ + } + else { + GtsPoint * p1 = GTS_POINT (GTS_SEGMENT (t->e1)->v1); + GtsPoint * p2 = GTS_POINT (GTS_SEGMENT (t->e1)->v2); + GtsPoint * p3 = GTS_POINT (gts_triangle_vertex (t)); + gint o1, oref = gts_point_orientation_3d_sos (p1, p2, p3, GTS_POINT (ev1)); + + o1 = oref; + while (i) { + gint o2 = triangle_point_orientation (t, GTS_OBJECT (i->data)->reserved, + oref, GTS_POINT (ev1)); + + if (o2 == 0) { +#ifdef DEBUG + g_warning ("add_edge_inter: safe sign evaluation failed\n"); +#endif /* DEBUG */ + o2 = gts_point_orientation_3d_sos (p1, p2, p3, i->data); + } + + if (o1*o2 < 0) + break; + ev1 = i->data; + o1 = o2; + i = i->next; + } + if (i != NULL) { + GList * n = g_list_prepend (NULL, v); + + ev2 = i->data; + n->next = i; + n->prev = i->prev; + i->prev = n; + if (n->prev == NULL) + GTS_OBJECT (e)->reserved = n; + else + n->prev->next = n; + } + else { + g_assert (o1*gts_point_orientation_3d_sos (p1, p2, p3, GTS_POINT (ev2)) + < 0); + GTS_OBJECT (e)->reserved = g_list_append (GTS_OBJECT (e)->reserved, v); + } +#ifdef DEBUG + fprintf (stderr, + "add_edge_inter: inserting %p (%p,%p) between %p and %p\n", + v, e, t, ev1, ev2); + i = GTS_OBJECT (e)->reserved; + while (i) { + fprintf (stderr, " %p", i->data); + i = i->next; + } + fprintf (stderr, "\n"); +#endif /* DEBUG */ + } +} + +static GtsVertex * intersects (GtsEdge * e, + GtsTriangle * t, + GtsSurface * s) +{ + GList * i = GTS_OBJECT (e)->reserved; + GtsVertex * v; + + while (i) { + if (GTS_OBJECT (i->data)->reserved == t) + return i->data; + i = i->next; + } + + v = GTS_VERTEX (segment_triangle_intersection (GTS_SEGMENT (e), t, + GTS_POINT_CLASS (s->vertex_class))); + if (v != NULL) { +#ifdef DEBUG + if (GTS_IS_NVERTEX (v) && GTS_IS_NEDGE (e) && GTS_IS_NFACE (t) && + GTS_NVERTEX (v)->name[0] == '\0') + g_snprintf (GTS_NVERTEX (v)->name, GTS_NAME_LENGTH, "%s|%s", + GTS_NEDGE (e)->name, GTS_NFACE (t)->name); +#endif /* DEBUG */ + if (s->vertex_class->intersection_attributes) + (*s->vertex_class->intersection_attributes) + (v, GTS_OBJECT (e), GTS_OBJECT (t)); + add_edge_inter (e, t, v); + } + return v; +} + +/* see figure misc/orientation.fig */ +static gint intersection_orientation (GtsTriangle * t1, + GtsEdge * e, + GtsTriangle * t2) +{ + GtsVertex * v1, * v2, * v3; + GtsEdge * e2, * e3; + GtsVertex * v4, * v5, * v6; + + gts_triangle_vertices_edges (t1, e, &v1, &v2, &v3, &e, &e2, &e3); + gts_triangle_vertices (t2, &v4, &v5, &v6); + + return gts_point_orientation_3d_sos (GTS_POINT (v4), + GTS_POINT (v5), + GTS_POINT (v6), + GTS_POINT (v2)); +} + +#define UPDATE_ORIENTATION if (o > 0) { vi2 = v; /* e2 = e; */ } else { vi2 = vi1;\ + /* e2 = e1; */\ + vi1 = v;\ + /* e1 = e; */ } + +static void intersect_edges (GtsBBox * bb1, GtsBBox * bb2, + GtsSurfaceInter * si) +{ + GtsSurface * s1 = GTS_OBJECT (si->s1)->reserved; + GtsTriangle * t1 = GTS_TRIANGLE (bb1->bounded); + GtsTriangle * t2 = GTS_TRIANGLE (bb2->bounded); + GtsVertex * v, * vi1 = NULL, * vi2 = NULL; + //GtsEdge * e1 = NULL, * e2 = NULL, * e; + + vi1 = intersects (t2->e1, t1, s1); + //e1 = t2->e1; + v = intersects (t2->e2, t1, s1); + //e = t2->e2; + if (!vi1) { + vi1 = v; + //e1 = e; + } + else if (v) { + gint o = intersection_orientation (t2, t2->e2, t1); + UPDATE_ORIENTATION; + } + if (!vi2) { + v = intersects (t2->e3, t1, s1); + //e = t2->e3; + if (!vi1) { + vi1 = v; + //e1 = e; + } + else if (v) { + gint o = intersection_orientation (t2, t2->e3, t1); + UPDATE_ORIENTATION; + } + } + if (!vi2) { + v = intersects (t1->e1, t2, s1); + //e = t1->e1; + if (!vi1) { + vi1 = v; + //e1 = e; + } + else if (v) { + gint o = - intersection_orientation (t1, t1->e1, t2); + UPDATE_ORIENTATION; + } + } + if (!vi2) { + v = intersects (t1->e2, t2, s1); + //e = t1->e2; + if (!vi1) { + vi1 = v; + //e1 = e; + } + else if (v) { + gint o = - intersection_orientation (t1, t1->e2, t2); + UPDATE_ORIENTATION; + } + } + if (!vi2) { + v = intersects (t1->e3, t2, s1); + //e = t1->e3; + if (!vi1) { + vi1 = v; + //e1 = e; + } + else if (v) { + gint o = - intersection_orientation (t1, t1->e3, t2); + UPDATE_ORIENTATION; + } + } + + g_assert ((!vi1 && !vi2) || (vi1 && vi2)); + if (vi1) { + GtsEdge * e = GTS_EDGE (edge_inter_new (vi1, vi2, t1, t2)); + +#ifdef DEBUG + fprintf (stderr, "creating constraint %p: %p->%p: %p/%p\n", + e, vi1, vi2, t1, t2); +#endif /* DEBUG */ + gts_surface_add_face (si->s1, GTS_FACE (t1)); + gts_surface_add_face (si->s2, GTS_FACE (t2)); + si->edges = g_slist_prepend (si->edges, e); + GTS_OBJECT (t1)->reserved = g_slist_prepend (GTS_OBJECT (t1)->reserved, e); + GTS_OBJECT (t2)->reserved = g_slist_prepend (GTS_OBJECT (t2)->reserved, e); + } +} + +static GtsSurfaceInter * surface_inter_new (GtsSurfaceInterClass * klass, + GtsSurface * s1, + GtsSurface * s2, + GNode * faces_tree1, + GNode * faces_tree2) +{ + GtsSurfaceInter * si; + + si = GTS_SURFACE_INTER (gts_object_new (GTS_OBJECT_CLASS (klass))); + si->s1 = gts_surface_new (gts_surface_class (), + s1->face_class, + s1->edge_class, + s1->vertex_class); + GTS_OBJECT (si->s1)->reserved = s1; + si->s2 = gts_surface_new (gts_surface_class (), + s2->face_class, + s2->edge_class, + s2->vertex_class); + GTS_OBJECT (si->s2)->reserved = s2; + gts_bb_tree_traverse_overlapping (faces_tree1, faces_tree2, + (GtsBBTreeTraverseFunc) intersect_edges, + si); + + return si; +} + +static void free_slist (GtsObject * o) +{ + g_slist_free (o->reserved); + o->reserved = NULL; +} + +static void free_glist (GtsObject * o) +{ + g_list_foreach (o->reserved, (GFunc) gts_object_reset_reserved, NULL); + g_list_free (o->reserved); + o->reserved = NULL; +} + +/** + * gts_surface_intersection: + * @s1: a #GtsSurface. + * @s2: a #GtsSurface. + * @faces_tree1: a bounding box tree (see gts_bb_tree_new()) for + * the faces of @s1. + * @faces_tree2: a bounding box tree for the faces of @s2. + * + * Returns: a list of #GtsEdge defining the curve intersection of the + * two surfaces. + */ +GSList * gts_surface_intersection (GtsSurface * s1, + GtsSurface * s2, + GNode * faces_tree1, + GNode * faces_tree2) +{ + GtsSurfaceInter * si; + GSList * inter; + + g_return_val_if_fail (s1 != NULL, NULL); + g_return_val_if_fail (s2 != NULL, NULL); + g_return_val_if_fail (faces_tree1 != NULL, NULL); + g_return_val_if_fail (faces_tree2 != NULL, NULL); + + si = surface_inter_new (gts_surface_inter_class (), + s1, s2, faces_tree1, faces_tree2); + + gts_surface_foreach_face (si->s1, (GtsFunc) free_slist, NULL); + gts_surface_foreach_face (si->s2, (GtsFunc) free_slist, NULL); + gts_surface_foreach_edge (si->s1, (GtsFunc) free_glist, NULL); + gts_surface_foreach_edge (si->s2, (GtsFunc) free_glist, NULL); + inter = si->edges; + si->edges = NULL; + gts_object_destroy (GTS_OBJECT (si)); + + return inter; +} + +typedef enum { + INTERIOR = 1 << (GTS_USER_FLAG), + RELEVANT = 1 << (GTS_USER_FLAG + 1) +} CurveFlag; + +#define IS_SET(s, f) ((GTS_OBJECT_FLAGS (s) & (f)) != 0) +#define SET(s, f) (GTS_OBJECT_FLAGS (s) |= (f)) +#define UNSET(s, f) (GTS_OBJECT_FLAGS (s) &= ~(f)) +#define NEXT(s) (GTS_OBJECT (s)->reserved) + +#ifdef DEBUG +static void print_segment (GtsSegment * s) +{ + fprintf (stderr, "%p: %s->%s ", s, + GTS_NVERTEX (s->v1)->name, + GTS_NVERTEX (s->v2)->name); + if (NEXT (s)) { + GtsSegment * next = NEXT (s); + + fprintf (stderr, "next %p: %s->%s\n", next, + GTS_NVERTEX (next->v1)->name, + GTS_NVERTEX (next->v2)->name); + } + else + fprintf (stderr, "next NULL\n"); +} + +static void write_nodes (GSList * i, GHashTable * hash, guint * nn, + FILE * fp) +{ + while (i) { + GtsSegment * s = i->data; + + if (!g_hash_table_lookup (hash, s->v1)) { + fprintf (fp, " %u [ label = \"%p\" ];\n", *nn, s->v1); + g_hash_table_insert (hash, s->v1, GUINT_TO_POINTER ((*nn)++)); + } + if (!g_hash_table_lookup (hash, s->v2)) { + fprintf (fp, " %u [ label = \"%p\" ];\n", *nn, s->v2); + g_hash_table_insert (hash, s->v2, GUINT_TO_POINTER ((*nn)++)); + } + i = i->next; + } +} + +static void write_edges (GSList * i, GHashTable * hash, + GtsSurface * surface, + FILE * fp) +{ + while (i) { + GtsSegment * s = i->data; + + fprintf (fp, " %u -> %u [ label = \"%p:%d\" ];\n", + GPOINTER_TO_UINT (g_hash_table_lookup (hash, s->v1)), + GPOINTER_TO_UINT (g_hash_table_lookup (hash, s->v2)), + s, + gts_edge_face_number (GTS_EDGE (s), surface)); + i = i->next; + } +} + +static void write_graph (GSList * boundary, GSList * interior, + GtsSurface * surface, + FILE * fp) +{ + GHashTable * hash = g_hash_table_new (NULL, NULL); + guint nn = 1; + + fprintf (fp, "digraph oriented_curve {\n"); + write_nodes (boundary, hash, &nn, fp); + write_nodes (interior, hash, &nn, fp); + write_edges (boundary, hash, surface, fp); + fprintf (fp, " edge [ color = red ];\n"); + write_edges (interior, hash, surface, fp); + fprintf (fp, "}\n"); + g_hash_table_destroy (hash); +} + +static void write_graph1 (GtsSegment * start, GSList * i, + GtsSurface * surface, + FILE * fp) +{ + GSList * boundary = NULL, * interior = NULL; + GtsSegment * s = start; + + do { + boundary = g_slist_prepend (boundary, s); + s = NEXT (s); + } while (s != start); + while (i) { + if (IS_SET (i->data, INTERIOR)) + interior = g_slist_prepend (interior, i->data); + i = i->next; + } + write_graph (boundary, interior, surface, fp); + g_slist_free (boundary); + g_slist_free (interior); +} + +static void print_loop (GtsSegment * start, FILE * fp) +{ + GtsSegment * s = start; + + do { + fprintf (fp, " %p: %p:%s -> %p:%s\n", + s, + s->v1, GTS_NVERTEX (s->v1)->name, + s->v2, GTS_NVERTEX (s->v2)->name); + s = NEXT (s); + } while (s != start && s != NULL); +} + +static void draw_vector (GtsPoint * p1, GtsPoint * p2, FILE * fp) +{ + gdouble x = p2->x - p1->x; + gdouble y = p2->y - p1->y; + gdouble z = p2->z - p1->z; + + fprintf (fp, "VECT 1 3 0 3 0 %g %g %g %g %g %g %g %g %g\n", + p1->x + x - (x - y/2.)/5., + p1->y + y - (x/2. + y)/5., + p1->z + z - (x/2. + z)/5., + p1->x + x, + p1->y + y, + p1->z + z, + p1->x + x - (x + y/2.)/5., + p1->y + y + (x/2. - y)/5., + p1->z + z + (x/2. - z)/5.); + fprintf (fp, "VECT 1 2 0 2 0 %g %g %g %g %g %g\n", + p1->x, p1->y, p1->z, + p1->x + x, + p1->y + y, + p1->z + z); +} + +static void draw_vector1 (GtsPoint * p1, GtsPoint * p2, GtsPoint * o, + FILE * fp) +{ + gdouble x1 = o->x + 0.9*(p1->x - o->x); + gdouble y1 = o->y + 0.9*(p1->y - o->y); + gdouble z1 = o->z + 0.9*(p1->z - o->z); + gdouble x2 = o->x + 0.9*(p2->x - o->x); + gdouble y2 = o->y + 0.9*(p2->y - o->y); + gdouble z2 = o->z + 0.9*(p2->z - o->z); + gdouble x = x2 - x1; + gdouble y = y2 - y1; + gdouble z = z2 - z1; + + fprintf (fp, "VECT 1 3 0 3 0 %g %g %g %g %g %g %g %g %g\n", + x1 + x - (x - y/2.)/5., + y1 + y - (x/2. + y)/5., + z1 + z - (x/2. + z)/5., + x1 + x, + y1 + y, + z1 + z, + x1 + x - (x + y/2.)/5., + y1 + y + (x/2. - y)/5., + z1 + z + (x/2. - z)/5.); + fprintf (fp, "VECT 1 2 0 2 0 %g %g %g %g %g %g\n", + x1, y1, z1, + x1 + x, + y1 + y, + z1 + z); +} + +static void write_segments (GSList * boundary, GSList * interior, + FILE * fp) +{ + GSList * i = boundary; + + fprintf (fp, "LIST {\n"); + while (i) { + GSList * inext = i->next; + GtsSegment * s = i->data; + GtsSegment * next = inext ? inext->data : boundary->data; + GtsVertex * v1, * v2; + + if (s->v1 != next->v1 && s->v1 != next->v2) { + v1 = s->v1; + v2 = s->v2; + } + else { + v1 = s->v2; + v2 = s->v1; + } + draw_vector (GTS_POINT (v1), GTS_POINT (v2), fp); + i = inext; + } + i = interior; + while (i) { + GtsSegment * s = i->data; + + draw_vector (GTS_POINT (s->v1), GTS_POINT (s->v2), fp); + i = i->next; + } + fprintf (fp, "}\n"); +} + +static void write_loops (GSList * i, FILE * fp) +{ + guint nl = 0; + + while (i) { + GtsSegment * start = i->data, * s; + GtsPoint os; + guint n = 0; + + fprintf (fp, "(geometry \"loop%d\" = LIST {\n", nl++); + + os.x = os.y = os.z = 0.; + s = start; + do { + GtsSegment * next = NEXT (s); + GtsPoint * p; + + if (s->v1 != next->v1 && s->v1 != next->v2) + p = GTS_POINT (s->v1); + else + p = GTS_POINT (s->v2); + os.x += p->x; os.y += p->y; os.z += p->z; n++; + s = next; + } while (s != start); + os.x /= n; os.y /= n; os.z /= n; + + s = start; + do { + GtsSegment * next = NEXT (s); + + if (s->v1 != next->v1 && s->v1 != next->v2) + draw_vector1 (GTS_POINT (s->v1), GTS_POINT (s->v2), &os, fp); + else + draw_vector1 (GTS_POINT (s->v2), GTS_POINT (s->v1), &os, fp); + s = next; + } while (s != start); + + fprintf (fp, "})\n"); + + i = i->next; + } +} + +#define NAME(v) (GTS_IS_NVERTEX (v) ? GTS_NVERTEX (v)->name : "") +#endif /* DEBUG */ + +static GtsSegment * prev_flag (GtsSegment * s, CurveFlag flag) +{ + GSList * i = s->v1->segments; + + while (i) { + if (i->data != s && IS_SET (i->data, flag)) + return i->data; + i = i->next; + } + return NULL; +} + +static GtsSegment * next_flag (GtsSegment * s, CurveFlag flag) +{ + GSList * i = s->v2->segments; + + while (i) { + if (i->data != s && IS_SET (i->data, flag)) + return i->data; + i = i->next; + } + return NULL; +} + +static GtsSegment * next_interior (GtsVertex * v) +{ + GSList * i = v->segments; + + while (i) { + GtsSegment * s = i->data; + + if (s->v1 == v && IS_SET (s, INTERIOR)) + return s; + i = i->next; + } + return NULL; +} + +static GtsSegment * prev_interior (GtsVertex * v) +{ + GSList * i = v->segments; + + while (i) { + GtsSegment * s = i->data; + + if (s->v2 == v && IS_SET (s, INTERIOR)) + return s; + i = i->next; + } + return NULL; +} + +static GtsSegment * reverse (GtsSegment * start, + gboolean interior, + gboolean * isloop) +{ + GtsSegment * s = start, * prev = NULL, * rprev = NULL; + GtsSegment * rstart = NULL, * rstart1 = NULL; + + do { + GtsSegment * rs; + + g_assert (IS_EDGE_INTER (s)); + rs = GTS_SEGMENT (edge_inter_new (s->v2, s->v1, + EDGE_INTER (s)->t1, EDGE_INTER (s)->t2)); + + if (rstart == NULL) + rstart = rs; + else if (rstart1 == NULL) + rstart1 = rs; + if (interior) + SET (rs, INTERIOR); + NEXT (rs) = rprev; + rprev = rs; + prev = s; + s = NEXT (s); + } while (s != NULL && s != start); + if (s == start) { + NEXT (rstart) = rprev; + *isloop = TRUE; + } + else { + NEXT (rstart) = start; + NEXT (prev) = rprev; + *isloop = FALSE; + } + return rstart1; +} + +static GSList * interior_loops (GSList * interior) +{ + GSList * i = interior; + GSList * loops = NULL; + + i = interior; + while (i) { + GtsSegment * s = i->data; + + if (IS_SET (s, RELEVANT)) { + GtsSegment * start = s, * end; + + do { + GtsSegment * next = next_flag (s, INTERIOR); + + UNSET (s, RELEVANT); + end = s; + s = NEXT (s) = next; + } while (s != NULL && s != start); + + if (s == start) + loops = g_slist_prepend (loops, start); + else { + GtsSegment * next, * prev; + gboolean isloop; + + s = prev_flag (start, INTERIOR); + while (s) { + UNSET (s, RELEVANT); + NEXT (s) = start; + start = s; + s = prev_flag (s, INTERIOR); + } + next = next_flag (end, RELEVANT); + prev = prev_flag (start, RELEVANT); + if (prev != NULL) + SET (start->v1, INTERIOR); + if (next != NULL) + SET (end->v2, INTERIOR); + if (next == NULL && prev == NULL) + loops = g_slist_prepend (loops, start); + else + reverse (start, TRUE, &isloop); + } + } + i = i->next; + } + return loops; +} + +#define ORIENTATION(p1,p2,p3,o) (gts_point_orientation_3d (p1, p2, o, p3)) +#define ORIENTATION_SOS(p1,p2,p3,o) (gts_point_orientation_3d_sos (p1, p2, o, p3)) + +#define ORIENTED_VERTICES(s,next,w1,w2) {\ + if ((s)->v1 == (next)->v1 || (s)->v1 == (next)->v2) {\ + w1 = (s)->v2;\ + w2 = (s)->v1;\ + }\ + else {\ + w1 = (s)->v1;\ + w2 = (s)->v2;\ + }\ +} + +#if 0 +static GtsSegment * segment_intersects (GtsPoint * p1, GtsPoint * p2, + GSList * i, + GtsPoint * o) +{ + while (i) { + GtsSegment * s = i->data; + GtsPoint * p3 = GTS_POINT (s->v1); + GtsPoint * p4 = GTS_POINT (s->v2); + + if (p3 != p1 && p3 != p2 && p4 != p1 && p4 != p2) { + gdouble o1 = ORIENTATION (p3, p4, p1, o); + gdouble o2 = ORIENTATION (p3, p4, p2, o); + + if ((o1 < 0. && o2 > 0.) || (o1 > 0. && o2 < 0.)) { + o1 = ORIENTATION (p1, p2, p3, o); + o2 = ORIENTATION (p1, p2, p4, o); + + if ((o1 <= 0. && o2 >= 0.) || (o1 >= 0. && o2 <= 0.)) + return s; + } + } + i = i->next; + } + return NULL; +} +#else +static GtsSegment * segment_intersects (GtsPoint * p1, GtsPoint * p2, + GSList * i, + GtsPoint * o) +{ + while (i) { + GtsSegment * s = i->data; + GtsPoint * p3 = GTS_POINT (s->v1); + GtsPoint * p4 = GTS_POINT (s->v2); + + if (p3 != p1 && p3 != p2 && p4 != p1 && p4 != p2) { + gint o1 = ORIENTATION_SOS (p3, p4, p1, o); + gint o2 = ORIENTATION_SOS (p3, p4, p2, o); + + if (o1*o2 < 0) { + o1 = ORIENTATION_SOS (p1, p2, p3, o); + o2 = ORIENTATION_SOS (p1, p2, p4, o); + + if (o1*o2 < 0) + return s; + } + } + i = i->next; + } + return NULL; +} +#endif + +static gboolean is_inside_wedge (GtsSegment * s1, GtsSegment * s2, + GtsPoint * p, GtsPoint * o) +{ + GtsVertex * v1, * v2, * v3; + + ORIENTED_VERTICES (s1, s2, v1, v2); + v3 = s2->v1 != v2 ? s2->v1 : s2->v2; + + if (ORIENTATION (GTS_POINT (v1), GTS_POINT (v2), + GTS_POINT (v3), o) >= 0.) { + if (ORIENTATION (GTS_POINT (v1), GTS_POINT (v2), p, o) <= 0. || + ORIENTATION (GTS_POINT (v2), GTS_POINT (v3), p, o) <= 0.) + return FALSE; + } + else if (ORIENTATION (GTS_POINT (v1), GTS_POINT (v2), p, o) <= 0. && + ORIENTATION (GTS_POINT (v2), GTS_POINT (v3), p, o) <= 0.) + return FALSE; + return TRUE; +} + +static GtsSegment * connection (GtsPoint * p, + GSList * interior, + GSList * bloops, + GtsPoint * o) +{ + while (bloops) { + GtsSegment * start = bloops->data, * s = start; + + do { + GtsSegment * next = NEXT (s); + GtsVertex * v2 = s->v1 == next->v1 || s->v1 == next->v2 ? s->v1 : s->v2; + + if (is_inside_wedge (s, next, p, o) && + !segment_intersects (p, GTS_POINT (v2), interior, o)) + return s; + s = next; + } while (s != start); + bloops = bloops->next; + } + return NULL; +} + +static gdouble loop_orientation (GtsSegment * start, + GtsPoint * p, GtsPoint * o) +{ + GtsSegment * s = start; + gdouble or = 0.; + + do { + GtsSegment * next = NEXT (s); + GtsVertex * v1, * v2; + + ORIENTED_VERTICES (s, next, v1, v2); + or += ORIENTATION (p, GTS_POINT (v1), GTS_POINT (v2), o); + s = next; + } while (s != start); + +#ifdef DEBUG + fprintf (stderr, "loop orientation: %g\n", or); +#endif /* DEBUG */ + + return or; +} + +static void connect_interior_loop (GtsSegment * start, + GSList ** interior, + GSList ** bloops, + GtsSurface * surface, + GtsPoint * o) +{ + GtsSegment * s = start, * c = NULL, * next, * s1, * rs1, * rs; + GtsVertex * v, * cv; + gboolean isloop; + + do { + if (!(c = connection (GTS_POINT (s->v2), *interior, *bloops, o))) + s = NEXT (s); + } while (s != start && !c); + g_assert (c); + next = NEXT (c); + v = c->v1 == next->v1 || c->v1 == next->v2 ? c->v1 : c->v2; + cv = s->v2; +#ifdef DEBUG + fprintf (stderr, "connecting %p:%s with %p:%s\n", + cv, NAME (cv), v, NAME (v)); + fprintf (stderr, " c: %p: %p:%s %p:%s\n", c, + c->v1, NAME (c->v1), + c->v2, NAME (c->v2)); + fprintf (stderr, " next: %p: %p:%s %p:%s\n", next, + next->v1, NAME (next->v1), + next->v2, NAME (next->v2)); +#endif /* DEBUG */ + rs = reverse (s, FALSE, &isloop); + if (isloop) { + if (loop_orientation (rs, GTS_POINT (v), o) < 0.) { + GtsSegment * tmp = s; + s = rs; + rs = tmp; + } + *bloops = g_slist_prepend (*bloops, rs); + } + s1 = GTS_SEGMENT (gts_edge_new (surface->edge_class, v, cv)); + rs1 = GTS_SEGMENT (gts_edge_new (surface->edge_class, cv, v)); + NEXT (c) = s1; + NEXT (rs1) = next; + *interior = g_slist_prepend (*interior, s1); + NEXT (s1) = NEXT (s); + NEXT (s) = rs1; +} + +static GSList * boundary_loops (GSList * boundary) +{ + GSList * i = boundary; + GtsSegment * start = i->data; + GSList * loops = NULL; + + while (i) { + GtsSegment * s = i->data; + GSList * inext = i->next; + GtsSegment * next = inext ? inext->data : start; + GtsVertex * v = s->v1 == next->v1 || s->v1 == next->v2 ? s->v1 : s->v2; + + if (IS_SET (v, INTERIOR)) { + GtsSegment * intprev = prev_interior (v); + + NEXT (intprev) = next; + NEXT (s) = next_interior (v); + UNSET (v, INTERIOR); + } + else + NEXT (s) = next; + i = inext; + } + + i = boundary; + while (i) { + start = i->data; + + if (IS_SET (start, RELEVANT)) { + GtsSegment * s = start; + + do { + UNSET (s, RELEVANT); + UNSET (s, INTERIOR); + s = NEXT (s); + } while (s != start); + loops = g_slist_prepend (loops, start); + } + i = i->next; + } + + return loops; +} + +typedef struct _Ear Ear; + +struct _Ear { + GtsVertex * v1, * v2, * v3; + GtsSegment * s1, * s2, * s3; +}; + +static gboolean point_in_wedge (GtsPoint * p1, GtsPoint * p2, GtsPoint * p3, + GtsPoint * p, gboolean closed, GtsPoint * o) +{ + gdouble o1; + + if (p == p2 || p == p3) + return FALSE; + o1 = ORIENTATION (p1, p2, p, o); + if ((closed && o1 < 0.) || (!closed && o1 <= 0.)) return FALSE; + o1 = ORIENTATION (p3, p1, p, o); + if ((closed && o1 < 0.) || (!closed && o1 <= 0.)) return FALSE; + return TRUE; +} + +#if 0 +static gboolean segment_intersects1 (GtsPoint * p1, GtsPoint * p2, + GtsPoint * p3, GtsPoint * p4, + gboolean closed, GtsPoint * o) +{ + gdouble o1 = ORIENTATION (p3, p4, p1, o); + gdouble o2 = ORIENTATION (p3, p4, p2, o); + gdouble o3, o4; + + if ((closed && ((o1 > 0. && o2 > 0.) || (o1 < 0. && o2 < 0.))) || + (!closed && ((o1 >= 0. && o2 >= 0.) || (o1 <= 0. && o2 <= 0.)))) + return FALSE; + o3 = ORIENTATION (p1, p2, p3, o); + o4 = ORIENTATION (p1, p2, p4, o); + if ((o3 > 0. && o4 > 0.) || (o3 < 0. && o4 < 0.)) + return FALSE; + if (closed) return TRUE; + if ((o3 == 0. && o4 > 0.) || (o4 == 0. && o3 > 0.)) + return TRUE; + return FALSE; +} +#else +static gboolean segment_intersects1 (GtsPoint * p1, GtsPoint * p2, + GtsPoint * p3, GtsPoint * p4, + gboolean closed, GtsPoint * o) +{ + gint o1, o2; + + o1 = ORIENTATION_SOS (p3, p4, p1, o); + o2 = ORIENTATION_SOS (p3, p4, p2, o); + if (o1*o2 > 0) + return FALSE; + o1 = ORIENTATION_SOS (p1, p2, p3, o); + o2 = ORIENTATION_SOS (p1, p2, p4, o); + if (o1*o2 > 0) + return FALSE; + return TRUE; +} +#endif + +static GtsSegment * triangle_intersects_segments (GtsPoint * p1, + GtsPoint * p2, + GtsPoint * p3, + gboolean closed, + GtsSegment * start, + GtsPoint * o) +{ + GtsSegment * s = start; + + do { + GtsPoint * p4 = GTS_POINT (s->v1); + GtsPoint * p5 = GTS_POINT (s->v2); + + if (p4 == p1) { + if (point_in_wedge (p1, p2, p3, p5, closed, o)) + return s; + } + else if (p4 == p2) { + if (point_in_wedge (p2, p3, p1, p5, closed, o)) + return s; + } + else if (p4 == p3) { + if (point_in_wedge (p3, p1, p2, p5, closed, o)) + return s; + } + else if (p5 == p1) { + if (point_in_wedge (p1, p2, p3, p4, closed, o)) + return s; + } + else if (p5 == p2) { + if (point_in_wedge (p2, p3, p1, p4, closed, o)) + return s; + } + else if (p5 == p3) { + if (point_in_wedge (p3, p1, p2, p4, closed, o)) + return s; + } + else if (segment_intersects1 (p1, p2, p4, p5, closed, o) || + segment_intersects1 (p2, p3, p4, p5, closed, o) || + segment_intersects1 (p3, p1, p4, p5, closed, o)) + return s; + s = NEXT (s); + } while (s != start); + return NULL; +} + +static gboolean new_ear (GtsSegment * s, + Ear * e, + GtsSegment * start, + guint sloppy, + GtsPoint * o) +{ + gdouble or; + + e->s1 = s; + e->s2 = NEXT (s); + + g_return_val_if_fail (e->s2, FALSE); + g_return_val_if_fail (e->s2 != e->s1, FALSE); + + ORIENTED_VERTICES (e->s1, e->s2, e->v1, e->v2); + e->v3 = e->s2->v1 != e->v2 ? e->s2->v1 : e->s2->v2; + if (e->v3 == e->v1) + return FALSE; + e->s3 = NEXT (e->s2); + if (gts_segment_connect (e->s3, e->v1, e->v3)) { + if (NEXT (e->s3) != e->s1) + return FALSE; + } + else if (gts_vertices_are_connected (e->v1, e->v3)) + return FALSE; + else + e->s3 = NULL; + or = ORIENTATION (GTS_POINT (e->v1), GTS_POINT (e->v2), GTS_POINT (e->v3),o); + switch (sloppy) { + case 0: + if (or <= 0. || + triangle_intersects_segments (GTS_POINT (e->v1), GTS_POINT (e->v2), + GTS_POINT (e->v3), TRUE, start, o)) + return FALSE; + break; + case 1: + if (or < 0. || + (or > 0. && + triangle_intersects_segments (GTS_POINT (e->v1), GTS_POINT (e->v2), + GTS_POINT (e->v3), FALSE, start, o))) + return FALSE; + break; + case 2: + if ((or > 0. && + triangle_intersects_segments (GTS_POINT (e->v1), GTS_POINT (e->v2), + GTS_POINT (e->v3), FALSE, start, o)) || + (or < 0. && + triangle_intersects_segments (GTS_POINT (e->v2), GTS_POINT (e->v1), + GTS_POINT (e->v3), FALSE, start, o))) + return FALSE; + break; + case 3: + if (or < 0.) + return FALSE; + break; + } +#ifdef DEBUG + if (or <= 0.) + fprintf (stderr, "or: %g\n", or); +#endif /* DEBUG */ + g_assert (or > -1e-6); + return TRUE; +} + +static void triangulate_loop (GtsSegment * start, + GtsSurface * surface, + GtsPoint * o) +{ + GtsSegment * prev = start, * s; + guint sloppy = 0; +#ifdef DEBUG + guint nt = 0; +#endif /* DEBUG */ + + s = NEXT (start); + while (NEXT (s) != s) { + GtsSegment * next = NEXT (s); + Ear e; + +#ifdef DEBUG + fprintf (stderr, "prev: %p s: %p next: %p\n", prev, s, next); +#endif /* DEBUG */ + + if (!new_ear (s, &e, start, sloppy, o)) { + if (s == start) { + sloppy++; +#ifdef DEBUG + fprintf (stderr, "sloppy: %u\n", sloppy); +#endif /* DEBUG */ + } + prev = s; + s = next; + } + else { + GtsFace * f; + + if (!GTS_IS_EDGE (e.s3)) + e.s3 = GTS_SEGMENT (gts_edge_new (surface->edge_class, e.v1, e.v3)); + f = gts_face_new (surface->face_class, + GTS_EDGE (e.s1), GTS_EDGE (e.s2), GTS_EDGE (e.s3)); + gts_surface_add_face (surface, f); + UNSET (e.s1, RELEVANT); + UNSET (e.s1, INTERIOR); + UNSET (e.s2, RELEVANT); + UNSET (e.s2, INTERIOR); + NEXT (prev) = e.s3; + NEXT (e.s3) = NEXT (e.s2); + NEXT (e.s1) = NEXT (e.s2) = NULL; + start = prev; + s = NEXT (prev); + sloppy = 0; +#ifdef DEBUG + { + gchar name[80]; + FILE * fp; + + fprintf (stderr, " t.%u: (%p:%s,%p:%s,%p:%s)\n", + nt, + e.v1, NAME (e.v1), + e.v2, NAME (e.v2), + e.v3, NAME (e.v3)); + sprintf (name, "/tmp/t.%u", nt++); + fp = fopen (name, "wt"); + // gts_surface_write (surface, fp); + gts_write_triangle (GTS_TRIANGLE (f), NULL, fp); + // write_graph1 (start, interior, surface, fp); + fclose (fp); + print_loop (start, stderr); + } +#endif /* DEBUG */ + } + } + UNSET (s, RELEVANT); + UNSET (s, INTERIOR); + NEXT (s) = NULL; +} + +#ifdef CHECK_ORIENTED +static void check_object (GtsObject * o) +{ + g_assert (o->reserved == NULL); + g_assert (o->flags == 0); +} + +static void check_boundary (GtsEdge * e, GtsSurface * s) +{ + check_object (GTS_OBJECT (e)); + check_object (GTS_OBJECT (GTS_SEGMENT (e)->v1)); + check_object (GTS_OBJECT (GTS_SEGMENT (e)->v2)); + g_assert (gts_edge_face_number (e, s) == 1); +} + +static void check_interior (GtsEdge * e, GtsSurface * s) +{ + guint n; + check_object (GTS_OBJECT (e)); + check_object (GTS_OBJECT (GTS_SEGMENT (e)->v1)); + check_object (GTS_OBJECT (GTS_SEGMENT (e)->v2)); + + n = gts_edge_face_number (e, s); +#ifdef DEBUG + if (n != 2) + gts_surface_print_stats (s, stderr); +#endif /* DEBUG */ + g_assert (n == 2); +} + +static void check_boundary_interior_triangulation (GSList * boundary, + GSList * interior, + GtsSurface * surface) +{ + g_slist_foreach (boundary, (GFunc) check_boundary, surface); + g_slist_foreach (interior, (GFunc) check_interior, surface); +} +#endif /*ifdef CHECK_ORIENTED */ + +static void merge_duplicate (GtsEdge * e) +{ + GtsEdge * dup = gts_edge_is_duplicate (e); + + g_assert (dup); + gts_edge_replace (dup, e); + gts_object_destroy (GTS_OBJECT (dup)); +} + +static void triangulate_boundary_interior (GSList * boundary, + GSList * interior, + GtsSurface * s, + GtsPoint * o) +{ + GSList * iloops, * bloops, * i; + + i = boundary; + while (i) { + SET (i->data, RELEVANT); + i = i->next; + } + i = interior; + while (i) { + SET (i->data, RELEVANT); + SET (i->data, INTERIOR); + i = i->next; + } + + iloops = interior_loops (interior); + bloops = boundary_loops (boundary); + + i = iloops; + while (i) { +#ifdef DEBUG + fprintf (stderr, "--- interior loop ---\n"); + print_loop (i->data, stderr); +#endif /* DEBUG */ + connect_interior_loop (i->data, &interior, &bloops, s, o); + i = i->next; + } + +#ifdef DEBUG + { + FILE * fp = fopen ("/tmp/bloops", "w"); + write_loops (bloops, fp); + fclose (fp); + } +#endif /* DEBUG */ + + i = bloops; + while (i) { +#ifdef DEBUG + fprintf (stderr, "--- boundary loop ---\n"); + print_loop (i->data, stderr); +#endif /* DEBUG */ + triangulate_loop (i->data, s, o); + i = i->next; + } + + g_slist_foreach (interior, (GFunc) merge_duplicate, NULL); + g_slist_free (iloops); + g_slist_free (bloops); + +#ifdef CHECK_ORIENTED + check_boundary_interior_triangulation (boundary, interior, s); +#endif /* CHECK_ORIENTED */ +} + +static void create_edges (GtsSegment * s, GtsSurface * surface) +{ + if (GTS_OBJECT (s)->reserved) { + GList * i = GTS_OBJECT (s)->reserved; + GtsVertex * v1 = i->data; + + GTS_OBJECT (s)->reserved = g_list_prepend (i, + gts_edge_new (surface->edge_class, s->v1, v1)); + while (i) { + GList * next = i->next; + GtsVertex * v2 = next ? next->data : s->v2; + + GTS_OBJECT (i->data)->reserved = NULL; + i->data = gts_edge_new (surface->edge_class, v1, v2); + v1 = v2; + i = next; + } + } +} + +static void add_boundary (GtsSegment * s, GtsSegment * next, + GSList ** boundary) +{ + if (GTS_OBJECT (s)->reserved == NULL) + *boundary = g_slist_prepend (*boundary, s); + else { + if (s->v2 == next->v2 || s->v2 == next->v1) { + GList * i = g_list_last (GTS_OBJECT (s)->reserved); + + while (i) { + *boundary = g_slist_prepend (*boundary, i->data); + i = i->prev; + } + } + else { + GList * i = GTS_OBJECT (s)->reserved; + + while (i) { + *boundary = g_slist_prepend (*boundary, i->data); + i = i->next; + } + } + } +} + +static void triangulate_face (GtsTriangle * t, GtsSurface * surface) +{ + GSList * interior = GTS_OBJECT (t)->reserved; + GSList * boundary = NULL; + GtsSurface * s = gts_surface_new (gts_surface_class (), + surface->face_class, + surface->edge_class, + surface->vertex_class); + gdouble x, y, z; + GtsPoint * p = GTS_POINT (GTS_SEGMENT (t->e1)->v1); + GtsPoint * o; + + GTS_OBJECT (t)->reserved = NULL; + gts_triangle_normal (t, &x, &y, &z); + g_assert (x != 0. || y != 0. || z != 0.); + o = gts_point_new (gts_point_class (), p->x + x, p->y + y, p->z + z); + add_boundary (GTS_SEGMENT (t->e3), GTS_SEGMENT (t->e1), &boundary); + add_boundary (GTS_SEGMENT (t->e2), GTS_SEGMENT (t->e3), &boundary); + add_boundary (GTS_SEGMENT (t->e1), GTS_SEGMENT (t->e2), &boundary); +#ifdef DEBUG + { + static guint nt = 0; + char name[80]; + FILE * fp; + + fprintf (stderr, "%u: triangulating %p\n", nt, t); +if (nt == 28) + fprintf (stderr, "tintin!!!!\n"); + sprintf (name, "/tmp/oc.%u", nt++); + fp = fopen (name, "wt"); + // write_graph (boundary, interior, s, fp); + write_segments (boundary, interior, fp); + fclose (fp); + } +#endif /* DEBUG */ + triangulate_boundary_interior (boundary, interior, s, o); + g_slist_free (interior); + g_slist_free (boundary); + if (GTS_OBJECT (t)->klass->attributes) + gts_surface_foreach_face (s, (GtsFunc) gts_object_attributes, t); + gts_surface_merge (surface, s); + gts_object_destroy (GTS_OBJECT (s)); + gts_object_destroy (GTS_OBJECT (o)); +} + +static void free_edge_list (GtsObject * o) +{ + g_list_free (o->reserved); + o->reserved = NULL; +} + +/** + * gts_surface_inter_new: + * @klass: a #GtsSurfaceInterClass. + * @s1: a #GtsSurface. + * @s2: a #GtsSurface. + * @faces_tree1: a bounding box tree (see gts_bb_tree_new()) for + * the faces of @s1. + * @faces_tree2: a bounding box tree for the faces of @s2. + * @is_open1: whether @s1 is an "open" surface. + * @is_open2: whether @s2 is an "open" surface. + * + * When triangulating the cut faces, the new faces inherit the + * attributes of these original faces through their attributes() + * method. + * + * Returns: a new #GtsSurfaceInter describing the intersection of @s1 + * and @s2. + */ +GtsSurfaceInter * gts_surface_inter_new (GtsSurfaceInterClass * klass, + GtsSurface * s1, + GtsSurface * s2, + GNode * faces_tree1, + GNode * faces_tree2, + gboolean is_open1, + gboolean is_open2) +{ + GtsSurfaceInter * si; + GtsSurface * s; + + g_return_val_if_fail (klass != NULL, NULL); + g_return_val_if_fail (s1 != NULL, NULL); + g_return_val_if_fail (s2 != NULL, NULL); + g_return_val_if_fail (faces_tree1 != NULL, NULL); + g_return_val_if_fail (faces_tree2 != NULL, NULL); + + si = surface_inter_new (klass, s1, s2, faces_tree1, faces_tree2); + + gts_surface_foreach_edge (si->s1, (GtsFunc) create_edges, si->s1); + gts_surface_foreach_edge (si->s2, (GtsFunc) create_edges, si->s2); + +#ifdef DEBUG + fprintf (stderr, "====== triangulating s1 ======\n"); +#endif /* DEBUG */ + s = gts_surface_new (gts_surface_class (), + s1->face_class, + s1->edge_class, + s1->vertex_class); + gts_surface_foreach_face (si->s1, (GtsFunc) triangulate_face, s); + gts_surface_foreach_edge (si->s1, (GtsFunc) free_edge_list, NULL); + gts_object_destroy (GTS_OBJECT (si->s1)); + si->s1 = s; + GTS_OBJECT (si->s1)->reserved = s1; + +#ifdef DEBUG + fprintf (stderr, "====== triangulating s2 ======\n"); +#endif /* DEBUG */ + s = gts_surface_new (gts_surface_class (), + s2->face_class, + s2->edge_class, + s2->vertex_class); + gts_surface_foreach_face (si->s2, (GtsFunc) triangulate_face, s); + gts_surface_foreach_edge (si->s2, (GtsFunc) free_edge_list, NULL); + gts_object_destroy (GTS_OBJECT (si->s2)); + si->s2 = s; + GTS_OBJECT (si->s2)->reserved = s2; + + return si; +} + +static void check_surface_edge (GtsEdge * e, gpointer * data) +{ + gboolean * ok = data[0]; + GtsSurface * s = data[1]; + GtsSurface * bs = GTS_OBJECT (s)->reserved; + guint nf = gts_edge_face_number (e, s); + + if (nf < 1 || nf > 2) { + *ok = FALSE; + g_return_if_fail (nf >= 1 && nf <= 2); + } + if (nf == 1 && gts_edge_face_number (e, bs) == 0) { + *ok = FALSE; + g_return_if_fail (gts_edge_face_number (e, bs) > 0); + } +} + +static void mark_edge (GtsObject * o, gpointer data) +{ + o->reserved = data; +} + +static gint triangle_orientation (GtsTriangle * t, GtsEdge * e) +{ + GtsSegment * s = GTS_SEGMENT (t->e1 == e ? t->e2 + : + t->e2 == e ? t->e3 + : + t->e1); + GtsVertex * v2 = GTS_SEGMENT (e)->v2; + + if (s->v1 == v2 || s->v2 == v2) + return 1; + return -1; +} + +static gboolean check_orientation (GtsEdge * e, GtsSurface * s) +{ + GtsTriangle * t1 = NULL, * t2 = NULL; + GSList * i = e->triangles; + gint o1 = 0, o2 = 0; + + while (i) { + if (GTS_IS_FACE (i->data) && + gts_face_has_parent_surface (i->data, s)) { + if (t1 == NULL) { + t1 = i->data; + o1 = triangle_orientation (t1, e); + } + else if (t2 == NULL) { + t2 = i->data; + o2 = triangle_orientation (t2, e); + g_return_val_if_fail (o1*o2 < 0, FALSE); + } + else + g_assert_not_reached (); + } + i = i->next; + } + g_return_val_if_fail (t1 && t2, FALSE); + return TRUE; +} + +static void check_edge (GtsSegment * s, gpointer * data) +{ + gboolean * ok = data[0]; + GtsSurfaceInter * si = data[1]; + gboolean * closed = data[2]; + GSList * j; + guint nn = 0; + + j = s->v1->segments; + while (j && *ok) { + GtsSegment * s1 = j->data; + + if (s1 != s && GTS_OBJECT (s1)->reserved == si) { + if (s1->v2 != s->v1) + *ok = FALSE; + nn++; + } + j = j->next; + } + j = s->v2->segments; + while (j && *ok) { + GtsSegment * s1 = j->data; + + if (s1 != s && GTS_OBJECT (s1)->reserved == si) { + if (s1->v1 != s->v2) + *ok = FALSE; + nn++; + } + j = j->next; + } + if (nn != 2) + *closed = FALSE; + + if (!check_orientation (GTS_EDGE (s), si->s1)) + *ok = FALSE; + if (!check_orientation (GTS_EDGE (s), si->s2)) + *ok = FALSE; +} + +/** + * gts_surface_inter_check: + * @si: a #GtsSurfaceInter. + * @closed: is set to %TRUE if @si->edges is a closed curve, %FALSE + * otherwise. + * + * Returns: %TRUE if the curve described by @si is an orientable + * manifold, %FALSE otherwise. + */ +gboolean gts_surface_inter_check (GtsSurfaceInter * si, + gboolean * closed) +{ + gboolean ok = TRUE; + gpointer data[3]; + + g_return_val_if_fail (si != NULL, FALSE); + g_return_val_if_fail (closed != NULL, FALSE); + + *closed = si->edges ? TRUE : FALSE; + + /* mark edges as used by si */ + g_slist_foreach (si->edges, (GFunc) mark_edge, si); + + data[0] = &ok; + data[1] = si; + data[2] = closed; + g_slist_foreach (si->edges, (GFunc) check_edge, data); + g_slist_foreach (si->edges, (GFunc) gts_object_reset_reserved, NULL); + + /* check connectivity of the faces of @si */ + if (*closed) { + gpointer data[2]; + + data[0] = &ok; + data[1] = si->s1; + gts_surface_foreach_edge (si->s1, (GtsFunc) check_surface_edge, data); + data[1] = si->s2; + gts_surface_foreach_edge (si->s2, (GtsFunc) check_surface_edge, data); + } + + return ok; +} + +/* Given @e and @f returns a #GtsFace compatible with @f and belonging to + @s1 or @s2 */ +static GtsFace * next_compatible_face (GtsEdge * e, + GtsFace * f, + GtsSurface * s1, + GtsSurface * s2) +{ + GSList * i = e->triangles; + GtsFace * f2 = NULL, * f3 = NULL; + + while (i) { + GtsFace * f1 = i->data; + + if (f1 != f && GTS_IS_FACE (f1)) { + if (gts_face_has_parent_surface (f1, s1)) + return f1; + if (gts_face_has_parent_surface (f1, s2)) { + if (f2 == NULL) f2 = f1; + else if (f3 == NULL) f3 = f1; + else g_assert_not_reached (); /* s2 is a non-manifold surface */ + } + } + i = i->next; + } + if (f3 == NULL) { + if (gts_edge_is_boundary (e, s2)) + return NULL; + return f2; + } + g_assert (gts_face_has_parent_surface (f, s1)); + if (gts_triangles_are_compatible (GTS_TRIANGLE (f), GTS_TRIANGLE (f2), e)) + return f2; + return f3; +} + +static void walk_faces (GtsEdge * e, GtsFace * f, + GtsSurface * s1, + GtsSurface * s2, + GtsSurface * s) +{ + GtsFifo * faces = gts_fifo_new (); + GtsFifo * edges = gts_fifo_new (); + + gts_fifo_push (faces, f); + gts_fifo_push (edges, e); + while ((f = gts_fifo_pop (faces)) && (e = gts_fifo_pop (edges))) { + if (!GTS_OBJECT (f)->reserved) { + GtsTriangle * t = GTS_TRIANGLE (f); + GtsFace * f1; + + gts_surface_add_face (s, f); + GTS_OBJECT (f)->reserved = s; + if (t->e1 != e && !GTS_OBJECT (t->e1)->reserved && + (f1 = next_compatible_face (t->e1, f, s1, s2))) { + gts_fifo_push (faces, f1); + gts_fifo_push (edges, t->e1); + } + if (t->e2 != e && !GTS_OBJECT (t->e2)->reserved && + (f1 = next_compatible_face (t->e2, f, s1, s2))) { + gts_fifo_push (faces, f1); + gts_fifo_push (edges, t->e2); + } + if (t->e3 != e && !GTS_OBJECT (t->e3)->reserved && + (f1 = next_compatible_face (t->e3, f, s1, s2))) { + gts_fifo_push (faces, f1); + gts_fifo_push (edges, t->e3); + } + } + } + gts_fifo_destroy (faces); + gts_fifo_destroy (edges); +} + +/** + * gts_surface_inter_boolean: + * @si: a #GtsSurfaceInter. + * @surface: a #GtsSurface. + * @op: a #GtsBooleanOperation. + * + * Adds to @surface the part of the surface described by @si and @op. + */ +void gts_surface_inter_boolean (GtsSurfaceInter * si, + GtsSurface * surface, + GtsBooleanOperation op) +{ + GtsSurface * s = NULL; + gint orient = 1; + GSList * i; + + g_return_if_fail (si != NULL); + g_return_if_fail (surface != NULL); + + switch (op) { + case GTS_1_OUT_2: s = si->s1; orient = 1; break; + case GTS_1_IN_2: s = si->s1; orient = -1; break; + case GTS_2_OUT_1: s = si->s2; orient = -1; break; + case GTS_2_IN_1: s = si->s2; orient = 1; break; + default: g_assert_not_reached (); + } + + /* mark edges as belonging to intersection */ + g_slist_foreach (si->edges, (GFunc) mark_edge, si); + + i = si->edges; + while (i) { + GtsEdge * e = i->data; + GSList * j = e->triangles; + + while (j) { + if (gts_face_has_parent_surface (j->data, s) && + orient*triangle_orientation (j->data, e) > 0) { +#ifdef DEBUG_BOOLEAN + GtsFace * boundary = gts_edge_is_boundary (e, surface); + + g_assert (!boundary || boundary == j->data); +#endif /* DEBUG_BOOLEAN */ + walk_faces (e, j->data, s, GTS_OBJECT (s)->reserved, surface); + break; + } + j = j->next; + } + i = i->next; + } + g_slist_foreach (si->edges, (GFunc) gts_object_reset_reserved, NULL); + gts_surface_foreach_face (surface, + (GtsFunc) gts_object_reset_reserved, NULL); +} + +static void self_intersecting (GtsBBox * bb1, GtsBBox * bb2, + gpointer * d) +{ + GtsTriangle * t1 = bb1->bounded; + GtsTriangle * t2 = bb2->bounded; + + if (t1 != t2) { + GtsSegment * s1 = GTS_SEGMENT (t1->e1); + GtsSegment * s2 = GTS_SEGMENT (t1->e2); + GtsSegment * s3 = GTS_SEGMENT (t1->e3); + GtsSegment * s4 = GTS_SEGMENT (t2->e1); + GtsSegment * s5 = GTS_SEGMENT (t2->e2); + GtsSegment * s6 = GTS_SEGMENT (t2->e3); + GtsPoint * pi; + + if ((!gts_segments_touch (s4, s1) && + !gts_segments_touch (s4, s2) && + !gts_segments_touch (s4, s3) && + (pi = segment_triangle_intersection (s4, t1, gts_point_class ())) + != NULL) || + (!gts_segments_touch (s5, s1) && + !gts_segments_touch (s5, s2) && + !gts_segments_touch (s5, s3) && + (pi = segment_triangle_intersection (s5, t1, gts_point_class ())) + != NULL) || + (!gts_segments_touch (s6, s1) && + !gts_segments_touch (s6, s2) && + !gts_segments_touch (s6, s3) && + (pi = segment_triangle_intersection (s6, t1, gts_point_class ())) + != NULL)) { + GtsBBTreeTraverseFunc func = d[0]; + gpointer data = d[1]; + gboolean * self_inter = d[2]; + + gts_object_destroy (GTS_OBJECT (pi)); + *self_inter = TRUE; + (* func) (bb1, bb2, data); + } + } +} + +/** + * gts_surface_foreach_intersecting_face: + * @s: a #GtsSurface. + * @func: a #GtsBBTreeTraverseFunc. + * @data: user data to pass to @func. + * + * Calls @func for each intersecting pair of faces of @s. + * + * Returns: %TRUE if @func was called at least once, %FALSE otherwise. + */ +gboolean gts_surface_foreach_intersecting_face (GtsSurface * s, + GtsBBTreeTraverseFunc func, + gpointer data) +{ + GNode * tree; + gpointer d[3]; + gboolean self_inter = FALSE; + + g_return_val_if_fail (s != NULL, FALSE); + g_return_val_if_fail (func != NULL, FALSE); + + tree = gts_bb_tree_surface (s); + d[0] = func; + d[1] = data; + d[2] = &self_inter; + gts_bb_tree_traverse_overlapping (tree, tree, + (GtsBBTreeTraverseFunc) self_intersecting, + d); + gts_bb_tree_destroy (tree, TRUE); + + return self_inter; +} + +static void add_intersecting (GtsBBox * bb1, GtsBBox * bb2, + GtsSurface * intersected) +{ + gts_surface_add_face (intersected, bb1->bounded); + gts_surface_add_face (intersected, bb2->bounded); +} + +/** + * gts_surface_is_self_intersecting: + * @s: a #GtsSurface. + * + * Returns: a new #GtsSurface containing the faces of @s which are + * self-intersecting or %NULL if no faces of @s are self-intersecting. + */ +GtsSurface * gts_surface_is_self_intersecting (GtsSurface * s) +{ + GtsSurface * intersected; + + g_return_val_if_fail (s != NULL, NULL); + + intersected = gts_surface_new (GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass), + s->face_class, + s->edge_class, + s->vertex_class); + if (!gts_surface_foreach_intersecting_face (s, + (GtsBBTreeTraverseFunc) add_intersecting, intersected)) { + gts_object_destroy (GTS_OBJECT (intersected)); + intersected = NULL; + } + return intersected; +} Index: trunk/src_3rd/gts/cdt.c =================================================================== --- trunk/src_3rd/gts/cdt.c (nonexistent) +++ trunk/src_3rd/gts/cdt.c (revision 1065) @@ -0,0 +1,1173 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + + +#include +#include "gts.h" + +#ifdef USE_SURFACE_BTREE + +static gint find_closest (GtsTriangle * t, gpointer value, gpointer * data) +{ + guint * ns = data[2]; + guint * n = data[3]; + + if (*n >= *ns) + return TRUE; + else { + gdouble * dmin = data[0]; + gpointer * closest = data[1]; + GtsPoint * p = data[4]; + + if (gts_triangle_orientation (t) > 0.) { + GtsPoint * p1 = GTS_POINT (GTS_SEGMENT (t->e1)->v1); + gdouble d = (p->x - p1->x)*(p->x - p1->x) + (p->y - p1->y)*(p->y - p1->y); + + if (d < *dmin) { + *dmin = d; + *closest = t; + } + (*n)++; + } + } + return FALSE; +} + +/* select the face closest to @p among n^1/3 randomly picked faces + * of @surface */ +static GtsFace * closest_face (GtsSurface * s, GtsPoint * p) +{ + guint n = 0, nt, ns; + gdouble dmin = G_MAXDOUBLE; + GtsFace * closest = NULL; + gpointer data[5]; + + nt = gts_surface_face_number (s); + if (!nt) + return NULL; + ns = exp (log ((gdouble) nt)/3.); + + data[0] = &dmin; + data[1] = &closest; + data[2] = &ns; + data[3] = &n; + data[4] = p; + g_tree_traverse (s->faces, (GTraverseFunc) find_closest, G_IN_ORDER, data); + + return closest; +} + +#else /* not USE_SURFACE_BTREE */ + +typedef struct _SFindClosest SFindClosest; + +struct _SFindClosest { + gdouble dmin; + GtsFace *closest; + GtsPoint * p; + gint stop; +}; + +# if GLIB_CHECK_VERSION(2,4,0) +/* finally, with g_hash_table_find we are able to stop iteration over the hash + table in the middle */ + +static gboolean find_closest (gpointer key, gpointer value, gpointer user_data) +{ + SFindClosest * data = (SFindClosest *) user_data; + GtsFace * f = GTS_FACE (value); + + if (gts_triangle_orientation (GTS_TRIANGLE (f)) > 0.) { + GtsPoint * p1 = GTS_POINT (GTS_SEGMENT (GTS_TRIANGLE (f)->e1)->v1); + gdouble d = ((data->p->x - p1->x)*(data->p->x - p1->x) + + (data->p->y - p1->y)*(data->p->y - p1->y)); + + if (d < data->dmin) { + data->dmin = d; + data->closest = f; + } + } + data->stop--; + return !(data->stop > 0); +} + +static GtsFace * closest_face (GtsSurface * s, GtsPoint * p) +{ + SFindClosest fc; + + fc.dmin = G_MAXDOUBLE; + fc.closest = NULL; + fc.p = p; + fc.stop = (gint) exp (log ((gdouble) g_hash_table_size (s->faces))/3.); + g_hash_table_find (s->faces, find_closest, &fc); + + return fc.closest; +} + +# else /* VERSION < 2.4.0 */ + +static void +find_closest (gpointer key, gpointer value, gpointer user_data) +{ + SFindClosest * data = (SFindClosest *) user_data; + GtsFace * f = GTS_FACE (value); + + if (gts_triangle_orientation (GTS_TRIANGLE (f)) > 0.) { + GtsPoint * p1 = GTS_POINT (GTS_SEGMENT (GTS_TRIANGLE (f)->e1)->v1); + gdouble d = ((data->p->x - p1->x)*(data->p->x - p1->x) + + (data->p->y - p1->y)*(data->p->y - p1->y)); + + if (d < data->dmin) { + data->dmin = d; + data->closest = f; + } + } + data->stop--; +} + +/* select the face closest to @p among n^1/3 randomly picked faces + * of @surface */ +static GtsFace * closest_face (GtsSurface * s, GtsPoint * p) +{ + SFindClosest fc; + + if (!g_hash_table_size (s->faces)) + return NULL; + + fc.dmin = G_MAXDOUBLE; + fc.closest = NULL; + fc.p = p; + fc.stop = (gint) exp (log ((gdouble) g_hash_table_size (s->faces))/3.); + g_hash_table_foreach (s->faces, find_closest, &fc); + return fc.closest; +} +# endif /* VERSION < 2.4.0 */ +#endif /* not USE_SURFACE_BTREE */ + +/* returns the face belonging to @surface and neighbor of @f via @e */ +static GtsFace * neighbor (GtsFace * f, + GtsEdge * e, + GtsSurface * surface) +{ + GSList * i = e->triangles; + GtsTriangle * t = GTS_TRIANGLE (f); + + while (i) { + GtsTriangle * t1 = i->data; + if (t1 != t && + GTS_IS_FACE (t1) && + gts_face_has_parent_surface (GTS_FACE (t1), surface)) + return GTS_FACE (t1); + i = i->next; + } + return NULL; +} + +/* given a triangle @t and a segment s (@o -> @p). + @o must be in @t. Returns the + edge of @t which is intersected by s or %NULL if @p is also + contained in @t (on_summit is set to %FALSE) or if s intersects @t + exactly on one of its summit (on_summit is set to %TRUE). */ +static GtsEdge * triangle_next_edge (GtsTriangle * t, + GtsPoint * o, GtsPoint * p, + gboolean * on_summit) +{ + GtsVertex * v1, * v2, * v3; + GtsEdge * e1, * e2, * e3; + gdouble orient = 0.0; + + gts_triangle_vertices_edges (t, NULL, + &v1, &v2, &v3, + &e1, &e2, &e3); + + *on_summit = FALSE; + orient = gts_point_orientation (o, GTS_POINT (v1), p); + if (orient > 0.0) { + orient = gts_point_orientation (o, GTS_POINT (v2), p); + if (orient > 0.0) { + if (gts_point_orientation (GTS_POINT (v2), GTS_POINT (v3), p) >= 0.0) + return NULL; + return e2; + } + if (orient < 0.0) { + if (gts_point_orientation (GTS_POINT (v1), GTS_POINT (v2), p) >= 0.0) + return NULL; + return e1; + } + if (gts_point_orientation (GTS_POINT (v1), GTS_POINT (v2), p) < 0.0) + *on_summit = TRUE; + return NULL; + } + + if (orient < 0.0) { + orient = gts_point_orientation (o, GTS_POINT (v3), p); + if (orient > 0.0) { + if (gts_point_orientation (GTS_POINT (v3), GTS_POINT (v1), p) >= 0.0) + return NULL; + return e3; + } + if (orient < 0.0) { + if (gts_point_orientation (GTS_POINT (v2), GTS_POINT (v3), p) >= 0.0) + return NULL; + return e2; + } + if (gts_point_orientation (GTS_POINT (v3), GTS_POINT (v1), p) < 0.0) + *on_summit = TRUE; + return NULL; + } + + if (gts_point_orientation (GTS_POINT (v2), GTS_POINT (v3), p) < 0.0) + return e2; + if (gts_point_orientation (GTS_POINT (v1), GTS_POINT (v2), p) < 0.0) + *on_summit = TRUE; + return NULL; +} + +static void triangle_barycenter (GtsTriangle * t, GtsPoint * b) +{ + GtsPoint * p = GTS_POINT (gts_triangle_vertex (t)); + b->x = (p->x + + GTS_POINT (GTS_SEGMENT(t->e1)->v1)->x + + GTS_POINT (GTS_SEGMENT(t->e1)->v2)->x)/3.; + b->y = (p->y + + GTS_POINT (GTS_SEGMENT(t->e1)->v1)->y + + GTS_POINT (GTS_SEGMENT(t->e1)->v2)->y)/3.; +} + +static GtsFace * point_locate (GtsPoint * o, + GtsPoint * p, + GtsFace * f, + GtsSurface * surface) +{ + GtsEdge * prev; + gboolean on_summit; + GtsVertex * v1, * v2, * v3; + GtsEdge * e2, * e3; + + prev = triangle_next_edge (GTS_TRIANGLE (f), o, p, &on_summit); + + if (!prev) { + GtsFace * f1; + + if (!on_summit) + return f; /* p is inside f */ + + /* s intersects f exactly on a summit: restarts from a neighbor of f */ + if ((f1 = neighbor (f, GTS_TRIANGLE (f)->e1, surface)) || + (f1 = neighbor (f, GTS_TRIANGLE (f)->e2, surface)) || + (f1 = neighbor (f, GTS_TRIANGLE (f)->e3, surface))) { + triangle_barycenter (GTS_TRIANGLE (f1), o); + return point_locate (o, p, f1, surface); + } + return NULL; + } + + f = neighbor (f, prev, surface); + if (f) + gts_triangle_vertices_edges (GTS_TRIANGLE (f), prev, + &v1, &v2, &v3, &prev, &e2, &e3); + while (f) { + gdouble orient = gts_point_orientation (o, GTS_POINT (v3), p); + + if (orient < 0.0) { + if (gts_point_orientation (GTS_POINT (v2), GTS_POINT (v3), p) >= 0.0) + return f; /* p is inside f */ + f = neighbor (f, e2, surface); + prev = e2; + v1 = v3; + } + else if (orient > 0.0) { + if (gts_point_orientation (GTS_POINT (v3), GTS_POINT (v1), p) >= 0.0) + return f; /* p is inside f */ + f = neighbor (f, e3, surface); + prev = e3; + v2 = v3; + } + else { + GtsFace * f1; + + if (gts_point_orientation (GTS_POINT (v2), GTS_POINT (v3), p) >= 0.0) + return f; /* p is inside f */ + + /* s intersects f exactly on v3: restarts from a neighbor of f */ + if ((f1 = neighbor (f, e2, surface)) || + (f1 = neighbor (f, e3, surface))) { + triangle_barycenter (GTS_TRIANGLE (f1), o); + return point_locate (o, p, f1, surface); + } + return NULL; + } + /* update e2, e3, v3 for the new triangle */ + if (f) { + if (prev == GTS_TRIANGLE (f)->e1) { + e2 = GTS_TRIANGLE (f)->e2; e3 = GTS_TRIANGLE (f)->e3; + } + else if (prev == GTS_TRIANGLE (f)->e2) { + e2 = GTS_TRIANGLE (f)->e3; e3 = GTS_TRIANGLE (f)->e1; + } + else { + e2 = GTS_TRIANGLE (f)->e1; e3 = GTS_TRIANGLE (f)->e2; + } + if (GTS_SEGMENT (e2)->v1 == v1 || GTS_SEGMENT (e2)->v1 == v2) + v3 = GTS_SEGMENT (e2)->v2; + else + v3 = GTS_SEGMENT (e2)->v1; + } + } + return NULL; +} + +/** + * gts_point_locate: + * @p: a #GtsPoint. + * @surface: a #GtsSurface. + * @guess: %NULL or a face of @surface close to @p. + * + * Locates the face of the planar projection of @surface containing + * @p. The planar projection of @surface must define a connected set + * of triangles without holes and bounded by a convex boundary. The + * algorithm is randomized and performs in O(n^1/3) expected time + * where n is the number of triangles of @surface. + * + * If a good @guess is given the point location can be significantly faster. + * + * Returns: a #GtsFace of @surface containing @p or %NULL if @p is not + * contained within the boundary of @surface. + */ +GtsFace * gts_point_locate (GtsPoint * p, + GtsSurface * surface, + GtsFace * guess) +{ + GtsFace * fr; + GtsPoint * o; + + g_return_val_if_fail (p != NULL, NULL); + g_return_val_if_fail (surface != NULL, NULL); + g_return_val_if_fail (guess == NULL || + gts_face_has_parent_surface (guess, surface), NULL); + + if (guess == NULL) + guess = closest_face (surface, p); + else + g_return_val_if_fail (gts_triangle_orientation (GTS_TRIANGLE (guess)) > 0., NULL); + + if (guess == NULL) + return NULL; + + o = GTS_POINT (gts_object_new (GTS_OBJECT_CLASS (gts_point_class ()))); + triangle_barycenter (GTS_TRIANGLE (guess), o); + fr = point_locate (o, p, guess, surface); + gts_object_destroy (GTS_OBJECT (o)); + + return fr; +} + + +/** + * gts_constraint_class: + * + * Returns: the #GtsConstraintClass. + */ +GtsConstraintClass * gts_constraint_class (void) +{ + static GtsConstraintClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo constraint_info = { + "GtsConstraint", + sizeof (GtsConstraint), + sizeof (GtsConstraintClass), + (GtsObjectClassInitFunc) NULL, + (GtsObjectInitFunc) NULL, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_edge_class ()), + &constraint_info); + } + + return klass; +} + +static void split_list (GtsListFace * f, GtsListFace * f1, GtsListFace * f2, + GtsPoint * p1, GtsPoint * p2, + GSList ** last1, GSList ** last2) +{ + GSList * i = f->points, * l1 = *last1, * l2 = *last2; + + while (i) { + GtsPoint * p = i->data; + + if (gts_point_orientation (p1, p2, p) >= 0.) { + if (l1) l1->next = i; else f1->points = i; + l1 = i; + } + else { + if (l2) l2->next = i; else f2->points = i; + l2 = i; + } + i = i->next; + } + f->points = NULL; + *last1 = l1; + *last2 = l2; +} + +/* cf. figure misc/swap.fig */ +static void swap_if_in_circle (GtsFace * f1, + GtsVertex * v1, + GtsVertex * v2, + GtsVertex * v3, + GtsEdge * e1, + GtsEdge * e2, + GtsEdge * e3, + GtsSurface * surface) +{ + GtsFace * f2; + GtsEdge * e4, *e5; + GtsVertex * v4; + + if (GTS_IS_CONSTRAINT (e1)) /* @e1 is a constraint can not swap */ + return; + + f2 = neighbor (f1, e1, surface); + if (f2 == NULL) /* @e1 is a boundary of @surface */ + return; + + if (GTS_TRIANGLE (f2)->e1 == e1) { + e4 = GTS_TRIANGLE (f2)->e2; e5 = GTS_TRIANGLE (f2)->e3; + } + else if (GTS_TRIANGLE (f2)->e2 == e1) { + e4 = GTS_TRIANGLE (f2)->e3; e5 = GTS_TRIANGLE (f2)->e1; + } + else { + e4 = GTS_TRIANGLE (f2)->e1; e5 = GTS_TRIANGLE (f2)->e2; + } + if (GTS_SEGMENT (e4)->v1 == GTS_SEGMENT (e1)->v1 || + GTS_SEGMENT (e4)->v1 == GTS_SEGMENT (e1)->v2) + v4 = GTS_SEGMENT (e4)->v2; + else + v4 = GTS_SEGMENT (e4)->v1; + + if (gts_point_in_circle (GTS_POINT (v4), GTS_POINT (v1), + GTS_POINT (v2), GTS_POINT (v3)) > 0.0) { + GtsEdge * en; + GtsSegment * sn = gts_vertices_are_connected (v3, v4); + GtsFace * f3, * f4; + + if (!GTS_IS_EDGE (sn)) + en = gts_edge_new (surface->edge_class, v3, v4); + else + en = GTS_EDGE (sn); + + f3 = gts_face_new (surface->face_class, en, e5, e2); + gts_object_attributes (GTS_OBJECT (f3), GTS_OBJECT (f1)); + f4 = gts_face_new (surface->face_class, en, e3, e4); + gts_object_attributes (GTS_OBJECT (f4), GTS_OBJECT (f2)); + + if (GTS_IS_LIST_FACE (f3)) { + GSList * last3 = NULL, * last4 = NULL; + + if (GTS_IS_LIST_FACE (f1)) + split_list (GTS_LIST_FACE (f1), GTS_LIST_FACE (f3), GTS_LIST_FACE (f4), + GTS_POINT (v3), GTS_POINT (v4), &last3, &last4); + if (GTS_IS_LIST_FACE (f2)) + split_list (GTS_LIST_FACE (f2), GTS_LIST_FACE (f3), GTS_LIST_FACE (f4), + GTS_POINT (v3), GTS_POINT (v4), &last3, &last4); + if (last3) last3->next = NULL; + if (last4) last4->next = NULL; + } + + gts_surface_remove_face (surface, f1); + gts_surface_remove_face (surface, f2); + gts_surface_add_face (surface, f3); + gts_surface_add_face (surface, f4); + + swap_if_in_circle (f3, v4, v2, v3, e5, e2, en, surface); + swap_if_in_circle (f4, v1, v4, v3, e4, en, e3, surface); + } +} + +/** + * gts_delaunay_add_vertex_to_face: + * @surface: a #GtsSurface. + * @v: a #GtsVertex. + * @f: a #GtsFace belonging to @surface. + * + * Adds vertex @v to the face @f of the Delaunay triangulation defined + * by @surface. + * + * Returns: %NULL is @v has been successfully added to @surface or was + * already contained in @surface or a #GtsVertex having the same x and + * y coordinates as @v. + */ +GtsVertex * gts_delaunay_add_vertex_to_face (GtsSurface * surface, + GtsVertex * v, + GtsFace * f) +{ + GtsEdge * e1, * e2, * e3; + GtsSegment * s4, * s5, * s6; + GtsEdge * e4, * e5, * e6; + GtsVertex * v1, * v2, * v3; + GtsFace * nf[3]; + + g_return_val_if_fail (surface != NULL, v); + g_return_val_if_fail (v != NULL, v); + g_return_val_if_fail (f != NULL, v); + + gts_triangle_vertices_edges (GTS_TRIANGLE (f), NULL, + &v1, &v2, &v3, &e1, &e2, &e3); + if (v == v1 || v == v2 || v == v3) /* v already in @surface */ + return NULL; + if (GTS_POINT (v)->x == GTS_POINT (v1)->x && + GTS_POINT (v)->y == GTS_POINT (v1)->y) + return v1; + if (GTS_POINT (v)->x == GTS_POINT (v2)->x && + GTS_POINT (v)->y == GTS_POINT (v2)->y) + return v2; + if (GTS_POINT (v)->x == GTS_POINT (v3)->x && + GTS_POINT (v)->y == GTS_POINT (v3)->y) + return v3; + + s4 = gts_vertices_are_connected (v, v1); + if (!GTS_IS_EDGE (s4)) + e4 = gts_edge_new (surface->edge_class, v, v1); + else + e4 = GTS_EDGE (s4); + s5 = gts_vertices_are_connected (v, v2); + if (!GTS_IS_EDGE (s5)) + e5 = gts_edge_new (surface->edge_class, v, v2); + else + e5 = GTS_EDGE (s5); + s6 = gts_vertices_are_connected (v, v3); + if (!GTS_IS_EDGE (s6)) + e6 = gts_edge_new (surface->edge_class, v, v3); + else + e6 = GTS_EDGE (s6); + + /* cf. figure misc/swap.fig */ + nf[0] = gts_face_new (surface->face_class, e4, e1, e5); + gts_object_attributes (GTS_OBJECT (nf[0]), GTS_OBJECT (f)); + nf[1] = gts_face_new (surface->face_class, e5, e2, e6); + gts_object_attributes (GTS_OBJECT (nf[1]), GTS_OBJECT (f)); + nf[2] = gts_face_new (surface->face_class, e6, e3, e4); + gts_object_attributes (GTS_OBJECT (nf[2]), GTS_OBJECT (f)); + + if (GTS_IS_LIST_FACE (f) && GTS_IS_LIST_FACE (nf[0])) { + GSList * i = GTS_LIST_FACE (f)->points, * last[3] = { NULL, NULL, NULL }; + + while (i) { + GtsPoint * p = i->data; + GSList * next = i->next; + guint j; + + if (p != GTS_POINT (v)) { + if (gts_point_orientation (GTS_POINT (v), GTS_POINT (v1), p) >= 0.) { + gdouble o = gts_point_orientation (GTS_POINT (v), GTS_POINT (v2), p); + + if (o != 0.) + j = o > 0. ? 1 : 0; + else + j = gts_point_orientation (GTS_POINT (v), GTS_POINT (v3), p) + > 0. ? 0 : 1; + } + else if (gts_point_orientation (GTS_POINT (v), GTS_POINT (v3), p) > 0.) + j = 2; + else + j = 1; + if (last[j]) + last[j]->next = i; + else + GTS_LIST_FACE (nf[j])->points = i; + last[j] = i; + } + else + g_slist_free_1 (i); + i = next; + } + GTS_LIST_FACE (f)->points = NULL; + if (last[0]) last[0]->next = NULL; + if (last[1]) last[1]->next = NULL; + if (last[2]) last[2]->next = NULL; + } + + gts_surface_remove_face (surface, f); + gts_surface_add_face (surface, nf[0]); + gts_surface_add_face (surface, nf[1]); + gts_surface_add_face (surface, nf[2]); + + swap_if_in_circle (nf[0], v1, v2, v, e1, e5, e4, surface); + swap_if_in_circle (nf[1], v2, v3, v, e2, e6, e5, surface); + swap_if_in_circle (nf[2], v3, v1, v, e3, e4, e6, surface); + + return NULL; +} + +/** + * gts_delaunay_add_vertex: + * @surface: a #GtsSurface. + * @v: a #GtsVertex. + * @guess: %NULL or a #GtsFace belonging to @surface to be used as an initial + * guess for point location. + * + * Adds vertex @v to the Delaunay triangulation defined by + * @surface. If @v is not contained in the convex hull bounding + * @surface, @v is not added to the triangulation. + * + * Returns: %NULL is @v has been successfully added to @surface or was + * already contained in @surface, @v if @v is not contained in the + * convex hull bounding surface or a #GtsVertex having the same x and + * y coordinates as @v. + */ +GtsVertex * gts_delaunay_add_vertex (GtsSurface * surface, + GtsVertex * v, + GtsFace * guess) +{ + GtsFace * f; + + g_return_val_if_fail (surface != NULL, v); + g_return_val_if_fail (v != NULL, v); + + if (!(f = gts_point_locate (GTS_POINT (v), surface, guess))) + return v; + return gts_delaunay_add_vertex_to_face (surface, v, f); +} + +static gboolean polygon_in_circle (GSList * poly, + GtsPoint * p1, + GtsPoint * p2, + GtsPoint * p3) +{ + GtsVertex * v1 = NULL, * v2 = NULL; + + while (poly) { + GtsSegment * s = poly->data; + GtsVertex * v; + v = s->v1; + if (v != v1 && v != v2 && + v != GTS_VERTEX (p1) && + v != GTS_VERTEX (p2) && + v != GTS_VERTEX (p3) && + gts_point_in_circle (GTS_POINT (v), p1, p2, p3) > 0.) + return TRUE; + v = s->v2; + if (v != v1 && v != v2 && + v != GTS_VERTEX (p1) && + v != GTS_VERTEX (p2) && + v != GTS_VERTEX (p3) && + gts_point_in_circle (GTS_POINT (v), p1, p2, p3) > 0.) + return TRUE; + v1 = s->v1; + v2 = s->v2; + poly = poly->next; + } + return FALSE; +} + +static void triangulate_polygon (GSList * poly, + GtsSurface * surface, + GtsFace * ref) +{ + GSList * i, * poly1, * poly2; + GtsVertex * v1, * v2, * v3 = NULL; + gboolean found = FALSE; + GtsSegment * s, * s1, * s2; + GtsEdge * e1, * e2; + GtsFace * f; + + if (poly == NULL || poly->next == NULL) { + g_slist_free (poly); + return; + } + + s = poly->data; + s1 = poly->next->data; + if (s->v1 == s1->v1 || s->v1 == s1->v2) { + v1 = s->v2; + v2 = s->v1; + } + else { + g_assert (s->v2 == s1->v1 || s->v2 == s1->v2); + v1 = s->v1; + v2 = s->v2; + } + + i = poly->next; + v3 = v2; + while (i && !found) { + s1 = i->data; + if (s1->v1 == v3) + v3 = s1->v2; + else { + g_assert (s1->v2 == v3); + v3 = s1->v1; + } + if (v3 != v1 && + gts_point_orientation (GTS_POINT (v1), + GTS_POINT (v2), + GTS_POINT (v3)) >= 0. && + !polygon_in_circle (poly, + GTS_POINT (v1), + GTS_POINT (v2), + GTS_POINT (v3))) + found = TRUE; + else + i = i->next; + } + + if (!found) { + g_slist_free (poly); + return; + } + + s1 = gts_vertices_are_connected (v2, v3); + if (!GTS_IS_EDGE (s1)) + e1 = gts_edge_new (surface->edge_class, v2, v3); + else + e1 = GTS_EDGE (s1); + s2 = gts_vertices_are_connected (v3, v1); + if (!GTS_IS_EDGE (s2)) + e2 = gts_edge_new (surface->edge_class, v3, v1); + else + e2 = GTS_EDGE (s2); + f = gts_face_new (surface->face_class, GTS_EDGE (s), e1, e2); + gts_object_attributes (GTS_OBJECT (f), GTS_OBJECT (ref)); + gts_surface_add_face (surface, f); + + poly1 = poly->next; + g_slist_free_1 (poly); + if (i->next && e2 != i->next->data) + poly2 = g_slist_prepend (i->next, e2); + else + poly2 = i->next; + if (e1 != i->data) + i->next = g_slist_prepend (NULL, e1); + else + i->next = NULL; + + triangulate_polygon (poly1, surface, ref); + triangulate_polygon (poly2, surface, ref); +} + +/** + * gts_delaunay_remove_vertex: + * @surface: a #GtsSurface. + * @v: a #GtsVertex. + * + * Removes @v from the Delaunay triangulation defined by @surface and + * restores the Delaunay property. Vertex @v must not be used by any + * constrained edge otherwise the triangulation is not guaranteed to + * be Delaunay. + */ +void gts_delaunay_remove_vertex (GtsSurface * surface, GtsVertex * v) +{ + GSList * triangles, * i; + GtsFace * ref = NULL; + + g_return_if_fail (surface != NULL); + g_return_if_fail (v != NULL); + + i = triangles = gts_vertex_triangles (v, NULL); + while (i && !ref) { + if (GTS_IS_FACE (i->data) && + gts_face_has_parent_surface (i->data, surface)) + ref = i->data; + i = i->next; + } + if (!ref) { + g_slist_free (triangles); + g_return_if_fail (ref); + } + triangulate_polygon (gts_vertex_fan_oriented (v, surface), surface, ref); + i = triangles; + while (i) { + if (GTS_IS_FACE (i->data) && + gts_face_has_parent_surface (i->data, surface)) + gts_surface_remove_face (surface, i->data); + i = i->next; + } + g_slist_free (triangles); +} + +#define NEXT_CUT(edge, edge1, list) { next = neighbor (f, edge, surface);\ + remove_triangles (e, surface);\ + if (!constraint && !e->triangles)\ + gts_object_destroy (GTS_OBJECT (e));\ + g_assert (next);\ + *list = g_slist_prepend (*list, edge1);\ + return g_slist_concat (constraint,\ + remove_intersected_edge (s, edge,\ + next, surface, left, right));\ + } + +static void remove_triangles (GtsEdge * e, GtsSurface * s) +{ + GSList * i = e->triangles; + + while (i) { + GSList * next = i->next; + + if (GTS_IS_FACE (i->data) && gts_face_has_parent_surface (i->data, s)) + gts_surface_remove_face (s, i->data); + i = next; + } +} + +static GSList * +remove_intersected_edge (GtsSegment * s, + GtsEdge * e, + GtsFace * f, + GtsSurface * surface, + GSList ** left, GSList ** right) +{ + GtsVertex * v1, * v2, * v3; + GtsEdge * e1, * e2; + gdouble o1, o2; + GtsFace * next; + GSList * constraint = NULL; + + if (GTS_IS_CONSTRAINT (e)) + constraint = g_slist_prepend (NULL, e); + + gts_triangle_vertices_edges (GTS_TRIANGLE (f), e, + &v1, &v2, &v3, &e, &e1, &e2); + + o1 = gts_point_orientation (GTS_POINT (v2), GTS_POINT (v3), + GTS_POINT (s->v2)); + o2 = gts_point_orientation (GTS_POINT (v3), GTS_POINT (v1), + GTS_POINT (s->v2)); + + if (o1 == 0. && o2 == 0.) { +/* if(o2 != 0.) { + fprintf(stderr, "o1 = %f o2 = %f\n", o1, o2); + fprintf(stderr, "v1 = %f, %f\n", GTS_POINT(v1)->x, GTS_POINT(v1)->y); + fprintf(stderr, "v2 = %f, %f\n", GTS_POINT(v2)->x, GTS_POINT(v2)->y); + fprintf(stderr, "v3 = %f, %f\n", GTS_POINT(v3)->x, GTS_POINT(v3)->y); + fprintf(stderr, "s->v2 = %f, %f\n", GTS_POINT(s->v2)->x, GTS_POINT(s->v2)->y); + + g_assert (o2 == 0.); + }*/ + // if(o2 == 0.) { + remove_triangles (e, surface); + if (!constraint && !e->triangles) + gts_object_destroy (GTS_OBJECT (e)); + *left = g_slist_prepend (*left, e2); + *right = g_slist_prepend (*right, e1); +// } + } + else if (o1 > 0.) { + g_assert (o2 <= 0.); + NEXT_CUT (e2, e1, right) + } + else if (o2 >= 0.) + NEXT_CUT (e1, e2, left) + else { + gdouble o3 = gts_point_orientation (GTS_POINT (s->v1), GTS_POINT (s->v2), + GTS_POINT (v3)); + if (o3 > 0.) + NEXT_CUT (e1, e2, left) + else + NEXT_CUT (e2, e1, right) + } + return constraint; +} + +static GSList * +remove_intersected_vertex (GtsSegment * s, + GtsVertex * v, + GtsSurface * surface, + GSList ** left, + GSList ** right, + GtsFace ** ref) +{ + GSList * triangles = gts_vertex_triangles (v, NULL); + GSList * i; + + i = triangles; + while (i) { + GtsTriangle * t = i->data; + if (GTS_IS_FACE (t) && + gts_face_has_parent_surface (GTS_FACE (t), surface)) { + GtsVertex * v1, * v2, * v3; + gdouble o1, o2; + + gts_triangle_vertices (t, &v1, &v2, &v3); + if (v == v2) { + v2 = v3; + v3 = v1; + } + else if (v == v3) { + v3 = v2; + v2 = v1; + } + else + g_assert (v == v1); + + if ((o1 = gts_point_orientation (GTS_POINT (v), GTS_POINT (v2), + GTS_POINT (s->v2))) >= 0. && + (o2 = gts_point_orientation (GTS_POINT (v3), GTS_POINT (v), + GTS_POINT (s->v2))) >= 0.) { + gdouble o3 = gts_point_orientation (GTS_POINT (v2), GTS_POINT (v3), + GTS_POINT (s->v2)); + GtsEdge * e = gts_triangle_edge_opposite (t, v); + GtsEdge * e1, * e2; + GtsFace * next = neighbor (GTS_FACE (t), e, surface); + + *ref = GTS_FACE (t); + gts_triangle_vertices_edges (t, e, &v2, &v3, &v, &e, &e2, &e1); + + g_slist_free (triangles); + + if (o3 >= 0.) /* @s->v2 is inside (or on the edge) of t */ + return NULL; + + gts_allow_floating_faces = TRUE; + gts_surface_remove_face (surface, GTS_FACE (t)); + gts_allow_floating_faces = FALSE; + + *left = g_slist_prepend (*left, e2); + *right = g_slist_prepend (*right, e1); + + g_assert (next); + return remove_intersected_edge (s, e, next, surface, left, right); + } + } + i = i->next; + } + + g_assert_not_reached (); + return NULL; +} + +/** + * gts_delaunay_add_constraint: + * @surface: a #GtsSurface. + * @c: a #GtsConstraint. + * + * Add constraint @c to the constrained Delaunay triangulation defined by + * @surface. + * + * Returns: a list of #GtsConstraint conflicting (i.e. intersecting) with @c + * which were removed from @surface (%NULL if there was none). + */ +GSList * gts_delaunay_add_constraint (GtsSurface * surface, + GtsConstraint * c) +{ + GSList * constraints; + GtsVertex * v1; //, * v2; + GSList * left = NULL, * right = NULL; + GtsFace * ref = NULL; + + g_return_val_if_fail (surface != NULL, NULL); + g_return_val_if_fail (c != NULL, NULL); + g_return_val_if_fail (GTS_IS_CONSTRAINT (c), NULL); + + v1 = GTS_SEGMENT (c)->v1; + //v2 = GTS_SEGMENT (c)->v2; + + gts_allow_floating_edges = TRUE; + constraints = remove_intersected_vertex (GTS_SEGMENT (c), v1, surface, + &left, &right, &ref); + gts_allow_floating_edges = FALSE; +#if 1 + triangulate_polygon (g_slist_prepend (g_slist_reverse (right), c), + surface, ref); + triangulate_polygon (g_slist_prepend (left, c), + surface, ref); +#else + right = g_slist_prepend (g_slist_reverse (right), c); + left = g_slist_prepend (left, c); + { + FILE * fp0 = fopen ("hole", "wt"); + FILE * fp1 = fopen ("right", "wt"); + FILE * fp2 = fopen ("left", "wt"); + GSList * i = left; + + gts_surface_write (surface, fp0); + fclose (fp0); + + fprintf (fp2, "LIST {\n"); + while (i) { + GtsSegment * s = i->data; + fprintf (fp2, + "# %p: %p->%p\n" + "VECT 1 2 0 2 0 %g %g 0 %g %g 0\n", + s, s->v1, s->v2, + GTS_POINT (s->v1)->x, GTS_POINT (s->v1)->y, + GTS_POINT (s->v2)->x, GTS_POINT (s->v2)->y); + i = i->next; + } + fprintf (fp2, "}\n"); + fprintf (fp1, "LIST {\n"); + i = right; + while (i) { + GtsSegment * s = i->data; + fprintf (fp1, + "# %p: %p->%p\n" + "VECT 1 2 0 2 0 %g %g 0 %g %g 0\n", + s, s->v1, s->v2, + GTS_POINT (s->v1)->x, GTS_POINT (s->v1)->y, + GTS_POINT (s->v2)->x, GTS_POINT (s->v2)->y); + i = i->next; + } + fprintf (fp1, "}\n"); + fclose (fp1); + fclose (fp2); + } + triangulate_polygon (right, surface); + triangulate_polygon (left, surface); +#endif + if (ref && !ref->surfaces) { + gts_allow_floating_edges = TRUE; + gts_object_destroy (GTS_OBJECT (ref)); + gts_allow_floating_edges = FALSE; + } + return constraints; +} + +static void delaunay_check (GtsTriangle * t, gpointer * data) +{ + GtsSurface * surface = data[0]; + GtsFace ** face = data[1]; + + if (*face == NULL) { + GSList * i, * list; + GtsVertex * v1, * v2, * v3; + + gts_triangle_vertices (t, &v1, &v2, &v3); + list = gts_vertex_neighbors (v1, NULL, surface); + list = gts_vertex_neighbors (v2, list, surface); + list = gts_vertex_neighbors (v3, list, surface); + i = list; + while (i && *face == NULL) { + GtsVertex * v = i->data; + if (v != v1 && v != v2 && v != v3 && + gts_point_in_circle (GTS_POINT (v), + GTS_POINT (v1), + GTS_POINT (v2), + GTS_POINT (v3)) > 0.) + *face = GTS_FACE (t); + i = i->next; + } + g_slist_free (list); + } +} + +/** + * gts_delaunay_check: + * @surface: a #GtsSurface. + * + * Returns: %NULL if the planar projection of @surface is a Delaunay + * triangulation (unconstrained), a #GtsFace violating the Delaunay + * property otherwise. + */ +GtsFace * gts_delaunay_check (GtsSurface * surface) +{ + GtsFace * face = NULL; + gpointer data[2]; + + g_return_val_if_fail (surface != NULL, FALSE); + + data[0] = surface; + data[1] = &face; + gts_surface_foreach_face (surface, (GtsFunc) delaunay_check, data); + + return face; +} + +/** + * gts_delaunay_remove_hull: + * @surface: a #GtsSurface. + * + * Removes all the edges of the boundary of @surface which are not + * constraints. + */ +void gts_delaunay_remove_hull (GtsSurface * surface) +{ + GSList * boundary; + + g_return_if_fail (surface != NULL); + + boundary = gts_surface_boundary (surface); + gts_allow_floating_edges = TRUE; + while (boundary) { + GSList * i = boundary; + GtsEdge * e = i->data; + + boundary = i->next; + g_slist_free_1 (i); + if (!GTS_IS_CONSTRAINT (e)) { + GtsTriangle * t = GTS_TRIANGLE (gts_edge_is_boundary (e, surface)); + + if (t != NULL) { + if (t->e1 != e && !GTS_IS_CONSTRAINT (t->e1) && + !gts_edge_is_boundary (t->e1, surface)) + boundary = g_slist_prepend (boundary, t->e1); + if (t->e2 != e && !GTS_IS_CONSTRAINT (t->e2) && + !gts_edge_is_boundary (t->e2, surface)) + boundary = g_slist_prepend (boundary, t->e2); + if (t->e3 != e && !GTS_IS_CONSTRAINT (t->e3) && + !gts_edge_is_boundary (t->e3, surface)) + boundary = g_slist_prepend (boundary, t->e3); + gts_surface_remove_face (surface, GTS_FACE (t)); + } + if (!e->triangles) + gts_object_destroy (GTS_OBJECT (e)); + } + } + gts_allow_floating_edges = FALSE; +} + +/* GtsListFace: Object */ + +static void gts_list_face_destroy (GtsObject * object) +{ + g_slist_free (GTS_LIST_FACE (object)->points); + + (* GTS_OBJECT_CLASS (gts_list_face_class ())->parent_class->destroy) + (object); +} + +static void gts_list_face_class_init (GtsFaceClass * klass) +{ + GTS_OBJECT_CLASS (klass)->destroy = gts_list_face_destroy; +} + +GtsFaceClass * gts_list_face_class (void) +{ + static GtsFaceClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo gts_list_face_info = { + "GtsListFace", + sizeof (GtsListFace), + sizeof (GtsFaceClass), + (GtsObjectClassInitFunc) gts_list_face_class_init, + (GtsObjectInitFunc) NULL, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_face_class ()), + >s_list_face_info); + } + + return klass; +} Index: trunk/src_3rd/gts/container.c =================================================================== --- trunk/src_3rd/gts/container.c (nonexistent) +++ trunk/src_3rd/gts/container.c (revision 1065) @@ -0,0 +1,493 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "gts.h" + +/* GtsContainee */ + +static void containee_class_init (GtsContaineeClass * klass) +{ + klass->remove_container = NULL; + klass->add_container = NULL; + klass->foreach = NULL; + klass->is_contained = NULL; + klass->replace = NULL; +} + +GtsContaineeClass * gts_containee_class (void) +{ + static GtsContaineeClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo containee_info = { + "GtsContainee", + sizeof (GtsContainee), + sizeof (GtsContaineeClass), + (GtsObjectClassInitFunc) containee_class_init, + (GtsObjectInitFunc) NULL, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (gts_object_class (), + &containee_info); + } + + return klass; +} + +GtsContainee * gts_containee_new (GtsContaineeClass * klass) +{ + GtsContainee * object; + + object = GTS_CONTAINEE (gts_object_new (GTS_OBJECT_CLASS (klass))); + + return object; +} + +gboolean gts_containee_is_contained (GtsContainee * item, + GtsContainer * c) +{ + g_return_val_if_fail (item != NULL, FALSE); + g_return_val_if_fail (c != NULL, FALSE); + + if (GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->is_contained) + return + (* GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->is_contained) + (item, c); + return FALSE; +} + +void gts_containee_replace (GtsContainee * item, + GtsContainee * with) +{ + if (GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->replace) + (* GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->replace) (item, with); + if (GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->foreach) { + (* GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->foreach) + (item, (GtsFunc) gts_container_add, with); + (* GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->foreach) + (item, (GtsFunc) gts_container_remove, item); + } +} + +/* GtsSListContainee */ + +static void slist_containee_destroy (GtsObject * object) +{ + GtsSListContainee * item = GTS_SLIST_CONTAINEE (object); + GSList * i; + + i = item->containers; + while (i) { + GSList * next = i->next; + + gts_container_remove (i->data, GTS_CONTAINEE (item)); + i = next; + } + g_assert (item->containers == NULL); + + (* GTS_OBJECT_CLASS (gts_slist_containee_class ())->parent_class->destroy) + (object); +} + +static void slist_containee_remove_container (GtsContainee * i, + GtsContainer * c) +{ + GtsSListContainee * item = GTS_SLIST_CONTAINEE (i); + item->containers = g_slist_remove (item->containers, c); +} + +static void slist_containee_add_container (GtsContainee * i, + GtsContainer * c) +{ + GtsSListContainee * item = GTS_SLIST_CONTAINEE (i); + if (!g_slist_find (item->containers, c)) + item->containers = g_slist_prepend (item->containers, c); +} + +static void slist_containee_foreach (GtsContainee * c, + GtsFunc func, + gpointer data) +{ + GSList * i = GTS_SLIST_CONTAINEE (c)->containers; + + while (i) { + GSList * next = i->next; + + (* func) (i->data, data); + i = next; + } +} + +static gboolean slist_containee_is_contained (GtsContainee * i, + GtsContainer * c) +{ + return g_slist_find (GTS_SLIST_CONTAINEE (i)->containers, c) ? TRUE : FALSE; +} + +static void slist_containee_class_init (GtsSListContaineeClass * klass) +{ + GTS_CONTAINEE_CLASS (klass)->remove_container = + slist_containee_remove_container; + GTS_CONTAINEE_CLASS (klass)->add_container = + slist_containee_add_container; + GTS_CONTAINEE_CLASS (klass)->foreach = + slist_containee_foreach; + GTS_CONTAINEE_CLASS (klass)->is_contained = + slist_containee_is_contained; + + GTS_OBJECT_CLASS (klass)->destroy = slist_containee_destroy; +} + +static void slist_containee_init (GtsSListContainee * object) +{ + object->containers = NULL; +} + +GtsSListContaineeClass * gts_slist_containee_class (void) +{ + static GtsSListContaineeClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo slist_containee_info = { + "GtsSListContainee", + sizeof (GtsSListContainee), + sizeof (GtsSListContaineeClass), + (GtsObjectClassInitFunc) slist_containee_class_init, + (GtsObjectInitFunc) slist_containee_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_containee_class ()), + &slist_containee_info); + } + + return klass; +} + +/* GtsContainer */ + +static void remove_container (GtsContainee * item, GtsContainer * c) +{ + if (GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->remove_container) + (* GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->remove_container) + (item, c); +} + +static void container_destroy (GtsObject * object) +{ + GtsContainer * c = GTS_CONTAINER (object); + + gts_container_foreach (c, (GtsFunc) remove_container, c); + + (* GTS_OBJECT_CLASS (gts_container_class ())->parent_class->destroy) + (object); +} + +static void container_add (GtsContainer * c, GtsContainee * item) +{ + if (GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->add_container) + (* GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->add_container) + (item, c); +} + +static void container_remove (GtsContainer * c, GtsContainee * item) +{ + if (GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->remove_container) + (* GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->remove_container) + (item, c); +} + +static void container_clone_add (GtsContainee * item, GtsContainer * clone) +{ + gts_container_add (clone, item); +} + +static void container_clone (GtsObject * clone, GtsObject * object) +{ + gts_object_init (clone, object->klass); + gts_container_foreach (GTS_CONTAINER (object), + (GtsFunc) container_clone_add, clone); +} + +static void container_class_init (GtsContainerClass * klass) +{ + klass->add = container_add; + klass->remove = container_remove; + klass->foreach = NULL; + klass->size = NULL; + + GTS_OBJECT_CLASS (klass)->destroy = container_destroy; + GTS_OBJECT_CLASS (klass)->clone = container_clone; +} + +GtsContainerClass * gts_container_class (void) +{ + static GtsContainerClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo container_info = { + "GtsContainer", + sizeof (GtsContainer), + sizeof (GtsContainerClass), + (GtsObjectClassInitFunc) container_class_init, + (GtsObjectInitFunc) NULL, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = + gts_object_class_new (GTS_OBJECT_CLASS (gts_slist_containee_class ()), + &container_info); + } + + return klass; +} + +GtsContainer * gts_container_new (GtsContainerClass * klass) +{ + GtsContainer * object; + + object = GTS_CONTAINER (gts_object_new (GTS_OBJECT_CLASS (klass))); + + return object; +} + +void gts_container_add (GtsContainer * c, + GtsContainee * item) +{ + g_return_if_fail (c != NULL); + g_return_if_fail (item != NULL); + + g_assert (GTS_CONTAINER_CLASS (GTS_OBJECT (c)->klass)->add); + (* GTS_CONTAINER_CLASS (GTS_OBJECT (c)->klass)->add) (c, item); +} + +void gts_container_remove (GtsContainer * c, + GtsContainee * item) +{ + g_return_if_fail (c != NULL); + g_return_if_fail (item != NULL); + + g_assert (GTS_CONTAINER_CLASS (GTS_OBJECT (c)->klass)->remove); + (* GTS_CONTAINER_CLASS (GTS_OBJECT (c)->klass)->remove) (c, item); +} + +void gts_container_foreach (GtsContainer * c, + GtsFunc func, + gpointer data) +{ + g_return_if_fail (c != NULL); + g_return_if_fail (func != NULL); + + if (GTS_CONTAINER_CLASS (GTS_OBJECT (c)->klass)->foreach) + (* GTS_CONTAINER_CLASS (GTS_OBJECT (c)->klass)->foreach) (c, func, data); +} + +guint gts_container_size (GtsContainer * c) +{ + g_return_val_if_fail (c != NULL, 0); + + if (GTS_CONTAINER_CLASS (GTS_OBJECT (c)->klass)->size) + return (* GTS_CONTAINER_CLASS (GTS_OBJECT (c)->klass)->size) (c); + return 0; +} + +/* GtsHashContainer */ + +static void hash_container_destroy (GtsObject * object) +{ + GHashTable * items = GTS_HASH_CONTAINER (object)->items; + + (* GTS_OBJECT_CLASS (gts_hash_container_class ())->parent_class->destroy) + (object); + + g_hash_table_destroy (items); +} + +static void hash_container_add (GtsContainer * c, GtsContainee * item) +{ + g_return_if_fail (GTS_HASH_CONTAINER (c)->frozen == FALSE); + + g_hash_table_insert (GTS_HASH_CONTAINER (c)->items, item, NULL); + + (* GTS_CONTAINER_CLASS (GTS_OBJECT_CLASS (gts_hash_container_class ())->parent_class)->add) (c, item); +} + +static void hash_container_remove (GtsContainer * c, GtsContainee * item) +{ + g_return_if_fail (GTS_HASH_CONTAINER (c)->frozen == FALSE); + + g_hash_table_remove (GTS_HASH_CONTAINER (c)->items, item); + + (* GTS_CONTAINER_CLASS (GTS_OBJECT_CLASS (gts_hash_container_class ())->parent_class)->remove) (c, item); +} + +static void hash_foreach (GtsContainee * item, + gpointer item_data, + gpointer * info) +{ + (* ((GtsFunc) info[0])) (item, info[1]); +} + +static void hash_container_foreach (GtsContainer * c, + GtsFunc func, + gpointer data) +{ + gpointer info[2]; + + info[0] = func; + info[1] = data; + /* prevent removing or adding items */ + GTS_HASH_CONTAINER (c)->frozen = TRUE; + g_hash_table_foreach (GTS_HASH_CONTAINER (c)->items, + (GHFunc) hash_foreach, info); + GTS_HASH_CONTAINER (c)->frozen = FALSE; +} + +static guint hash_container_size (GtsContainer * c) +{ + return g_hash_table_size (GTS_HASH_CONTAINER (c)->items); +} + +static void hash_container_class_init (GtsHashContainerClass * klass) +{ + GTS_CONTAINER_CLASS (klass)->add = hash_container_add; + GTS_CONTAINER_CLASS (klass)->remove = hash_container_remove; + GTS_CONTAINER_CLASS (klass)->foreach = hash_container_foreach; + GTS_CONTAINER_CLASS (klass)->size = hash_container_size; + + GTS_OBJECT_CLASS (klass)->destroy = hash_container_destroy; +} + +static void hash_container_init (GtsHashContainer * object) +{ + object->items = g_hash_table_new (NULL, NULL); + object->frozen = FALSE; +} + +GtsHashContainerClass * gts_hash_container_class (void) +{ + static GtsHashContainerClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo hash_container_info = { + "GtsHashContainer", + sizeof (GtsHashContainer), + sizeof (GtsHashContainerClass), + (GtsObjectClassInitFunc) hash_container_class_init, + (GtsObjectInitFunc) hash_container_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_container_class ()), + &hash_container_info); + } + + return klass; +} + +/* GtsSListContainer */ + +static void slist_container_destroy (GtsObject * object) +{ + GSList * items = GTS_SLIST_CONTAINER (object)->items; + + (* GTS_OBJECT_CLASS (gts_slist_container_class ())->parent_class->destroy) + (object); + + g_slist_free (items); +} + +static void slist_container_add (GtsContainer * c, GtsContainee * item) +{ + g_return_if_fail (GTS_SLIST_CONTAINER (c)->frozen == FALSE); + + if (!g_slist_find (GTS_SLIST_CONTAINER (c)->items, item)) + GTS_SLIST_CONTAINER (c)->items = + g_slist_prepend (GTS_SLIST_CONTAINER (c)->items, item); + + (* GTS_CONTAINER_CLASS (GTS_OBJECT_CLASS (gts_slist_container_class ())->parent_class)->add) (c, item); +} + +static void slist_container_remove (GtsContainer * c, GtsContainee * item) +{ + g_return_if_fail (GTS_SLIST_CONTAINER (c)->frozen == FALSE); + + GTS_SLIST_CONTAINER (c)->items = + g_slist_remove (GTS_SLIST_CONTAINER (c)->items, item); + + (* GTS_CONTAINER_CLASS (GTS_OBJECT_CLASS (gts_slist_container_class ())->parent_class)->remove) (c, item); +} + +static void slist_container_foreach (GtsContainer * c, + GtsFunc func, + gpointer data) +{ + GSList * i; + + i = GTS_SLIST_CONTAINER (c)->items; + while (i) { + GSList * next = i->next; + + (* func) (i->data, data); + i = next; + } +} + +static guint slist_container_size (GtsContainer * c) +{ + return g_slist_length (GTS_SLIST_CONTAINER (c)->items); +} + +static void slist_container_class_init (GtsSListContainerClass * klass) +{ + GTS_CONTAINER_CLASS (klass)->add = slist_container_add; + GTS_CONTAINER_CLASS (klass)->remove = slist_container_remove; + GTS_CONTAINER_CLASS (klass)->foreach = slist_container_foreach; + GTS_CONTAINER_CLASS (klass)->size = slist_container_size; + + GTS_OBJECT_CLASS (klass)->destroy = slist_container_destroy; +} + +static void slist_container_init (GtsSListContainer * object) +{ + object->items = NULL; + object->frozen = FALSE; +} + +GtsSListContainerClass * gts_slist_container_class (void) +{ + static GtsSListContainerClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo slist_container_info = { + "GtsSListContainer", + sizeof (GtsSListContainer), + sizeof (GtsSListContainerClass), + (GtsObjectClassInitFunc) slist_container_class_init, + (GtsObjectInitFunc) slist_container_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_container_class ()), + &slist_container_info); + } + + return klass; +} Index: trunk/src_3rd/gts/curvature.c =================================================================== --- trunk/src_3rd/gts/curvature.c (nonexistent) +++ trunk/src_3rd/gts/curvature.c (revision 1065) @@ -0,0 +1,621 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999-2002 Ray Jones, Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "gts.h" + +static gboolean angle_obtuse (GtsVertex * v, GtsFace * f) +{ + GtsEdge * e = gts_triangle_edge_opposite (GTS_TRIANGLE (f), v); + GtsVector vec1, vec2; + + gts_vector_init (vec1, GTS_POINT (v), GTS_POINT (GTS_SEGMENT (e)->v1)); + gts_vector_init (vec2, GTS_POINT (v), GTS_POINT (GTS_SEGMENT (e)->v2)); + + return (gts_vector_scalar (vec1, vec2) < 0.0); +} + +static gboolean triangle_obtuse (GtsVertex * v, GtsFace * f) +{ + GtsEdge * e = gts_triangle_edge_opposite (GTS_TRIANGLE (f), v); + + return (angle_obtuse (v, f) || + angle_obtuse (GTS_SEGMENT (e)->v1, f) || + angle_obtuse (GTS_SEGMENT (e)->v2, f)); +} + +static gdouble cotan (GtsVertex * vo, GtsVertex * v1, GtsVertex * v2) +{ + /* cf. Appendix B of [Meyer et al 2002] */ + GtsVector u, v; + gdouble udotv, denom; + + gts_vector_init (u, GTS_POINT (vo), GTS_POINT (v1)); + gts_vector_init (v, GTS_POINT (vo), GTS_POINT (v2)); + + udotv = gts_vector_scalar (u, v); + denom = sqrt (gts_vector_scalar (u,u)*gts_vector_scalar (v,v) - + udotv*udotv); + + + /* denom can be zero if u==v. Returning 0 is acceptable, based on + * the callers of this function below. */ + if (denom == 0.0) return (0.0); + + return (udotv/denom); +} + +static gdouble angle_from_cotan (GtsVertex * vo, + GtsVertex * v1, GtsVertex * v2) +{ + /* cf. Appendix B and the caption of Table 1 from [Meyer et al 2002] */ + GtsVector u, v; + gdouble udotv, denom; + + gts_vector_init (u, GTS_POINT (vo), GTS_POINT (v1)); + gts_vector_init (v, GTS_POINT (vo), GTS_POINT (v2)); + + udotv = gts_vector_scalar (u, v); + denom = sqrt (gts_vector_scalar (u,u)*gts_vector_scalar (v,v) + - udotv*udotv); + + /* Note: I assume this is what they mean by using atan2 (). -Ray Jones */ + + /* tan = denom/udotv = y/x (see man page for atan2) */ + return (fabs (atan2 (denom, udotv))); +} + +static gdouble region_area (GtsVertex * v, GtsFace * f) +{ + /* cf. Section 3.3 of [Meyer et al 2002] */ + + if (gts_triangle_area (GTS_TRIANGLE (f)) == 0.0) return (0.0); + + if (triangle_obtuse (v, f)) { + if (angle_obtuse (v, f)) + return (gts_triangle_area (GTS_TRIANGLE (f))/2.0); + else + return (gts_triangle_area (GTS_TRIANGLE (f))/4.0); + } else { + GtsEdge * e = gts_triangle_edge_opposite (GTS_TRIANGLE (f), v); + + return ((cotan (GTS_SEGMENT (e)->v1, v, GTS_SEGMENT (e)->v2)* + gts_point_distance2 (GTS_POINT (v), + GTS_POINT (GTS_SEGMENT (e)->v2)) + + cotan (GTS_SEGMENT (e)->v2, v, GTS_SEGMENT (e)->v1)* + gts_point_distance2 (GTS_POINT (v), + GTS_POINT (GTS_SEGMENT (e)->v1))) + /8.0); + } +} + +/** + * gts_vertex_mean_curvature_normal: + * @v: a #GtsVertex. + * @s: a #GtsSurface. + * @Kh: the Mean Curvature Normal at @v. + * + * Computes the Discrete Mean Curvature Normal approximation at @v. + * The mean curvature at @v is half the magnitude of the vector @Kh. + * + * Note: the normal computed is not unit length, and may point either + * into or out of the surface, depending on the curvature at @v. It + * is the responsibility of the caller of the function to use the mean + * curvature normal appropriately. + * + * This approximation is from the paper: + * Discrete Differential-Geometry Operators for Triangulated 2-Manifolds + * Mark Meyer, Mathieu Desbrun, Peter Schroder, Alan H. Barr + * VisMath '02, Berlin (Germany) + * http://www-grail.usc.edu/pubs.html + * + * Returns: %TRUE if the operator could be evaluated, %FALSE if the + * evaluation failed for some reason (@v is boundary or is the + * endpoint of a non-manifold edge.) + */ +gboolean gts_vertex_mean_curvature_normal (GtsVertex * v, GtsSurface * s, + GtsVector Kh) +{ + GSList * faces, * edges, * i; + gdouble area = 0.0; + + g_return_val_if_fail (v != NULL, FALSE); + g_return_val_if_fail (s != NULL, FALSE); + + /* this operator is not defined for boundary edges */ + if (gts_vertex_is_boundary (v, s)) return (FALSE); + + faces = gts_vertex_faces (v, s, NULL); + g_return_val_if_fail (faces != NULL, FALSE); + + edges = gts_vertex_fan_oriented (v, s); + if (edges == NULL) { + g_slist_free (faces); + return (FALSE); + } + + i = faces; + while (i) { + GtsFace * f = i->data; + + area += region_area (v, f); + i = i->next; + } + g_slist_free (faces); + + Kh[0] = Kh[1] = Kh[2] = 0.0; + + i = edges; + while (i) { + GtsEdge * e = i->data; + GtsVertex * v1 = GTS_SEGMENT (e)->v1; + GtsVertex * v2 = GTS_SEGMENT (e)->v2; + gdouble temp; + + temp = cotan (v1, v, v2); + Kh[0] += temp*(GTS_POINT (v2)->x - GTS_POINT (v)->x); + Kh[1] += temp*(GTS_POINT (v2)->y - GTS_POINT (v)->y); + Kh[2] += temp*(GTS_POINT (v2)->z - GTS_POINT (v)->z); + + temp = cotan (v2, v, v1); + Kh[0] += temp*(GTS_POINT (v1)->x - GTS_POINT (v)->x); + Kh[1] += temp*(GTS_POINT (v1)->y - GTS_POINT (v)->y); + Kh[2] += temp*(GTS_POINT (v1)->z - GTS_POINT (v)->z); + + i = i->next; + } + g_slist_free (edges); + + if (area > 0.0) { + Kh[0] /= 2*area; + Kh[1] /= 2*area; + Kh[2] /= 2*area; + } else { + return (FALSE); + } + + return TRUE; +} + +/** + * gts_vertex_gaussian_curvature: + * @v: a #GtsVertex. + * @s: a #GtsSurface. + * @Kg: the Discrete Gaussian Curvature approximation at @v. + * + * Computes the Discrete Gaussian Curvature approximation at @v. + * + * This approximation is from the paper: + * Discrete Differential-Geometry Operators for Triangulated 2-Manifolds + * Mark Meyer, Mathieu Desbrun, Peter Schroder, Alan H. Barr + * VisMath '02, Berlin (Germany) + * http://www-grail.usc.edu/pubs.html + * + * Returns: %TRUE if the operator could be evaluated, %FALSE if the + * evaluation failed for some reason (@v is boundary or is the + * endpoint of a non-manifold edge.) + */ +gboolean gts_vertex_gaussian_curvature (GtsVertex * v, GtsSurface * s, + gdouble * Kg) +{ + GSList * faces, * edges, * i; + gdouble area = 0.0; + gdouble angle_sum = 0.0; + + g_return_val_if_fail (v != NULL, FALSE); + g_return_val_if_fail (s != NULL, FALSE); + g_return_val_if_fail (Kg != NULL, FALSE); + + /* this operator is not defined for boundary edges */ + if (gts_vertex_is_boundary (v, s)) return (FALSE); + + faces = gts_vertex_faces (v, s, NULL); + g_return_val_if_fail (faces != NULL, FALSE); + + edges = gts_vertex_fan_oriented (v, s); + if (edges == NULL) { + g_slist_free (faces); + return (FALSE); + } + + i = faces; + while (i) { + GtsFace * f = i->data; + + area += region_area (v, f); + i = i->next; + } + g_slist_free (faces); + + i = edges; + while (i) { + GtsEdge * e = i->data; + GtsVertex * v1 = GTS_SEGMENT (e)->v1; + GtsVertex * v2 = GTS_SEGMENT (e)->v2; + + angle_sum += angle_from_cotan (v, v1, v2); + i = i->next; + } + g_slist_free (edges); + + *Kg = (2.0*M_PI - angle_sum)/area; + + return TRUE; +} + +/** + * gts_vertex_principal_curvatures: + * @Kh: mean curvature. + * @Kg: Gaussian curvature. + * @K1: first principal curvature. + * @K2: second principal curvature. + * + * Computes the principal curvatures at a point given the mean and + * Gaussian curvatures at that point. + * + * The mean curvature can be computed as one-half the magnitude of the + * vector computed by gts_vertex_mean_curvature_normal(). + * + * The Gaussian curvature can be computed with + * gts_vertex_gaussian_curvature(). + */ +void gts_vertex_principal_curvatures (gdouble Kh, gdouble Kg, + gdouble * K1, gdouble * K2) +{ + gdouble temp = Kh*Kh - Kg; + + g_return_if_fail (K1 != NULL); + g_return_if_fail (K2 != NULL); + + if (temp < 0.0) temp = 0.0; + temp = sqrt (temp); + *K1 = Kh + temp; + *K2 = Kh - temp; +} + +/* from Maple */ +static void linsolve (gdouble m11, gdouble m12, gdouble b1, + gdouble m21, gdouble m22, gdouble b2, + gdouble * x1, gdouble * x2) +{ + gdouble temp; + + temp = 1.0 / (m21*m12 - m11*m22); + *x1 = (m12*b2 - m22*b1)*temp; + *x2 = (m11*b2 - m21*b1)*temp; +} + +/* from Maple - largest eigenvector of [a b; b c] */ +static void eigenvector (gdouble a, gdouble b, gdouble c, + GtsVector e) +{ + if (b == 0.0) { + e[0] = 0.0; + } else { + e[0] = -(c - a - sqrt (c*c - 2*a*c + a*a + 4*b*b))/(2*b); + } + e[1] = 1.0; + e[2] = 0.0; +} + +/** + * gts_vertex_principal_directions: + * @v: a #GtsVertex. + * @s: a #GtsSurface. + * @Kh: mean curvature normal (a #GtsVector). + * @Kg: Gaussian curvature (a gdouble). + * @e1: first principal curvature direction (direction of largest curvature). + * @e2: second principal curvature direction. + * + * Computes the principal curvature directions at a point given @Kh + * and @Kg, the mean curvature normal and Gaussian curvatures at that + * point, computed with gts_vertex_mean_curvature_normal() and + * gts_vertex_gaussian_curvature(), respectively. + * + * Note that this computation is very approximate and tends to be + * unstable. Smoothing of the surface or the principal directions may + * be necessary to achieve reasonable results. + */ +void gts_vertex_principal_directions (GtsVertex * v, GtsSurface * s, + GtsVector Kh, gdouble Kg, + GtsVector e1, GtsVector e2) +{ + GtsVector N; + gdouble normKh; + GSList * i, * j; + GtsVector basis1, basis2, d, eig; + gdouble ve2, vdotN; + gdouble aterm_da, bterm_da, cterm_da, const_da; + gdouble aterm_db, bterm_db, cterm_db, const_db; + gdouble a, b, c; + gdouble K1, K2; + gdouble *weights, *kappas, *d1s, *d2s; + gint edge_count; + gdouble err_e1, err_e2; + int e; + + /* compute unit normal */ + normKh = sqrt (gts_vector_scalar (Kh, Kh)); + + if (normKh > 0.0) { + N[0] = Kh[0] / normKh; + N[1] = Kh[1] / normKh; + N[2] = Kh[2] / normKh; + } else { + /* This vertex is a point of zero mean curvature (flat or saddle + * point). Compute a normal by averaging the adjacent triangles + */ + N[0] = N[1] = N[2] = 0.0; + i = gts_vertex_faces (v, s, NULL); + while (i) { + gdouble x, y, z; + gts_triangle_normal (GTS_TRIANGLE ((GtsFace *) i->data), + &x, &y, &z); + N[0] += x; + N[1] += y; + N[2] += z; + + i = i->next; + } + g_return_if_fail (gts_vector_norm (N) > 0.0); + gts_vector_normalize (N); + } + + + /* construct a basis from N: */ + /* set basis1 to any component not the largest of N */ + basis1[0] = basis1[1] = basis1[2] = 0.0; + if (fabs (N[0]) > fabs (N[1])) + basis1[1] = 1.0; + else + basis1[0] = 1.0; + + /* make basis2 orthogonal to N */ + gts_vector_cross (basis2, N, basis1); + gts_vector_normalize (basis2); + + /* make basis1 orthogonal to N and basis2 */ + gts_vector_cross (basis1, N, basis2); + gts_vector_normalize (basis1); + + aterm_da = bterm_da = cterm_da = const_da = 0.0; + aterm_db = bterm_db = cterm_db = const_db = 0.0; + + weights = g_malloc (sizeof (gdouble)*g_slist_length (v->segments)); + kappas = g_malloc (sizeof (gdouble)*g_slist_length (v->segments)); + d1s = g_malloc (sizeof (gdouble)*g_slist_length (v->segments)); + d2s = g_malloc (sizeof (gdouble)*g_slist_length (v->segments)); + edge_count = 0; + + i = v->segments; + while (i) { + GtsEdge * e; + GtsFace * f1, * f2; + gdouble weight, kappa, d1, d2; + GtsVector vec_edge; + + if (! GTS_IS_EDGE (i->data)) { + i = i->next; + continue; + } + + e = i->data; + + /* since this vertex passed the tests in + * gts_vertex_mean_curvature_normal(), this should be true. */ + g_assert (gts_edge_face_number (e, s) == 2); + + /* identify the two triangles bordering e in s */ + f1 = f2 = NULL; + j = e->triangles; + while (j) { + if ((! GTS_IS_FACE (j->data)) || + (! gts_face_has_parent_surface (GTS_FACE (j->data), s))) { + j = j->next; + continue; + } + if (f1 == NULL) + f1 = GTS_FACE (j->data); + else { + f2 = GTS_FACE (j->data); + break; + } + j = j->next; + } + g_assert (f2 != NULL); + + /* We are solving for the values of the curvature tensor + * B = [ a b ; b c ]. + * The computations here are from section 5 of [Meyer et al 2002]. + * + * The first step is to calculate the linear equations governing + * the values of (a,b,c). These can be computed by setting the + * derivatives of the error E to zero (section 5.3). + * + * Since a + c = norm(Kh), we only compute the linear equations + * for dE/da and dE/db. (NB: [Meyer et al 2002] has the + * equation a + b = norm(Kh), but I'm almost positive this is + * incorrect.) + * + * Note that the w_ij (defined in section 5.2) are all scaled by + * (1/8*A_mixed). We drop this uniform scale factor because the + * solution of the linear equations doesn't rely on it. + * + * The terms of the linear equations are xterm_dy with x in + * {a,b,c} and y in {a,b}. There are also const_dy terms that are + * the constant factors in the equations. + */ + + /* find the vector from v along edge e */ + gts_vector_init (vec_edge, GTS_POINT (v), + GTS_POINT ((GTS_SEGMENT (e)->v1 == v) ? + GTS_SEGMENT (e)->v2 : GTS_SEGMENT (e)->v1)); + ve2 = gts_vector_scalar (vec_edge, vec_edge); + vdotN = gts_vector_scalar (vec_edge, N); + + /* section 5.2 - There is a typo in the computation of kappa. The + * edges should be x_j-x_i. + */ + kappa = 2.0 * vdotN / ve2; + + /* section 5.2 */ + + /* I don't like performing a minimization where some of the + * weights can be negative (as can be the case if f1 or f2 are + * obtuse). To ensure all-positive weights, we check for + * obtuseness and use values similar to those in region_area(). */ + weight = 0.0; + if (! triangle_obtuse(v, f1)) { + weight += ve2 * + cotan (gts_triangle_vertex_opposite (GTS_TRIANGLE (f1), e), + GTS_SEGMENT (e)->v1, GTS_SEGMENT (e)->v2) / 8.0; + } else { + if (angle_obtuse (v, f1)) { + weight += ve2 * gts_triangle_area (GTS_TRIANGLE (f1)) / 4.0; + } else { + weight += ve2 * gts_triangle_area (GTS_TRIANGLE (f1)) / 8.0; + } + } + + if (! triangle_obtuse(v, f2)) { + weight += ve2 * + cotan (gts_triangle_vertex_opposite (GTS_TRIANGLE (f2), e), + GTS_SEGMENT (e)->v1, GTS_SEGMENT (e)->v2) / 8.0; + } else { + if (angle_obtuse (v, f2)) { + weight += ve2 * gts_triangle_area (GTS_TRIANGLE (f2)) / 4.0; + } else { + weight += ve2 * gts_triangle_area (GTS_TRIANGLE (f2)) / 8.0; + } + } + + /* projection of edge perpendicular to N (section 5.3) */ + d[0] = vec_edge[0] - vdotN * N[0]; + d[1] = vec_edge[1] - vdotN * N[1]; + d[2] = vec_edge[2] - vdotN * N[2]; + gts_vector_normalize (d); + + /* not explicit in the paper, but necessary. Move d to 2D basis. */ + d1 = gts_vector_scalar (d, basis1); + d2 = gts_vector_scalar (d, basis2); + + /* store off the curvature, direction of edge, and weights for later use */ + weights[edge_count] = weight; + kappas[edge_count] = kappa; + d1s[edge_count] = d1; + d2s[edge_count] = d2; + edge_count++; + + /* Finally, update the linear equations */ + aterm_da += weight * d1 * d1 * d1 * d1; + bterm_da += weight * d1 * d1 * 2 * d1 * d2; + cterm_da += weight * d1 * d1 * d2 * d2; + const_da += weight * d1 * d1 * (- kappa); + + aterm_db += weight * d1 * d2 * d1 * d1; + bterm_db += weight * d1 * d2 * 2 * d1 * d2; + cterm_db += weight * d1 * d2 * d2 * d2; + const_db += weight * d1 * d2 * (- kappa); + + i = i->next; + } + + /* now use the identity (Section 5.3) a + c = |Kh| = 2 * kappa_h */ + aterm_da -= cterm_da; + const_da += cterm_da * normKh; + + aterm_db -= cterm_db; + const_db += cterm_db * normKh; + + /* check for solvability of the linear system */ + if (((aterm_da * bterm_db - aterm_db * bterm_da) != 0.0) && + ((const_da != 0.0) || (const_db != 0.0))) { + linsolve (aterm_da, bterm_da, -const_da, + aterm_db, bterm_db, -const_db, + &a, &b); + + c = normKh - a; + + eigenvector (a, b, c, eig); + } else { + /* region of v is planar */ + eig[0] = 1.0; + eig[1] = 0.0; + } + + /* Although the eigenvectors of B are good estimates of the + * principal directions, it seems that which one is attached to + * which curvature direction is a bit arbitrary. This may be a bug + * in my implementation, or just a side-effect of the inaccuracy of + * B due to the discrete nature of the sampling. + * + * To overcome this behavior, we'll evaluate which assignment best + * matches the given eigenvectors by comparing the curvature + * estimates computed above and the curvatures calculated from the + * discrete differential operators. */ + + gts_vertex_principal_curvatures (0.5 * normKh, Kg, &K1, &K2); + + err_e1 = err_e2 = 0.0; + /* loop through the values previously saved */ + for (e = 0; e < edge_count; e++) { + gdouble weight, kappa, d1, d2; + gdouble temp1, temp2; + gdouble delta; + + weight = weights[e]; + kappa = kappas[e]; + d1 = d1s[e]; + d2 = d2s[e]; + + temp1 = fabs (eig[0] * d1 + eig[1] * d2); + temp1 = temp1 * temp1; + temp2 = fabs (eig[1] * d1 - eig[0] * d2); + temp2 = temp2 * temp2; + + /* err_e1 is for K1 associated with e1 */ + delta = K1 * temp1 + K2 * temp2 - kappa; + err_e1 += weight * delta * delta; + + /* err_e2 is for K1 associated with e2 */ + delta = K2 * temp1 + K1 * temp2 - kappa; + err_e2 += weight * delta * delta; + } + g_free (weights); + g_free (kappas); + g_free (d1s); + g_free (d2s); + + /* rotate eig by a right angle if that would decrease the error */ + if (err_e2 < err_e1) { + gdouble temp = eig[0]; + + eig[0] = eig[1]; + eig[1] = -temp; + } + + e1[0] = eig[0] * basis1[0] + eig[1] * basis2[0]; + e1[1] = eig[0] * basis1[1] + eig[1] * basis2[1]; + e1[2] = eig[0] * basis1[2] + eig[1] * basis2[2]; + gts_vector_normalize (e1); + + /* make N,e1,e2 a right handed coordinate sytem */ + gts_vector_cross (e2, N, e1); + gts_vector_normalize (e2); +} Index: trunk/src_3rd/gts/edge.c =================================================================== --- trunk/src_3rd/gts/edge.c (nonexistent) +++ trunk/src_3rd/gts/edge.c (revision 1065) @@ -0,0 +1,582 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "gts.h" + +gboolean gts_allow_floating_edges = FALSE; + +static void edge_destroy (GtsObject * object) +{ + GtsEdge * edge = GTS_EDGE (object); + GSList * i; + + i = edge->triangles; + while (i) { + GSList * next = i->next; + gts_object_destroy (i->data); + i = next; + } + g_assert (edge->triangles == NULL); + + (* GTS_OBJECT_CLASS (gts_edge_class ())->parent_class->destroy) (object); +} + +static void edge_clone (GtsObject * clone, GtsObject * object) +{ + (* GTS_OBJECT_CLASS (gts_edge_class ())->parent_class->clone) (clone, + object); + GTS_SEGMENT (clone)->v1 = GTS_SEGMENT (clone)->v2 = NULL; + GTS_EDGE (clone)->triangles = NULL; +} + +static void edge_class_init (GtsObjectClass * klass) +{ + klass->clone = edge_clone; + klass->destroy = edge_destroy; +} + +static void edge_init (GtsEdge * edge) +{ + edge->triangles = NULL; +} + +/** + * gts_edge_class: + * + * Returns: the #GtsEdgeClass. + */ +GtsEdgeClass * gts_edge_class (void) +{ + static GtsEdgeClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo edge_info = { + "GtsEdge", + sizeof (GtsEdge), + sizeof (GtsEdgeClass), + (GtsObjectClassInitFunc) edge_class_init, + (GtsObjectInitFunc) edge_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_segment_class ()), + &edge_info); + } + + return klass; +} + +/** + * gts_edge_new: + * @klass: a #GtsEdgeClass. + * @v1: a #GtsVertex. + * @v2: a #GtsVertex. + * + * Returns: a new #GtsEdge linking @v1 and @v2. + */ +GtsEdge * gts_edge_new (GtsEdgeClass * klass, + GtsVertex * v1, GtsVertex * v2) +{ + return GTS_EDGE (gts_segment_new (GTS_SEGMENT_CLASS (klass), v1, v2)); +} + +void gts_edge_remove(GtsEdge *edge) +{ + edge->segment.v1->segments = g_slist_remove(edge->segment.v1->segments, &edge->segment); + edge->segment.v2->segments = g_slist_remove(edge->segment.v2->segments, &edge->segment); + edge_destroy(GTS_OBJECT (edge)); +} + +/** + * gts_edge_replace: + * @e: a #GtsEdge. + * @with: a #GtsEdge. + * + * Replaces @e with @with. For each triangle which uses @e as an + * edge, @e is replaced with @with. The @with->triangles list is + * updated appropriately and the @e->triangles list is freed and set + * to %NULL. + */ +void gts_edge_replace (GtsEdge * e, GtsEdge * with) +{ + GSList * i; + + g_return_if_fail (e != NULL && with != NULL && e != with); + + i = e->triangles; + while (i) { + GtsTriangle * t = i->data; + if (t->e1 == e) t->e1 = with; + if (t->e2 == e) t->e2 = with; + if (t->e3 == e) t->e3 = with; + if (!g_slist_find (with->triangles, t)) + with->triangles = g_slist_prepend (with->triangles, t); + i = i->next; + } + g_slist_free (e->triangles); + e->triangles = NULL; +} + +/** + * gts_edge_has_parent_surface: + * @e: a #GtsEdge. + * @surface: a #GtsSurface. + * + * Returns: a #GtsFace of @surface having @e as an edge, %NULL otherwise. + */ +GtsFace * gts_edge_has_parent_surface (GtsEdge * e, GtsSurface * surface) +{ + GSList * i; + + g_return_val_if_fail (e != NULL, NULL); + + i = e->triangles; + while (i) { + if (GTS_IS_FACE (i->data) && + gts_face_has_parent_surface (i->data, surface)) + return i->data; + i = i->next; + } + return NULL; +} + +/** + * gts_edge_has_any_parent_surface: + * @e: a #GtsEdge. + * + * Returns: %NULL if @e is not an edge of any triangle or if all the + * faces having @e has an edge do not belong to any surface, + * a #GtsFace belonging to a surface and having @e as an edge. + */ +GtsFace * gts_edge_has_any_parent_surface (GtsEdge * e) +{ + GSList * i; + + g_return_val_if_fail (e != NULL, NULL); + + i = e->triangles; + while (i) { + GtsTriangle * t = i->data; + if (GTS_IS_FACE (t) && GTS_FACE (t)->surfaces != NULL) + return GTS_FACE (t); + i = i->next; + } + return NULL; +} + +/** + * gts_edge_is_boundary: + * @e: a #GtsEdge. + * @surface: a #GtsSurface or %NULL. + * + * Returns: the unique #GtsFace (which belongs to @surface) and which + * has @e as an edge (i.e. @e is a boundary edge (of @surface)) or %NULL + * if there is more than one or no faces (belonging to @surface) and + * with @e as an edge. + */ +GtsFace * gts_edge_is_boundary (GtsEdge * e, GtsSurface * surface) +{ + GSList * i; + GtsFace * f = NULL; + + g_return_val_if_fail (e != NULL, NULL); + + i = e->triangles; + while (i) { + if (GTS_IS_FACE (i->data)) { + if (!surface || gts_face_has_parent_surface (i->data, surface)) { + if (f != NULL) + return NULL; + f = i->data; + } + } + i = i->next; + } + return f; +} + +/** + * gts_edges_from_vertices: + * @vertices: a list of #GtsVertex. + * @parent: a #GtsSurface. + * + * Returns: a list of unique #GtsEdge which have one of their vertices in + * @vertices and are used by a face of @parent. + */ +GSList * gts_edges_from_vertices (GSList * vertices, GtsSurface * parent) +{ + GHashTable * hash; + GSList * edges = NULL, * i; + + g_return_val_if_fail (parent != NULL, NULL); + + hash = g_hash_table_new (NULL, NULL); + i = vertices; + while (i) { + GSList * j = GTS_VERTEX (i->data)->segments; + while (j) { + GtsSegment * s = j->data; + if (GTS_IS_EDGE (s) && + gts_edge_has_parent_surface (GTS_EDGE (s), parent) && + g_hash_table_lookup (hash, s) == NULL) { + edges = g_slist_prepend (edges, s); + g_hash_table_insert (hash, s, i); + } + j = j->next; + } + i = i->next; + } + g_hash_table_destroy (hash); + return edges; +} + +/** + * gts_edge_face_number: + * @e: a #GtsEdge. + * @s: a #GtsSurface. + * + * Returns: the number of faces using @e and belonging to @s. + */ +guint gts_edge_face_number (GtsEdge * e, GtsSurface * s) +{ + GSList * i; + guint nt = 0; + + g_return_val_if_fail (e != NULL, 0); + g_return_val_if_fail (s != NULL, 0); + + i = e->triangles; + while (i) { + if (GTS_IS_FACE (i->data) && + gts_face_has_parent_surface (GTS_FACE (i->data), s)) + nt++; + i = i->next; + } + return nt; +} + +/** + * gts_edge_is_duplicate: + * @e: a #GtsEdge. + * + * Returns: the first #GtsEdge different from @e which shares the + * same endpoints or %NULL if there is none. + */ +GtsEdge * gts_edge_is_duplicate (GtsEdge * e) +{ + GSList * i; + GtsVertex * v2; + + g_return_val_if_fail (e != NULL, NULL); + + v2 = GTS_SEGMENT (e)->v2; + i = GTS_SEGMENT (e)->v1->segments; + if (GTS_SEGMENT (e)->v1 == v2) /* e is degenerate: special treatment */ + while (i) { + GtsSegment * s = i->data; + if (s != GTS_SEGMENT (e) && + GTS_IS_EDGE (s) && + s->v1 == v2 && s->v2 == v2) + return GTS_EDGE (s); + i = i->next; + } + else /* e is not degenerate */ + while (i) { + GtsSegment * s = i->data; + if (s != GTS_SEGMENT (e) && + GTS_IS_EDGE (s) && + (s->v1 == v2 || s->v2 == v2)) + return GTS_EDGE (s); + i = i->next; + } + return NULL; +} + +/** + * gts_edges_merge: + * @edges: a list of #GtsEdge. + * + * For each edge in @edges check if it is duplicated (as + * returned by gts_edge_is_duplicate()). If it is replace it by its + * duplicate, destroy it and remove it from the list. + * + * Returns: the updated @edges list. + */ +GList * gts_edges_merge (GList * edges) +{ + GList * i = edges; + + /* we want to control edge destruction */ + gts_allow_floating_edges = TRUE; + while (i) { + GtsEdge * e = i->data; + GtsEdge * de = gts_edge_is_duplicate (e); + if (de) { + GList * next = i->next; + edges = g_list_remove_link (edges, i); + g_list_free_1 (i); + i = next; + gts_edge_replace (e, de); + gts_object_destroy (GTS_OBJECT (e)); + } + else + i = i->next; + } + gts_allow_floating_edges = FALSE;; + + return edges; +} + +static void triangle_vertices_edges (GtsTriangle * t, + GtsEdge * e, + GtsVertex ** v, + GtsEdge ** ee1, + GtsEdge ** ee2) +{ + GtsEdge * e1 = t->e1, * e2 = t->e2, * e3 = t->e3; + GtsVertex * v1 = GTS_SEGMENT (e)->v1; + + if (e1 == e) e1 = e3; + else if (e2 == e) e2 = e3; + else g_assert (e3 == e); + + if (GTS_SEGMENT (e2)->v1 == v1 || GTS_SEGMENT (e2)->v2 == v1) { + e3 = e1; e1 = e2; e2 = e3; + } + if (GTS_SEGMENT (e1)->v1 == v1) + *v = GTS_SEGMENT (e1)->v2; + else + *v = GTS_SEGMENT (e1)->v1; + *ee1 = e1; + *ee2 = e2; +} + +/** + * gts_edge_belongs_to_tetrahedron: + * @e: a #GtsEdge. + * + * Returns: %TRUE if @e is used by faces forming a tetrahedron, %FALSE + * otherwise. + */ +gboolean gts_edge_belongs_to_tetrahedron (GtsEdge * e) +{ + GSList * i; + + g_return_val_if_fail (e != NULL, FALSE); + + i = e->triangles; + while (i) { + GtsEdge * e1, * e2; + GtsVertex * vt1; + GSList * j = i->next; + triangle_vertices_edges (i->data, e, &vt1, &e1, &e2); + while (j) { + GtsSegment * s5; + GtsEdge * e3, * e4; + GtsVertex * vt2; + + triangle_vertices_edges (j->data, e, &vt2, &e3, &e4); + s5 = gts_vertices_are_connected (vt1, vt2); + if (GTS_IS_EDGE (s5) && + gts_triangle_use_edges (e1, e3, GTS_EDGE (s5)) && + gts_triangle_use_edges (e2, e4, GTS_EDGE (s5))) + return TRUE; + j = j->next; + } + i = i->next; + } + + return FALSE; +} + +#define edge_use_vertex(e, v) (GTS_SEGMENT(e)->v1 == v ||\ + GTS_SEGMENT(e)->v2 == v) + +static GtsEdge * next_edge (GtsTriangle * t, + GtsEdge * e1, + GtsEdge * e) +{ + GtsVertex * v1 = GTS_SEGMENT (e)->v1; + GtsVertex * v2 = GTS_SEGMENT (e)->v2; + + if (t->e1 != e1 && t->e1 != e && + (edge_use_vertex (t->e1, v1) || edge_use_vertex (t->e1, v2))) + return t->e1; + else if (t->e2 != e1 && t->e2 != e && + (edge_use_vertex (t->e2, v1) || edge_use_vertex (t->e2, v2))) + return t->e2; + else if (t->e3 != e1 && t->e3 != e && + (edge_use_vertex (t->e3, v1) || edge_use_vertex (t->e3, v2))) + return t->e3; + g_assert_not_reached (); + return NULL; +} + +static void triangle_next (GtsEdge * e1, GtsEdge * e) +{ + GSList * i; + + i = e1->triangles; + while (i) { + GtsTriangle * t = i->data; + if (GTS_OBJECT (t)->reserved) { + GTS_OBJECT (t)->reserved = NULL; + triangle_next (next_edge (t, e1, e), e); + } + i = i->next; + } +} + +/** + * gts_edge_is_contact: + * @e: a #GtsEdge. + * + * Returns: the number of sets of connected triangles sharing @e as a + * contact edge. + */ +guint gts_edge_is_contact (GtsEdge * e) +{ + GSList * i, * triangles; + guint ncomponent = 0; + + g_return_val_if_fail (e != NULL, 0); + + triangles = gts_vertex_triangles (GTS_SEGMENT (e)->v1, NULL); + i = triangles = gts_vertex_triangles (GTS_SEGMENT (e)->v2, triangles); + while (i) { + GTS_OBJECT (i->data)->reserved = i; + i = i->next; + } + + i = e->triangles; + while (i) { + GtsTriangle * t = i->data; + if (GTS_OBJECT (t)->reserved) { + GtsEdge * e1; + GTS_OBJECT (t)->reserved = NULL; + e1 = next_edge (t, NULL, e); + triangle_next (e1, e); + triangle_next (next_edge (t, e1, e), e); + ncomponent++; + } + i = i->next; + } + + g_slist_foreach (triangles, (GFunc) gts_object_reset_reserved, NULL); + g_slist_free (triangles); + + return ncomponent; +} + +/** + * gts_edge_swap: + * @e: a #GtsEdge. + * @s: a #GtsSurface. + * + * Performs an "edge swap" on the two triangles sharing @e and + * belonging to @s. + */ +void gts_edge_swap (GtsEdge * e, GtsSurface * s) +{ + GtsTriangle * t1 = NULL, * t2 = NULL, * t; + GtsFace * f; + GSList * i; + GtsVertex * v1, * v2, * v3, * v4, * v5, * v6; + GtsEdge * e1, * e2, * e3, * e4; + GtsSegment * v3v6; + + g_return_if_fail (e != NULL); + g_return_if_fail (s != NULL); + + i = e->triangles; + while (i) { + if (GTS_IS_FACE (i->data) && gts_face_has_parent_surface (i->data, s)) { + if (!t1) + t1 = i->data; + else if (!t2) + t2 = i->data; + else + g_return_if_fail (gts_edge_face_number (e, s) == 2); + } + i = i->next; + } + g_assert (t1 && t2); + + gts_triangle_vertices_edges (t1, e, &v1, &v2, &v3, &e, &e1, &e2); + gts_triangle_vertices_edges (t2, e, &v4, &v5, &v6, &e, &e3, &e4); + g_assert (v2 == v4 && v1 == v5); + + v3v6 = gts_vertices_are_connected (v3, v6); + if (!GTS_IS_EDGE (v3v6)) + v3v6 = GTS_SEGMENT (gts_edge_new (s->edge_class, v3, v6)); + f = gts_face_new (s->face_class, e1, GTS_EDGE (v3v6), e4); + if ((t = gts_triangle_is_duplicate (GTS_TRIANGLE (f))) && + GTS_IS_FACE (t)) { + gts_object_destroy (GTS_OBJECT (f)); + f = GTS_FACE (t); + } + gts_surface_add_face (s, f); + + f = gts_face_new (s->face_class, GTS_EDGE (v3v6), e2, e3); + if ((t = gts_triangle_is_duplicate (GTS_TRIANGLE (f))) && + GTS_IS_FACE (t)) { + gts_object_destroy (GTS_OBJECT (f)); + f = GTS_FACE (t); + } + gts_surface_add_face (s, f); + + gts_surface_remove_face (s, GTS_FACE (t1)); + gts_surface_remove_face (s, GTS_FACE (t2)); +} + +/** + * gts_edge_manifold_faces: + * @e: a #GtsEdge. + * @s: a #GtsSurface. + * @f1: pointer for first face. + * @f2: pointer for second face. + * + * If @e is a manifold edge of surface @s, fills @f1 and @f2 with the + * faces belonging to @s and sharing @e. + * + * Returns: %TRUE if @e is a manifold edge, %FALSE otherwise. + */ +gboolean gts_edge_manifold_faces (GtsEdge * e, GtsSurface * s, + GtsFace ** f1, GtsFace ** f2) +{ + GSList * i; + + g_return_val_if_fail (e != NULL, FALSE); + g_return_val_if_fail (s != NULL, FALSE); + g_return_val_if_fail (f1 != NULL, FALSE); + g_return_val_if_fail (f2 != NULL, FALSE); + + *f1 = *f2 = NULL; + i = e->triangles; + while (i) { + if (GTS_IS_FACE (i->data) && gts_face_has_parent_surface (i->data, s)) { + if (!(*f1)) *f1 = i->data; + else if (!(*f2)) *f2 = i->data; + else return FALSE; + } + i = i->next; + } + + return (*f1 && *f2); +} Index: trunk/src_3rd/gts/eheap.c =================================================================== --- trunk/src_3rd/gts/eheap.c (nonexistent) +++ trunk/src_3rd/gts/eheap.c (revision 1065) @@ -0,0 +1,461 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "gts.h" + +#define PARENT(i) ((i) >= 2 ? (i)/2 : 0) +#define LEFT_CHILD(i) (2*(i)) +#define RIGHT_CHILD(i) (2*(i) + 1) + + +/** + * gts_eheap_new: + * @key_func: a #GtsKeyFunc or %NULL. + * @data: user data to be passed to @key_func. + * + * Returns: a new #GtsEHeap using @key_func as key. + */ +GtsEHeap * gts_eheap_new (GtsKeyFunc key_func, + gpointer data) +{ + GtsEHeap * heap; + + heap = g_malloc (sizeof(GtsEHeap)); + heap->elts = g_ptr_array_new (); + heap->func = key_func; + heap->data = data; + heap->frozen = FALSE; + heap->randomized = FALSE; + return heap; +} + +static void sift_up (GtsEHeap * heap, guint i) +{ + GtsEHeapPair * parent, * child; + guint p; + gpointer * pdata = heap->elts->pdata; + gdouble key; + + child = pdata[i - 1]; + key = child->key; + while ((p = PARENT (i))) { + parent = pdata[p - 1]; + if (parent->key > key || + (heap->randomized && parent->key == key && rand () < RAND_MAX/2)) { + pdata[p - 1] = child; + pdata[i - 1] = parent; + child->pos = p; + parent->pos = i; + i = p; + } + else + i = 0; + } +} + +/** + * gts_eheap_insert: + * @heap: a #GtsEHeap. + * @p: a pointer to add to the heap. + * + * Inserts a new element @p in the heap. + * + * Returns: a #GtsEHeapPair describing the position of the element in the heap. + * This pointer is necessary for gts_eheap_remove() and + * gts_eheap_decrease_key(). + */ +GtsEHeapPair * gts_eheap_insert (GtsEHeap * heap, gpointer p) +{ + GtsEHeapPair * pair; + GPtrArray * elts; + + g_return_val_if_fail (heap != NULL, NULL); + g_return_val_if_fail (heap->func != NULL, NULL); + + elts = heap->elts; + pair = g_malloc (sizeof (GtsEHeapPair)); + g_ptr_array_add (elts, pair); + pair->data = p; + pair->pos = elts->len; + pair->key = (*heap->func) (p, heap->data); + if (!heap->frozen) + sift_up (heap, elts->len); + return pair; +} + +/** + * gts_eheap_insert_with_key: + * @heap: a #GtsEHeap. + * @p: a pointer to add to the heap. + * @key: the value of the key associated to @p. + * + * Inserts a new element @p in the heap. + * + * Returns: a #GtsEHeapPair describing the position of the element in the heap. + * This pointer is necessary for gts_eheap_remove() and + * gts_eheap_decrease_key(). + */ +GtsEHeapPair * gts_eheap_insert_with_key (GtsEHeap * heap, + gpointer p, + gdouble key) +{ + GtsEHeapPair * pair; + GPtrArray * elts; + + g_return_val_if_fail (heap != NULL, NULL); + + elts = heap->elts; + pair = g_malloc (sizeof (GtsEHeapPair)); + g_ptr_array_add (elts, pair); + pair->data = p; + pair->pos = elts->len; + pair->key = key; + if (!heap->frozen) + sift_up (heap, elts->len); + return pair; +} + +static void sift_down (GtsEHeap * heap, guint i) +{ + GtsEHeapPair * left_child, * right_child, * child, * parent; + guint lc, rc, c; + gpointer * pdata = heap->elts->pdata; + guint len = heap->elts->len; + gdouble key; + + lc = LEFT_CHILD (i); + rc = RIGHT_CHILD (i); + left_child = lc <= len ? pdata[lc - 1] : NULL; + right_child = rc <= len ? pdata[rc - 1] : NULL; + + parent = pdata[i - 1]; + key = parent->key; + while (left_child != NULL) { + if (right_child == NULL || left_child->key < right_child->key) { + child = left_child; + c = lc; + } + else { + child = right_child; + c = rc; + } + if (key > child->key) { + pdata[i - 1] = child; + child->pos = i; + pdata[c - 1] = parent; + parent->pos = c; + i = c; + lc = LEFT_CHILD (i); + rc = RIGHT_CHILD (i); + left_child = lc <= len ? pdata[lc - 1] : NULL; + right_child = rc <= len ? pdata[rc - 1] : NULL; + } + else + left_child = NULL; + } +} + +/** + * gts_eheap_remove_top: + * @heap: a #GtsEHeap. + * @key: a pointer on a gdouble or %NULL. + * + * Removes the element at the top of the heap and optionally (if @key is not + * %NULL) returns the value of its key. + * + * Returns: the element at the top of the heap. + */ +gpointer gts_eheap_remove_top (GtsEHeap * heap, gdouble * key) +{ + gpointer root; + GPtrArray * elts; + guint len; + GtsEHeapPair * pair; + + g_return_val_if_fail (heap != NULL, NULL); + + elts = heap->elts; + len = elts->len; + + if (len == 0) + return NULL; + if (len == 1) { + pair = g_ptr_array_remove_index (elts, 0); + root = pair->data; + if (key) + *key = pair->key; + g_free (pair); + return root; + } + + pair = elts->pdata[0]; + root = pair->data; + if (key) + *key = pair->key; + g_free (pair); + pair = g_ptr_array_remove_index (elts, len - 1); + elts->pdata[0] = pair; + pair->pos = 1; + sift_down (heap, 1); + return root; +} + +/** + * gts_eheap_top: + * @heap: a #GtsEHeap. + * @key: a pointer on a gdouble or %NULL. + * + * Returns: the element at the top of the heap and optionally (if @key is not + * %NULL) its key. + */ +gpointer gts_eheap_top (GtsEHeap * heap, gdouble * key) +{ + GtsEHeapPair * pair; + GPtrArray * elts; + + g_return_val_if_fail (heap != NULL, NULL); + + elts = heap->elts; + + if (elts->len == 0) + return NULL; + + pair = elts->pdata[0]; + if (key) + *key = pair->key; + return pair->data; +} + +/** + * gts_eheap_destroy: + * @heap: a #GtsEHeap. + * + * Free all the memory allocated for @heap. + */ +void gts_eheap_destroy (GtsEHeap * heap) +{ + guint i; + + g_return_if_fail (heap != NULL); + + for (i = 0; i < heap->elts->len; i++) + g_free (heap->elts->pdata[i]); + g_ptr_array_free (heap->elts, TRUE); + g_free (heap); +} + +/** + * gts_eheap_thaw: + * @heap: a #GtsEHeap. + * + * If @heap has been frozen previously using gts_eheap_freeze(), reorder it + * in O(n) time and unfreeze it. + */ +void gts_eheap_thaw (GtsEHeap * heap) +{ + guint i; + + g_return_if_fail (heap != NULL); + + if (!heap->frozen) + return; + + for (i = heap->elts->len/2; i > 0; i--) + sift_down (heap, i); + + heap->frozen = FALSE; +} + +/** + * gts_eheap_foreach: + * @heap: a #GtsEHeap. + * @func: the function to call for each element in the heap. + * @data: to pass to @func. + */ +void gts_eheap_foreach (GtsEHeap * heap, + GFunc func, + gpointer data) +{ + guint i; + GPtrArray * elts; + + g_return_if_fail (heap != NULL); + g_return_if_fail (func != NULL); + + elts = heap->elts; + for (i = 0; i < elts->len; i++) + (*func) (((GtsEHeapPair *) elts->pdata[i])->data, data); +} + +/** + * gts_eheap_remove: + * @heap: a #GtsEHeap. + * @p: a #GtsEHeapPair. + * + * Removes element corresponding to @p from @heap in O(log n). + * + * Returns: the element just removed from @heap. + */ +gpointer gts_eheap_remove (GtsEHeap * heap, GtsEHeapPair * p) +{ + GtsEHeapPair ** pdata; + GtsEHeapPair * parent; + guint i, par; + gpointer data; + + g_return_val_if_fail (heap != NULL, NULL); + g_return_val_if_fail (p != NULL, NULL); + + pdata = (GtsEHeapPair **)heap->elts->pdata; + i = p->pos; + data = p->data; + + g_return_val_if_fail (i > 0 && i <= heap->elts->len, NULL); + g_return_val_if_fail (p == pdata[i - 1], NULL); + + /* move element to the top */ + while ((par = PARENT (i))) { + parent = pdata[par - 1]; + pdata[par - 1] = p; + pdata[i - 1] = parent; + p->pos = par; + parent->pos = i; + i = par; + } + + gts_eheap_remove_top (heap, NULL); + + return data; +} + +/** + * gts_eheap_decrease_key: + * @heap: a #GtsEHeap. + * @p: a #GtsEHeapPair. + * @new_key: the new value of the key for this element. Must be smaller than + * the current key. + * + * Decreases the value of the key of the element at position @p. + */ +void gts_eheap_decrease_key (GtsEHeap * heap, + GtsEHeapPair * p, + gdouble new_key) +{ + guint i; + + g_return_if_fail (heap != NULL); + g_return_if_fail (p != NULL); + + i = p->pos; + g_return_if_fail (i > 0 && i <= heap->elts->len); + g_return_if_fail (p == heap->elts->pdata[i - 1]); + + g_return_if_fail (new_key <= p->key); + + p->key = new_key; + if (!heap->frozen) + sift_up (heap, i); +} + +/** + * gts_eheap_freeze: + * @heap: a #GtsEHeap. + * + * Freezes the heap. Any subsequent operation will not preserve the heap + * property. Used in conjunction with gts_eheap_insert() and gts_eheap_thaw() + * to create a heap in O(n) time. + */ +void gts_eheap_freeze (GtsEHeap * heap) +{ + g_return_if_fail (heap != NULL); + + heap->frozen = TRUE; +} + +/** + * gts_eheap_size: + * @heap: a #GtsEHeap. + * + * Returns: the number of items in @heap. + */ +guint gts_eheap_size (GtsEHeap * heap) +{ + g_return_val_if_fail (heap != NULL, 0); + + return heap->elts->len; +} + +/** + * gts_eheap_update: + * @heap: a #GtsEHeap. + * + * Updates the key of each element of @heap and reorders it. + */ +void gts_eheap_update (GtsEHeap * heap) +{ + guint i, len; + GtsEHeapPair ** pairs; + gpointer data; + GtsKeyFunc func; + + g_return_if_fail (heap != NULL); + g_return_if_fail (heap->func != NULL); + + heap->frozen = TRUE; + + len = heap->elts->len; + pairs = (GtsEHeapPair **) heap->elts->pdata; + data = heap->data; + func = heap->func; + + for (i = 0; i < len; i++) { + GtsEHeapPair * pair = pairs[i]; + pair->key = (*func) (pair->data, data); + } + + gts_eheap_thaw (heap); +} + +/** + * gts_eheap_key: + * @heap: a #GtsEHeap. + * @p: a pointer to be tested; + * + * Returns: the value of the key for pointer @p. + */ +gdouble gts_eheap_key (GtsEHeap * heap, gpointer p) +{ + g_return_val_if_fail (heap != NULL, 0.); + g_return_val_if_fail (heap->func != NULL, 0.); + + return (* heap->func) (p, heap->data); +} + +/** + * gts_eheap_randomized: + * @heap: a #GtsEHeap. + * @randomized: whether @heap should be randomized. + */ +void gts_eheap_randomized (GtsEHeap * heap, gboolean randomized) +{ + g_return_if_fail (heap != NULL); + + heap->randomized = randomized; +} Index: trunk/src_3rd/gts/face.c =================================================================== --- trunk/src_3rd/gts/face.c (nonexistent) +++ trunk/src_3rd/gts/face.c (revision 1065) @@ -0,0 +1,297 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "gts.h" + +gboolean gts_allow_floating_faces = FALSE; + +static void face_destroy (GtsObject * object) +{ + GtsFace * face = GTS_FACE (object); + GSList * i; + + i = face->surfaces; + while (i) { + GSList * next = i->next; + gts_surface_remove_face (i->data, face); + i = next; + } + g_assert (face->surfaces == NULL); + + (* GTS_OBJECT_CLASS (gts_face_class ())->parent_class->destroy) (object); +} + +static void face_clone (GtsObject * clone, GtsObject * object) +{ + (* GTS_OBJECT_CLASS (gts_face_class ())->parent_class->clone) (clone, + object); + GTS_FACE (clone)->surfaces = NULL; +} + +static void face_class_init (GtsFaceClass * klass) +{ + GTS_OBJECT_CLASS (klass)->clone = face_clone; + GTS_OBJECT_CLASS (klass)->destroy = face_destroy; +} + +static void face_init (GtsFace * face) +{ + face->surfaces = NULL; +} + +/** + * gts_face_class: + * + * Returns: the #GtsFaceClass. + */ +GtsFaceClass * gts_face_class (void) +{ + static GtsFaceClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo face_info = { + "GtsFace", + sizeof (GtsFace), + sizeof (GtsFaceClass), + (GtsObjectClassInitFunc) face_class_init, + (GtsObjectInitFunc) face_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_triangle_class ()), + &face_info); + } + + return klass; +} + +/** + * gts_face_new: + * @klass: a #GtsFaceClass. + * @e1: a #GtsEdge. + * @e2: a #GtsEdge. + * @e3: a #GtsEdge. + * + * Returns: a new #GtsFace using @e1, @e2 and @e3 as edges. + */ +GtsFace * gts_face_new (GtsFaceClass * klass, + GtsEdge * e1, GtsEdge * e2, GtsEdge * e3) +{ + GtsFace * f; + + f = GTS_FACE (gts_object_new (GTS_OBJECT_CLASS (klass))); + gts_triangle_set (GTS_TRIANGLE (f), e1, e2, e3); + + return f; +} + +/** + * gts_face_has_parent_surface: + * @f: a #GtsFace. + * @s: a #GtsSurface. + * + * Returns: %TRUE if @f belongs to @s, %FALSE otherwise. + */ +gboolean gts_face_has_parent_surface (GtsFace * f, GtsSurface * s) +{ + GSList * i; + + g_return_val_if_fail (f != NULL, FALSE); + + i = f->surfaces; + while (i) { + if (i->data == s) + return TRUE; + i = i->next; + } + return FALSE; +} + +/** + * gts_faces_from_edges: + * @edges: a list of #GtsEdge. + * @s: a #GtsSurface or %NULL. + * + * Builds a list of unique faces which belong to @s and have + * one of their edges in @edges. + * + * Returns: the list of faces. + */ +GSList * gts_faces_from_edges (GSList * edges, GtsSurface * s) +{ + GHashTable * hash; + GSList * faces = NULL, * i; + + hash = g_hash_table_new (NULL, NULL); + i = edges; + while (i) { + GSList * j = GTS_EDGE (i->data)->triangles; + while (j) { + GtsTriangle * t = j->data; + if (GTS_IS_FACE (t) && + (!s || gts_face_has_parent_surface (GTS_FACE (t), s)) && + g_hash_table_lookup (hash, t) == NULL) { + faces = g_slist_prepend (faces, t); + g_hash_table_insert (hash, t, i); + } + j = j->next; + } + i = i->next; + } + g_hash_table_destroy (hash); + + return faces; +} + +/** + * gts_face_neighbor_number: + * @f: a #GtsFace. + * @s: a #GtsSurface or %NULL. + * + * Returns: the number of faces neighbors of @f and belonging to @s. + */ +guint gts_face_neighbor_number (GtsFace * f, GtsSurface * s) +{ + GSList * i; + guint nn = 0; + GtsEdge * e[4], ** e1 = e; + + g_return_val_if_fail (f != NULL, 0); + + e[0] = GTS_TRIANGLE (f)->e1; + e[1] = GTS_TRIANGLE (f)->e2; + e[2] = GTS_TRIANGLE (f)->e3; + e[3] = NULL; + while (*e1) { + i = (*e1++)->triangles; + while (i) { + GtsTriangle * t = i->data; + if (GTS_FACE (t) != f && + GTS_IS_FACE (t) && + (!s || gts_face_has_parent_surface (GTS_FACE (t), s))) + nn++; + i = i->next; + } + } + + return nn; +} + +/** + * gts_face_neighbors: + * @f: a #GtsFace. + * @s: a #GtsSurface or %NULL. + * + * Returns: a list of unique #GtsFace neighbors of @f and belonging to @s. + */ +GSList * gts_face_neighbors (GtsFace * f, GtsSurface * s) +{ + GSList * i, * list = NULL; + GtsEdge * e[4], ** e1 = e; + + g_return_val_if_fail (f != NULL, NULL); + + e[0] = GTS_TRIANGLE (f)->e1; + e[1] = GTS_TRIANGLE (f)->e2; + e[2] = GTS_TRIANGLE (f)->e3; + e[3] = NULL; + while (*e1) { + i = (*e1++)->triangles; + while (i) { + GtsTriangle * t = i->data; + if (GTS_FACE (t) != f && + GTS_IS_FACE (t) && + (!s || gts_face_has_parent_surface (GTS_FACE (t), s))) + list = g_slist_prepend (list, t); + i = i->next; + } + } + + return list; +} + +/** + * gts_face_foreach_neighbor: + * @f: a #GtsFace. + * @s: a #GtsSurface or %NULL. + * @func: a #GtsFunc. + * @data: user data to pass to @func. + * + * Calls @func for each neighbor of @f belonging to @s (if not %NULL). + */ +void gts_face_foreach_neighbor (GtsFace * f, + GtsSurface * s, + GtsFunc func, + gpointer data) +{ + GSList * i; + GtsEdge * e[4], ** e1 = e; + + g_return_if_fail (f != NULL); + g_return_if_fail (func != NULL); + + e[0] = GTS_TRIANGLE (f)->e1; + e[1] = GTS_TRIANGLE (f)->e2; + e[2] = GTS_TRIANGLE (f)->e3; + e[3] = NULL; + while (*e1) { + i = (*e1++)->triangles; + while (i) { + GtsTriangle * t = i->data; + if (GTS_FACE (t) != f && + GTS_IS_FACE (t) && + (!s || gts_face_has_parent_surface (GTS_FACE (t), s))) + (* func) (t, data); + i = i->next; + } + } +} + +static gboolean triangle_is_incompatible (GtsTriangle * t, GtsEdge * e, GtsSurface * s) +{ + GSList * i = e->triangles; + + while (i) { + if (i->data != t && + GTS_IS_FACE (i->data) && + gts_face_has_parent_surface (i->data, s) && + !gts_triangles_are_compatible (t, i->data, e)) + return TRUE; + i = i->next; + } + return FALSE; +} + +/** + * gts_face_is_compatible: + * @f: a #GtsFace. + * @s: a #GtsSurface. + * + * Returns: %TRUE if @f is compatible with all its neighbors belonging + * to @s, %FALSE otherwise. + */ +gboolean gts_face_is_compatible (GtsFace * f, GtsSurface * s) +{ + g_return_val_if_fail (f != NULL, FALSE); + g_return_val_if_fail (s != NULL, FALSE); + + return !(triangle_is_incompatible (GTS_TRIANGLE (f), GTS_TRIANGLE (f)->e1, s) || + triangle_is_incompatible (GTS_TRIANGLE (f), GTS_TRIANGLE (f)->e2, s) || + triangle_is_incompatible (GTS_TRIANGLE (f), GTS_TRIANGLE (f)->e3, s)); +} Index: trunk/src_3rd/gts/fifo.c =================================================================== --- trunk/src_3rd/gts/fifo.c (nonexistent) +++ trunk/src_3rd/gts/fifo.c (revision 1065) @@ -0,0 +1,192 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "gts.h" + +struct _GtsFifo { + GList * head; + GList * tail; +}; + +/** + * gts_fifo_new: + * + * Returns: a new #GtsFifo. + */ +GtsFifo * gts_fifo_new () +{ + GtsFifo * fifo = g_malloc (sizeof (GtsFifo)); + + fifo->head = fifo->tail = NULL; + return fifo; +} + +/** + * gts_fifo_write: + * @fifo: a #GtsFifo. + * @fp: a file pointer. + * + * Writes the content of @fifo in @fp. + */ +void gts_fifo_write (GtsFifo * fifo, FILE * fp) +{ + GList * i; + + g_return_if_fail (fifo != NULL); + g_return_if_fail (fp != NULL); + + fprintf (fp, "["); + i = fifo->head; + while (i) { + fprintf (fp, "%p ", i->data); + i = i->next; + } + fprintf (fp, "]"); +} + +/** + * gts_fifo_push: + * @fifo: a #GtsFifo. + * @data: data to add to @fifo. + * + * Push @data into @fifo. + */ +void gts_fifo_push (GtsFifo * fifo, gpointer data) +{ + g_return_if_fail (fifo != NULL); + + fifo->head = g_list_prepend (fifo->head, data); + if (fifo->tail == NULL) + fifo->tail = fifo->head; +} + +/** + * gts_fifo_pop: + * @fifo: a #GtsFifo. + * + * Removes the first element from @fifo. + * + * Returns: the first element in @fifo or %NULL if @fifo is empty. + */ +gpointer gts_fifo_pop (GtsFifo * fifo) +{ + gpointer data; + GList * tail; + + g_return_val_if_fail (fifo != NULL, NULL); + + if (fifo->tail == NULL) + return NULL; + tail = fifo->tail->prev; + data = fifo->tail->data; + fifo->head = g_list_remove_link (fifo->head, fifo->tail); + g_list_free_1 (fifo->tail); + fifo->tail = tail; + return data; +} + +/** + * gts_fifo_top: + * @fifo: a #GtsFifo. + * + * Returns: the first element in @fifo or %NULL if @fifo is empty. + */ +gpointer gts_fifo_top (GtsFifo * fifo) +{ + g_return_val_if_fail (fifo != NULL, NULL); + + if (fifo->tail == NULL) + return NULL; + return fifo->tail->data; +} + +/** + * gts_fifo_size: + * @fifo: a #GtsFifo. + * + * Returns: the number of elements in @fifo. + */ +guint gts_fifo_size (GtsFifo * fifo) +{ + g_return_val_if_fail (fifo != NULL, 0); + + return g_list_length (fifo->head); +} + +/** + * gts_fifo_destroy: + * @fifo: a #GtsFifo. + * + * Frees all the memory allocated for @fifo. + */ +void gts_fifo_destroy (GtsFifo * fifo) +{ + g_return_if_fail (fifo != NULL); + g_list_free (fifo->head); + g_free (fifo); +} + +/** + * gts_fifo_is_empty: + * @fifo: a #GtsFifo. + * + * Returns: %TRUE if @fifo is empty, %FALSE otherwise. + */ +gboolean gts_fifo_is_empty (GtsFifo * fifo) +{ + g_return_val_if_fail (fifo != NULL, TRUE); + + return (fifo->head == NULL); +} + +/** + * gts_fifo_foreach: + * @fifo: a #GtsFifo. + * @func: a #GtsFunc. + * @data: user data to be passed to @func. + * + * Calls @func in order for each item in @fifo, passing @data. + */ +void gts_fifo_foreach (GtsFifo * fifo, GtsFunc func, gpointer data) +{ + GList * i; + + g_return_if_fail (fifo != NULL); + g_return_if_fail (func != NULL); + + i = fifo->tail; + while (i) { + (* func) (i->data, data); + i = i->prev; + } +} + +/** + * gts_fifo_reverse: + * @fifo: a #GtsFifo. + * + * Reverses the order of elements in @fifo. + */ +void gts_fifo_reverse (GtsFifo * fifo) +{ + g_return_if_fail (fifo != NULL); + + fifo->tail = fifo->head; + fifo->head = g_list_reverse (fifo->head); +} Index: trunk/src_3rd/gts/graph.c =================================================================== --- trunk/src_3rd/gts/graph.c (nonexistent) +++ trunk/src_3rd/gts/graph.c (revision 1065) @@ -0,0 +1,1776 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include +#include "gts.h" + +/* GtsGNode */ + +gboolean gts_allow_floating_gnodes = FALSE; + +static void gnode_remove_container (GtsContainee * i, GtsContainer * c) +{ + (* GTS_CONTAINEE_CLASS (GTS_OBJECT_CLASS (gts_gnode_class ())->parent_class)->remove_container) (i, c); + if (GTS_SLIST_CONTAINEE (i)->containers == NULL && + !gts_allow_floating_gnodes && + !GTS_OBJECT_DESTROYED(GTS_OBJECT (i))) + gts_object_destroy (GTS_OBJECT (i)); +} + +static void gnode_class_init (GtsGNodeClass * klass) +{ + klass->weight = NULL; + + GTS_CONTAINEE_CLASS (klass)->remove_container = gnode_remove_container; +} + +static void gnode_init (GtsGNode * n) +{ + n->level = 0; +} + +/** + * gts_gnode_class: + * + * Returns: the #GtsGNodeClass. + */ +GtsGNodeClass * gts_gnode_class (void) +{ + static GtsGNodeClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo gnode_info = { + "GtsGNode", + sizeof (GtsGNode), + sizeof (GtsGNodeClass), + (GtsObjectClassInitFunc) gnode_class_init, + (GtsObjectInitFunc) gnode_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = + gts_object_class_new (GTS_OBJECT_CLASS (gts_slist_container_class ()), + &gnode_info); + } + + return klass; +} + +/** + * gts_gnode_new: + * @klass: a #GtsGNodeClass. + * + * Returns: a new #GtsGNode. + */ +GtsGNode * gts_gnode_new (GtsGNodeClass * klass) +{ + GtsGNode * object; + + object = GTS_GNODE (gts_object_new (GTS_OBJECT_CLASS (klass))); + + return object; +} + +/** + * gts_gnode_foreach_neighbor: + * @n: a #GtsGNode. + * @g: a #GtsGraph or %NULL. + * @func: a #GtsFunc. + * @data: user data to be passed to @func. + * + * Calls @func for each neighbor #GtsGNode of @n (belonging to @g if + * @g is not %NULL. + */ +void gts_gnode_foreach_neighbor (GtsGNode * n, + GtsGraph * g, + GtsFunc func, + gpointer data) +{ + GSList * i; + + g_return_if_fail (n != NULL); + g_return_if_fail (func != NULL); + + i = GTS_SLIST_CONTAINER (n)->items; + while (i) { + GtsGNode * n1 = GTS_GNODE_NEIGHBOR (n, i->data); + if (g == NULL || gts_containee_is_contained (GTS_CONTAINEE (n1), + GTS_CONTAINER (g))) + (* func) (n1, data); + i = i->next; + } +} + +/** + * gts_gnode_foreach_edge: + * @n: a #GtsGNode. + * @g: a #GtsGraph or %NULL. + * @func: a #GtsFunc. + * @data: user data to be passed to @func. + * + * Calls @func for each #GtsGEdge connecting @n to another #GtsGNode + * (belonging to @g if @g is not %NULL. + */ +void gts_gnode_foreach_edge (GtsGNode * n, + GtsGraph * g, + GtsFunc func, + gpointer data) +{ + GSList * i; + + g_return_if_fail (n != NULL); + g_return_if_fail (func != NULL); + + i = GTS_SLIST_CONTAINER (n)->items; + while (i) { + GtsGNode * n1 = GTS_GNODE_NEIGHBOR (n, i->data); + if (g == NULL || gts_containee_is_contained (GTS_CONTAINEE (n1), + GTS_CONTAINER (g))) + (* func) (i->data, data); + i = i->next; + } +} + +/** + * gts_gnode_degree: + * @n: a #GtsGNode. + * @g: a #GtsGraph or %NULL. + * + * Returns: the number of neighbors of @n (belonging to @g if @g is not %NULL). + */ +guint gts_gnode_degree (GtsGNode * n, + GtsGraph * g) +{ + GSList * i; + guint nn = 0; + + g_return_val_if_fail (n != NULL, 0); + + i = GTS_SLIST_CONTAINER (n)->items; + while (i) { + GtsGNode * n1 = GTS_GNODE_NEIGHBOR (n, i->data); + if (g == NULL || gts_containee_is_contained (GTS_CONTAINEE (n1), + GTS_CONTAINER (g))) + nn++; + i = i->next; + } + + return nn; +} + +/** + * gts_gnode_move_cost: + * @n: a #GtsGNode. + * @src: a #GtsGraph containing @n. + * @dst: another #GtsGraph. + * + * Returns: the cost (increase in the sum of the weights of the edges cut) of + * moving @n from @src to @dst. + */ +gfloat gts_gnode_move_cost (GtsGNode * n, + GtsGraph * src, + GtsGraph * dst) +{ + GSList * i; + gfloat cost = 0.; + + g_return_val_if_fail (n != NULL, G_MAXFLOAT); + g_return_val_if_fail (src != NULL, G_MAXFLOAT); + g_return_val_if_fail (dst != NULL, G_MAXFLOAT); + g_return_val_if_fail (gts_containee_is_contained (GTS_CONTAINEE (n), + GTS_CONTAINER (src)), + G_MAXFLOAT); + + i = GTS_SLIST_CONTAINER (n)->items; + while (i) { + GtsGEdge * ge = i->data; + GtsGNode * neighbor = GTS_GNODE_NEIGHBOR (n, ge); + + if (gts_containee_is_contained (GTS_CONTAINEE (neighbor), + GTS_CONTAINER (src))) + cost += gts_gedge_weight (ge); + else if (gts_containee_is_contained (GTS_CONTAINEE (neighbor), + GTS_CONTAINER (dst))) + cost -= gts_gedge_weight (ge); + i = i->next; + } + + return cost; +} + +/** + * gts_gnode_weight: + * @n: a #GtsGNode. + * + * Returns: the weight of @n as defined by the weight() method of the + * #GtsGNodeClass. + */ +gfloat gts_gnode_weight (GtsGNode * n) +{ + g_return_val_if_fail (n != NULL, 0.); + + if (GTS_GNODE_CLASS (GTS_OBJECT (n)->klass)->weight) + return (* GTS_GNODE_CLASS (GTS_OBJECT (n)->klass)->weight) (n); + return 1.; +} + +/* GtsNGNode */ + +static void ngnode_init (GtsNGNode * n) +{ + n->id = 0; +} + +/** + * gts_ngnode_class: + * + * Returns: the #GtsNGNodeClass. + */ +GtsNGNodeClass * gts_ngnode_class (void) +{ + static GtsNGNodeClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo ngnode_info = { + "GtsNGNode", + sizeof (GtsNGNode), + sizeof (GtsNGNodeClass), + (GtsObjectClassInitFunc) NULL, + (GtsObjectInitFunc) ngnode_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_gnode_class ()), + &ngnode_info); + } + + return klass; +} + +/** + * gts_ngnode_new: + * @klass: a #GtsNGNodeClass. + * + * Returns: a new #GtsNGNode with identity @id. + */ +GtsNGNode * gts_ngnode_new (GtsNGNodeClass * klass, + guint id) +{ + GtsNGNode * n; + + n = GTS_NGNODE (gts_gnode_new (GTS_GNODE_CLASS (klass))); + n->id = id; + + return n; +} + +/* GtsWGNode */ + +static gfloat wgnode_weight (GtsGNode * n) +{ + return GTS_WGNODE (n)->weight; +} + +static void wgnode_class_init (GtsWGNodeClass * klass) +{ + GTS_GNODE_CLASS (klass)->weight = wgnode_weight; +} + +static void wgnode_init (GtsWGNode * n) +{ + n->weight = 1.; +} + +/** + * gts_wgnode_class: + * + * Returns: the #GtsWGNodeClass. + */ +GtsWGNodeClass * gts_wgnode_class (void) +{ + static GtsWGNodeClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo wgnode_info = { + "GtsWGNode", + sizeof (GtsWGNode), + sizeof (GtsWGNodeClass), + (GtsObjectClassInitFunc) wgnode_class_init, + (GtsObjectInitFunc) wgnode_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_gnode_class ()), + &wgnode_info); + } + + return klass; +} + +/** + * gts_wgnode_new: + * @klass: a #GtsWGNodeClass. + * @weight: the weight of the #GtsWGNode to create. + * + * Returns: a new #GtsWGNode of weight @weight. + */ +GtsWGNode * gts_wgnode_new (GtsWGNodeClass * klass, + gfloat weight) +{ + GtsWGNode * n; + + n = GTS_WGNODE (gts_gnode_new (GTS_GNODE_CLASS (klass))); + n->weight = weight; + + return n; +} + +/* GtsPNode */ + +static void pnode_write (GtsGNode * n, FILE * fp) +{ + if (GTS_IS_NVERTEX (GTS_PNODE (n)->data)) + fprintf (fp, "label=\"%p:%s\",", + GTS_PNODE (n)->data, + GTS_NVERTEX (GTS_PNODE (n)->data)->name); + else + fprintf (fp, "label=\"%p\",", GTS_PNODE (n)->data); +} + +static void pnode_class_init (GtsPNodeClass * klass) +{ + GTS_GNODE_CLASS (klass)->write = pnode_write; +} + +static void pnode_init (GtsPNode * pn) +{ + pn->data = NULL; +} + +/** + * gts_pnode_class: + * + * Returns: the #GtsPNodeClass. + */ +GtsPNodeClass * gts_pnode_class (void) +{ + static GtsPNodeClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo pnode_info = { + "GtsPNode", + sizeof (GtsPNode), + sizeof (GtsPNodeClass), + (GtsObjectClassInitFunc) pnode_class_init, + (GtsObjectInitFunc) pnode_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_gnode_class ()), + &pnode_info); + } + + return klass; +} + +/** + * gts_pnode_new: + * @klass: a #GtsPNodeClass. + * @data: user data. + * + * Returns: a new #GtsPNode associated with @data. + */ +GtsPNode * gts_pnode_new (GtsPNodeClass * klass, gpointer data) +{ + GtsPNode * pn; + + pn = GTS_PNODE (gts_object_new (GTS_OBJECT_CLASS (klass))); + pn->data = data; + + return pn; +} + +/* GtsFNode */ + +static void fnode_write (GtsGNode * n, FILE * fp) +{ + fprintf (fp, "label=\"%p\",", GTS_FNODE (n)->f); +} + +static void fnode_class_init (GtsGNodeClass * klass) +{ + klass->write = fnode_write; +} + +static void fnode_init (GtsFNode * fn) +{ + fn->f = NULL; +} + +/** + * gts_fnode_class: + * + * Returns: the #GtsFNodeClass. + */ +GtsFNodeClass * gts_fnode_class (void) +{ + static GtsFNodeClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo fnode_info = { + "GtsFNode", + sizeof (GtsFNode), + sizeof (GtsFNodeClass), + (GtsObjectClassInitFunc) fnode_class_init, + (GtsObjectInitFunc) fnode_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_gnode_class ()), + &fnode_info); + } + + return klass; +} + +/** + * gts_fnode_new: + * @klass: a #GtsFNodeClass. + * @f: a #GtsFace. + * + * Returns: a new #GtsFNode associated with face @f. + */ +GtsFNode * gts_fnode_new (GtsFNodeClass * klass, GtsFace * f) +{ + GtsFNode * fn; + + g_return_val_if_fail (f != NULL, NULL); + + fn = GTS_FNODE (gts_object_new (GTS_OBJECT_CLASS (klass))); + fn->f = f; + + return fn; +} + +/* GtsGEdge */ + +static void gedge_destroy (GtsObject * object) +{ + GtsGEdge * ge = GTS_GEDGE (object); + + if (ge->n1) + gts_container_remove (GTS_CONTAINER (ge->n1), GTS_CONTAINEE (ge)); + if (ge->n2) + gts_container_remove (GTS_CONTAINER (ge->n2), GTS_CONTAINEE (ge)); + + (* GTS_OBJECT_CLASS (gts_gedge_class ())->parent_class->destroy) (object); +} + +static void gedge_remove_container (GtsContainee * i, GtsContainer * c) +{ + GtsGEdge * ge = GTS_GEDGE (i); + GtsGNode * n1 = ge->n1; + GtsGNode * n2 = ge->n2; + + ge->n1 = ge->n2 = NULL; + if (n1 != NULL && n2 != NULL) { + if (GTS_CONTAINER (n1) == c) { + if (n2 && n2 != n1) gts_container_remove (GTS_CONTAINER (n2), i); + } + else if (GTS_CONTAINER (n2) == c) { + if (n1 && n1 != n2) gts_container_remove (GTS_CONTAINER (n1), i); + } + else + g_assert_not_reached (); + (* GTS_OBJECT_CLASS (gts_gedge_class ())->parent_class->destroy) + (GTS_OBJECT (i)); + } +} + +static gboolean gedge_is_contained (GtsContainee * i, GtsContainer * c) +{ + GtsGEdge * ge = GTS_GEDGE (i); + + if (GTS_CONTAINER (ge->n1) == c || GTS_CONTAINER (ge->n2) == c) + return TRUE; + return FALSE; +} + +static void gedge_class_init (GtsGEdgeClass * klass) +{ + klass->link = NULL; + klass->weight = NULL; + + GTS_CONTAINEE_CLASS (klass)->remove_container = gedge_remove_container; + GTS_CONTAINEE_CLASS (klass)->is_contained = gedge_is_contained; + + GTS_OBJECT_CLASS (klass)->destroy = gedge_destroy; +} + +static void gedge_init (GtsGEdge * object) +{ + object->n1 = object->n2 = NULL; +} + +/** + * gts_gedge_class: + * + * Returns: the #GtsGEdgeClass. + */ +GtsGEdgeClass * gts_gedge_class (void) +{ + static GtsGEdgeClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo gedge_info = { + "GtsGEdge", + sizeof (GtsGEdge), + sizeof (GtsGEdgeClass), + (GtsObjectClassInitFunc) gedge_class_init, + (GtsObjectInitFunc) gedge_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_containee_class ()), + &gedge_info); + } + + return klass; +} + +/** + * gts_gedge_new: + * @klass: a #GtsGEdgeClass. + * @n1: a #GtsGNode. + * @n2: another #GtsGNode. + * + * Returns: a new #GtsGEdge linking @n1 and @n2. + */ +GtsGEdge * gts_gedge_new (GtsGEdgeClass * klass, GtsGNode * n1, GtsGNode * n2) +{ + GtsGEdge * object; + + g_return_val_if_fail (n1 != NULL, NULL); + g_return_val_if_fail (n2 != NULL, NULL); + + object = GTS_GEDGE (gts_object_new (GTS_OBJECT_CLASS (klass))); + object->n1 = n1; + gts_container_add (GTS_CONTAINER (n1), GTS_CONTAINEE (object)); + object->n2 = n2; + if (n1 != n2) + gts_container_add (GTS_CONTAINER (n2), GTS_CONTAINEE (object)); + + if (klass->link) + object = (* klass->link) (object, n1, n2); + + return object; +} + +/** + * gts_gedge_weight: + * @e: a #GtsGEdge. + * + * Returns: the weight of edge @e as defined by the weight() method of + * #GtsGEdgeClass. + */ +gfloat gts_gedge_weight (GtsGEdge * e) +{ + g_return_val_if_fail (e != NULL, 0.); + + if (GTS_GEDGE_CLASS (GTS_OBJECT (e)->klass)->weight) + return (* GTS_GEDGE_CLASS (GTS_OBJECT (e)->klass)->weight) (e); + return 1.; +} + +/* GtsPGEdge */ + +static void pgedge_write (GtsGEdge * ge, FILE * fp) +{ + if (GTS_IS_EDGE (GTS_PGEDGE (ge)->data)) { + GtsEdge * e = GTS_PGEDGE (ge)->data; + guint n = g_slist_length (e->triangles); + + fprintf (fp, "label=\"%p:%s:%d\",color=%s", e, + GTS_IS_NEDGE (e) ? GTS_NEDGE (e)->name : "", + n, + n == 0 ? "black" : + n == 1 ? "blue" : + n == 2 ? "green" : + n == 3 ? "violet" : + n == 4 ? "red" : + "pink"); + } + else + fprintf (fp, "label=\"%p\",", GTS_PGEDGE (ge)->data); +} + +static void pgedge_class_init (GtsPGEdgeClass * klass) +{ + GTS_GEDGE_CLASS (klass)->write = pgedge_write; +} + +static void pgedge_init (GtsPGEdge * e) +{ + e->data = NULL; +} + +/** + * gts_pgedge_class: + * + * Returns: the #GtsPGEdgeClass. + */ +GtsPGEdgeClass * gts_pgedge_class (void) +{ + static GtsPGEdgeClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo pgedge_info = { + "GtsPGEdge", + sizeof (GtsPGEdge), + sizeof (GtsPGEdgeClass), + (GtsObjectClassInitFunc) pgedge_class_init, + (GtsObjectInitFunc) pgedge_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_gedge_class ()), + &pgedge_info); + } + + return klass; +} + +/** + * gts_pgedge_new: + * @klass: a #GtsPGEdgeClass. + * @n1: a #GtsGNode. + * @n2: another #GtsGNode. + * @data: user data. + * + * Returns: a new #GtsPGEdge associated with @data linking @n1 and @n2. + */ +GtsPGEdge * gts_pgedge_new (GtsPGEdgeClass * klass, + GtsGNode * g1, + GtsGNode * g2, + gpointer data) +{ + GtsPGEdge * we; + + we = GTS_PGEDGE (gts_gedge_new (GTS_GEDGE_CLASS (klass), g1, g2)); + we->data = data; + + return we; +} + +/* GtsWGEdge */ + +static gfloat wgedge_weight (GtsGEdge * e) +{ + return GTS_WGEDGE (e)->weight; +} + +static void wgedge_class_init (GtsWGEdgeClass * klass) +{ + GTS_GEDGE_CLASS (klass)->weight = wgedge_weight; +} + +static void wgedge_init (GtsWGEdge * e) +{ + e->weight = 1.; +} + +/** + * gts_wgedge_class: + * + * Returns: the #GtsWGEdgeClass. + */ +GtsWGEdgeClass * gts_wgedge_class (void) +{ + static GtsWGEdgeClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo wgedge_info = { + "GtsWGEdge", + sizeof (GtsWGEdge), + sizeof (GtsWGEdgeClass), + (GtsObjectClassInitFunc) wgedge_class_init, + (GtsObjectInitFunc) wgedge_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_gedge_class ()), + &wgedge_info); + } + + return klass; +} + +/** + * gts_wgedge_new: + * @klass: a #GtsWGEdgeClass. + * @n1: a #GtsGNode. + * @n2: another #GtsGNode. + * @weight: the weight of the new edge. + * + * Returns: a new #GtsWGEdge of weight @weight linking @n1 and @n2. + */ +GtsWGEdge * gts_wgedge_new (GtsWGEdgeClass * klass, + GtsGNode * g1, + GtsGNode * g2, + gfloat weight) +{ + GtsWGEdge * we; + + we = GTS_WGEDGE (gts_gedge_new (GTS_GEDGE_CLASS (klass), g1, g2)); + we->weight = weight; + + return we; +} + +/* GtsGraph */ + +static void graph_init (GtsGraph * g) +{ + g->graph_class = gts_graph_class (); + g->node_class = gts_gnode_class (); + g->edge_class = gts_gedge_class (); +} + +static void graph_write (GtsObject * object, FILE * fp) +{ + GtsGraph * graph = GTS_GRAPH (object); + + fprintf (fp, " %s %s %s", + object->klass->info.name, + GTS_OBJECT_CLASS (graph->node_class)->info.name, + GTS_OBJECT_CLASS (graph->edge_class)->info.name); +} + +static void graph_read (GtsObject ** object, GtsFile * f) +{ + GtsObjectClass * klass; + + if (f->type != GTS_STRING) { + gts_file_error (f, "expecting a string (GtsGNodeClass)"); + return; + } + klass = gts_object_class_from_name (f->token->str); + if (klass == NULL) { + gts_file_error (f, "unknown class `%s'", f->token->str); + return; + } + if (!gts_object_class_is_from_class (klass, gts_gnode_class ())) { + gts_file_error (f, "class `%s' is not a GtsGNodeClass", f->token->str); + return; + } + GTS_GRAPH (*object)->node_class = GTS_GNODE_CLASS (klass); + gts_file_next_token (f); + + if (f->type != GTS_STRING) { + gts_file_error (f, "expecting a string (GtsGEdgeClass)"); + return; + } + klass = gts_object_class_from_name (f->token->str); + if (klass == NULL) { + gts_file_error (f, "unknown class `%s'", f->token->str); + return; + } + if (!gts_object_class_is_from_class (klass, gts_gedge_class ())) { + gts_file_error (f, "class `%s' is not a GtsGEdgeClass", f->token->str); + return; + } + GTS_GRAPH (*object)->edge_class = GTS_GEDGE_CLASS (klass); + gts_file_next_token (f); +} + +static void graph_class_init (GtsGraphClass * klass) +{ + klass->weight = NULL; + + GTS_OBJECT_CLASS (klass)->write = graph_write; + GTS_OBJECT_CLASS (klass)->read = graph_read; +} + +/** + * gts_graph_class: + * + * Returns: the #GtsGraphClass. + */ +GtsGraphClass * gts_graph_class (void) +{ + static GtsGraphClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo graph_info = { + "GtsGraph", + sizeof (GtsGraph), + sizeof (GtsGraphClass), + (GtsObjectClassInitFunc) graph_class_init, + (GtsObjectInitFunc) graph_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_hash_container_class ()), + &graph_info); + } + + return klass; +} + +/** + * gts_graph_new: + * @klass: a #GtsGraphClass. + * @node_class: a #GtsGNodeClass. + * @edge_class: a #GtsGEdgeClass. + * + * Returns: a new #GtsGraph using @node_class and @edge_class as node types. + */ +GtsGraph * gts_graph_new (GtsGraphClass * klass, + GtsGNodeClass * node_class, + GtsGEdgeClass * edge_class) +{ + GtsGraph * g; + + g_return_val_if_fail (klass != NULL, NULL); + g_return_val_if_fail (node_class != NULL, NULL); + g_return_val_if_fail (edge_class != NULL, NULL); + + g = GTS_GRAPH (gts_object_new (GTS_OBJECT_CLASS (klass))); + g->node_class = node_class; + g->edge_class = edge_class; + + return g; +} + +static void compute_degree (GtsGNode * n, gpointer * data) +{ + GtsGraph * g = data[0]; + GtsRange * degree = data[1]; + + gts_range_add_value (degree, gts_gnode_degree (n, g)); +} + +/** + * gts_graph_print_stats: + * @g: a #GtsGraph. + * @fp: a file pointer. + * + * Writes to @fp a summary of the properties of @g. + */ +void gts_graph_print_stats (GtsGraph * g, FILE * fp) +{ + GtsRange degree; + gpointer data[2]; + + g_return_if_fail (g != NULL); + g_return_if_fail (fp != NULL); + + fprintf (fp, "# nodes: %d weight: %g\n", + gts_container_size (GTS_CONTAINER (g)), + gts_graph_weight (g)); + fprintf (fp, "# degree: "); + gts_range_init (°ree); + data[0] = g; + data[1] = °ree; + gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) compute_degree, data); + gts_range_update (°ree); + gts_range_print (°ree, fp); + fprintf (fp, "\n"); + fprintf (fp, "# edges cut: %d edges cut weight: %g\n", + gts_graph_edges_cut (g), + gts_graph_edges_cut_weight (g)); +} + +struct _GtsGraphTraverse { + GtsFifo * q; + GtsGraph * g; +}; + +static void reset_level (GtsGNode * n) +{ + n->level = 0; +} + +/** + * gts_graph_traverse_new: + * @g: a #GtsGraph. + * @n: a #GtsGNode belonging to @g. + * @type: the type of traversal. + * @reinit: if %TRUE, the traversal is reinitialized. + * + * Returns: a new #GtsGraphTraverse initialized for the traversal of + * @g of type @type, starting from @n. + */ +GtsGraphTraverse * gts_graph_traverse_new (GtsGraph * g, + GtsGNode * n, + GtsTraverseType type, + gboolean reinit) +{ + GtsGraphTraverse * t; + + g_return_val_if_fail (g != NULL, NULL); + g_return_val_if_fail (n != NULL, NULL); + g_return_val_if_fail (gts_containee_is_contained (GTS_CONTAINEE (n), + GTS_CONTAINER (g)), + NULL); + + if (reinit) + gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) reset_level, NULL); + + t = g_malloc (sizeof (GtsGraphTraverse)); + t->q = gts_fifo_new (); + t->g = g; + n->level = 1; + gts_fifo_push (t->q, n); + + return t; +} + +static void push_neighbor (GtsGNode * n, gpointer * data) +{ + GtsFifo * q = data[0]; + GtsGNode * u = data[1]; + + if (n->level == 0) { + n->level = u->level + 1; + gts_fifo_push (q, n); + } +} + +/** + * gts_graph_traverse_next: + * @t: a #GtsGraphTraverse. + * + * Returns: the next #GtsGNode of the traversal defined by @t or %NULL + * if the traversal is complete. + */ +GtsGNode * gts_graph_traverse_next (GtsGraphTraverse * t) +{ + GtsGNode * u; + + g_return_val_if_fail (t != NULL, NULL); + + u = gts_fifo_pop (t->q); + if (u) { + gpointer data[2]; + + data[0] = t->q; + data[1] = u; + gts_gnode_foreach_neighbor (u, t->g, (GtsFunc) push_neighbor, data); + } + + return u; +} + +/** + * gts_graph_traverse_what_next: + * @t: a #GtsGraphTraverse. + * + * Returns: the next #GtsGNode of the traversal defined by @t or %NULL + * if the traversal is complete but without advancing the traversal. + */ +GtsGNode * gts_graph_traverse_what_next (GtsGraphTraverse * t) +{ + g_return_val_if_fail (t != NULL, NULL); + + return gts_fifo_top (t->q); +} + +/** + * gts_graph_traverse_destroy: + * @t: a #GtsGraphTraverse. + * + * Frees all the memory allocated for @t. + */ +void gts_graph_traverse_destroy (GtsGraphTraverse * t) +{ + g_return_if_fail (t != NULL); + + gts_fifo_destroy (t->q); + g_free (t); +} + +static void edge_foreach_node (GtsGNode * n, gpointer * info) +{ + GtsFunc func = (GtsFunc) info[0]; + gpointer data = info[1]; + GHashTable * hash = info[2]; + GSList * i = GTS_SLIST_CONTAINER (n)->items; + + while (i) { + GtsGEdge * e = i->data; + if (!g_hash_table_lookup (hash, e)) { + (* func) (e, data); + g_hash_table_insert (hash, e, e); + } + i = i->next; + } +} + +/** + * gts_graph_foreach_edge: + * @g: a #GtsGraph. + * @func: a #GtsFunc. + * @data: user data to be passed to @func. + * + * Calls @func for each #GtsEdge of @g. + */ +void gts_graph_foreach_edge (GtsGraph * g, GtsFunc func, gpointer data) +{ + gpointer info[3]; + GHashTable * hash; + + g_return_if_fail (g != NULL); + g_return_if_fail (func != NULL); + + info[0] = func; + info[1] = data; + info[2] = hash = g_hash_table_new (NULL, NULL); + gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) edge_foreach_node, info); + g_hash_table_destroy (hash); +} + +/** + * gts_graph_weight: + * @g: a #GtsGraph. + * + * Returns: the weight of graph @g as defined by the weight() method + * of #GtsGraphClass. + */ +gfloat gts_graph_weight (GtsGraph * g) +{ + g_return_val_if_fail (g != NULL, 0.); + + if (GTS_GRAPH_CLASS (GTS_OBJECT (g)->klass)->weight) + return (* GTS_GRAPH_CLASS (GTS_OBJECT (g)->klass)->weight) (g); + return (gfloat) gts_container_size (GTS_CONTAINER (g)); +} + +/** + * gts_graph_distance_sum: + * @g: a #GtsGraph. + * @center: a #GtsGNode of @g. + * + * Returns: the sum of the distances between all the other #GtsGNode + * of @g and @center. + */ +guint gts_graph_distance_sum (GtsGraph * g, GtsGNode * center) +{ + GtsGraphTraverse * t; + GtsGNode * n; + guint sum = 0; + + g_return_val_if_fail (g != NULL, 0); + g_return_val_if_fail (center != NULL, 0); + + t = gts_graph_traverse_new (g, center, GTS_BREADTH_FIRST, TRUE); + while ((n = gts_graph_traverse_next (t))) + sum += n->level - 1; + gts_graph_traverse_destroy (t); + + return sum; +} + +/** + * gts_graph_farthest: + * @g: a #GtsGraph. + * @gnodes: a list of #GtsGNode belonging to @g. + * + * Returns: the #GtsGNode belonging to @g and farthest from all the nodes in + * @gnodes (hmmm, definition of "farthest"?). + */ +GtsGNode * gts_graph_farthest (GtsGraph * g, GSList * gnodes) +{ + GtsGNode * farthest = NULL; + GSList * i; + gboolean reinit = TRUE, changed = TRUE; + guint level = 1; + + g_return_val_if_fail (g != NULL, NULL); + + /* initialize traversals */ + i = gnodes; + while (i) { + GTS_OBJECT (i->data)->reserved = + gts_graph_traverse_new (g, i->data, GTS_BREADTH_FIRST, reinit); + reinit = FALSE; + i = i->next; + } + + while (changed) { + changed = FALSE; + i = gnodes; + while (i) { + GtsGraphTraverse * t = GTS_OBJECT (i->data)->reserved; + GtsGNode * n; + while ((n = gts_graph_traverse_what_next (t)) && n->level == level) { + changed = TRUE; + farthest = n; + gts_graph_traverse_next (t); + } + i = i->next; + } + level++; + } + + /* destroy traversals */ + i = gnodes; + while (i) { + gts_graph_traverse_destroy (GTS_OBJECT (i->data)->reserved); + GTS_OBJECT (i->data)->reserved = NULL; + i = i->next; + } + return farthest; +} + +static void neighbor_count (GtsGNode * n, gpointer * data) +{ + guint * cuts = data[0]; + GtsGraph * g = data[1]; + + if (!gts_containee_is_contained (GTS_CONTAINEE (n), GTS_CONTAINER (g))) + (*cuts)++; +} + +static void count_edge_cuts (GtsGNode * n, gpointer * data) +{ + gts_gnode_foreach_neighbor (n, NULL, (GtsFunc) neighbor_count, data); +} + +/** + * gts_graph_edges_cut: + * @g: a #GtsGraph. + * + * Returns: the number of edges of @g connecting nodes belonging to @g + * to nodes not belonging to @g. + */ +guint gts_graph_edges_cut (GtsGraph * g) +{ + guint cuts = 0; + gpointer data[2]; + + g_return_val_if_fail (g != NULL, 0); + + data[0] = &cuts; + data[1] = g; + gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) count_edge_cuts, data); + + return cuts; +} + +static void sum_edge_cuts_weight (GtsGNode * n, gpointer * data) +{ + gfloat * weight = data[0]; + GtsGraph * g = data[1]; + GSList * i = GTS_SLIST_CONTAINER (n)->items; + + while (i) { + GtsGNode * n1 = GTS_GNODE_NEIGHBOR (n, i->data); + if (!gts_containee_is_contained (GTS_CONTAINEE (n1), GTS_CONTAINER (g))) + *weight += gts_gedge_weight (i->data); + i = i->next; + } +} + +/** + * gts_graph_edges_cut_weight: + * @g: a #GtsGraph. + * + * Returns: the sum of the weights of the edges of @g connecting nodes + * belonging to @g to nodes not belonging to @g. + */ +gfloat gts_graph_edges_cut_weight (GtsGraph * g) +{ + gfloat weight = 0.; + gpointer data[2]; + + g_return_val_if_fail (g != NULL, 0); + + data[0] = &weight; + data[1] = g; + gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) sum_edge_cuts_weight, + data); + + return weight; +} + +/** + * gts_graph_read_jostle: + * @g: a #GtsGraph. + * @fp: a #GtsFile. + * + * Adds to @g the nodes and edges defined in the file pointed to by + * @fp. This file must use the Jostle "graph" ASCII format. + * The nodes created are of type #GtsNGNode and their identities are the + * line number at which they appear in @fp. + * + * Returns: 0 if the lecture was successful, the line number at which + * an error occured otherwise (in which case the @error field of @fp + * is set). + */ +guint gts_graph_read_jostle (GtsGraph * g, GtsFile * fp) +{ + guint nn, ne, n; + GtsGNode ** nodes; + + g_return_val_if_fail (g != NULL, 1); + g_return_val_if_fail (fp != NULL, 1); + + if (fp->type != GTS_INT) { + gts_file_error (fp, "expecting an integer (number of nodes)"); + return fp->line; + } + nn = atoi (fp->token->str); + gts_file_next_token (fp); + + if (fp->type != GTS_INT) { + gts_file_error (fp, "expecting an integer (number of edges)"); + return fp->line; + } + ne = atoi (fp->token->str); + + gts_file_first_token_after (fp, '\n'); + nodes = g_malloc (sizeof (GtsGNode *)*(nn + 1)); + + n = 0; + while (n < nn && fp->type != GTS_ERROR) { + GtsNGNode * node = gts_ngnode_new (gts_ngnode_class (), fp->line); + + nodes[n++] = GTS_GNODE (node); + gts_container_add (GTS_CONTAINER (g), GTS_CONTAINEE (node)); + do { + if (fp->type != GTS_INT) + gts_file_error (fp, "expecting an integer (node index)"); + else { + guint in = atoi (fp->token->str); + + if (in == 0 || in > nn) + gts_file_error (fp, "node index `%d' is out of range `[1,%d]'", + in, nn); + else if (in == n) + gts_file_error (fp, "node index `%d' references itself", in); + else if (in < n) { + gts_gedge_new (g->edge_class, GTS_GNODE (node), nodes[in - 1]); + ne--; + gts_file_next_token (fp); + } + } + } while (fp->type != GTS_ERROR && fp->type != '\n'); + } + g_free (nodes); + + if (fp->type != GTS_ERROR) { + if (n != nn) + gts_file_error (fp, "only `%d' nodes read out of `%d'", + n, nn); + else if (ne > 0) + gts_file_error (fp, "`%d' unallocated edges remaining", + ne); + } + + if (fp->type == GTS_ERROR) + return fp->line; + return 0; +} + +static void count_edges (GtsGEdge * e, guint * nedge) +{ + (*nedge)++; +} + +static void write_node (GtsObject * node, gpointer * data) +{ + FILE * fp = data[0]; + guint * nnode = data[1]; + + node->reserved = GUINT_TO_POINTER ((*nnode)++); + if (node->klass->write) + (* node->klass->write) (node, fp); + fputc ('\n', fp); +} + +static void write_edge (GtsGEdge * edge, FILE * fp) +{ + fprintf (fp, "%u %u", + GPOINTER_TO_UINT (GTS_OBJECT (edge->n1)->reserved), + GPOINTER_TO_UINT (GTS_OBJECT (edge->n2)->reserved)); + if (GTS_OBJECT (edge)->klass->write) + (* GTS_OBJECT (edge)->klass->write) (GTS_OBJECT (edge), fp); + fputc ('\n', fp); +} + +/** + * gts_graph_write: + * @g: a #GtsGraph. + * @fp: a file pointer. + * + * Writes in the file @fp an ASCII representation of @g. The file + * format is as follows. + * + * All the lines beginning with #GTS_COMMENTS are ignored. The first line + * contains two unsigned integers separated by spaces. The first + * integer is the number of nodes, nn, the second is the number of + * edges, ne. + * + * Follows nn lines containing node description. + * Follows ne lines containing the two indices (starting + * from one) of the nodes of each edge. + * + * The format described above is the least common denominator to all + * GTS files. Consistent with an object-oriented approach, the GTS + * file format is extensible. Each of the lines of the file can be + * extended with user-specific attributes accessible through the + * read() and write() virtual methods of each of the objects written + * (graph, nodes or edges). When read with different object classes, + * these extra attributes are just ignored. + */ +void gts_graph_write (GtsGraph * g, FILE * fp) +{ + guint nnode = 1, nedge = 0; + gpointer data[2]; + + g_return_if_fail (g != NULL); + g_return_if_fail (fp != NULL); + + gts_graph_foreach_edge (g, (GtsFunc) count_edges, &nedge); + fprintf (fp, "%u %u", gts_container_size (GTS_CONTAINER (g)), nedge); + if (GTS_OBJECT (g)->klass->write) + (* GTS_OBJECT (g)->klass->write) (GTS_OBJECT (g), fp); + fputc ('\n', fp); + data[0] = fp; + data[1] = &nnode; + gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) write_node, data); + gts_graph_foreach_edge (g, (GtsFunc) write_edge, fp); + gts_container_foreach (GTS_CONTAINER (g), + (GtsFunc) gts_object_reset_reserved, NULL); +} + +/** + * gts_graph_read: + * @fp: a #GtsFile. + * + * Reads a graph from a file. + * + * Returns: the new #GtsGraph or %NULL if an error occured (in which + * case the @error field of @fp is set). + */ +GtsGraph * gts_graph_read (GtsFile * fp) +{ + GtsGraph * g; + GtsGNode ** nodes; + guint nn, ne, n; + + g_return_val_if_fail (fp != NULL, NULL); + + if (fp->type != GTS_INT) { + gts_file_error (fp, "expecting an integer (number of nodes)"); + return NULL; + } + nn = atoi (fp->token->str); + gts_file_next_token (fp); + + if (fp->type != GTS_INT) { + gts_file_error (fp, "expecting an integer (number of edges)"); + return NULL; + } + ne = atoi (fp->token->str); + + gts_file_next_token (fp); + if (fp->type != '\n') { + GtsObjectClass * klass; + + gts_graph_class (); + gts_gnode_class (); + gts_gedge_class (); + + if (fp->type != GTS_STRING) { + gts_file_error (fp, "expecting a string (GtsGraphClass)"); + return NULL; + } + klass = gts_object_class_from_name (fp->token->str); + if (klass == NULL) { + gts_file_error (fp, "unknown class `%s'", fp->token->str); + return NULL; + } + if (!gts_object_class_is_from_class (klass, gts_graph_class ())) { + gts_file_error (fp, "class `%s' is not a GtsGraphClass", fp->token->str); + return NULL; + } + g = GTS_GRAPH (gts_object_new (klass)); + g->graph_class = GTS_GRAPH_CLASS (klass); + gts_file_next_token (fp); + (* klass->read) ((GtsObject **) &g, fp); + if (fp->type == GTS_ERROR) { + gts_object_destroy (GTS_OBJECT (g)); + return NULL; + } + } + else + g = GTS_GRAPH (gts_object_new (GTS_OBJECT_CLASS (gts_graph_class ()))); + gts_file_first_token_after (fp, '\n'); + if (nn <= 0) + return g; + + nodes = g_malloc ((nn + 1)*sizeof (GtsGNode *)); + + n = 0; + while (n < nn && fp->type != GTS_ERROR) { + GtsObject * new_node = + gts_object_new (GTS_OBJECT_CLASS (g->node_class)); + + gts_container_add (GTS_CONTAINER (g), GTS_CONTAINEE (new_node)); + if (GTS_OBJECT_CLASS (g->node_class)->read) + (*GTS_OBJECT_CLASS (g->node_class)->read) (&new_node, fp); + gts_file_first_token_after (fp, '\n'); + nodes[n++] = GTS_GNODE (new_node); + } + if (fp->type == GTS_ERROR) + nn = n; + + n = 0; + while (n < ne && fp->type != GTS_ERROR) { + guint n1, n2; + + if (fp->type != GTS_INT) + gts_file_error (fp, "expecting an integer (first node index)"); + else { + n1 = atoi (fp->token->str); + if (n1 == 0 || n1 > nn) + gts_file_error (fp, "node index `%d' is out of range `[1,%d]'", + n1, nn); + else { + gts_file_next_token (fp); + if (fp->type != GTS_INT) + gts_file_error (fp, "expecting an integer (second node index)"); + else { + n2 = atoi (fp->token->str); + if (n2 == 0 || n2 > nn) + gts_file_error (fp, "node index `%d' is out of range `[1,%d]'", + n2, nn); + else { + GtsGEdge * new_edge = + gts_gedge_new (g->edge_class, nodes[n1 - 1], nodes [n2 - 1]); + + gts_file_next_token (fp); + if (fp->type != '\n') + if (GTS_OBJECT_CLASS (g->edge_class)->read) + (*GTS_OBJECT_CLASS (g->edge_class)->read) + ((GtsObject **) &new_edge, fp); + gts_file_first_token_after (fp, '\n'); + n++; + } + } + } + } + } + + if (fp->type == GTS_ERROR) { + gts_allow_floating_gnodes = TRUE; + while (nn) + gts_object_destroy (GTS_OBJECT (nodes[nn-- - 1])); + gts_allow_floating_gnodes = FALSE; + } + g_free (nodes); + + if (fp->type == GTS_ERROR) { + gts_object_destroy (GTS_OBJECT (g)); + return NULL; + } + return g; +} + +static void write_dot_node (GtsGNode * node, gpointer * data) +{ + FILE * fp = data[0]; + guint * nnode = data[1]; + + fprintf (fp, " n%u", *nnode); + if (GTS_GNODE_CLASS (GTS_OBJECT (node)->klass)->write) { + fputs (" [", fp); + (* GTS_GNODE_CLASS (GTS_OBJECT (node)->klass)->write) (node, fp); + fputc (']', fp); + } + fputs (";\n", fp); + GTS_OBJECT (node)->reserved = GUINT_TO_POINTER ((*nnode)++); +} + +static void write_dot_edge (GtsGEdge * edge, FILE * fp) +{ + fprintf (fp, " n%u -> n%u", + GPOINTER_TO_UINT (GTS_OBJECT (edge->n1)->reserved), + GPOINTER_TO_UINT (GTS_OBJECT (edge->n2)->reserved)); + if (GTS_GEDGE_CLASS (GTS_OBJECT (edge)->klass)->write) { + fputs (" [", fp); + (* GTS_GEDGE_CLASS (GTS_OBJECT (edge)->klass)->write) (edge, fp); + fputc (']', fp); + } + fputs (";\n", fp); +} + +/** + * gts_graph_write_dot: + * @g: a #GtsGraph. + * @fp: a file pointer. + * + * Writes in the file @fp an ASCII representation of @g in the dot format of + * AT&T Bell Labs. + */ +void gts_graph_write_dot (GtsGraph * g, FILE * fp) +{ + guint nnode = 1; + gpointer data[2]; + + g_return_if_fail (g != NULL); + g_return_if_fail (fp != NULL); + + fprintf (fp, "digraph \"%p\" {\n", g); + data[0] = fp; + data[1] = &nnode; + gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) write_dot_node, data); + gts_graph_foreach_edge (g, (GtsFunc) write_dot_edge, fp); + fputs ("}\n", fp); + + gts_container_foreach (GTS_CONTAINER (g), + (GtsFunc) gts_object_reset_reserved, NULL); +} + +/* GtsWGraph */ + +static gfloat wgraph_weight (GtsGraph * g) +{ + return GTS_WGRAPH (g)->weight; +} + +static void wgraph_add (GtsContainer * g, GtsContainee * n) +{ + GtsWGraph * wg = GTS_WGRAPH (g); + gfloat w = gts_gnode_weight (GTS_GNODE (n)); + + wg->weight += w; + + (* GTS_CONTAINER_CLASS (GTS_OBJECT_CLASS (gts_wgraph_class ())->parent_class)->add) (g, n); +} + +static void wgraph_remove (GtsContainer * g, GtsContainee * n) +{ + GTS_WGRAPH (g)->weight -= gts_gnode_weight (GTS_GNODE (n)); + + (* GTS_CONTAINER_CLASS (GTS_OBJECT_CLASS (gts_wgraph_class ())->parent_class)->remove) (g, n); +} + +static void wgraph_class_init (GtsWGraphClass * klass) +{ + GTS_GRAPH_CLASS (klass)->weight = wgraph_weight; + + GTS_CONTAINER_CLASS (klass)->add = wgraph_add; + GTS_CONTAINER_CLASS (klass)->remove = wgraph_remove; +} + +static void wgraph_init (GtsWGraph * g) +{ + g->weight = 0.; +} + +/** + * gts_wgraph_class: + * + * Returns: the #GtsWGraphClass. + */ +GtsWGraphClass * gts_wgraph_class (void) +{ + static GtsWGraphClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo wgraph_info = { + "GtsWGraph", + sizeof (GtsWGraph), + sizeof (GtsWGraphClass), + (GtsObjectClassInitFunc) wgraph_class_init, + (GtsObjectInitFunc) wgraph_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_graph_class ()), + &wgraph_info); + } + + return klass; +} + +static void weight_max (GtsGNode * n, gfloat * wmax) +{ + gfloat w = gts_gnode_weight (n); + + if (w > *wmax) + *wmax = w; +} + +/** + * gts_wgraph_weight_max: + * @wg: a #GtsWGraph. + * + * Returns: the maximum weight of any vertices belonging to @g. + */ +gfloat gts_wgraph_weight_max (GtsWGraph * wg) +{ + gfloat wmax = - G_MAXFLOAT; + + g_return_val_if_fail (wg != NULL, 0.); + + gts_container_foreach (GTS_CONTAINER (wg), (GtsFunc) weight_max, &wmax); + + return wmax; +} + +/* Surface graph */ + +static void create_node (GtsFace * f, GtsGraph * graph) +{ + GtsFNode * fn = gts_fnode_new (gts_fnode_class (), f); + + gts_container_add (GTS_CONTAINER (graph), GTS_CONTAINEE (fn)); + GTS_OBJECT (f)->reserved = fn; +} + +static void create_edge (GtsEdge * e, GtsSurface * s) +{ + GSList * i = e->triangles; + + while (i) { + GtsFace * f = i->data; + if (GTS_IS_FACE (f) && gts_face_has_parent_surface (f, s)) { + GSList * j = i->next; + while (j) { + GtsFace * f1 = j->data; + if (GTS_IS_FACE (f1) && gts_face_has_parent_surface (f1, s)) + gts_pgedge_new (gts_pgedge_class (), + GTS_OBJECT (f)->reserved, + GTS_OBJECT (f1)->reserved, + e); + j = j->next; + } + } + i = i->next; + } +} + +/** + * gts_surface_graph_new: + * @klass: a #GtsGraphClass. + * @s: a #GtsSurface. + * + * Returns: a new #GtsGraph representing the connectivity of the faces + * of @s. This graph uses #GtsFGNode as nodes which allows to store + * the dependencies between nodes and faces of @s. + */ +GtsGraph * gts_surface_graph_new (GtsGraphClass * klass, + GtsSurface * s) +{ + GtsGraph * graph; + + g_return_val_if_fail (klass != NULL, NULL); + g_return_val_if_fail (s != NULL, NULL); + + graph = GTS_GRAPH (gts_object_new (GTS_OBJECT_CLASS (klass))); + gts_surface_foreach_face (s, (GtsFunc) create_node, graph); + gts_surface_foreach_edge (s, (GtsFunc) create_edge, s); + gts_surface_foreach_face (s, (GtsFunc) gts_object_reset_reserved, NULL); + + return graph; +} + +static void create_segment_edge (GtsSegment * s, GtsGraph * graph) +{ + GtsGNode * n1 = GTS_OBJECT (s->v1)->reserved, * n2; + + if (n1 == NULL) { + n1 = GTS_GNODE (gts_pnode_new (gts_pnode_class (), s->v1)); + gts_container_add (GTS_CONTAINER (graph), GTS_CONTAINEE (n1)); + GTS_OBJECT (s->v1)->reserved = n1; + } + + n2 = GTS_OBJECT (s->v2)->reserved; + if (n2 == NULL) { + n2 = GTS_GNODE (gts_pnode_new (gts_pnode_class (), s->v2)); + gts_container_add (GTS_CONTAINER (graph), GTS_CONTAINEE (n2)); + GTS_OBJECT (s->v2)->reserved = n2; + } + + gts_pgedge_new (gts_pgedge_class (), n1, n2, s); +} + +static void reset_reserved (GtsSegment * s) +{ + GTS_OBJECT (s->v1)->reserved = GTS_OBJECT (s->v2)->reserved = NULL; +} + +/** + * gts_segments_graph_new: + * @klass: a #GtsGraphClass. + * @segments: a list of #GtsSegment. + * + * Returns: a new #GtsGraph representing the connectivity of the segments + * in @segments. + */ +GtsGraph * gts_segments_graph_new (GtsGraphClass * klass, + GSList * segments) +{ + GtsGraph * graph; + + g_return_val_if_fail (klass != NULL, NULL); + + graph = GTS_GRAPH (gts_object_new (GTS_OBJECT_CLASS (klass))); + g_slist_foreach (segments, (GFunc) create_segment_edge, graph); + g_slist_foreach (segments, (GFunc) reset_reserved, NULL); + + return graph; +} + +static void add_to_surface (GtsGNode * n, GtsSurface * s) +{ + if (GTS_IS_FNODE (n)) + gts_surface_add_face (s, GTS_FNODE (n)->f); +} + +/** + * gts_surface_graph_surface: + * @surface_graph: a #GtsGraph using #GtsFGNode as nodes. + * @s: a #GtsSurface. + * + * Returns: a new #GtsSurface using the same classes as @s and + * composed of the faces defined by @surface_graph. + */ +GtsSurface * gts_surface_graph_surface (GtsGraph * surface_graph, + GtsSurface * s) +{ + GtsSurface * s1; + + g_return_val_if_fail (surface_graph != NULL, NULL); + g_return_val_if_fail (s != NULL, NULL); + + s1 = gts_surface_new (GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass), + s->face_class, + s->edge_class, + s->vertex_class); + gts_container_foreach (GTS_CONTAINER (surface_graph), + (GtsFunc) add_to_surface, s1); + return s1; +} + Index: trunk/src_3rd/gts/gts-private.h =================================================================== --- trunk/src_3rd/gts/gts-private.h (nonexistent) +++ trunk/src_3rd/gts/gts-private.h (revision 1065) @@ -0,0 +1,37 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GTS_PRIVATE_H__ +#define __GTS_PRIVATE_H__ + +/* Debugging flags */ + +/* #define DEBUG_FUNCTIONS */ + +#ifdef DEBUG_FUNCTIONS +/* #define DEBUG_LEAKS */ +#define DEBUG_IDENTITY +guint id (gpointer p); +void id_insert (gpointer p); +void id_remove (gpointer p); +void gts_write_triangle (GtsTriangle * t, GtsPoint * o, FILE * fptr); +void gts_write_segment (GtsSegment * s, GtsPoint * o, FILE * fptr); +#endif /* DEBUG_FUNCTIONS */ + +#endif /* __GTS_PRIVATE_H__ */ Index: trunk/src_3rd/gts/gts.h =================================================================== --- trunk/src_3rd/gts/gts.h (nonexistent) +++ trunk/src_3rd/gts/gts.h (revision 1065) @@ -0,0 +1,2577 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GTS_H__ +#define __GTS_H__ + +#include +#include + +#define GTS_MAJOR_VERSION 0 +#define GTS_MINOR_VERSION 7 +#define GTS_MICRO_VERSION 6 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Added based on glib.h by M J Loehr 01/01/01 */ +/* GTS version. + * we prefix variable declarations so they can + * properly get exported in windows dlls. + */ +#ifdef NATIVE_WIN32 +# ifdef GTS_COMPILATION +# define GTS_C_VAR __declspec(dllexport) +# else /* not GTS_COMPILATION */ +# define GTS_C_VAR extern __declspec(dllimport) +# endif /* not GTS_COMPILATION */ +#else /* not NATIVE_WIN32 */ +# define GTS_C_VAR extern +#endif /* not NATIVE_WIN32 */ + +GTS_C_VAR const guint gts_major_version; +GTS_C_VAR const guint gts_minor_version; +GTS_C_VAR const guint gts_micro_version; +GTS_C_VAR const guint gts_interface_age; +GTS_C_VAR const guint gts_binary_age; + +#define GTS_CHECK_VERSION(major,minor,micro) \ + (gts_major_version > (major) || \ + (gts_major_version == (major) && gts_minor_version > (minor)) || \ + (gts_major_version == (major) && gts_minor_version == (minor) && \ + gts_micro_version >= (micro))) + +#define GTS_COMMENTS "#!" +#define GTS_MAINTAINER "popinet@users.sourceforge.net" + + + + +void gts_predicates_init(); + + +/* Class declarations for base types */ + +typedef struct _GtsObjectClassInfo GtsObjectClassInfo; +typedef struct _GtsObject GtsObject; +typedef struct _GtsObjectClass GtsObjectClass; +typedef struct _GtsPoint GtsPoint; +typedef struct _GtsPointClass GtsPointClass; +typedef struct _GtsVertex GtsVertex; +typedef struct _GtsVertexClass GtsVertexClass; +typedef struct _GtsSegment GtsSegment; +typedef struct _GtsSegmentClass GtsSegmentClass; +typedef struct _GtsEdge GtsEdge; +typedef struct _GtsEdgeClass GtsEdgeClass; +typedef struct _GtsTriangle GtsTriangle; +typedef struct _GtsTriangleClass GtsTriangleClass; +typedef struct _GtsFace GtsFace; +typedef struct _GtsFaceClass GtsFaceClass; +typedef struct _GtsBBox GtsBBox; +typedef struct _GtsBBoxClass GtsBBoxClass; +typedef struct _GtsSurface GtsSurface; +typedef struct _GtsSurfaceClass GtsSurfaceClass; + +typedef void (*GtsObjectClassInitFunc) (GtsObjectClass * objclass); +typedef void (*GtsObjectInitFunc) (GtsObject * obj); +typedef void (*GtsArgSetFunc) (GtsObject * obj); +typedef void (*GtsArgGetFunc) (GtsObject * obj); + +typedef gdouble GtsVector[3]; +typedef gdouble GtsVector4[4]; +typedef GtsVector4 GtsMatrix; +/** + * GtsKeyFunc: + * @item: A pointer to an item to be stored in the heap. + * @data: User data passed to gts_eheap_new(). + * + * Returns: the value of the key for the given item. + */ +typedef gdouble (*GtsKeyFunc) (gpointer item, + gpointer data); +typedef enum +{ + GTS_OUT = -1, + GTS_ON = 0, + GTS_IN = 1 +} GtsIntersect; + +typedef struct _GtsColor GtsColor; + +struct _GtsColor { + gfloat r, g, b; +}; + +typedef gint (*GtsFunc) (gpointer item, + gpointer data); + +/* misc.c */ + +typedef struct _GtsFile GtsFile; + +typedef enum { + GTS_NONE = 1 << 8, + GTS_INT = 1 << 9, + GTS_UINT = 1 << 10, + GTS_FLOAT = 1 << 11, + GTS_DOUBLE = 1 << 12, + GTS_STRING = 1 << 13, + GTS_FILE = 1 << 14, + GTS_ERROR = 1 << 15 +} GtsTokenType; + +struct _GtsFile { + FILE * fp; + gchar * s, * s1; + guint line, pos; + GString * token; + GtsTokenType type; + gchar * error; + + guint curline, curpos; + guint scope, scope_max; + gint next_token; + gchar * delimiters; + gchar * comments; + gchar * tokens; +}; + +typedef struct _GtsFileVariable GtsFileVariable; + +struct _GtsFileVariable { + GtsTokenType type; + gchar name[30]; + gboolean unique; + gpointer data; + gboolean set; + guint line, pos; +}; + + +GtsFile * gts_file_new (FILE * fp); +GtsFile * gts_file_new_from_string (const gchar * s); +void gts_file_verror (GtsFile * f, + const gchar * format, + va_list args); +void gts_file_error (GtsFile * f, + const gchar * format, + ...); +gint gts_file_getc (GtsFile * f); +guint gts_file_read (GtsFile * f, + gpointer ptr, + guint size, + guint nmemb); +gint gts_file_getc_scope (GtsFile * f); +void gts_file_next_token (GtsFile * f); +void gts_file_first_token_after (GtsFile * f, + GtsTokenType type); +void gts_file_assign_start (GtsFile * f, + GtsFileVariable * vars); +GtsFileVariable * gts_file_assign_next (GtsFile * f, + GtsFileVariable * vars); +void gts_file_assign_variables (GtsFile * f, + GtsFileVariable * vars); +void gts_file_variable_error (GtsFile * f, + GtsFileVariable * vars, + const gchar * name, + const gchar * format, + ...); +void gts_file_destroy (GtsFile * f); + +/* Objects: object.c */ + +#ifdef GTS_CHECK_CASTS +# define GTS_OBJECT_CAST(obj, type, klass) ((type *) gts_object_check_cast (obj, klass)) +# define GTS_OBJECT_CLASS_CAST(objklass, type, klass) ((type *) gts_object_class_check_cast (objklass, klass)) +#else /* not GTS_CHECK_CASTS */ +# define GTS_OBJECT_CAST(obj, type, klass) ((type *) (obj)) +# define GTS_OBJECT_CLASS_CAST(objklass, type, klass) ((type *) (objklass)) +#endif /* not GTS_CHECK_CASTS */ + +#define GTS_CLASS_NAME_LENGTH 40 +#define GTS_OBJECT(obj) GTS_OBJECT_CAST (obj,\ + GtsObject,\ + gts_object_class ()) +#define GTS_OBJECT_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsObjectClass,\ + gts_object_class()) +#define GTS_IS_OBJECT(obj) (gts_object_is_from_class (obj,\ + gts_object_class ())) + +typedef enum +{ + GTS_DESTROYED = 1 << 0, + GTS_USER_FLAG = 1 /* user flags start from here */ +} GtsObjectFlags; + +#define GTS_OBJECT_FLAGS(obj) (GTS_OBJECT (obj)->flags) +#define GTS_OBJECT_DESTROYED(obj) ((GTS_OBJECT_FLAGS (obj) & GTS_DESTROYED) != 0) +#define GTS_OBJECT_SET_FLAGS(obj,flag) G_STMT_START{ (GTS_OBJECT_FLAGS (obj) |= (flag)); }G_STMT_END +#define GTS_OBJECT_UNSET_FLAGS(obj,flag) G_STMT_START{ (GTS_OBJECT_FLAGS (obj) &= ~(flag)); }G_STMT_END + +struct _GtsObjectClassInfo { + gchar name[GTS_CLASS_NAME_LENGTH]; + guint object_size; + guint class_size; + GtsObjectClassInitFunc class_init_func; + GtsObjectInitFunc object_init_func; + GtsArgSetFunc arg_set_func; + GtsArgGetFunc arg_get_func; +}; + +struct _GtsObject { + GtsObjectClass * klass; + + gpointer reserved; + guint32 flags; +}; + +struct _GtsObjectClass { + GtsObjectClassInfo info; + GtsObjectClass * parent_class; + + void (* clone) (GtsObject *, GtsObject *); + void (* destroy) (GtsObject *); + void (* read) (GtsObject **, GtsFile *); + void (* write) (GtsObject *, FILE *); + GtsColor (* color) (GtsObject *); + void (* attributes) (GtsObject *, GtsObject *); +}; + +gpointer gts_object_class_new (GtsObjectClass * parent_class, + GtsObjectClassInfo * info); +GtsObjectClass * gts_object_class (void); +gpointer gts_object_check_cast (gpointer object, + gpointer klass); +gpointer gts_object_class_check_cast (gpointer klass, + gpointer from); + +static inline +gpointer gts_object_is_from_class (gpointer object, + gpointer klass) +{ + GtsObjectClass * c; + + g_return_val_if_fail (klass != NULL, NULL); + + if (object == NULL) + return NULL; + + c = ((GtsObject *) object)->klass; + + g_return_val_if_fail (c != NULL, NULL); + + while (c) { + if (c == klass) + return object; + c = c->parent_class; + } + + return NULL; +} + +static inline +gpointer gts_object_class_is_from_class (gpointer klass, + gpointer from) +{ + GtsObjectClass * c; + + g_return_val_if_fail (klass != NULL, NULL); + g_return_val_if_fail (from != NULL, NULL); + + c = (GtsObjectClass *) klass; + while (c) { + if (c == from) + return klass; + c = c->parent_class; + } + + return NULL; +} + +GtsObjectClass * gts_object_class_from_name (const gchar * name); + +GtsObject * gts_object_new (GtsObjectClass * klass); +GtsObject * gts_object_clone (GtsObject * object); +void gts_object_attributes (GtsObject * object, + GtsObject * from); +void gts_object_init (GtsObject * object, + GtsObjectClass * klass); +void gts_object_reset_reserved (GtsObject * object); +void gts_object_destroy (GtsObject * object); +void gts_finalize (void); + +/* Ranges: surface.c */ +typedef struct _GtsRange GtsRange; + +struct _GtsRange { + gdouble min, max, sum, sum2, mean, stddev; + guint n; +}; + +void gts_range_init (GtsRange * r); +void gts_range_reset (GtsRange * r); +void gts_range_add_value (GtsRange * r, + gdouble val); +void gts_range_update (GtsRange * r); +void gts_range_print (GtsRange * r, + FILE * fptr); + +/* Points: point.c */ + +#define GTS_IS_POINT(obj) (gts_object_is_from_class (obj,\ + gts_point_class ())) +#define GTS_POINT(obj) GTS_OBJECT_CAST (obj,\ + GtsPoint,\ + gts_point_class ()) +#define GTS_POINT_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsPointClass,\ + gts_point_class ()) + +struct _GtsPoint { + GtsObject object; + + gdouble x, y, z; /* must be contiguous (cast to robust functions) */ +}; + +struct _GtsPointClass { + GtsObjectClass parent_class; + gboolean binary; +}; + +GtsPointClass * gts_point_class (void); +GtsPoint * gts_point_new (GtsPointClass * klass, + gdouble x, + gdouble y, + gdouble z); +void gts_point_set (GtsPoint * p, + gdouble x, + gdouble y, + gdouble z); +#define gts_point_is_in_rectangle(p, p1, p2) ((p)->x >= (p1)->x &&\ + (p)->x <= (p2)->x &&\ + (p)->y >= (p1)->y &&\ + (p)->y <= (p2)->y &&\ + (p)->z >= (p1)->z &&\ + (p)->z <= (p2)->z) +GtsPoint * gts_segment_triangle_intersection (GtsSegment * s, + GtsTriangle * t, + gboolean boundary, + GtsPointClass * klass); +void gts_point_transform (GtsPoint * p, + GtsMatrix * m); +gdouble gts_point_distance (GtsPoint * p1, + GtsPoint * p2); +gdouble gts_point_distance2 (GtsPoint * p1, + GtsPoint * p2); +gdouble gts_point_orientation_3d (GtsPoint * p1, + GtsPoint * p2, + GtsPoint * p3, + GtsPoint * p4); +gint gts_point_orientation_3d_sos (GtsPoint * p1, + GtsPoint * p2, + GtsPoint * p3, + GtsPoint * p4); +GtsIntersect gts_point_is_in_triangle (GtsPoint * p, + GtsTriangle * t); +gdouble gts_point_in_circle (GtsPoint * p, + GtsPoint * p1, + GtsPoint * p2, + GtsPoint * p3); +gdouble gts_point_in_sphere (GtsPoint * p, + GtsPoint * p1, + GtsPoint * p2, + GtsPoint * p3, + GtsPoint * p4); +gdouble gts_point_in_triangle_circle (GtsPoint * p, + GtsTriangle * t); +gdouble gts_point_orientation (GtsPoint * p1, + GtsPoint * p2, + GtsPoint * p3); +gint gts_point_orientation_sos (GtsPoint * p1, + GtsPoint * p2, + GtsPoint * p3); +gdouble gts_point_segment_distance2 (GtsPoint * p, + GtsSegment * s); +gdouble gts_point_segment_distance (GtsPoint * p, + GtsSegment * s); +void gts_point_segment_closest (GtsPoint * p, + GtsSegment * s, + GtsPoint * closest); +gdouble gts_point_triangle_distance2 (GtsPoint * p, + GtsTriangle * t); +gdouble gts_point_triangle_distance (GtsPoint * p, + GtsTriangle * t); +void gts_point_triangle_closest (GtsPoint * p, + GtsTriangle * t, + GtsPoint * closest); +gboolean gts_point_is_inside_surface (GtsPoint * p, + GNode * tree, + gboolean is_open); + +/* Vertices: vertex.c */ + +#define GTS_IS_VERTEX(obj) (gts_object_is_from_class (obj,\ + gts_vertex_class ())) +#define GTS_VERTEX(obj) GTS_OBJECT_CAST (obj,\ + GtsVertex,\ + gts_vertex_class ()) +#define GTS_VERTEX_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsVertexClass,\ + gts_vertex_class ()) +struct _GtsVertex { + GtsPoint p; + + GSList * segments; +}; + +struct _GtsVertexClass { + GtsPointClass parent_class; + + void (* intersection_attributes) (GtsVertex *, + GtsObject *, + GtsObject *); +}; + +GTS_C_VAR +gboolean gts_allow_floating_vertices; + +GtsVertexClass * gts_vertex_class (void); +GtsVertex * gts_vertex_new (GtsVertexClass * klass, + gdouble x, + gdouble y, + gdouble z); +void gts_vertex_replace (GtsVertex * v, + GtsVertex * with); +gboolean gts_vertex_is_unattached (GtsVertex * v); +GtsSegment * gts_vertices_are_connected (GtsVertex * v1, + GtsVertex * v2); +GSList * gts_vertex_triangles (GtsVertex * v, + GSList * list); +GSList * gts_vertex_faces (GtsVertex * v, + GtsSurface * surface, + GSList * list); +GSList * gts_vertex_neighbors (GtsVertex * v, + GSList * list, + GtsSurface * surface); +GSList * gts_vertices_from_segments (GSList * segments); +gboolean gts_vertex_is_boundary (GtsVertex * v, + GtsSurface * surface); +GList * gts_vertices_merge (GList * vertices, + gdouble epsilon, + gboolean (* check) (GtsVertex *, GtsVertex *)); +GSList * gts_vertex_fan_oriented (GtsVertex * v, + GtsSurface * surface); +guint gts_vertex_is_contact (GtsVertex * v, gboolean sever); + +/* GtsVertexNormal: Header */ + +typedef struct _GtsVertexNormal GtsVertexNormal; + +struct _GtsVertexNormal { + /*< private >*/ + GtsVertex parent; + + /*< public >*/ + GtsVector n; +}; + +#define GTS_VERTEX_NORMAL(obj) GTS_OBJECT_CAST (obj,\ + GtsVertexNormal,\ + gts_vertex_normal_class ()) +#define GTS_IS_VERTEX_NORMAL(obj) (gts_object_is_from_class (obj,\ + gts_vertex_normal_class ())) + +GtsVertexClass * gts_vertex_normal_class (void); + +/* GtsColorVertex: Header */ + +typedef struct _GtsColorVertex GtsColorVertex; + +struct _GtsColorVertex { + /*< private >*/ + GtsVertex parent; + + /*< public >*/ + GtsColor c; +}; + +#define GTS_COLOR_VERTEX(obj) GTS_OBJECT_CAST (obj,\ + GtsColorVertex,\ + gts_color_vertex_class ()) +#define GTS_IS_COLOR_VERTEX(obj) (gts_object_is_from_class (obj,\ + gts_color_vertex_class ())) + +GtsVertexClass * gts_color_vertex_class (void); + +/* Segments: segment.c */ + +#define GTS_IS_SEGMENT(obj) (gts_object_is_from_class (obj,\ + gts_segment_class ())) +#define GTS_SEGMENT(obj) GTS_OBJECT_CAST (obj,\ + GtsSegment,\ + gts_segment_class ()) +#define GTS_SEGMENT_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsSegmentClass,\ + gts_segment_class ()) + +struct _GtsSegment { + GtsObject object; + + GtsVertex * v1; + GtsVertex * v2; +}; + +struct _GtsSegmentClass { + GtsObjectClass parent_class; +}; + +GtsSegmentClass * gts_segment_class (void); +GtsSegment * gts_segment_new (GtsSegmentClass * klass, + GtsVertex * v1, + GtsVertex * v2); +#define gts_segment_connect(s, e1, e2) (((s)->v1 == e1 &&\ + (s)->v2 == e2) || \ + ((s)->v1 == e2 &&\ + (s)->v2 == e1)) +#define gts_segments_are_identical(s1, s2) (((s1)->v1 == (s2)->v1 &&\ + (s1)->v2 == (s2)->v2)\ + ||\ + ((s1)->v1 == (s2)->v2 &&\ + (s1)->v2 == (s2)->v1)) +#define gts_segments_touch(s1, s2) ((s1)->v1 == (s2)->v1 ||\ + (s1)->v1 == (s2)->v2 ||\ + (s1)->v2 == (s2)->v1 ||\ + (s1)->v2 == (s2)->v2) +GtsIntersect gts_segments_are_intersecting (GtsSegment * s1, + GtsSegment * s2); +GtsSegment * gts_segment_is_duplicate (GtsSegment * s); +GtsVertex * gts_segment_midvertex (GtsSegment * s, + GtsVertexClass * klass); +GSList * gts_segments_from_vertices (GSList * vertices); +gboolean gts_segment_is_ok (GtsSegment * s); + +/* Edges: edge.c */ + +#define GTS_IS_EDGE(obj) (gts_object_is_from_class (obj,\ + gts_edge_class ())) +#define GTS_EDGE(obj) GTS_OBJECT_CAST (obj,\ + GtsEdge,\ + gts_edge_class ()) +#define GTS_EDGE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsEdgeClass,\ + gts_edge_class ()) + +struct _GtsEdge { + GtsSegment segment; + + GSList * triangles; +}; + +struct _GtsEdgeClass { + GtsSegmentClass parent_class; +}; + +GTS_C_VAR +gboolean gts_allow_floating_edges; + +GtsEdgeClass * gts_edge_class (void); +GtsEdge * gts_edge_new (GtsEdgeClass * klass, + GtsVertex * v1, + GtsVertex * v2); +/** + * gts_edge_is_unattached: + * @s: a #GtsEdge. + * + * Evaluates to %TRUE if no triangles uses @s as an edge, %FALSE otherwise. + */ +#define gts_edge_is_unattached(s) ((s)->triangles == NULL ? TRUE : FALSE) +GtsFace * gts_edge_has_parent_surface (GtsEdge * e, + GtsSurface * surface); +GtsFace * gts_edge_has_any_parent_surface (GtsEdge * e); +GtsFace * gts_edge_is_boundary (GtsEdge * e, + GtsSurface * surface); +void gts_edge_replace (GtsEdge * e, + GtsEdge * with); +GSList * gts_edges_from_vertices (GSList * vertices, + GtsSurface * parent); +guint gts_edge_face_number (GtsEdge * e, + GtsSurface * s); +gboolean gts_edge_collapse_is_valid (GtsEdge * e); +gboolean gts_edge_collapse_creates_fold (GtsEdge * e, + GtsVertex * v, + gdouble max); +GtsEdge * gts_edge_is_duplicate (GtsEdge * e); +GList * gts_edges_merge (GList * edges); +gboolean gts_edge_belongs_to_tetrahedron (GtsEdge * e); +guint gts_edge_is_contact (GtsEdge * e); +void gts_edge_swap (GtsEdge * e, + GtsSurface * s); +gboolean gts_edge_manifold_faces (GtsEdge * e, + GtsSurface * s, + GtsFace ** f1, + GtsFace ** f2); + +/* Triangles: triangle.c */ + +#define GTS_IS_TRIANGLE(obj) (gts_object_is_from_class (obj,\ + gts_triangle_class ())) +#define GTS_TRIANGLE(obj) GTS_OBJECT_CAST (obj,\ + GtsTriangle,\ + gts_triangle_class ()) +#define GTS_TRIANGLE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsTriangleClass,\ + gts_triangle_class ()) + +struct _GtsTriangle { + GtsObject object; + + GtsEdge * e1; + GtsEdge * e2; + GtsEdge * e3; +}; + +struct _GtsTriangleClass { + GtsObjectClass parent_class; +}; + +GtsTriangleClass * gts_triangle_class (void); +void gts_triangle_set (GtsTriangle * triangle, + GtsEdge * e1, + GtsEdge * e2, + GtsEdge * e3); +GtsTriangle * gts_triangle_new (GtsTriangleClass * klass, + GtsEdge * e1, + GtsEdge * e2, + GtsEdge * e3); +#define gts_triangle_vertex(t) (GTS_SEGMENT (GTS_TRIANGLE (t)->e1)->v1 ==\ + GTS_SEGMENT (GTS_TRIANGLE (t)->e2)->v1 || \ + GTS_SEGMENT (GTS_TRIANGLE (t)->e1)->v2 ==\ + GTS_SEGMENT (GTS_TRIANGLE (t)->e2)->v1 ? \ + GTS_SEGMENT (GTS_TRIANGLE (t)->e2)->v2 :\ + GTS_SEGMENT (GTS_TRIANGLE (t)->e2)->v1) +GtsVertex * gts_triangle_vertex_opposite (GtsTriangle * t, + GtsEdge * e); +GtsEdge * gts_triangle_edge_opposite (GtsTriangle * t, + GtsVertex * v); +gdouble gts_triangles_angle (GtsTriangle * t1, + GtsTriangle * t2); +gboolean gts_triangles_are_compatible (GtsTriangle * t1, + GtsTriangle * t2, + GtsEdge * e); +gdouble gts_triangle_area (GtsTriangle * t); +gdouble gts_triangle_perimeter (GtsTriangle * t); +gdouble gts_triangle_quality (GtsTriangle * t); +void gts_triangle_normal (GtsTriangle * t, + gdouble * x, + gdouble * y, + gdouble * z); +gdouble gts_triangle_orientation (GtsTriangle * t); +void gts_triangle_revert (GtsTriangle * t); +GSList * gts_triangles_from_edges (GSList * edges); +void gts_triangle_vertices_edges (GtsTriangle * t, + GtsEdge * e, + GtsVertex ** v1, + GtsVertex ** v2, + GtsVertex ** v3, + GtsEdge ** e1, + GtsEdge ** e2, + GtsEdge ** e3); +GtsTriangle * gts_triangle_enclosing (GtsTriangleClass * klass, + GSList * points, + gdouble scale); +guint gts_triangle_neighbor_number (GtsTriangle * t); +GSList * gts_triangle_neighbors (GtsTriangle * t); +GtsEdge * gts_triangles_common_edge (GtsTriangle * t1, + GtsTriangle * t2); +GtsTriangle * gts_triangle_is_duplicate (GtsTriangle * t); +GtsTriangle * gts_triangle_use_edges (GtsEdge * e1, + GtsEdge * e2, + GtsEdge * e3); +gboolean gts_triangle_is_ok (GtsTriangle * t); +void gts_triangle_vertices (GtsTriangle * t, + GtsVertex ** v1, + GtsVertex ** v2, + GtsVertex ** v3); +GtsPoint * gts_triangle_circumcircle_center (GtsTriangle * t, + GtsPointClass * point_class); +gboolean gts_triangles_are_folded (GSList * triangles, + GtsVertex * A, GtsVertex * B, + gdouble max); +GtsObject * gts_triangle_is_stabbed (GtsTriangle * t, + GtsPoint * p, + gdouble * orientation); +void gts_triangle_interpolate_height (GtsTriangle * t, + GtsPoint * p); + +/* Faces: face.c */ + +#define GTS_IS_FACE(obj) (gts_object_is_from_class (obj,\ + gts_face_class ())) +#define GTS_FACE(obj) GTS_OBJECT_CAST (obj,\ + GtsFace,\ + gts_face_class ()) +#define GTS_FACE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsFaceClass,\ + gts_face_class ()) + +struct _GtsFace { + GtsTriangle triangle; + + GSList * surfaces; +}; + +struct _GtsFaceClass { + GtsTriangleClass parent_class; +}; + +GTS_C_VAR +gboolean gts_allow_floating_faces; + +GtsFaceClass * gts_face_class (void); +GtsFace * gts_face_new (GtsFaceClass * klass, + GtsEdge * e1, + GtsEdge * e2, + GtsEdge * e3); +gboolean gts_face_has_parent_surface (GtsFace * f, + GtsSurface * s); +GSList * gts_faces_from_edges (GSList * edges, + GtsSurface * s); +guint gts_face_neighbor_number (GtsFace * f, + GtsSurface * s); +GSList * gts_face_neighbors (GtsFace * f, + GtsSurface * s); +void gts_face_foreach_neighbor (GtsFace * f, + GtsSurface * s, + GtsFunc func, + gpointer data); +gboolean gts_face_is_compatible (GtsFace * f, + GtsSurface * s); + +/* Matrices: matrix.c */ + +#define gts_vector_cross(C,A,B) ((C)[0] = (A)[1]*(B)[2] - (A)[2]*(B)[1],\ + (C)[1] = (A)[2]*(B)[0] - (A)[0]*(B)[2],\ + (C)[2] = (A)[0]*(B)[1] - (A)[1]*(B)[0]) + +#define gts_vector_init(v, p1, p2) ((v)[0] = (p2)->x - (p1)->x,\ + (v)[1] = (p2)->y - (p1)->y,\ + (v)[2] = (p2)->z - (p1)->z) +#define gts_vector_scalar(v1, v2) ((v1)[0]*(v2)[0] +\ + (v1)[1]*(v2)[1] +\ + (v1)[2]*(v2)[2]) +#define gts_vector_norm(v) (sqrt ((v)[0]*(v)[0] +\ + (v)[1]*(v)[1] +\ + (v)[2]*(v)[2])) +#define gts_vector_normalize(v) {\ + gdouble __gts_n = gts_vector_norm (v);\ + if (__gts_n > 0.) {\ + (v)[0] /= __gts_n;\ + (v)[1] /= __gts_n;\ + (v)[2] /= __gts_n;\ + }\ +} +GtsMatrix * gts_matrix_new (gdouble a00, gdouble a01, gdouble a02, gdouble a03, + gdouble a10, gdouble a11, gdouble a12, gdouble a13, + gdouble a20, gdouble a21, gdouble a22, gdouble a23, + gdouble a30, gdouble a31, gdouble a32, gdouble a33); +void gts_matrix_assign (GtsMatrix * m, + gdouble a00, gdouble a01, gdouble a02, gdouble a03, + gdouble a10, gdouble a11, gdouble a12, gdouble a13, + gdouble a20, gdouble a21, gdouble a22, gdouble a23, + gdouble a30, gdouble a31, gdouble a32, gdouble a33); +GtsMatrix * gts_matrix_projection (GtsTriangle * t); +GtsMatrix * gts_matrix_transpose (GtsMatrix * m); +gdouble gts_matrix_determinant (GtsMatrix * m); +GtsMatrix * gts_matrix_inverse (GtsMatrix * m); +GtsMatrix * gts_matrix3_inverse (GtsMatrix * m); +void gts_matrix_print (GtsMatrix * m, + FILE * fptr); +guint gts_matrix_compatible_row (GtsMatrix * A, + GtsVector b, + guint n, + GtsVector A1, + gdouble b1); +guint gts_matrix_quadratic_optimization (GtsMatrix * A, + GtsVector b, + guint n, + GtsMatrix * H, + GtsVector c); +GtsMatrix * gts_matrix_product (GtsMatrix * m1, + GtsMatrix * m2); +GtsMatrix * gts_matrix_zero (GtsMatrix * m); +GtsMatrix * gts_matrix_identity (GtsMatrix * m); +GtsMatrix * gts_matrix_scale (GtsMatrix * m, + GtsVector s); +GtsMatrix * gts_matrix_translate (GtsMatrix * m, + GtsVector t); +GtsMatrix * gts_matrix_rotate (GtsMatrix * m, + GtsVector r, + gdouble angle); +void gts_matrix_destroy (GtsMatrix * m); +void gts_vector_print (GtsVector v, + FILE * fptr); +void gts_vector4_print (GtsVector4 v, + FILE * fptr); + +/* Kdtrees: kdtree.c */ + +#define gts_kdtree_destroy(tree) g_node_destroy(tree) + +GNode * gts_kdtree_new (GPtrArray * points, + int (*compare) + (const void *, + const void *)); +GSList * gts_kdtree_range (GNode * tree, + GtsBBox * bbox, + int (*compare) + (const void *, + const void *)); + +/* Bboxtrees: bbtree.c */ + +/** + * GtsBBTreeTraverseFunc: + * @bb1: a #GtsBBox. + * @bb2: another #GtsBBox. + * @data: user data passed to the function. + * + * User function called for each pair of overlapping bounding + * boxes. See gts_bb_tree_traverse_overlapping(). + */ +typedef void (*GtsBBTreeTraverseFunc) (GtsBBox * bb1, + GtsBBox * bb2, + gpointer data); +/** + * GtsBBoxDistFunc: + * @p: a #GtsPoint. + * @bounded: an object bounded by a #GtsBBox. + * + * User function returning the (minimum) distance between the object + * defined by @bounded and point @p. + * + * Returns: the distance between @p and @bounded. + */ +typedef gdouble (*GtsBBoxDistFunc) (GtsPoint * p, + gpointer bounded); +/** + * GtsBBoxClosestFunc: + * @p: a #GtsPoint. + * @bounded: an object bounded by a #GtsBBox. + * + * User function returning a #GtsPoint belonging to the object defined + * by @bounded and closest to @p. + * + * Returns: a #GtsPoint. + */ +typedef GtsPoint * (*GtsBBoxClosestFunc) (GtsPoint * p, + gpointer bounded); + +/** + * GTS_IS_BBOX: + * @obj: a #GtsObject. + * + * Evaluates to %TRUE if @obj is a #GtsBBox, %FALSE otherwise. + */ +#define GTS_IS_BBOX(obj) (gts_object_is_from_class (obj,\ + gts_bbox_class ())) +/** + * GTS_BBOX: + * @obj: a #GtsObject. + * + * Casts @obj to #GtsBBox. + */ +#define GTS_BBOX(obj) GTS_OBJECT_CAST (obj,\ + GtsBBox,\ + gts_bbox_class ()) +/** + * GTS_BBOX_CLASS: + * @klass: a descendant of #GtsBBoxClass. + * + * Casts @klass to #GtsBBoxClass. + */ +#define GTS_BBOX_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsBBoxClass,\ + gts_bbox_class ()) + +struct _GtsBBox { + GtsObject object; + gpointer bounded; + gdouble x1, y1, z1; + gdouble x2, y2, z2; +}; + +struct _GtsBBoxClass { + GtsObjectClass parent_class; +}; + +GtsBBoxClass * gts_bbox_class (void); +GtsBBox * gts_bbox_new (GtsBBoxClass * klass, + gpointer bounded, + gdouble x1, + gdouble y1, + gdouble z1, + gdouble x2, + gdouble y2, + gdouble z2); +void gts_bbox_set (GtsBBox * bbox, + gpointer bounded, + gdouble x1, + gdouble y1, + gdouble z1, + gdouble x2, + gdouble y2, + gdouble z2); +GtsBBox * gts_bbox_segment (GtsBBoxClass * klass, + GtsSegment * s); +GtsBBox * gts_bbox_triangle (GtsBBoxClass * klass, + GtsTriangle * t); +GtsBBox * gts_bbox_surface (GtsBBoxClass * klass, + GtsSurface * surface); +GtsBBox * gts_bbox_bboxes (GtsBBoxClass * klass, + GSList * bboxes); +GtsBBox * gts_bbox_points (GtsBBoxClass * klass, + GSList * points); +/** + * gts_bbox_point_is_inside: + * @bbox: a #GtsBBox. + * @p: a #GtsPoint. + * + * Evaluates to %TRUE if @p is inside (or on the boundary) of @bbox, %FALSE otherwise. + */ +#define gts_bbox_point_is_inside(bbox, p) ((p)->x >= (bbox)->x1 &&\ + (p)->y >= (bbox)->y1 &&\ + (p)->z >= (bbox)->z1 &&\ + (p)->x <= (bbox)->x2 &&\ + (p)->y <= (bbox)->y2 &&\ + (p)->z <= (bbox)->z2) +gboolean gts_bboxes_are_overlapping (GtsBBox * bb1, + GtsBBox * bb2); +void gts_bbox_draw (GtsBBox * bb, + FILE * fptr); +gdouble gts_bbox_diagonal2 (GtsBBox * bb); +void gts_bbox_point_distance2 (GtsBBox * bb, + GtsPoint * p, + gdouble * min, + gdouble * max); +gboolean gts_bbox_is_stabbed (GtsBBox * bb, + GtsPoint * p); +gboolean gts_bbox_overlaps_triangle (GtsBBox * bb, + GtsTriangle * t); +gboolean gts_bbox_overlaps_segment (GtsBBox * bb, + GtsSegment * s); + +GNode * gts_bb_tree_new (GSList * bboxes); +GNode * gts_bb_tree_surface (GtsSurface * s); +GSList * gts_bb_tree_stabbed (GNode * tree, + GtsPoint * p); +GSList * gts_bb_tree_overlap (GNode * tree, + GtsBBox * bbox); +gboolean gts_bb_tree_is_overlapping (GNode * tree, + GtsBBox * bbox); +void gts_bb_tree_traverse_overlapping (GNode * tree1, + GNode * tree2, + GtsBBTreeTraverseFunc func, + gpointer data); +void gts_bb_tree_draw (GNode * tree, + guint depth, + FILE * fptr); +GSList * gts_bb_tree_point_closest_bboxes (GNode * tree, + GtsPoint * p); +gdouble gts_bb_tree_point_distance (GNode * tree, + GtsPoint * p, + GtsBBoxDistFunc distance, + GtsBBox ** bbox); +GtsPoint * gts_bb_tree_point_closest (GNode * tree, + GtsPoint * p, + GtsBBoxClosestFunc closest, + gdouble * distance); +void gts_bb_tree_segment_distance (GNode * tree, + GtsSegment * s, + GtsBBoxDistFunc distance, + gdouble delta, + GtsRange * range); +void gts_bb_tree_triangle_distance (GNode * tree, + GtsTriangle * t, + GtsBBoxDistFunc distance, + gdouble delta, + GtsRange * range); +void gts_bb_tree_surface_distance (GNode * tree, + GtsSurface * s, + GtsBBoxDistFunc distance, + gdouble delta, + GtsRange * range); +void gts_bb_tree_surface_boundary_distance + (GNode * tree, + GtsSurface * s, + GtsBBoxDistFunc distance, + gdouble delta, + GtsRange * range); +void gts_bb_tree_destroy (GNode * tree, + gboolean free_leaves); + +/* Surfaces: surface.c */ + +typedef struct _GtsSurfaceStats GtsSurfaceStats; +typedef struct _GtsSurfaceQualityStats GtsSurfaceQualityStats; +typedef GtsVertex * (*GtsRefineFunc) (GtsEdge * e, + GtsVertexClass * klass, + gpointer data); +typedef GtsVertex * (*GtsCoarsenFunc) (GtsEdge * e, + GtsVertexClass * klass, + gpointer data); +typedef gboolean (*GtsStopFunc) (gdouble cost, + guint nedge, + gpointer data); + +struct _GtsSurfaceStats { + guint n_faces; + guint n_incompatible_faces; + guint n_duplicate_faces; + guint n_duplicate_edges; + guint n_boundary_edges; + guint n_non_manifold_edges; + GtsRange edges_per_vertex, faces_per_edge; + GtsSurface * parent; +}; + +struct _GtsSurfaceQualityStats { + GtsRange face_quality; + GtsRange face_area; + GtsRange edge_length; + GtsRange edge_angle; + GtsSurface * parent; +}; + +struct _GtsSurface { + GtsObject object; + +#ifdef USE_SURFACE_BTREE + GTree * faces; +#else /* not USE_SURFACE_BTREE */ + GHashTable * faces; +#endif /* not USE_SURFACE_BTREE */ + GtsFaceClass * face_class; + GtsEdgeClass * edge_class; + GtsVertexClass * vertex_class; + gboolean keep_faces; +}; + +struct _GtsSurfaceClass { + GtsObjectClass parent_class; + + void (* add_face) (GtsSurface *, GtsFace *); + void (* remove_face) (GtsSurface *, GtsFace *); +}; + +#define GTS_IS_SURFACE(obj) (gts_object_is_from_class (obj,\ + gts_surface_class ())) +#define GTS_SURFACE(obj) GTS_OBJECT_CAST (obj,\ + GtsSurface,\ + gts_surface_class ()) +#define GTS_SURFACE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsSurfaceClass,\ + gts_surface_class ()) + +GtsSurfaceClass * gts_surface_class (void); +GtsSurface * gts_surface_new (GtsSurfaceClass * klass, + GtsFaceClass * face_class, + GtsEdgeClass * edge_class, + GtsVertexClass * vertex_class); +void gts_surface_add_face (GtsSurface * s, + GtsFace * f); +void gts_surface_remove_face (GtsSurface * s, + GtsFace * f); +guint gts_surface_read (GtsSurface * surface, + GtsFile * f); +gdouble gts_surface_area (GtsSurface * s); +void gts_surface_stats (GtsSurface * s, + GtsSurfaceStats * stats); +void gts_surface_quality_stats (GtsSurface * s, + GtsSurfaceQualityStats * stats); +void gts_surface_print_stats (GtsSurface * s, + FILE * fptr); +void gts_surface_write (GtsSurface * s, + FILE * fptr); +void gts_surface_write_oogl (GtsSurface * s, + FILE * fptr); +void gts_surface_write_vtk (GtsSurface * s, + FILE * fptr); +void gts_surface_write_oogl_boundary (GtsSurface * s, + FILE * fptr); +void gts_surface_foreach_vertex (GtsSurface * s, + GtsFunc func, + gpointer data); +void gts_surface_foreach_edge (GtsSurface * s, + GtsFunc func, + gpointer data); +void gts_surface_foreach_face (GtsSurface * s, + GtsFunc func, + gpointer data); +guint gts_surface_foreach_face_remove (GtsSurface * s, + GtsFunc func, + gpointer data); +typedef struct _GtsSurfaceTraverse GtsSurfaceTraverse; +GtsSurfaceTraverse * gts_surface_traverse_new (GtsSurface * s, + GtsFace * f); +GtsFace * gts_surface_traverse_next (GtsSurfaceTraverse * t, + guint * level); +void gts_surface_traverse_destroy (GtsSurfaceTraverse * t); +void gts_surface_refine (GtsSurface * surface, + GtsKeyFunc cost_func, + gpointer cost_data, + GtsRefineFunc refine_func, + gpointer refine_data, + GtsStopFunc stop_func, + gpointer stop_data); +gboolean gts_edge_collapse_is_valid (GtsEdge * e); +void gts_surface_coarsen (GtsSurface * surface, + GtsKeyFunc cost_func, + gpointer cost_data, + GtsCoarsenFunc coarsen_func, + gpointer coarsen_data, + GtsStopFunc stop_func, + gpointer stop_data, + gdouble minangle); +gboolean gts_coarsen_stop_number (gdouble cost, + guint nedge, + guint * min_number); +gboolean gts_coarsen_stop_cost (gdouble cost, + guint nedge, + gdouble * max_cost); +void gts_surface_tessellate (GtsSurface * s, + GtsRefineFunc refine_func, + gpointer refine_data); +GtsSurface * gts_surface_generate_sphere (GtsSurface * s, + guint geodesation_order); +GtsSurface * gts_surface_copy (GtsSurface * s1, + GtsSurface * s2); +void gts_surface_merge (GtsSurface * s, + GtsSurface * with); +gboolean gts_surface_is_manifold (GtsSurface * s); +gboolean gts_surface_is_closed (GtsSurface * s); +gboolean gts_surface_is_orientable (GtsSurface * s); +gdouble gts_surface_volume (GtsSurface * s); +gdouble gts_surface_center_of_mass (GtsSurface * s, + GtsVector cm); +gdouble gts_surface_center_of_area (GtsSurface * s, + GtsVector cm); +guint gts_surface_vertex_number (GtsSurface * s); +guint gts_surface_edge_number (GtsSurface * s); +guint gts_surface_face_number (GtsSurface * s); +void gts_surface_distance (GtsSurface * s1, + GtsSurface * s2, + gdouble delta, + GtsRange * face_range, + GtsRange * boundary_range); +GSList * gts_surface_boundary (GtsSurface * surface); +GSList * gts_surface_split (GtsSurface * s); + +/* Discrete differential operators: curvature.c */ + +gboolean gts_vertex_mean_curvature_normal (GtsVertex * v, + GtsSurface * s, + GtsVector Kh); +gboolean gts_vertex_gaussian_curvature (GtsVertex * v, + GtsSurface * s, + gdouble * Kg); +void gts_vertex_principal_curvatures (gdouble Kh, + gdouble Kg, + gdouble * K1, + gdouble * K2); +void gts_vertex_principal_directions (GtsVertex * v, + GtsSurface * s, + GtsVector Kh, + gdouble Kg, + GtsVector e1, + GtsVector e2); + +/* Volume optimization: vopt.c */ +typedef struct _GtsVolumeOptimizedParams GtsVolumeOptimizedParams; + +struct _GtsVolumeOptimizedParams { + gdouble volume_weight; + gdouble boundary_weight; + gdouble shape_weight; +}; + +GtsVertex * gts_volume_optimized_vertex (GtsEdge * edge, + GtsVertexClass * klass, + GtsVolumeOptimizedParams * params); +gdouble gts_volume_optimized_cost (GtsEdge * e, + GtsVolumeOptimizedParams * params); + +/* bool operations: boolean.c */ + +GSList * gts_surface_intersection (GtsSurface * s1, + GtsSurface * s2, + GNode * faces_tree1, + GNode * faces_tree2); + +typedef struct _GtsSurfaceInter GtsSurfaceInter; +typedef struct _GtsSurfaceInterClass GtsSurfaceInterClass; +/** + * GtsBooleanOperation: + * @GTS_1_OUT_2: identifies the part of the first surface which lies + * outside the second surface. + * @GTS_1_IN_2: identifies the part of the first surface which lies + * inside the second surface. + * @GTS_2_OUT_1: identifies the part of the second surface which lies + * outside the first surface. + * @GTS_2_IN_1: identifies the part of the second surface which lies + * inside the first surface. + */ +typedef enum { GTS_1_OUT_2, + GTS_1_IN_2, + GTS_2_OUT_1, + GTS_2_IN_1 } GtsBooleanOperation; + +/** + * GTS_IS_SURFACE_INTER: + * @obj: a #GtsObject. + * + * Evaluates to %TRUE if @obj is a #GtsSurfaceInter, %FALSE otherwise. + */ +#define GTS_IS_SURFACE_INTER(obj) (gts_object_is_from_class (obj,\ + gts_surface_inter_class ())) +/** + * GTS_SURFACE_INTER: + * @obj: a descendant of #GtsSurfaceInter. + * + * Casts @obj to #GtsSurfaceInter. + */ +#define GTS_SURFACE_INTER(obj) GTS_OBJECT_CAST (obj,\ + GtsSurfaceInter,\ + gts_surface_inter_class ()) +/** + * GTS_SURFACE_INTER_CLASS: + * @klass: a descendant of #GtsSurfaceInterClass. + * + * Casts @klass to #GtsSurfaceInterClass. + */ +#define GTS_SURFACE_INTER_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsSurfaceInterClass,\ + gts_surface_inter_class ()) + +struct _GtsSurfaceInter { + GtsObject object; + + GtsSurface * s1; + GtsSurface * s2; + GSList * edges; +}; + +struct _GtsSurfaceInterClass { + GtsObjectClass parent_class; +}; + +GtsSurfaceInterClass * +gts_surface_inter_class (void); +GtsSurfaceInter * +gts_surface_inter_new (GtsSurfaceInterClass * klass, + GtsSurface * s1, + GtsSurface * s2, + GNode * faces_tree1, + GNode * faces_tree2, + gboolean is_open1, + gboolean is_open2); +gboolean +gts_surface_inter_check (GtsSurfaceInter * si, + gboolean * closed); +void +gts_surface_inter_boolean (GtsSurfaceInter * si, + GtsSurface * surface, + GtsBooleanOperation op); +gboolean gts_surface_foreach_intersecting_face (GtsSurface * s, + GtsBBTreeTraverseFunc func, + gpointer data); +GtsSurface * +gts_surface_is_self_intersecting (GtsSurface * s); + +/* Binary Heap: heap.c */ + +typedef struct _GtsHeap GtsHeap; + +GtsHeap * gts_heap_new (GCompareFunc compare_func); +void gts_heap_insert (GtsHeap * heap, gpointer p); +gpointer gts_heap_remove_top (GtsHeap * heap); +gpointer gts_heap_top (GtsHeap * heap); +void gts_heap_thaw (GtsHeap * heap); +void gts_heap_foreach (GtsHeap * heap, + GFunc func, + gpointer user_data); +void gts_heap_freeze (GtsHeap * heap); +guint gts_heap_size (GtsHeap * heap); +void gts_heap_destroy (GtsHeap * heap); + +/* Extended Binary Heap: eheap.c */ + +typedef struct _GtsEHeap GtsEHeap; +typedef struct _GtsEHeapPair GtsEHeapPair; + +struct _GtsEHeap { + GPtrArray * elts; + GtsKeyFunc func; + gpointer data; + gboolean frozen, randomized; +}; + +/** + * _GtsEHeapPair: + * @data: Points to the item stored in the heap. + * @key: Value of the key for this item. + * @pos: Private field. + */ +struct _GtsEHeapPair { + gpointer data; + gdouble key; + guint pos; +}; + +GtsEHeap * gts_eheap_new (GtsKeyFunc key_func, + gpointer data); +GtsEHeapPair * gts_eheap_insert (GtsEHeap * heap, + gpointer p); +GtsEHeapPair * gts_eheap_insert_with_key (GtsEHeap * heap, + gpointer p, + gdouble key); +gpointer gts_eheap_remove_top (GtsEHeap * heap, + gdouble * key); +gpointer gts_eheap_top (GtsEHeap * heap, + gdouble * key); +void gts_eheap_thaw (GtsEHeap * heap); +void gts_eheap_foreach (GtsEHeap * heap, + GFunc func, + gpointer data); +gpointer gts_eheap_remove (GtsEHeap * heap, + GtsEHeapPair * p); +void gts_eheap_decrease_key (GtsEHeap * heap, + GtsEHeapPair * p, + gdouble new_key); +void gts_eheap_freeze (GtsEHeap * heap); +guint gts_eheap_size (GtsEHeap * heap); +void gts_eheap_update (GtsEHeap * heap); +gdouble gts_eheap_key (GtsEHeap * heap, + gpointer p); +void gts_eheap_randomized (GtsEHeap * heap, + gboolean randomized); +void gts_eheap_destroy (GtsEHeap * heap); + +/* FIFO queues: fifo.c */ + +typedef struct _GtsFifo GtsFifo; + +GtsFifo * gts_fifo_new (void); +void gts_fifo_write (GtsFifo * fifo, + FILE * fp); +void gts_fifo_push (GtsFifo * fifo, + gpointer data); +gpointer gts_fifo_pop (GtsFifo * fifo); +gpointer gts_fifo_top (GtsFifo * fifo); +guint gts_fifo_size (GtsFifo * fifo); +gboolean gts_fifo_is_empty (GtsFifo * fifo); +void gts_fifo_foreach (GtsFifo * fifo, + GtsFunc func, + gpointer data); +void gts_fifo_reverse (GtsFifo * fifo); +void gts_fifo_destroy (GtsFifo * fifo); + +/* Progressive surfaces */ + +/* split.c */ + +typedef struct _GtsSplit GtsSplit; +typedef struct _GtsSplitClass GtsSplitClass; +typedef struct _GtsSplitCFace GtsSplitCFace; + +struct _GtsSplit { + GtsObject object; + + GtsVertex * v; + GtsObject * v1; + GtsObject * v2; + GtsSplitCFace * cfaces; + guint ncf; +}; + +struct _GtsSplitClass { + GtsObjectClass parent_class; +}; + +#define GTS_IS_SPLIT(obj) (gts_object_is_from_class (obj,\ + gts_split_class ())) +#define GTS_SPLIT(obj) GTS_OBJECT_CAST (obj,\ + GtsSplit,\ + gts_split_class ()) +#define GTS_SPLIT_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsSplitClass,\ + gts_split_class ()) +#define GTS_SPLIT_V1(vs) (GTS_IS_SPLIT ((vs)->v1) ?\ + GTS_SPLIT ((vs)->v1)->v :\ + GTS_VERTEX ((vs)->v1)) +#define GTS_SPLIT_V2(vs) (GTS_IS_SPLIT ((vs)->v2) ?\ + GTS_SPLIT ((vs)->v2)->v :\ + GTS_VERTEX ((vs)->v2)) + +GtsSplitClass * gts_split_class (void); +GtsSplit * gts_split_new (GtsSplitClass * klass, + GtsVertex * v, + GtsObject * o1, + GtsObject * o2); +void gts_split_collapse (GtsSplit * vs, + GtsEdgeClass * klass, + GtsEHeap * heap); +void gts_split_expand (GtsSplit * vs, + GtsSurface * s, + GtsEdgeClass * klass); +typedef gboolean (*GtsSplitTraverseFunc) (GtsSplit * vs, + gpointer data); +void gts_split_traverse (GtsSplit * root, + GTraverseType order, + gint depth, + GtsSplitTraverseFunc func, + gpointer data); +guint gts_split_height (GtsSplit * root); + +/* psurface.c */ + +typedef struct _GtsPSurface GtsPSurface; +typedef struct _GtsPSurfaceClass GtsPSurfaceClass; + +struct _GtsPSurface { + GtsObject object; + + GtsSurface * s; + GPtrArray * split; + GtsSplitClass * split_class; + guint pos, min; + + GPtrArray * vertices; + GPtrArray * faces; +}; + +struct _GtsPSurfaceClass { + GtsObjectClass parent_class; +}; + +#define GTS_IS_PSURFACE(obj) (gts_object_is_from_class (obj,\ + gts_psurface_class ())) +#define GTS_PSURFACE(obj) GTS_OBJECT_CAST (obj,\ + GtsPSurface,\ + gts_psurface_class ()) +#define GTS_PSURFACE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsPSurfaceClass,\ + gts_psurface_class ()) +#define GTS_PSURFACE_IS_CLOSED(ps) (!(ps)->vertices) + +GtsPSurfaceClass * gts_psurface_class (void); +GtsPSurface * gts_psurface_new (GtsPSurfaceClass * klass, + GtsSurface * surface, + GtsSplitClass * split_class, + GtsKeyFunc cost_func, + gpointer cost_data, + GtsCoarsenFunc coarsen_func, + gpointer coarsen_data, + GtsStopFunc stop_func, + gpointer stop_data, + gdouble minangle); +GtsSplit * gts_psurface_add_vertex (GtsPSurface * ps); +GtsSplit * gts_psurface_remove_vertex (GtsPSurface * ps); +guint gts_psurface_max_vertex_number (GtsPSurface * ps); +guint gts_psurface_min_vertex_number (GtsPSurface * ps); +void gts_psurface_set_vertex_number (GtsPSurface * ps, + guint n); +guint gts_psurface_get_vertex_number (GtsPSurface * ps); +void gts_psurface_write (GtsPSurface * ps, + FILE * fptr); +GtsPSurface * gts_psurface_open (GtsPSurfaceClass * klass, + GtsSurface * s, + GtsSplitClass * split_class, + GtsFile * f); +GtsSplit * gts_psurface_read_vertex (GtsPSurface * ps, + GtsFile * fp); +void gts_psurface_close (GtsPSurface * ps); +void gts_psurface_foreach_vertex (GtsPSurface * ps, + GtsFunc func, + gpointer data); + +/* hsurface.c */ + +typedef struct _GtsHSplit GtsHSplit; +typedef struct _GtsHSplitClass GtsHSplitClass; +typedef struct _GtsHSurface GtsHSurface; +typedef struct _GtsHSurfaceClass GtsHSurfaceClass; + +struct _GtsHSplit { + GtsSplit split; + + GtsEHeapPair * index; + GtsHSplit * parent; + guint nchild; +}; + +struct _GtsHSplitClass { + GtsSplitClass parent_class; +}; + +#define GTS_IS_HSPLIT(obj) (gts_object_is_from_class (obj,\ + gts_hsplit_class ())) +#define GTS_HSPLIT(obj) GTS_OBJECT_CAST (obj,\ + GtsHSplit,\ + gts_hsplit_class ()) +#define GTS_HSPLIT_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsHSplitClass,\ + gts_hsplit_class ()) + +GtsHSplitClass * gts_hsplit_class (void); +GtsHSplit * gts_hsplit_new (GtsHSplitClass * klass, + GtsSplit * vs); +void gts_hsplit_collapse (GtsHSplit * hs, + GtsHSurface * hsurface); +void gts_hsplit_expand (GtsHSplit * hs, + GtsHSurface * hsurface); +void gts_hsplit_force_expand (GtsHSplit * hs, + GtsHSurface * hsurface); + +struct _GtsHSurface { + GtsObject object; + + GtsSurface * s; + GSList * roots; + GtsEHeap * expandable; + GtsEHeap * collapsable; + GPtrArray * split; + guint nvertex; +}; + +struct _GtsHSurfaceClass { + GtsObjectClass parent_class; +}; + +#define GTS_IS_HSURFACE(obj) (gts_object_is_from_class (obj,\ + gts_hsurface_class ())) +#define GTS_HSURFACE(obj) GTS_OBJECT_CAST (obj,\ + GtsHSurface,\ + gts_hsurface_class ()) +#define GTS_HSURFACE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsHSurfaceClass,\ + gts_hsurface_class ()) + +GtsHSurfaceClass * gts_hsurface_class (void); +GtsHSurface * gts_hsurface_new (GtsHSurfaceClass * klass, + GtsHSplitClass * hsplit_class, + GtsPSurface * psurface, + GtsKeyFunc expand_key, + gpointer expand_data, + GtsKeyFunc collapse_key, + gpointer collapse_data); +void gts_hsurface_traverse (GtsHSurface * hsurface, + GTraverseType order, + gint depth, + GtsSplitTraverseFunc func, + gpointer data); +void gts_hsurface_foreach (GtsHSurface * hsurface, + GTraverseType order, + GtsFunc func, + gpointer data); +guint gts_hsurface_height (GtsHSurface * hsurface); + +/* Constrained Delaunay triangulation: cdt.c */ + +/** + * GTS_IS_CONSTRAINT: + * @obj: a #GtsObject. + * + * Evaluates to %TRUE if @obj is a #GtsConstraint, %FALSE otherwise. + */ +#define GTS_IS_CONSTRAINT(obj) (gts_object_is_from_class (obj,\ + gts_constraint_class ())) +/** + * GTS_CONSTRAINT: + * @obj: a descendant of #GtsConstraint. + * + * Casts @obj to #GtsConstraint. + */ +#define GTS_CONSTRAINT(obj) GTS_OBJECT_CAST (obj,\ + GtsConstraint,\ + gts_constraint_class ()) +/** + * GTS_CONSTRAINT_CLASS: + * @klass: a desscendant of #GtsConstraintClass. + * + * Casts @klass to #GtsConstraintClass. + */ +#define GTS_CONSTRAINT_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsConstraintClass,\ + gts_constraint_class ()) + +struct _GtsConstraint { + GtsEdge edge; +}; + +struct _GtsConstraintClass { + GtsEdgeClass parent_class; +}; + +typedef struct _GtsConstraint GtsConstraint; +typedef struct _GtsConstraintClass GtsConstraintClass; + +GtsConstraintClass * gts_constraint_class (void); + +GtsFace * gts_point_locate (GtsPoint * p, + GtsSurface * surface, + GtsFace * guess); +GtsVertex * gts_delaunay_add_vertex_to_face (GtsSurface * surface, + GtsVertex * v, + GtsFace * f); +GtsVertex * gts_delaunay_add_vertex (GtsSurface * surface, + GtsVertex * v, + GtsFace * guess); +void gts_delaunay_remove_vertex (GtsSurface * surface, + GtsVertex * v); +GtsFace * gts_delaunay_check (GtsSurface * surface); +GSList * gts_delaunay_add_constraint (GtsSurface * surface, + GtsConstraint * c); +void gts_delaunay_remove_hull (GtsSurface * surface); + +/* GtsListFace: Header */ + +typedef struct _GtsListFace GtsListFace; + +struct _GtsListFace { + /*< private >*/ + GtsFace parent; + + /*< public >*/ + GSList * points; +}; + +#define GTS_LIST_FACE(obj) GTS_OBJECT_CAST (obj,\ + GtsListFace,\ + gts_list_face_class ()) +#define GTS_IS_LIST_FACE(obj) (gts_object_is_from_class (obj,\ + gts_list_face_class ())) + +GtsFaceClass * gts_list_face_class (void); + +/* Constrained Delaunay refinement: refine.c */ + +typedef gboolean (* GtsEncroachFunc) (GtsVertex * v, + GtsEdge * e, + GtsSurface * s, + gpointer data); + +gboolean gts_vertex_encroaches_edge (GtsVertex * v, + GtsEdge * e); +GtsVertex * gts_edge_is_encroached (GtsEdge * e, + GtsSurface * s, + GtsEncroachFunc encroaches, + gpointer data); +guint gts_delaunay_conform (GtsSurface * surface, + gint steiner_max, + GtsEncroachFunc encroaches, + gpointer data); +guint gts_delaunay_refine (GtsSurface * surface, + gint steiner_max, + GtsEncroachFunc encroaches, + gpointer encroach_data, + GtsKeyFunc cost, + gpointer cost_data); + +/* Isosurfaces (marching cubes): iso.c */ + +typedef struct _GtsGridPlane GtsGridPlane; +typedef struct _GtsIsoSlice GtsIsoSlice; +typedef struct _GtsCartesianGrid GtsCartesianGrid; + +struct _GtsGridPlane { + GtsPoint ** p; + guint nx, ny; +}; + +struct _GtsCartesianGrid { + guint nx, ny, nz; + gdouble x, dx, y, dy, z, dz; +}; + +typedef void (*GtsIsoCartesianFunc) (gdouble ** a, + GtsCartesianGrid g, + guint i, + gpointer data); + +GtsGridPlane * gts_grid_plane_new (guint nx, + guint ny); +void gts_grid_plane_destroy (GtsGridPlane * g); +GtsIsoSlice * gts_iso_slice_new (guint nx, guint ny); +void gts_iso_slice_fill (GtsIsoSlice * slice, + GtsGridPlane * plane1, + GtsGridPlane * plane2, + gdouble ** f1, + gdouble ** f2, + gdouble iso, + GtsVertexClass * klass); +void gts_iso_slice_fill_cartesian (GtsIsoSlice * slice, + GtsCartesianGrid g, + gdouble ** f1, + gdouble ** f2, + gdouble iso, + GtsVertexClass * klass); +void gts_iso_slice_destroy (GtsIsoSlice * slice); +void gts_isosurface_slice (GtsIsoSlice * slice1, + GtsIsoSlice * slice2, + GtsSurface * surface); +void gts_isosurface_cartesian (GtsSurface * surface, + GtsCartesianGrid g, + GtsIsoCartesianFunc f, + gpointer data, + gdouble iso); + +/* Isosurfaces (marching tetrahedra): isotetra.c */ + +void gts_isosurface_tetra (GtsSurface * surface, + GtsCartesianGrid g, + GtsIsoCartesianFunc f, + gpointer data, + gdouble iso); +void gts_isosurface_tetra_bcl (GtsSurface * surface, + GtsCartesianGrid g, + GtsIsoCartesianFunc f, + gpointer data, + gdouble iso); +void gts_isosurface_tetra_bounded (GtsSurface * surface, + GtsCartesianGrid g, + GtsIsoCartesianFunc f, + gpointer data, + gdouble iso); + +/* Named vertices, edges and triangles: named.c */ + +#define GTS_NAME_LENGTH 40 + +#define GTS_NVERTEX(obj) GTS_OBJECT_CAST (obj,\ + GtsNVertex,\ + gts_nvertex_class ()) +#define GTS_NVERTEX_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsNVertexClass,\ + gts_nvertex_class()) +#define GTS_IS_NVERTEX(obj) (gts_object_is_from_class (obj,\ + gts_nvertex_class ())) + +typedef struct _GtsNVertex GtsNVertex; +typedef struct _GtsNVertexClass GtsNVertexClass; + +struct _GtsNVertex { + GtsVertex parent; + char name[GTS_NAME_LENGTH]; +}; + +struct _GtsNVertexClass { + GtsVertexClass parent_class; +}; + +GtsNVertexClass * gts_nvertex_class (void); + +#define GTS_NEDGE(obj) GTS_OBJECT_CAST (obj,\ + GtsNEdge,\ + gts_nedge_class ()) +#define GTS_NEDGE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsNEdgeClass,\ + gts_nedge_class()) +#define GTS_IS_NEDGE(obj) (gts_object_is_from_class (obj,\ + gts_nedge_class ())) + +typedef struct _GtsNEdge GtsNEdge; +typedef struct _GtsNEdgeClass GtsNEdgeClass; + +struct _GtsNEdge { + GtsEdge parent; + char name[GTS_NAME_LENGTH]; +}; + +struct _GtsNEdgeClass { + GtsEdgeClass parent_class; +}; + +GtsNEdgeClass * gts_nedge_class (void); + +#define GTS_NFACE(obj) GTS_OBJECT_CAST (obj,\ + GtsNFace,\ + gts_nface_class ()) +#define GTS_NFACE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsNFaceClass,\ + gts_nface_class()) +#define GTS_IS_NFACE(obj) (gts_object_is_from_class (obj,\ + gts_nface_class ())) + +typedef struct _GtsNFace GtsNFace; +typedef struct _GtsNFaceClass GtsNFaceClass; + +struct _GtsNFace { + GtsFace parent; + char name[GTS_NAME_LENGTH]; +}; + +struct _GtsNFaceClass { + GtsFaceClass parent_class; +}; + +GtsNFaceClass * gts_nface_class (void); + +/* Cluster object for out-of-core simplification: oocs.c */ + +#define GTS_CLUSTER(obj) GTS_OBJECT_CAST (obj,\ + GtsCluster,\ + gts_cluster_class ()) +#define GTS_CLUSTER_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsClusterClass,\ + gts_cluster_class()) +#define GTS_IS_CLUSTER(obj) (gts_object_is_from_class (obj,\ + gts_cluster_class ())) + +typedef struct _GtsCluster GtsCluster; +typedef struct _GtsClusterClass GtsClusterClass; +typedef struct _GtsClusterId GtsClusterId; + +struct _GtsClusterId { + guint x, y, z; +}; + +struct _GtsCluster { + GtsObject parent; + + GtsClusterId id; + GtsVertex * v; + guint n; +}; + +struct _GtsClusterClass { + GtsObjectClass parent_class; + + void (* add) (GtsCluster * c, GtsPoint * p, gpointer data); + void (* update) (GtsCluster * c); +}; + +GtsClusterClass * gts_cluster_class (void); +GtsCluster * gts_cluster_new (GtsClusterClass * klass, + GtsClusterId id, + GtsVertexClass * vklass); +void gts_cluster_add (GtsCluster * c, + GtsPoint * p, + gpointer data); +void gts_cluster_update (GtsCluster * c); + +/* Cluster group object for out-of-core simplification: oocs.c */ + +#define GTS_CLUSTER_GRID(obj) GTS_OBJECT_CAST (obj,\ + GtsClusterGrid,\ + gts_cluster_grid_class ()) +#define GTS_CLUSTER_GRID_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsClusterGridClass,\ + gts_cluster_grid_class()) +#define GTS_IS_CLUSTER_GRID(obj) (gts_object_is_from_class (obj,\ + gts_cluster_grid_class ())) + +typedef struct _GtsClusterGrid GtsClusterGrid; +typedef struct _GtsClusterGridClass GtsClusterGridClass; + +struct _GtsClusterGrid { + GtsObject parent; + + GtsSurface * surface; + GtsBBox * bbox; + GtsVector size; + + GtsClusterClass * cluster_class; + GHashTable * clusters; +}; + +struct _GtsClusterGridClass { + GtsObjectClass parent_class; +}; + +GtsClusterGridClass * gts_cluster_grid_class (void); +GtsClusterGrid * gts_cluster_grid_new (GtsClusterGridClass * klass, + GtsClusterClass * cluster_class, + GtsSurface * s, + GtsBBox * bbox, + gdouble delta); +void gts_cluster_grid_add_triangle (GtsClusterGrid * cluster_grid, + GtsPoint * p1, + GtsPoint * p2, + GtsPoint * p3, + gpointer data); +GtsRange gts_cluster_grid_update (GtsClusterGrid * cluster_grid); + +/* Triangle strip generation: stripe.c */ +GSList * gts_surface_strip (GtsSurface * s); + +/* GtsContainee: container.c */ + +typedef struct _GtsContainee GtsContainee; +typedef struct _GtsContaineeClass GtsContaineeClass; +typedef struct _GtsContainer GtsContainer; +typedef struct _GtsContainerClass GtsContainerClass; + +struct _GtsContainee { + GtsObject object; +}; + +struct _GtsContaineeClass { + GtsObjectClass parent_class; + + void (* add_container) (GtsContainee *, GtsContainer *); + void (* remove_container) (GtsContainee *, GtsContainer *); + void (* foreach) (GtsContainee *, GtsFunc, gpointer); + gboolean (* is_contained) (GtsContainee *, GtsContainer *); + void (* replace) (GtsContainee *, GtsContainee *); +}; + +#define GTS_CONTAINEE(obj) GTS_OBJECT_CAST (obj,\ + GtsContainee,\ + gts_containee_class ()) +#define GTS_CONTAINEE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsContaineeClass,\ + gts_containee_class()) +#define GTS_IS_CONTAINEE(obj) (gts_object_is_from_class (obj,\ + gts_containee_class ())) + +GtsContaineeClass * gts_containee_class (void); +GtsContainee * gts_containee_new (GtsContaineeClass * klass); +gboolean gts_containee_is_contained (GtsContainee * item, + GtsContainer * c); +void gts_containee_replace (GtsContainee * item, + GtsContainee * with); + +/* GtsSListContainee: container.c */ + +typedef struct _GtsSListContainee GtsSListContainee; +typedef struct _GtsSListContaineeClass GtsSListContaineeClass; + +struct _GtsSListContainee { + GtsContainee containee; + + GSList * containers; +}; + +struct _GtsSListContaineeClass { + GtsContaineeClass parent_class; +}; + +#define GTS_SLIST_CONTAINEE(obj) GTS_OBJECT_CAST (obj,\ + GtsSListContainee,\ + gts_slist_containee_class ()) +#define GTS_SLIST_CONTAINEE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsSListContaineeClass,\ + gts_slist_containee_class()) +#define GTS_IS_SLIST_CONTAINEE(obj) (gts_object_is_from_class (obj,\ + gts_slist_containee_class ())) + +GtsSListContaineeClass * gts_slist_containee_class (void); + +/* GtsContainer: container.c */ + +struct _GtsContainer { + GtsSListContainee object; +}; + +struct _GtsContainerClass { + GtsSListContaineeClass parent_class; + + void (* add) (GtsContainer *, GtsContainee *); + void (* remove) (GtsContainer *, GtsContainee *); + void (* foreach) (GtsContainer *, GtsFunc, gpointer); + guint (* size) (GtsContainer *); +}; + +#define GTS_CONTAINER(obj) GTS_OBJECT_CAST (obj,\ + GtsContainer,\ + gts_container_class ()) +#define GTS_CONTAINER_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsContainerClass,\ + gts_container_class()) +#define GTS_IS_CONTAINER(obj) (gts_object_is_from_class (obj,\ + gts_container_class ())) + +GtsContainerClass * gts_container_class (void); +GtsContainer * gts_container_new (GtsContainerClass * klass); +void gts_container_add (GtsContainer * c, + GtsContainee * item); +void gts_container_remove (GtsContainer * c, + GtsContainee * item); +void gts_container_foreach (GtsContainer * c, + GtsFunc func, + gpointer data); +guint gts_container_size (GtsContainer * c); + +/* GtsHashContainer: container.c */ + +typedef struct _GtsHashContainer GtsHashContainer; +typedef struct _GtsHashContainerClass GtsHashContainerClass; + +struct _GtsHashContainer { + GtsContainer c; + + GHashTable * items; + gboolean frozen; +}; + +struct _GtsHashContainerClass { + GtsContainerClass parent_class; +}; + +#define GTS_HASH_CONTAINER(obj) GTS_OBJECT_CAST (obj,\ + GtsHashContainer,\ + gts_hash_container_class ()) +#define GTS_HASH_CONTAINER_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsHashContainerClass,\ + gts_hash_container_class()) +#define GTS_IS_HASH_CONTAINER(obj) (gts_object_is_from_class (obj,\ + gts_hash_container_class ())) + +GtsHashContainerClass * gts_hash_container_class (void); + +/* GtsSListContainer: container.c */ + +typedef struct _GtsSListContainer GtsSListContainer; +typedef struct _GtsSListContainerClass GtsSListContainerClass; + +struct _GtsSListContainer { + GtsContainer c; + + GSList * items; + gboolean frozen; +}; + +struct _GtsSListContainerClass { + GtsContainerClass parent_class; +}; + +#define GTS_SLIST_CONTAINER(obj) GTS_OBJECT_CAST (obj,\ + GtsSListContainer,\ + gts_slist_container_class ()) +#define GTS_SLIST_CONTAINER_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsSListContainerClass,\ + gts_slist_container_class()) +#define GTS_IS_SLIST_CONTAINER(obj) (gts_object_is_from_class (obj,\ + gts_slist_container_class ())) + +GtsSListContainerClass * gts_slist_container_class (void); + +/* GtsGNode: graph.c */ + +typedef struct _GtsGNode GtsGNode; +typedef struct _GtsGNodeClass GtsGNodeClass; +typedef struct _GtsGraph GtsGraph; +typedef struct _GtsGraphClass GtsGraphClass; + +struct _GtsGNode { + GtsSListContainer container; + + guint level; +}; + +struct _GtsGNodeClass { + GtsSListContainerClass parent_class; + + gfloat (* weight) (GtsGNode *); + void (* write) (GtsGNode *, FILE *); +}; + +#define GTS_GNODE(obj) GTS_OBJECT_CAST (obj,\ + GtsGNode,\ + gts_gnode_class ()) +#define GTS_GNODE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsGNodeClass,\ + gts_gnode_class()) +#define GTS_IS_GNODE(obj) (gts_object_is_from_class (obj,\ + gts_gnode_class ())) +#define GTS_GNODE_NEIGHBOR(n,e) (GTS_GEDGE (e)->n1 == n ? GTS_GEDGE (e)->n2 : GTS_GEDGE (e)->n2 == n ? GTS_GEDGE (e)->n1 : NULL) + +GtsGNodeClass * gts_gnode_class (void); +GtsGNode * gts_gnode_new (GtsGNodeClass * klass); +void gts_gnode_foreach_neighbor (GtsGNode * n, + GtsGraph * g, + GtsFunc func, + gpointer data); +void gts_gnode_foreach_edge (GtsGNode * n, + GtsGraph * g, + GtsFunc func, + gpointer data); +guint gts_gnode_degree (GtsGNode * n, + GtsGraph * g); +gfloat gts_gnode_move_cost (GtsGNode * n, + GtsGraph * src, + GtsGraph * dst); +gfloat gts_gnode_weight (GtsGNode * n); + +GTS_C_VAR +gboolean gts_allow_floating_gnodes; + +/* GtsNGNode: graph.c */ + +typedef struct _GtsNGNode GtsNGNode; +typedef struct _GtsNGNodeClass GtsNGNodeClass; + +struct _GtsNGNode { + GtsGNode node; + + guint id; +}; + +struct _GtsNGNodeClass { + GtsGNodeClass parent_class; +}; + +#define GTS_NGNODE(obj) GTS_OBJECT_CAST (obj,\ + GtsNGNode,\ + gts_ngnode_class ()) +#define GTS_NGNODE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsNGNodeClass,\ + gts_ngnode_class()) +#define GTS_IS_NGNODE(obj) (gts_object_is_from_class (obj,\ + gts_ngnode_class ())) + +GtsNGNodeClass * gts_ngnode_class (void); +GtsNGNode * gts_ngnode_new (GtsNGNodeClass * klass, + guint id); + +/* GtsWGNode: graph.c */ + +typedef struct _GtsWGNode GtsWGNode; +typedef struct _GtsWGNodeClass GtsWGNodeClass; + +struct _GtsWGNode { + GtsGNode node; + + gfloat weight; +}; + +struct _GtsWGNodeClass { + GtsGNodeClass parent_class; +}; + +#define GTS_WGNODE(obj) GTS_OBJECT_CAST (obj,\ + GtsWGNode,\ + gts_wgnode_class ()) +#define GTS_WGNODE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsWGNodeClass,\ + gts_wgnode_class()) +#define GTS_IS_WGNODE(obj) (gts_object_is_from_class (obj,\ + gts_wgnode_class ())) + +GtsWGNodeClass * gts_wgnode_class (void); +GtsWGNode * gts_wgnode_new (GtsWGNodeClass * klass, + gfloat weight); + +/* GtsPNode */ + +typedef struct _GtsPNode GtsPNode; +typedef struct _GtsPNodeClass GtsPNodeClass; + +struct _GtsPNode { + GtsGNode node; + + gpointer data; +}; + +struct _GtsPNodeClass { + GtsGNodeClass parent_class; +}; + +#define GTS_PNODE(obj) GTS_OBJECT_CAST (obj,\ + GtsPNode,\ + gts_pnode_class ()) +#define GTS_PNODE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsPNodeClass,\ + gts_pnode_class()) +#define GTS_IS_PNODE(obj) (gts_object_is_from_class (obj,\ + gts_pnode_class ())) + +GtsPNodeClass * gts_pnode_class (void); +GtsPNode * gts_pnode_new (GtsPNodeClass * klass, + gpointer data); + +/* GtsFNode */ + +typedef struct _GtsFNode GtsFNode; +typedef struct _GtsFNodeClass GtsFNodeClass; + +struct _GtsFNode { + GtsGNode node; + + GtsFace * f; +}; + +struct _GtsFNodeClass { + GtsGNodeClass parent_class; +}; + +#define GTS_FNODE(obj) GTS_OBJECT_CAST (obj,\ + GtsFNode,\ + gts_fnode_class ()) +#define GTS_FNODE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsFNodeClass,\ + gts_fnode_class()) +#define GTS_IS_FNODE(obj) (gts_object_is_from_class (obj,\ + gts_fnode_class ())) + +GtsFNodeClass * gts_fnode_class (void); +GtsFNode * gts_fnode_new (GtsFNodeClass * klass, + GtsFace * f); + +/* GtsGEdge: graph.c */ + +typedef struct _GtsGEdge GtsGEdge; +typedef struct _GtsGEdgeClass GtsGEdgeClass; + +struct _GtsGEdge { + GtsContainee containee; + + GtsGNode * n1; + GtsGNode * n2; +}; + +struct _GtsGEdgeClass { + GtsContaineeClass parent_class; + + GtsGEdge * (* link) (GtsGEdge * e, GtsGNode * n1, GtsGNode * n2); + gfloat (* weight) (GtsGEdge * e); + void (* write) (GtsGEdge * e, FILE * fp); +}; + +#define GTS_GEDGE(obj) GTS_OBJECT_CAST (obj,\ + GtsGEdge,\ + gts_gedge_class ()) +#define GTS_GEDGE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsGEdgeClass,\ + gts_gedge_class()) +#define GTS_IS_GEDGE(obj) (gts_object_is_from_class (obj,\ + gts_gedge_class ())) + +GtsGEdgeClass * gts_gedge_class (void); +GtsGEdge * gts_gedge_new (GtsGEdgeClass * klass, + GtsGNode * n1, + GtsGNode * n2); +gfloat gts_gedge_weight (GtsGEdge * e); +#define gts_gedge_connects(e, a1, a2)\ + (((e)->n1 == a1 && (e)->n2 == a2) || ((e)->n1 == a2 && (e)->n2 == a1)) + +/* GtsPGEdge: graph.c */ + +typedef struct _GtsPGEdge GtsPGEdge; +typedef struct _GtsPGEdgeClass GtsPGEdgeClass; + +struct _GtsPGEdge { + GtsGEdge gedge; + + gpointer data; +}; + +struct _GtsPGEdgeClass { + GtsGEdgeClass parent_class; +}; + +#define GTS_PGEDGE(obj) GTS_OBJECT_CAST (obj,\ + GtsPGEdge,\ + gts_pgedge_class ()) +#define GTS_PGEDGE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsPGEdgeClass,\ + gts_pgedge_class()) +#define GTS_IS_PGEDGE(obj) (gts_object_is_from_class (obj,\ + gts_pgedge_class ())) + +GtsPGEdgeClass * gts_pgedge_class (void); +GtsPGEdge * gts_pgedge_new (GtsPGEdgeClass * klass, + GtsGNode * n1, + GtsGNode * n2, + gpointer data); + +/* GtsWGEdge: graph.c */ + +typedef struct _GtsWGEdge GtsWGEdge; +typedef struct _GtsWGEdgeClass GtsWGEdgeClass; + +struct _GtsWGEdge { + GtsGEdge gedge; + + gfloat weight; +}; + +struct _GtsWGEdgeClass { + GtsGEdgeClass parent_class; +}; + +#define GTS_WGEDGE(obj) GTS_OBJECT_CAST (obj,\ + GtsWGEdge,\ + gts_wgedge_class ()) +#define GTS_WGEDGE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsWGEdgeClass,\ + gts_wgedge_class()) +#define GTS_IS_WGEDGE(obj) (gts_object_is_from_class (obj,\ + gts_wgedge_class ())) + +GtsWGEdgeClass * gts_wgedge_class (void); +GtsWGEdge * gts_wgedge_new (GtsWGEdgeClass * klass, + GtsGNode * n1, + GtsGNode * n2, + gfloat weight); + +/* GtsGraph: graph.c */ + +struct _GtsGraph { + GtsHashContainer object; + + GtsGraphClass * graph_class; + GtsGNodeClass * node_class; + GtsGEdgeClass * edge_class; +}; + +struct _GtsGraphClass { + GtsHashContainerClass parent_class; + + gfloat (* weight) (GtsGraph *); +}; + +#define GTS_GRAPH(obj) GTS_OBJECT_CAST (obj,\ + GtsGraph,\ + gts_graph_class ()) +#define GTS_GRAPH_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsGraphClass,\ + gts_graph_class()) +#define GTS_IS_GRAPH(obj) (gts_object_is_from_class (obj,\ + gts_graph_class ())) + +GtsGraphClass * gts_graph_class (void); +GtsGraph * gts_graph_new (GtsGraphClass * klass, + GtsGNodeClass * node_class, + GtsGEdgeClass * edge_class); +void gts_graph_print_stats (GtsGraph * g, + FILE * fp); +typedef struct _GtsGraphTraverse GtsGraphTraverse; +typedef enum { GTS_BREADTH_FIRST + } GtsTraverseType; +GtsGraphTraverse * gts_graph_traverse_new (GtsGraph * g, + GtsGNode * n, + GtsTraverseType type, + gboolean reinit); +GtsGNode * gts_graph_traverse_next (GtsGraphTraverse * t); +GtsGNode * gts_graph_traverse_what_next (GtsGraphTraverse * t); +void gts_graph_traverse_destroy (GtsGraphTraverse * t); +void gts_graph_foreach_edge (GtsGraph * g, + GtsFunc func, + gpointer data); +gfloat gts_graph_weight (GtsGraph * g); +guint gts_graph_distance_sum (GtsGraph * g, + GtsGNode * center); +GtsGNode * gts_graph_farthest (GtsGraph * g, + GSList * gnodes); +guint gts_graph_edges_cut (GtsGraph * g); +gfloat gts_graph_edges_cut_weight (GtsGraph * g); +void gts_graph_write (GtsGraph * g, + FILE * fp); +void gts_graph_write_dot (GtsGraph * g, + FILE * fp); +GtsGraph * gts_graph_read (GtsFile * fp); +guint gts_graph_read_jostle (GtsGraph * g, + GtsFile * fp); + +/* GtsWGraph: graph.c */ + +typedef struct _GtsWGraph GtsWGraph; +typedef struct _GtsWGraphClass GtsWGraphClass; + +struct _GtsWGraph { + GtsGraph graph; + + gfloat weight; +}; + +struct _GtsWGraphClass { + GtsGraphClass parent_class; +}; + +#define GTS_WGRAPH(obj) GTS_OBJECT_CAST (obj,\ + GtsWGraph,\ + gts_wgraph_class ()) +#define GTS_WGRAPH_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsWGraphClass,\ + gts_wgraph_class()) +#define GTS_IS_WGRAPH(obj) (gts_object_is_from_class (obj,\ + gts_wgraph_class ())) + +GtsWGraphClass * gts_wgraph_class (void); +gfloat gts_wgraph_weight_max (GtsWGraph * wg); + +/* Surface graph: graph.c */ + +GtsGraph * gts_surface_graph_new (GtsGraphClass * klass, + GtsSurface * s); +GtsSurface * gts_surface_graph_surface (GtsGraph * surface_graph, + GtsSurface * s); + +/* Segments graph: graph.c */ + +GtsGraph * gts_segments_graph_new (GtsGraphClass * klass, + GSList * segments); + +/* GtsGNodeSplit: pgraph.c */ + +typedef struct _GtsGNodeSplit GtsGNodeSplit; +typedef struct _GtsGNodeSplitClass GtsGNodeSplitClass; + +struct _GtsGNodeSplit { + GtsObject object; + + GtsGNode * n; + GtsObject * n1; + GtsObject * n2; +}; + +struct _GtsGNodeSplitClass { + GtsObjectClass parent_class; +}; + +#define GTS_GNODE_SPLIT(obj) GTS_OBJECT_CAST (obj,\ + GtsGNodeSplit,\ + gts_gnode_split_class ()) +#define GTS_GNODE_SPLIT_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsGNodeSplitClass,\ + gts_gnode_split_class()) +#define GTS_IS_GNODE_SPLIT(obj) (gts_object_is_from_class (obj,\ + gts_gnode_split_class ())) +#define GTS_GNODE_SPLIT_N1(ns) (GTS_IS_GNODE_SPLIT ((ns)->n1) ? GTS_GNODE_SPLIT ((ns)->n1)->n : GTS_GNODE ((ns)->n1)) +#define GTS_GNODE_SPLIT_N2(ns) (GTS_IS_GNODE_SPLIT ((ns)->n2) ? GTS_GNODE_SPLIT ((ns)->n2)->n : GTS_GNODE ((ns)->n2)) + +GtsGNodeSplitClass * gts_gnode_split_class (void); +GtsGNodeSplit * gts_gnode_split_new (GtsGNodeSplitClass * klass, + GtsGNode * n, + GtsObject * n1, + GtsObject * n2); +void gts_gnode_split_collapse (GtsGNodeSplit * ns, + GtsGraph * g, + GtsWGEdgeClass * klass); +void gts_gnode_split_expand (GtsGNodeSplit * ns, + GtsGraph * g); + +/* GtsPGraph: pgraph.c */ + +typedef struct _GtsPGraph GtsPGraph; +typedef struct _GtsPGraphClass GtsPGraphClass; + +struct _GtsPGraph { + GtsObject object; + + GtsGraph * g; + GPtrArray * split; + GArray * levels; + GtsGNodeSplitClass * split_class; + GtsWGEdgeClass * edge_class; + guint pos, min, level; +}; + +struct _GtsPGraphClass { + GtsObjectClass parent_class; +}; + +#define GTS_PGRAPH(obj) GTS_OBJECT_CAST (obj,\ + GtsPGraph,\ + gts_pgraph_class ()) +#define GTS_PGRAPH_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsPGraphClass,\ + gts_pgraph_class()) +#define GTS_IS_PGRAPH(obj) (gts_object_is_from_class (obj,\ + gts_pgraph_class ())) + +GtsPGraphClass * gts_pgraph_class (void); +GtsPGraph * gts_pgraph_new (GtsPGraphClass * klass, + GtsGraph * g, + GtsGNodeSplitClass * split_class, + GtsWGNodeClass * node_class, + GtsWGEdgeClass * edge_class, + guint min); +GtsGNodeSplit * gts_pgraph_add_node (GtsPGraph * pg); +GtsGNodeSplit * gts_pgraph_remove_node (GtsPGraph * pg); +void gts_pgraph_set_node_number (GtsPGraph *pg, + guint n); +guint gts_pgraph_get_node_number (GtsPGraph *pg); +guint gts_pgraph_min_node_number (GtsPGraph *pg); +guint gts_pgraph_max_node_number (GtsPGraph *pg); +void gts_pgraph_foreach_node (GtsPGraph *pg, + GtsFunc func, + gpointer data); +gboolean gts_pgraph_down (GtsPGraph * pg, + GtsFunc func, + gpointer data); +/* Graph partition: partition.c */ + +GSList * gts_graph_bubble_partition (GtsGraph * g, + guint np, + guint niter, + GtsFunc step_info, + gpointer data); +guint gts_graph_partition_edges_cut (GSList * partition); +gfloat gts_graph_partition_edges_cut_weight (GSList * partition); +void gts_graph_partition_print_stats (GSList * partition, + FILE * fp); +gfloat gts_graph_partition_balance (GSList * partition); +GSList * gts_graph_partition_clone (GSList * partition); +GSList * gts_graph_recursive_bisection (GtsWGraph * wg, + guint n, + guint ntry, + guint mmax, + guint nmin, + gfloat imbalance); +void gts_graph_partition_destroy (GSList * partition); + +/* Graph bisection: partition.c */ + +typedef struct _GtsGraphBisection GtsGraphBisection; + +struct _GtsGraphBisection { + GtsGraph * g; + GtsGraph * g1; + GtsGraph * g2; + GHashTable * bg1; + GHashTable * bg2; +}; + +gboolean gts_graph_bisection_check (GtsGraphBisection * bg); +GtsGraphBisection * gts_graph_ggg_bisection (GtsGraph * g, + guint ntry); +GtsGraphBisection * gts_graph_bfgg_bisection (GtsGraph * g, + guint ntry); +gdouble gts_graph_bisection_kl_refine (GtsGraphBisection * bg, + guint mmax); +gdouble gts_graph_bisection_bkl_refine (GtsGraphBisection * bg, + guint mmax, + gfloat imbalance); +GtsGraphBisection * gts_graph_bisection_new (GtsWGraph * wg, + guint ntry, + guint mmax, + guint nmin, + gfloat imbalance); +void gts_graph_bisection_destroy (GtsGraphBisection * bg, + gboolean destroy_graphs); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __GTS_H__ */ Index: trunk/src_3rd/gts/heap.c =================================================================== --- trunk/src_3rd/gts/heap.c (nonexistent) +++ trunk/src_3rd/gts/heap.c (revision 1065) @@ -0,0 +1,258 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "gts.h" + +#define PARENT(i) ((i) >= 2 ? (i)/2 : 0) +#define LEFT_CHILD(i) (2*(i)) +#define RIGHT_CHILD(i) (2*(i) + 1) + +struct _GtsHeap { + GPtrArray * elts; + GCompareFunc func; + gboolean frozen; +}; + +/** + * gts_heap_new: + * @compare_func: a GCompareFunc. + * + * Returns: a new #GtsHeap using @compare_func as a sorting function. + */ +GtsHeap * gts_heap_new (GCompareFunc compare_func) +{ + GtsHeap * heap; + + g_return_val_if_fail (compare_func != NULL, NULL); + + heap = g_malloc (sizeof(GtsHeap)); + heap->elts = g_ptr_array_new (); + heap->func = compare_func; + heap->frozen = FALSE; + return heap; +} + +static void sift_up (GtsHeap * heap, guint i) +{ + gpointer parent, child; + guint p; + gpointer * pdata = heap->elts->pdata; + GCompareFunc func = heap->func; + + child = pdata[i - 1]; + while ((p = PARENT (i))) { + parent = pdata[p - 1]; + if ((*func) (parent, child) > 0) { + pdata[p - 1] = child; + pdata[i - 1] = parent; + i = p; + } + else + i = 0; + } +} + +/** + * gts_heap_insert: + * @heap: a #GtsHeap. + * @p: a pointer to add to the heap. + * + * Inserts a new element @p in the heap. + */ +void gts_heap_insert (GtsHeap * heap, gpointer p) +{ + g_return_if_fail (heap != NULL); + + g_ptr_array_add (heap->elts, p); + if (!heap->frozen) + sift_up (heap, heap->elts->len); +} + +static void sift_down (GtsHeap * heap, guint i) +{ + gpointer left_child, right_child, child, parent; + guint lc, rc, c; + gpointer * pdata = heap->elts->pdata; + guint len = heap->elts->len; + GCompareFunc func = heap->func; + + lc = LEFT_CHILD (i); + rc = RIGHT_CHILD (i); + left_child = lc <= len ? pdata[lc - 1] : NULL; + right_child = rc <= len ? pdata[rc - 1] : NULL; + + parent = pdata[i - 1]; + while (left_child != NULL) { + if (right_child == NULL || + (*func) (left_child, right_child) < 0) { + child = left_child; + c = lc; + } + else { + child = right_child; + c = rc; + } + if ((*func) (parent, child) > 0) { + pdata[i - 1] = child; + pdata[c - 1] = parent; + i = c; + lc = LEFT_CHILD (i); + rc = RIGHT_CHILD (i); + left_child = lc <= len ? pdata[lc - 1] : NULL; + right_child = rc <= len ? pdata[rc - 1] : NULL; + } + else + left_child = NULL; + } +} + +/** + * gts_heap_remove_top: + * @heap: a #GtsHeap. + * + * Removes the element at the top of the heap. + * + * Returns: the element at the top of the heap. + */ +gpointer gts_heap_remove_top (GtsHeap * heap) +{ + gpointer root; + GPtrArray * elts; + guint len; + + g_return_val_if_fail (heap != NULL, NULL); + + elts = heap->elts; len = elts->len; + + if (len == 0) + return NULL; + if (len == 1) + return g_ptr_array_remove_index (elts, 0); + + root = elts->pdata[0]; + elts->pdata[0] = g_ptr_array_remove_index (elts, len - 1); + sift_down (heap, 1); + return root; +} + +/** + * gts_heap_top: + * @heap: a #GtsHeap. + * + * Returns: the element at the top of the heap. + */ +gpointer gts_heap_top (GtsHeap * heap) +{ + GPtrArray * elts; + guint len; + + g_return_val_if_fail (heap != NULL, NULL); + + elts = heap->elts; + len = elts->len; + if (len == 0) + return NULL; + return elts->pdata[0]; +} + +/** + * gts_heap_destroy: + * @heap: a #GtsHeap. + * + * Free all the memory allocated for @heap. + */ +void gts_heap_destroy (GtsHeap * heap) +{ + g_return_if_fail (heap != NULL); + + g_ptr_array_free (heap->elts, TRUE); + g_free (heap); +} + +/** + * gts_heap_thaw: + * @heap: a #GtsHeap. + * + * If @heap has been frozen previously using gts_heap_freeze(), reorder it + * in O(n) time and unfreeze it. + */ +void gts_heap_thaw (GtsHeap * heap) +{ + guint i; + + g_return_if_fail (heap != NULL); + + if (!heap->frozen) + return; + + for (i = heap->elts->len/2; i > 0; i--) + sift_down (heap, i); + + heap->frozen = FALSE; +} + +/** + * gts_heap_foreach: + * @heap: a #GtsHeap. + * @func: the function to call for each element in the heap. + * @user_data: to pass to @func. + */ +void gts_heap_foreach (GtsHeap * heap, + GFunc func, + gpointer user_data) +{ + guint i; + GPtrArray * elts; + + g_return_if_fail (heap != NULL); + g_return_if_fail (func != NULL); + + elts = heap->elts; + for (i = 0; i < elts->len; i++) + (*func) (elts->pdata[i], user_data); +} + +/** + * gts_heap_freeze: + * @heap: a #GtsHeap. + * + * Freezes the heap. Any subsequent operation will not preserve the heap + * property. Used in conjunction with gts_heap_insert() and gts_heap_thaw() + * to create a heap in O(n) time. + */ +void gts_heap_freeze (GtsHeap * heap) +{ + g_return_if_fail (heap != NULL); + + heap->frozen = TRUE; +} + +/** + * gts_heap_size: + * @heap: a #GtsHeap. + * + * Returns: the number of items in @heap. + */ +guint gts_heap_size (GtsHeap * heap) +{ + g_return_val_if_fail (heap != NULL, 0); + + return heap->elts->len; +} Index: trunk/src_3rd/gts/hsurface.c =================================================================== --- trunk/src_3rd/gts/hsurface.c (nonexistent) +++ trunk/src_3rd/gts/hsurface.c (revision 1065) @@ -0,0 +1,405 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include +#include "gts.h" + +#define HEAP_INSERT_HSPLIT(h, e) ((e)->index = gts_eheap_insert (h, e)) +#define HEAP_REMOVE_HSPLIT(h, e) (gts_eheap_remove (h, (e)->index),\ + (e)->index = NULL) + +static void hsplit_init (GtsHSplit * hsplit) +{ + hsplit->index = NULL; + hsplit->parent = NULL; + hsplit->nchild = 0; +} + +/** + * gts_hsplit_class: + * + * Returns: the #GtsHSplitClass. + */ +GtsHSplitClass * gts_hsplit_class (void) +{ + static GtsHSplitClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo hsplit_info = { + "GtsHSplit", + sizeof (GtsHSplit), + sizeof (GtsHSplitClass), + (GtsObjectClassInitFunc) NULL, + (GtsObjectInitFunc) hsplit_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_split_class ()), + &hsplit_info); + } + + return klass; +} + +/** + * gts_hsplit_new: + * @klass: a #GtsHSplitClass. + * @vs: a #GtsSplit. + * + * Returns: a new #GtsHSplit, hierarchical extension of @vs. + */ +GtsHSplit * gts_hsplit_new (GtsHSplitClass * klass, GtsSplit * vs) +{ + GtsHSplit * hs; + + g_return_val_if_fail (vs != NULL, NULL); + + hs = GTS_HSPLIT (gts_object_new (GTS_OBJECT_CLASS (klass))); + memcpy (hs, vs, sizeof (GtsSplit)); + GTS_OBJECT (hs)->reserved = NULL; + + return hs; +} + +/** + * gts_hsplit_collapse: + * @hs: a #GtsHSplit. + * @hsurface: a #GtsHSurface. + * + * Collapses the #GtsSplit defined by @hs, updates the expandable and + * collapsable priority heaps of @hsurface. + */ +void gts_hsplit_collapse (GtsHSplit * hs, + GtsHSurface * hsurface) +{ + GtsHSplit * parent; + GtsSplit * vs; + + g_return_if_fail (hs != NULL); + g_return_if_fail (hs->nchild == 2); + g_return_if_fail (hsurface != NULL); + + gts_split_collapse (GTS_SPLIT (hs), hsurface->s->edge_class, NULL); + + hsurface->nvertex--; + hs->nchild = 0; + HEAP_REMOVE_HSPLIT (hsurface->collapsable, hs); + HEAP_INSERT_HSPLIT (hsurface->expandable, hs); + + vs = GTS_SPLIT (hs); + if (GTS_IS_HSPLIT (vs->v1)) + HEAP_REMOVE_HSPLIT (hsurface->expandable, GTS_HSPLIT (vs->v1)); + if (GTS_IS_HSPLIT (vs->v2)) + HEAP_REMOVE_HSPLIT (hsurface->expandable, GTS_HSPLIT (vs->v2)); + + parent = hs->parent; + if (parent && ++parent->nchild == 2) + HEAP_INSERT_HSPLIT (hsurface->collapsable, parent); +} + +/** + * gts_hsplit_expand: + * @hs: a #GtsHSplit. + * @hsurface: a #GtsHSurface. + * + * Expands the #GtsSplit defined by @hs (which must be expandable) + * and updates the priority heaps of @hsurface. + */ +void gts_hsplit_expand (GtsHSplit * hs, + GtsHSurface * hsurface) +{ + GtsHSplit * parent; + GtsSplit * vs; + + g_return_if_fail (hs != NULL); + g_return_if_fail (hsurface != NULL); + g_return_if_fail (hs->nchild == 0); + + gts_split_expand (GTS_SPLIT (hs), hsurface->s, hsurface->s->edge_class); + hsurface->nvertex++; + hs->nchild = 2; + HEAP_REMOVE_HSPLIT (hsurface->expandable, hs); + HEAP_INSERT_HSPLIT (hsurface->collapsable, hs); + + vs = GTS_SPLIT (hs); + if (GTS_IS_HSPLIT (vs->v1)) + HEAP_INSERT_HSPLIT (hsurface->expandable, GTS_HSPLIT (vs->v1)); + if (GTS_IS_HSPLIT (vs->v2)) + HEAP_INSERT_HSPLIT (hsurface->expandable, GTS_HSPLIT (vs->v2)); + + parent = hs->parent; + if (parent && parent->nchild-- == 2) + HEAP_REMOVE_HSPLIT (hsurface->collapsable, parent); +} + +static void hsurface_destroy (GtsObject * object) +{ + GtsHSurface * hs = GTS_HSURFACE (object); + + gts_hsurface_traverse (hs, G_POST_ORDER, -1, + (GtsSplitTraverseFunc) gts_object_destroy, + NULL); + g_slist_free (hs->roots); + if (hs->expandable) + gts_eheap_destroy (hs->expandable); + if (hs->collapsable) + gts_eheap_destroy (hs->collapsable); + g_ptr_array_free (hs->split, TRUE); + + (* GTS_OBJECT_CLASS (gts_hsurface_class ())->parent_class->destroy) (object); +} + +static void hsurface_class_init (GtsObjectClass * klass) +{ + klass->destroy = hsurface_destroy; +} + +static void hsurface_init (GtsHSurface * hsurface) +{ + hsurface->s = NULL; + hsurface->roots = NULL; + hsurface->expandable = hsurface->collapsable = NULL; + hsurface->split = g_ptr_array_new (); + hsurface->nvertex = 0; +} + +/** + * gts_hsurface_class: + * + * Returns: the #GtsHSurfaceClass. + */ +GtsHSurfaceClass * gts_hsurface_class (void) +{ + static GtsHSurfaceClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo hsurface_info = { + "GtsHSurface", + sizeof (GtsHSurface), + sizeof (GtsHSurfaceClass), + (GtsObjectClassInitFunc) hsurface_class_init, + (GtsObjectInitFunc) hsurface_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (gts_object_class (), + &hsurface_info); + } + + return klass; +} + +/** + * gts_hsurface_new: + * @klass: a #GtsHSurfaceClass. + * @hsplit_class: a #GtsHSplitClass. + * @psurface: a #GtsPSurface. + * @expand_key: a #GtsKeyFunc used to order the priority heap of expandable + * #GtsHSplit. + * @expand_data: data to be passed to @expand_key. + * @collapse_key: a #GtsKeyFunc used to order the priority heap of collapsable + * #GtsHSplit. + * @collapse_data: data to be passed to @collapsed_key. + * + * Returns: a new #GtsHSurface, hierarchical extension of @psurface + * and using #GtsHSplit of class @hsplit_class. Note that @psurface is + * destroyed in the process. + */ +GtsHSurface * gts_hsurface_new (GtsHSurfaceClass * klass, + GtsHSplitClass * hsplit_class, + GtsPSurface * psurface, + GtsKeyFunc expand_key, + gpointer expand_data, + GtsKeyFunc collapse_key, + gpointer collapse_data) +{ + GtsHSurface * hsurface; + + g_return_val_if_fail (klass != NULL, NULL); + g_return_val_if_fail (hsplit_class != NULL, NULL); + g_return_val_if_fail (psurface != NULL, NULL); + g_return_val_if_fail (expand_key != NULL, NULL); + g_return_val_if_fail (collapse_key != NULL, NULL); + + hsurface = GTS_HSURFACE (gts_object_new (GTS_OBJECT_CLASS (klass))); + hsurface->s = psurface->s; + hsurface->expandable = gts_eheap_new (expand_key, expand_data); + hsurface->collapsable = gts_eheap_new (collapse_key, collapse_data); + g_ptr_array_set_size (hsurface->split, psurface->split->len); + + while (gts_psurface_remove_vertex (psurface)) + ; + while (psurface->pos) { + GtsSplit * vs = g_ptr_array_index (psurface->split, psurface->pos - 1); + GtsHSplit * hs = gts_hsplit_new (hsplit_class, vs); + + g_ptr_array_index (hsurface->split, psurface->pos - 1) = hs; + psurface->pos--; + + hs->parent = GTS_OBJECT (vs)->reserved; + if (hs->parent) { + GtsSplit * vsp = GTS_SPLIT (hs->parent); + + if (vsp->v1 == GTS_OBJECT (vs)) { + g_assert (vsp->v2 != GTS_OBJECT (vs)); + vsp->v1 = GTS_OBJECT (hs); + } + else { + g_assert (vsp->v2 == GTS_OBJECT (vs)); + vsp->v2 = GTS_OBJECT (hs); + } + } + else + hsurface->roots = g_slist_prepend (hsurface->roots, hs); + + hs->nchild = 0; + if (GTS_IS_SPLIT (vs->v1)) + GTS_OBJECT (vs->v1)->reserved = hs; + else + hs->nchild++; + if (GTS_IS_SPLIT (vs->v2)) + GTS_OBJECT (vs->v2)->reserved = hs; + else + hs->nchild++; + + gts_split_expand (vs, psurface->s, psurface->s->edge_class); + + if (hs->nchild == 2) + HEAP_INSERT_HSPLIT (hsurface->collapsable, hs); + } + + hsurface->nvertex = gts_surface_vertex_number (hsurface->s); + gts_object_destroy (GTS_OBJECT (psurface)); + + return hsurface; +} + +/** + * gts_hsurface_traverse: + * @hsurface: a #GtsHSurface. + * @order: the order in which nodes are visited - G_PRE_ORDER or G_POST_ORDER. + * @depth: the maximum depth of the traversal. Nodes below this depth + * will not be visited. If max_depth is -1 all nodes in the tree are + * visited. If depth is 1, only the root is visited. If depth is 2, + * the root and its children are visited. And so on. + * @func: the function to call for each visited #GtsHSplit. + * @data: user data to pass to the function. + * + * Traverses a hierarchical surface starting from its roots. It calls + * the given function for each #GtsHSplit visited. + * See also gts_split_traverse(). + */ +void gts_hsurface_traverse (GtsHSurface * hsurface, + GTraverseType order, + gint depth, + GtsSplitTraverseFunc func, + gpointer data) +{ + GSList * i; + + g_return_if_fail (hsurface != NULL); + g_return_if_fail (func != NULL); + g_return_if_fail (order < G_LEVEL_ORDER); + g_return_if_fail (depth == -1 || depth > 0); + + i = hsurface->roots; + while (i) { + gts_split_traverse (i->data, order, depth, func, data); + i = i->next; + } +} + +/** + * gts_hsurface_foreach: + * @hsurface: a #GtsHSurface. + * @order: the order in which #GtsHSplit are visited - G_PRE_ORDER or + * G_POST_ORDER. + * @func: the function to call for each visited #GtsHSplit. + * @data: user data to pass to the function. + * + * Starts by expanding all the #GtsHSplit of @hsurface. If @order is + * G_PRE_ORDER, calls @func for each #GtsHSplit and collapses it. If + * order is G_POST_ORDER, collapses each #GtsHSplit first and then + * calls @func. The traversal can be halted at any point by returning + * TRUE from func. + */ +void gts_hsurface_foreach (GtsHSurface * hsurface, + GTraverseType order, + GtsFunc func, + gpointer data) +{ + GtsHSplit * hs; + guint i = 0, len; + gboolean stop = FALSE; + + g_return_if_fail (hsurface != NULL); + g_return_if_fail (func != NULL); + g_return_if_fail (order == G_PRE_ORDER || order == G_POST_ORDER); + + while ((hs = gts_eheap_top (hsurface->expandable, NULL))) + gts_hsplit_expand (hs, hsurface); + + len = hsurface->split->len; + switch (order) { + case G_PRE_ORDER: + while (i < len && !stop) { + GtsHSplit * hs = g_ptr_array_index (hsurface->split, i); + stop = (*func) (hs, data); + if (!stop) + gts_hsplit_collapse (hs, hsurface); + i++; + } + break; + case G_POST_ORDER: + while (i < len && !stop) { + GtsHSplit * hs = g_ptr_array_index (hsurface->split, i); + gts_hsplit_collapse (hs, hsurface); + stop = (*func) (hs, data); + i++; + } + break; + default: + g_assert_not_reached (); + } +} + +/** + * gts_hsurface_height: + * @hsurface: a #GtsHSurface. + * + * Returns: the maximum height of the tree described by @hsurface. + */ +guint gts_hsurface_height (GtsHSurface * hsurface) +{ + GSList * i; + guint height = 0; + + g_return_val_if_fail (hsurface != NULL, 0); + + i = hsurface->roots; + while (i) { + guint tmp_height = gts_split_height (i->data); + if (tmp_height > height) + height = tmp_height; + i = i->next; + } + + return height; +} Index: trunk/src_3rd/gts/iso.c =================================================================== --- trunk/src_3rd/gts/iso.c (nonexistent) +++ trunk/src_3rd/gts/iso.c (revision 1065) @@ -0,0 +1,455 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "gts.h" + +typedef enum { LEFT = 0, RIGHT = 1 } Orientation; + +typedef struct { + GtsVertex * v; + Orientation orientation; +} OrientedVertex; + +struct _GtsIsoSlice { + OrientedVertex *** vertices; + guint nx, ny; +}; + +/* coordinates of the edges of the cube (see doc/isocube.fig) */ +static guint c[12][4] = { + {0, 0, 0, 0}, {0, 0, 0, 1}, {0, 0, 1, 1}, {0, 0, 1, 0}, + {1, 0, 0, 0}, {1, 0, 0, 1}, {1, 1, 0, 1}, {1, 1, 0, 0}, + {2, 0, 0, 0}, {2, 1, 0, 0}, {2, 1, 1, 0}, {2, 0, 1, 0}}; + +/* first index is the edge number, second index is the edge orientation + (RIGHT or LEFT), third index are the edges which this edge may connect to + in order */ +static guint edge[12][2][3] = { + {{9, 1, 8}, {4, 3, 7}}, /* 0 */ + {{6, 2, 5}, {8, 0, 9}}, /* 1 */ + {{10, 3, 11}, {5, 1, 6}}, /* 2 */ + {{7, 0, 4}, {11, 2, 10}}, /* 3 */ + {{3, 7, 0}, {8, 5, 11}}, /* 4 */ + {{11, 4, 8}, {1, 6, 2}}, /* 5 */ + {{2, 5, 1}, {9, 7, 10}}, /* 6 */ + {{10, 6, 9}, {0, 4, 3}}, /* 7 */ + {{5, 11, 4}, {0, 9, 1}}, /* 8 */ + {{1, 8, 0}, {7, 10, 6}}, /* 9 */ + {{6, 9, 7}, {3, 11, 2}}, /* 10 */ + {{2, 10, 3}, {4, 8, 5}} /* 11 */ +}; + +static void ** malloc2D (guint nx, guint ny, gulong size) +{ + void ** m = g_malloc (nx*sizeof (void *)); + guint i; + + for (i = 0; i < nx; i++) + m[i] = g_malloc0 (ny*size); + + return m; +} + +static void free2D (void ** m, guint nx) +{ + guint i; + + g_return_if_fail (m != NULL); + + for (i = 0; i < nx; i++) + g_free (m[i]); + g_free (m); +} + +/** + * gts_grid_plane_new: + * @nx: + * @ny: + * + * Returns: + */ +GtsGridPlane * gts_grid_plane_new (guint nx, guint ny) +{ + GtsGridPlane * g = g_malloc (sizeof (GtsGridPlane)); + + g->p = (GtsPoint **) malloc2D (nx, ny, sizeof (GtsPoint)); + g->nx = nx; + g->ny = ny; + + return g; +} + +/** + * gts_grid_plane_destroy: + * @g: + * + */ +void gts_grid_plane_destroy (GtsGridPlane * g) +{ + g_return_if_fail (g != NULL); + + free2D ((void **) g->p, g->nx); + g_free (g); +} + +/** + * gts_iso_slice_new: + * @nx: number of vertices in the x direction. + * @ny: number of vertices in the y direction. + * + * Returns: a new #GtsIsoSlice. + */ +GtsIsoSlice * gts_iso_slice_new (guint nx, guint ny) +{ + GtsIsoSlice * slice; + + g_return_val_if_fail (nx > 1, NULL); + g_return_val_if_fail (ny > 1, NULL); + + slice = g_malloc (sizeof (GtsIsoSlice)); + + slice->vertices = g_malloc (3*sizeof (OrientedVertex **)); + slice->vertices[0] = + (OrientedVertex **) malloc2D (nx, ny, sizeof (OrientedVertex)); + slice->vertices[1] = + (OrientedVertex **) malloc2D (nx - 1, ny, sizeof (OrientedVertex)); + slice->vertices[2] = + (OrientedVertex **) malloc2D (nx, ny - 1, sizeof (OrientedVertex)); + slice->nx = nx; + slice->ny = ny; + + return slice; +} + +/** + * gts_iso_slice_fill: + * @slice: a #GtsIsoSlice. + * @plane1: a #GtsGridPlane. + * @plane2: another #GtsGridPlane. + * @f1: values of the function corresponding to @plane1. + * @f2: values of the function corresponding to @plane2. + * @iso: isosurface value. + * @klass: a #GtsVertexClass or one of its descendant to be used for the + * new vertices. + * + * Fill @slice with the coordinates of the vertices defined by + * f1 (x,y,z) = @iso and f2 (x, y, z) = @iso. + */ +void gts_iso_slice_fill (GtsIsoSlice * slice, + GtsGridPlane * plane1, + GtsGridPlane * plane2, + gdouble ** f1, + gdouble ** f2, + gdouble iso, + GtsVertexClass * klass) +{ + OrientedVertex *** vertices; + GtsPoint ** p1, ** p2 = NULL; + guint i, j, nx, ny; + + g_return_if_fail (slice != NULL); + g_return_if_fail (plane1 != NULL); + g_return_if_fail (f1 != NULL); + g_return_if_fail (f2 == NULL || plane2 != NULL); + + p1 = plane1->p; + if (plane2) + p2 = plane2->p; + vertices = slice->vertices; + nx = slice->nx; + ny = slice->ny; + + if (f2) + for (i = 0; i < nx; i++) + for (j = 0; j < ny; j++) { + gdouble v1 = f1[i][j] - iso; + gdouble v2 = f2[i][j] - iso; + if ((v1 >= 0. && v2 < 0.) || (v1 < 0. && v2 >= 0.)) { + gdouble c2 = v1/(v1 - v2), c1 = 1. - c2; + vertices[0][i][j].v = + gts_vertex_new (klass, + c1*p1[i][j].x + c2*p2[i][j].x, + c1*p1[i][j].y + c2*p2[i][j].y, + c1*p1[i][j].z + c2*p2[i][j].z); + vertices[0][i][j].orientation = v2 >= 0. ? RIGHT : LEFT; + } + else + vertices[0][i][j].v = NULL; + } + for (i = 0; i < nx - 1; i++) + for (j = 0; j < ny; j++) { + gdouble v1 = f1[i][j] - iso; + gdouble v2 = f1[i+1][j] - iso; + if ((v1 >= 0. && v2 < 0.) || (v1 < 0. && v2 >= 0.)) { + gdouble c2 = v1/(v1 - v2), c1 = 1. - c2; + vertices[1][i][j].v = + gts_vertex_new (klass, + c1*p1[i][j].x + c2*p1[i+1][j].x, + c1*p1[i][j].y + c2*p1[i+1][j].y, + c1*p1[i][j].z + c2*p1[i+1][j].z); + vertices[1][i][j].orientation = v2 >= 0. ? RIGHT : LEFT; + } + else + vertices[1][i][j].v = NULL; + } + for (i = 0; i < nx; i++) + for (j = 0; j < ny - 1; j++) { + gdouble v1 = f1[i][j] - iso; + gdouble v2 = f1[i][j+1] - iso; + if ((v1 >= 0. && v2 < 0.) || (v1 < 0. && v2 >= 0.)) { + gdouble c2 = v1/(v1 - v2), c1 = 1. - c2; + vertices[2][i][j].v = + gts_vertex_new (klass, + c1*p1[i][j].x + c2*p1[i][j+1].x, + c1*p1[i][j].y + c2*p1[i][j+1].y, + c1*p1[i][j].z + c2*p1[i][j+1].z); + vertices[2][i][j].orientation = v2 >= 0. ? RIGHT : LEFT; + } + else + vertices[2][i][j].v = NULL; + } +} + +/** + * gts_iso_slice_fill_cartesian: + * @slice: a #GtsIsoSlice. + * @g: a #GtsCartesianGrid. + * @f1: values of the function for plane z = @g.z. + * @f2: values of the function for plane z = @g.z + @g.dz. + * @iso: isosurface value. + * @klass: a #GtsVertexClass. + * + * Fill @slice with the coordinates of the vertices defined by + * f1 (x,y,z) = @iso and f2 (x, y, z) = @iso. + */ +void gts_iso_slice_fill_cartesian (GtsIsoSlice * slice, + GtsCartesianGrid g, + gdouble ** f1, + gdouble ** f2, + gdouble iso, + GtsVertexClass * klass) +{ + OrientedVertex *** vertices; + guint i, j; + gdouble x, y; + + g_return_if_fail (slice != NULL); + g_return_if_fail (f1 != NULL); + + vertices = slice->vertices; + + if (f2) + for (i = 0, x = g.x; i < g.nx; i++, x += g.dx) + for (j = 0, y = g.y; j < g.ny; j++, y += g.dy) { + gdouble v1 = f1[i][j] - iso; + gdouble v2 = f2[i][j] - iso; + if ((v1 >= 0. && v2 < 0.) || (v1 < 0. && v2 >= 0.)) { + vertices[0][i][j].v = + gts_vertex_new (klass, + x, y, g.z + g.dz*v1/(v1 - v2)); + vertices[0][i][j].orientation = v2 >= 0. ? RIGHT : LEFT; + } + else + vertices[0][i][j].v = NULL; + } + for (i = 0, x = g.x; i < g.nx - 1; i++, x += g.dx) + for (j = 0, y = g.y; j < g.ny; j++, y += g.dy) { + gdouble v1 = f1[i][j] - iso; + gdouble v2 = f1[i+1][j] - iso; + if ((v1 >= 0. && v2 < 0.) || (v1 < 0. && v2 >= 0.)) { + vertices[1][i][j].v = + gts_vertex_new (klass, x + g.dx*v1/(v1 - v2), y, g.z); + vertices[1][i][j].orientation = v2 >= 0. ? RIGHT : LEFT; + } + else + vertices[1][i][j].v = NULL; + } + for (i = 0, x = g.x; i < g.nx; i++, x += g.dx) + for (j = 0, y = g.y; j < g.ny - 1; j++, y += g.dy) { + gdouble v1 = f1[i][j] - iso; + gdouble v2 = f1[i][j+1] - iso; + if ((v1 >= 0. && v2 < 0.) || (v1 < 0. && v2 >= 0.)) { + vertices[2][i][j].v = + gts_vertex_new (klass, x, y + g.dy*v1/(v1 - v2), g.z); + vertices[2][i][j].orientation = v2 >= 0. ? RIGHT : LEFT; + } + else + vertices[2][i][j].v = NULL; + } +} + +/** + * gts_iso_slice_destroy: + * @slice: a #GtsIsoSlice. + * + * Free all memory allocated for @slice. + */ +void gts_iso_slice_destroy (GtsIsoSlice * slice) +{ + g_return_if_fail (slice != NULL); + + free2D ((void **) slice->vertices[0], slice->nx); + free2D ((void **) slice->vertices[1], slice->nx - 1); + free2D ((void **) slice->vertices[2], slice->nx); + g_free (slice->vertices); + g_free (slice); +} + +/** + * gts_isosurface_slice: + * @slice1: a #GtsIsoSlice. + * @slice2: another #GtsIsoSlice. + * @surface: a #GtsSurface. + * + * Given two successive slices @slice1 and @slice2 link their vertices with + * segments and triangles which are added to @surface. + */ +void gts_isosurface_slice (GtsIsoSlice * slice1, + GtsIsoSlice * slice2, + GtsSurface * surface) +{ + guint j, k, l, nx, ny; + OrientedVertex *** vertices[2]; + GtsVertex * va[12]; + + g_return_if_fail (slice1 != NULL); + g_return_if_fail (slice2 != NULL); + g_return_if_fail (surface != NULL); + g_return_if_fail (slice1->nx == slice2->nx && slice1->ny == slice2->ny); + + vertices[0] = slice1->vertices; + vertices[1] = slice2->vertices; + nx = slice1->nx; + ny = slice1->ny; + + /* link vertices with segments and triangles */ + for (j = 0; j < nx - 1; j++) + for (k = 0; k < ny - 1; k++) { + gboolean cube_is_cut = FALSE; + for (l = 0; l < 12; l++) { + guint nv = 0, e = l; + OrientedVertex ov = + vertices[c[e][1]][c[e][0]][j + c[e][2]][k + c[e][3]]; + while (ov.v && !GTS_OBJECT (ov.v)->reserved) { + guint m = 0, * ne = edge[e][ov.orientation]; + va[nv++] = ov.v; + GTS_OBJECT (ov.v)->reserved = surface; + ov.v = NULL; + while (m < 3 && !ov.v) { + e = ne[m++]; + ov = vertices[c[e][1]][c[e][0]][j + c[e][2]][k + c[e][3]]; + } + } + /* create edges and faces */ + if (nv > 2) { + GtsEdge * e1, * e2, * e3; + guint m; + if (!(e1 = GTS_EDGE (gts_vertices_are_connected (va[0], va[1])))) + e1 = gts_edge_new (surface->edge_class, va[0], va[1]); + for (m = 1; m < nv - 1; m++) { + if (!(e2 = GTS_EDGE (gts_vertices_are_connected (va[m], va[m+1])))) + e2 = gts_edge_new (surface->edge_class, va[m], va[m+1]); + if (!(e3 = GTS_EDGE (gts_vertices_are_connected (va[m+1], va[0])))) + e3 = gts_edge_new (surface->edge_class, va[m+1], va[0]); + gts_surface_add_face (surface, + gts_face_new (surface->face_class, + e1, e2, e3)); + e1 = e3; + } + } + if (nv > 0) + cube_is_cut = TRUE; + } + if (cube_is_cut) + for (l = 0; l < 12; l++) { + GtsVertex * v = + vertices[c[l][1]][c[l][0]][j + c[l][2]][k + c[l][3]].v; + if (v) + GTS_OBJECT (v)->reserved = NULL; + } + } +} + +#define SWAP(s1, s2, tmp) (tmp = s1, s1 = s2, s2 = tmp) + +/** + * gts_isosurface_cartesian: + * @surface: a #GtsSurface. + * @g: a #GtsCartesianGrid. + * @f: a #GtsIsoCartesianFunc. + * @data: user data to be passed to @f. + * @iso: isosurface value. + * + * Adds to @surface new faces defining the isosurface f(x,y,z) = @iso. By + * convention, the normals to the surface are pointing toward the positive + * values of f(x,y,z) - @iso. + * + * The user function @f is called successively for each value of the z + * coordinate defined by @g. It must fill the corresponding (x,y) plane with + * the values of the function for which the isosurface is to be computed. + */ +void gts_isosurface_cartesian (GtsSurface * surface, + GtsCartesianGrid g, + GtsIsoCartesianFunc f, + gpointer data, + gdouble iso) +{ + void * tmp; + gdouble ** f1, ** f2; + GtsIsoSlice * slice1, * slice2; + guint i; + + g_return_if_fail (surface != NULL); + g_return_if_fail (f != NULL); + g_return_if_fail (g.nx > 1); + g_return_if_fail (g.ny > 1); + g_return_if_fail (g.nz > 1); + + slice1 = gts_iso_slice_new (g.nx, g.ny); + slice2 = gts_iso_slice_new (g.nx, g.ny); + f1 = (gdouble **) malloc2D (g.nx, g.ny, sizeof (gdouble)); + f2 = (gdouble **) malloc2D (g.nx, g.ny, sizeof (gdouble)); + + (*f) (f1, g, 0, data); + g.z += g.dz; + (*f) (f2, g, 1, data); + g.z -= g.dz; + gts_iso_slice_fill_cartesian (slice1, g, f1, f2, iso, + surface->vertex_class); + g.z += g.dz; + for (i = 2; i < g.nz; i++) { + g.z += g.dz; + (*f) (f1, g, i, data); + SWAP (f1, f2, tmp); + g.z -= g.dz; + gts_iso_slice_fill_cartesian (slice2, g, f1, f2, iso, + surface->vertex_class); + g.z += g.dz; + gts_isosurface_slice (slice1, slice2, surface); + SWAP (slice1, slice2, tmp); + } + gts_iso_slice_fill_cartesian (slice2, g, f2, NULL, iso, + surface->vertex_class); + gts_isosurface_slice (slice1, slice2, surface); + + gts_iso_slice_destroy (slice1); + gts_iso_slice_destroy (slice2); + free2D ((void **) f1, g.nx); + free2D ((void **) f2, g.nx); +} Index: trunk/src_3rd/gts/isotetra.c =================================================================== --- trunk/src_3rd/gts/isotetra.c (nonexistent) +++ trunk/src_3rd/gts/isotetra.c (revision 1065) @@ -0,0 +1,840 @@ +/* GTS-Library conform marching tetrahedra algorithm + * Copyright (C) 2002 Gert Wollny + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#ifdef NATIVE_WIN32 +# include +# define M_SQRT2 1.41421356237309504880 +#endif /* NATIVE_WIN32 */ + +typedef struct { + gint nx, ny; + gdouble ** data; +} slice_t; + +typedef struct { + gint x, y, z; + gboolean mid; + gdouble d; +} tetra_vertex_t; + +/* this helper is a lookup table for vertices */ +typedef struct { + gint nx, ny; + GtsVertex ** vtop, ** vmid, **vbot; +} helper_t ; + +typedef struct { + GHashTable * vbot, * vtop; +} helper_bcl ; + + +static helper_t * init_helper (int nx, int ny) +{ + gint nxy = 4*nx*ny; + helper_t *retval = g_malloc0 (sizeof (helper_t)); + + retval->nx = nx; + retval->ny = ny; + retval->vtop = g_malloc0 (sizeof (GtsVertex *)*nxy); + retval->vmid = g_malloc0 (sizeof (GtsVertex *)*nxy); + retval->vbot = g_malloc0 (sizeof (GtsVertex *)*nxy); + return retval; +} + +static helper_bcl * init_helper_bcl (void) +{ + helper_bcl *retval = g_malloc0 (sizeof (helper_bcl)); + + retval->vtop = g_hash_table_new (g_str_hash, g_str_equal); + retval->vbot = g_hash_table_new (g_str_hash, g_str_equal); + return retval; +} + +static void free_helper (helper_t * h) +{ + g_free (h->vtop); + g_free (h->vmid); + g_free (h->vbot); + g_free (h); +} + +static void free_helper_bcl (helper_bcl * h) +{ + g_hash_table_destroy (h->vtop); + g_hash_table_destroy (h->vbot); + g_free (h); +} + +/* move the vertices in the bottom slice to the top, and clear the + other slices in the lookup tables */ +static void helper_advance (helper_t * h) +{ + GtsVertex ** help = h->vbot; + h->vbot = h->vtop; + h->vtop = help; + + memset (h->vmid, 0, 4*sizeof(GtsVertex *) * h->nx * h->ny); + memset (h->vbot, 0, 4*sizeof(GtsVertex *) * h->nx * h->ny); +} + +static void helper_advance_bcl (helper_bcl * h) +{ + GHashTable * help = g_hash_table_new (g_str_hash, g_str_equal); + + g_hash_table_destroy (h->vbot); + h->vbot = h->vtop; + h->vtop = help; +} + +/* find the zero-crossing of line through v1 and v2 and return the + corresponding GtsVertex */ +static GtsVertex * get_vertex (gint mz, + const tetra_vertex_t * v1, + const tetra_vertex_t * v2, + helper_t * help, + GtsCartesianGrid * g, + GtsVertexClass * klass) +{ + GtsVertex ** vertex; + gint x, y, index, idx2, z; + gdouble dx, dy, dz, d; + + g_assert (v1->d - v2->d != 0.); + + dx = dy = dz = 0.0; + d = v1->d/(v1->d - v2->d); + + index = 0; + + if (v1->x != v2->x) { + index |= 1; + dx = d; + } + + if (v1->y != v2->y) { + index |= 2; + dy = d; + } + + if (v1->z != v2->z) { + dz = d; + } + + x = v1->x; + if (v1->x > v2->x) { x = v2->x; dx = 1.0 - dx; } + + y = v1->y; + if (v1->y > v2->y) { y = v2->y; dy = 1.0 - dy;} + + z = v1->z; + if (v1->z > v2->z) { z = v2->z; dz = 1.0 - dz;} + + idx2 = 4 * ( x + y * help->nx ) + index; + + if (v1->z == v2->z) + vertex = (mz == z) ? &help->vtop[idx2] : &help->vbot[idx2]; + else + vertex = &help->vmid[idx2]; + + if (mz != z && dz != 0.0) { + fprintf(stderr, "%f \n", dz); + } + + /* if vertex is not yet created, do it now */ + if (!*vertex) + *vertex = gts_vertex_new (klass, + g->dx * ( x + dx) + g->x, + g->dy * ( y + dy) + g->y, + g->dz * ( z + dz) + g->z); + + return *vertex; +} + +static GtsVertex * get_vertex_bcl (gint mz, + const tetra_vertex_t * v1, + const tetra_vertex_t * v2, + helper_bcl * help, + GtsCartesianGrid * g, + GtsVertexClass * klass) +{ + GtsVertex * v; + GHashTable * table; + gchar * s1, * s2, * hash; + gdouble x1, x2, y1, y2, z1, z2, d; + + g_assert (v1->d - v2->d != 0.); + + /* first find correct hash table */ + if ((v1->z > mz) && (v2->z > mz)) + table = help->vtop; + else + table = help->vbot; + + d = v1->d / (v1->d - v2->d); + + /* sort vertices */ + s1 = g_strdup_printf ("%d %d %d %d", v1->x, v1->y, v1->z, v1->mid); + s2 = g_strdup_printf ("%d %d %d %d", v2->x, v2->y, v2->z, v2->mid); + + hash = (d == 0.0) ? g_strdup (s1) : + (d == 1.0) ? g_strdup (s2) : + (strcmp (s1, s2) < 0) ? g_strjoin (" ", s1, s2, NULL) : + g_strjoin (" ", s2, s1, NULL); + + /* return existing vertex or make a new one */ + v = g_hash_table_lookup (table, hash); + if (!v){ + + x1 = g->dx * (v1->x + (v1->mid / 2.0)) + g->x; + x2 = g->dx * (v2->x + (v2->mid / 2.0)) + g->x; + y1 = g->dy * (v1->y + (v1->mid / 2.0)) + g->y; + y2 = g->dy * (v2->y + (v2->mid / 2.0)) + g->y; + z1 = g->dz * (v1->z + (v1->mid / 2.0)) + g->z; + z2 = g->dz * (v2->z + (v2->mid / 2.0)) + g->z; + + v = gts_vertex_new (klass, x1 * (1.0 - d) + d * x2, + y1 * (1.0 - d) + d * y2, + z1 * (1.0 - d) + d * z2); + + g_hash_table_insert (table, g_strdup(hash), v); + } + g_free (s1); + g_free (s2); + g_free (hash); + + return v; +} + +/* create an edge connecting the zero crossings of lines through a + pair of vertices, or return an existing one */ +static GtsEdge * get_edge (GtsVertex * v1, GtsVertex * v2, + GtsEdgeClass * klass) +{ + GtsSegment *s; + GtsEdge *edge; + + g_assert (v1); + g_assert (v2); + + s = gts_vertices_are_connected (v1, v2); + + if (GTS_IS_EDGE (s)) + edge = GTS_EDGE(s); + else + edge = gts_edge_new (klass, v1, v2); + return edge; +} + +static void add_face (GtsSurface * surface, + const tetra_vertex_t * a1, const tetra_vertex_t * a2, + const tetra_vertex_t * b1, const tetra_vertex_t * b2, + const tetra_vertex_t * c1, const tetra_vertex_t * c2, + gint rev, helper_t * help, + gint z, GtsCartesianGrid * g) +{ + GtsFace * t; + GtsEdge * e1, * e2, * e3; + GtsVertex * v1 = get_vertex (z, a1, a2, help, g, surface->vertex_class); + GtsVertex * v2 = get_vertex (z, b1, b2, help, g, surface->vertex_class); + GtsVertex * v3 = get_vertex (z, c1, c2, help, g, surface->vertex_class); + + g_assert (v1 != v2); + g_assert (v2 != v3); + g_assert (v1 != v3); + + if (!rev) { + e1 = get_edge (v1, v2, surface->edge_class); + e2 = get_edge (v2, v3, surface->edge_class); + e3 = get_edge (v1, v3, surface->edge_class); + } else { + e1 = get_edge (v1, v3, surface->edge_class); + e2 = get_edge (v2, v3, surface->edge_class); + e3 = get_edge (v1, v2, surface->edge_class); + } + + t = gts_face_new (surface->face_class, e1, e2, e3); + gts_surface_add_face (surface, t); +} + +static void add_face_bcl (GtsSurface * surface, + const tetra_vertex_t * a1, + const tetra_vertex_t * a2, + const tetra_vertex_t * b1, + const tetra_vertex_t * b2, + const tetra_vertex_t * c1, + const tetra_vertex_t * c2, + gint rev, helper_bcl * help, + gint z, GtsCartesianGrid * g) +{ + GtsFace * t; + GtsEdge * e1, * e2, * e3; + GtsVertex * v1 = get_vertex_bcl (z, a1, a2, help, g, surface->vertex_class); + GtsVertex * v2 = get_vertex_bcl (z, b1, b2, help, g, surface->vertex_class); + GtsVertex * v3 = get_vertex_bcl (z, c1, c2, help, g, surface->vertex_class); + + if (v1 == v2 || v2 == v3 || v1 == v3) + return; + + if (!rev) { + e1 = get_edge (v1, v2, surface->edge_class); + e2 = get_edge (v2, v3, surface->edge_class); + e3 = get_edge (v1, v3, surface->edge_class); + } else { + e1 = get_edge (v1, v3, surface->edge_class); + e2 = get_edge (v2, v3, surface->edge_class); + e3 = get_edge (v1, v2, surface->edge_class); + } + + t = gts_face_new (surface->face_class, e1, e2, e3); + gts_surface_add_face (surface, t); +} + +/* create a new slice of site nx \times ny */ +static slice_t * new_slice (gint nx, gint ny) +{ + gint x; + slice_t * retval = g_malloc (sizeof (slice_t)); + + retval->data = g_malloc (nx*sizeof(gdouble *)); + retval->nx = nx; + retval->ny = ny; + for (x = 0; x < nx; x++) + retval->data[x] = g_malloc (ny*sizeof (gdouble)); + return retval; +} + +/* initialize a slice with inival */ +static void slice_init (slice_t * slice, gdouble inival) +{ + gint x, y; + + g_assert (slice); + + for (x = 0; x < slice->nx; x++) + for (y = 0; y < slice->ny; y++) + slice->data[x][y] = inival; +} + +/* free the memory of a slice */ +static void free_slice (slice_t * slice) +{ + gint x; + + g_return_if_fail (slice != NULL); + + for (x = 0; x < slice->nx; x++) + g_free (slice->data[x]); + g_free (slice->data); + g_free (slice); +} + +static void analyze_tetrahedra (const tetra_vertex_t * a, + const tetra_vertex_t * b, + const tetra_vertex_t * c, + const tetra_vertex_t * d, + gint parity, GtsSurface * surface, + helper_t * help, + gint z, GtsCartesianGrid * g) +{ + gint rev = parity; + gint code = 0; + + if (a->d >= 0.) code |= 1; + if (b->d >= 0.) code |= 2; + if (c->d >= 0.) code |= 4; + if (d->d >= 0.) code |= 8; + + switch (code) { + case 15: + case 0: return; /* all inside or outside */ + + case 14:rev = !parity; + case 1:add_face (surface, a, b, a, d, a, c, rev, help, z, g); + break; + case 13:rev = ! parity; + case 2:add_face (surface, a, b, b, c, b, d, rev, help, z, g); + break; + case 12:rev = !parity; + case 3:add_face (surface, a, d, a, c, b, c, rev, help, z, g); + add_face (surface, a, d, b, c, b, d, rev, help, z, g); + break; + case 11:rev = !parity; + case 4:add_face (surface, a, c, c, d, b, c, rev, help, z, g); + break; + case 10:rev = !parity; + case 5: add_face (surface, a, b, a, d, c, d, rev, help, z, g); + add_face (surface, a, b, c, d, b, c, rev, help, z, g); + break; + case 9:rev = !parity; + case 6:add_face (surface, a, b, a, c, c, d, rev, help, z, g); + add_face (surface, a, b, c, d, b, d, rev, help, z, g); + break; + case 7:rev = !parity; + case 8:add_face (surface, a, d, b, d, c, d, rev, help, z, g); + break; + } +} + +static void analyze_tetrahedra_bcl (const tetra_vertex_t * a, + const tetra_vertex_t * b, + const tetra_vertex_t * c, + const tetra_vertex_t * d, + GtsSurface * surface, + helper_bcl * help, + gint z, GtsCartesianGrid * g) +{ + gint rev = 0; + gint code = 0; + + if (a->d >= 0.) code |= 1; + if (b->d >= 0.) code |= 2; + if (c->d >= 0.) code |= 4; + if (d->d >= 0.) code |= 8; + + switch (code) { + case 15: + case 0: return; /* all inside or outside */ + + case 14:rev = !rev; + case 1:add_face_bcl (surface, a, b, a, d, a, c, rev, help, z, g); + break; + case 13:rev = !rev; + case 2:add_face_bcl (surface, a, b, b, c, b, d, rev, help, z, g); + break; + case 12:rev = !rev; + case 3:add_face_bcl (surface, a, d, a, c, b, c, rev, help, z, g); + add_face_bcl (surface, a, d, b, c, b, d, rev, help, z, g); + break; + case 11:rev = !rev; + case 4:add_face_bcl (surface, a, c, c, d, b, c, rev, help, z, g); + break; + case 10:rev = !rev; + case 5: add_face_bcl (surface, a, b, a, d, c, d, rev, help, z, g); + add_face_bcl (surface, a, b, c, d, b, c, rev, help, z, g); + break; + case 9:rev = !rev; + case 6:add_face_bcl (surface, a, b, a, c, c, d, rev, help, z, g); + add_face_bcl (surface, a, b, c, d, b, d, rev, help, z, g); + break; + case 7:rev = !rev; + case 8:add_face_bcl (surface, a, d, b, d, c, d, rev, help, z, g); + break; + } +} + +static void iso_slice_evaluate (slice_t * s1, slice_t * s2, + GtsCartesianGrid g, + gint z, GtsSurface * surface, helper_t * help) +{ + gint x,y; + tetra_vertex_t v0, v1, v2, v3, v4, v5, v6, v7; + gdouble ** s1p = s1->data; + gdouble ** s2p = s2->data; + + for (y = 0; y < g.ny-1; y++) + for (x = 0; x < g.nx-1; x++) { + gint parity = (((x ^ y) ^ z) & 1); + + v0.x = x ; v0.y = y ; v0.z = z ; v0.mid = FALSE; v0.d = s1p[x ][y ]; + v1.x = x ; v1.y = y+1; v1.z = z ; v1.mid = FALSE; v1.d = s1p[x ][y+1]; + v2.x = x+1; v2.y = y ; v2.z = z ; v2.mid = FALSE; v2.d = s1p[x+1][y ]; + v3.x = x+1; v3.y = y+1; v3.z = z ; v3.mid = FALSE; v3.d = s1p[x+1][y+1]; + v4.x = x ; v4.y = y ; v4.z = z+1; v4.mid = FALSE; v4.d = s2p[x ][y ]; + v5.x = x ; v5.y = y+1; v5.z = z+1; v5.mid = FALSE; v5.d = s2p[x ][y+1]; + v6.x = x+1; v6.y = y ; v6.z = z+1; v6.mid = FALSE; v6.d = s2p[x+1][y ]; + v7.x = x+1; v7.y = y+1; v7.z = z+1; v7.mid = FALSE; v7.d = s2p[x+1][y+1]; + + if (parity == 0) { + analyze_tetrahedra (&v0, &v1, &v2, &v4, parity, surface, help, z, &g); + analyze_tetrahedra (&v7, &v1, &v4, &v2, parity, surface, help, z, &g); + analyze_tetrahedra (&v1, &v7, &v3, &v2, parity, surface, help, z, &g); + analyze_tetrahedra (&v1, &v7, &v4, &v5, parity, surface, help, z, &g); + analyze_tetrahedra (&v2, &v6, &v4, &v7, parity, surface, help, z, &g); + }else{ + analyze_tetrahedra (&v4, &v5, &v6, &v0, parity, surface, help, z, &g); + analyze_tetrahedra (&v3, &v5, &v0, &v6, parity, surface, help, z, &g); + analyze_tetrahedra (&v5, &v3, &v7, &v6, parity, surface, help, z, &g); + analyze_tetrahedra (&v5, &v3, &v0, &v1, parity, surface, help, z, &g); + analyze_tetrahedra (&v6, &v2, &v0, &v3, parity, surface, help, z, &g); + } + } +} + +static void iso_slice_evaluate_bcl (slice_t * s1, slice_t * s2, slice_t * s3, + GtsCartesianGrid g, + gint z, GtsSurface * surface, + helper_bcl * help) +{ + gint x,y; + tetra_vertex_t v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, w0; + gdouble ** s1p = s1->data; + gdouble ** s2p = s2->data; + gdouble ** s3p = s3->data; + + for (y = 0; y < g.ny-2; y++) + for (x = 0; x < g.nx-2; x++) { + v0.x = x ; v0.y = y ; v0.z = z ; v0.mid = TRUE; + v0.d = (s1p[x ][y ] + s2p[x ][y ] + + s1p[x ][y+1] + s2p[x ][y+1] + + s1p[x+1][y ] + s2p[x+1][y ] + + s1p[x+1][y+1] + s2p[x+1][y+1])/8.0; + + v1.x = x+1; v1.y = y ; v1.z = z ; v1.mid = TRUE; + v1.d = (s1p[x+1][y ] + s2p[x+1][y ] + + s1p[x+1][y+1] + s2p[x+1][y+1] + + s1p[x+2][y ] + s2p[x+2][y ] + + s1p[x+2][y+1] + s2p[x+2][y+1])/8.0; + + v2.x = x ; v2.y = y+1; v2.z = z ; v2.mid = TRUE; + v2.d = (s1p[x ][y+1] + s2p[x ][y+1] + + s1p[x ][y+2] + s2p[x ][y+2] + + s1p[x+1][y+1] + s2p[x+1][y+1] + + s1p[x+1][y+2] + s2p[x+1][y+2])/8.0; + + v3.x = x ; v3.y = y ; v3.z = z+1; v3.mid = TRUE; + v3.d = (s2p[x ][y ] + s3p[x ][y ] + + s2p[x ][y+1] + s3p[x ][y+1] + + s2p[x+1][y ] + s3p[x+1][y ] + + s2p[x+1][y+1] + s3p[x+1][y+1])/8.0; + + v4.x = x+1; v4.y = y ; v4.z = z ; v4.mid = FALSE; v4.d = s1p[x+1][y ]; + v5.x = x ; v5.y = y+1; v5.z = z ; v5.mid = FALSE; v5.d = s1p[x ][y+1]; + v6.x = x+1; v6.y = y+1; v6.z = z ; v6.mid = FALSE; v6.d = s1p[x+1][y+1]; + v7.x = x+1; v7.y = y ; v7.z = z+1; v7.mid = FALSE; v7.d = s2p[x+1][y ]; + v8.x = x ; v8.y = y+1; v8.z = z+1; v8.mid = FALSE; v8.d = s2p[x ][y+1]; + v9.x = x+1; v9.y = y+1; v9.z = z+1; v9.mid = FALSE; v9.d = s2p[x+1][y+1]; + w0.x = x ; w0.y = y ; w0.z = z+1; w0.mid = FALSE; w0.d = s2p[x ][y ]; + + analyze_tetrahedra_bcl (&v0, &v9, &v6, &v1, surface, help, z, &g); + analyze_tetrahedra_bcl (&v0, &v6, &v4, &v1, surface, help, z, &g); + analyze_tetrahedra_bcl (&v0, &v4, &v7, &v1, surface, help, z, &g); + analyze_tetrahedra_bcl (&v0, &v7, &v9, &v1, surface, help, z, &g); + analyze_tetrahedra_bcl (&v0, &v5, &v6, &v2, surface, help, z, &g); + analyze_tetrahedra_bcl (&v0, &v6, &v9, &v2, surface, help, z, &g); + analyze_tetrahedra_bcl (&v0, &v9, &v8, &v2, surface, help, z, &g); + analyze_tetrahedra_bcl (&v0, &v8, &v5, &v2, surface, help, z, &g); + analyze_tetrahedra_bcl (&v0, &v8, &v9, &v3, surface, help, z, &g); + analyze_tetrahedra_bcl (&v0, &v9, &v7, &v3, surface, help, z, &g); + analyze_tetrahedra_bcl (&v0, &v7, &w0, &v3, surface, help, z, &g); + analyze_tetrahedra_bcl (&v0, &w0, &v8, &v3, surface, help, z, &g); + } +} + +/* copy src into dest by stripping off the iso value and leave out + the boundary (which should be G_MINDOUBLE) */ +static void copy_to_bounded (slice_t * dest, slice_t * src, + gdouble iso, gdouble fill) +{ + gint x,y; + gdouble * src_ptr; + gdouble * dest_ptr = dest->data[0]; + + g_assert(dest->ny == src->ny + 2); + g_assert(dest->nx == src->nx + 2); + + for (y = 0; y < dest->ny; ++y, ++dest_ptr) + *dest_ptr = fill; + + for (x = 1; x < src->nx - 1; ++x) { + dest_ptr = dest->data[x]; + src_ptr = src->data[x-1]; + *dest_ptr++ = fill; + for (y = 0; y < src->ny; ++y, ++dest_ptr, ++src_ptr) + *dest_ptr = *src_ptr - iso; + *dest_ptr++ = fill; + } + + dest_ptr = dest->data[y]; + + for (y = 0; y < dest->ny; ++y, ++dest_ptr) + *dest_ptr = fill; +} + +static void iso_sub (slice_t * s, gdouble iso) +{ + gint x,y; + + for (x = 0; x < s->nx; ++x) { + gdouble *ptr = s->data[x]; + + for (y = 0; y < s->ny; ++y, ++ptr) + *ptr -= iso; + } +} + + +/** + * gts_isosurface_tetra_bounded: + * @surface: a #GtsSurface. + * @g: a #GtsCartesianGrid. + * @f: a #GtsIsoCartesianFunc. + * @data: user data to be passed to @f. + * @iso: isosurface value. + * + * Adds to @surface new faces defining the isosurface f(x,y,z) = + * @iso. By convention, the normals to the surface are pointing toward + * the positive values of f(x,y,z) - @iso. To ensure a closed object, + * a boundary of G_MINDOUBLE is added around the domain + * + * The user function @f is called successively for each value of the z + * coordinate defined by @g. It must fill the corresponding (x,y) + * plane with the values of the function for which the isosurface is + * to be computed. + */ +void gts_isosurface_tetra_bounded (GtsSurface * surface, + GtsCartesianGrid g, + GtsIsoCartesianFunc f, + gpointer data, + gdouble iso) +{ + slice_t *slice1, *slice2, *transfer_slice; + GtsCartesianGrid g_intern = g; + helper_t *helper; + gint z; + + g_return_if_fail (surface != NULL); + g_return_if_fail (f != NULL); + g_return_if_fail (g.nx > 1); + g_return_if_fail (g.ny > 1); + g_return_if_fail (g.nz > 1); + + /* create the helper slices */ + slice1 = new_slice (g.nx + 2, g.ny + 2); + slice2 = new_slice (g.nx + 2, g.ny + 2); + + /* initialize the first slice as OUTSIDE */ + slice_init (slice1, -1.0); + + /* create a slice of the original image size */ + transfer_slice = new_slice (g.nx, g.ny); + + /* adapt the parameters to our enlarged image */ + g_intern.x -= g.dx; + g_intern.y -= g.dy; + g_intern.z -= g.dz; + g_intern.nx = g.nx + 2; + g_intern.ny = g.ny + 2; + g_intern.nz = g.nz; + + /* create the helper for vertex-lookup */ + helper = init_helper (g_intern.nx, g_intern.ny); + + /* go slicewise through the data */ + z = 0; + while (z < g.nz) { + slice_t * hs; + + /* request slice */ + f (transfer_slice->data, g, z, data); + g.z += g.dz; + + /* copy slice in enlarged image and mark the border as OUTSIDE */ + copy_to_bounded (slice2, transfer_slice, iso, -1.); + + /* triangulate */ + iso_slice_evaluate (slice1, slice2, g_intern, z, surface, helper); + + /* switch the input slices */ + hs = slice1; slice1 = slice2; slice2 = hs; + + /* switch the vertex lookup tables */ + helper_advance(helper); + ++z; + } + + /* initialize the last slice as OUTSIDE */ + slice_init (slice2, - 1.0); + + /* close the object */ + iso_slice_evaluate(slice1, slice2, g_intern, z, surface, helper); + + free_helper (helper); + free_slice (slice1); + free_slice (slice2); + free_slice (transfer_slice); +} + +/** + * gts_isosurface_tetra: + * @surface: a #GtsSurface. + * @g: a #GtsCartesianGrid. + * @f: a #GtsIsoCartesianFunc. + * @data: user data to be passed to @f. + * @iso: isosurface value. + * + * Adds to @surface new faces defining the isosurface f(x,y,z) = + * @iso. By convention, the normals to the surface are pointing toward + * the positive values of f(x,y,z) - @iso. + * + * The user function @f is called successively for each value of the z + * coordinate defined by @g. It must fill the corresponding (x,y) + * plane with the values of the function for which the isosurface is + * to be computed. + */ +void gts_isosurface_tetra (GtsSurface * surface, + GtsCartesianGrid g, + GtsIsoCartesianFunc f, + gpointer data, + gdouble iso) +{ + slice_t *slice1, *slice2; + helper_t *helper; + gint z; + GtsCartesianGrid g_internal; + + g_return_if_fail (surface != NULL); + g_return_if_fail (f != NULL); + g_return_if_fail (g.nx > 1); + g_return_if_fail (g.ny > 1); + g_return_if_fail (g.nz > 1); + + memcpy (&g_internal, &g, sizeof (GtsCartesianGrid)); + + /* create the helper slices */ + slice1 = new_slice (g.nx, g.ny); + slice2 = new_slice (g.nx, g.ny); + + /* create the helper for vertex-lookup */ + helper = init_helper (g.nx, g.ny); + + z = 0; + f (slice1->data, g, z, data); + iso_sub (slice1, iso); + + z++; + g.z += g.dz; + + /* go slicewise through the data */ + while (z < g.nz) { + slice_t * hs; + + /* request slice */ + f (slice2->data, g, z, data); + iso_sub (slice2, iso); + + g.z += g.dz; + + /* triangulate */ + iso_slice_evaluate (slice1, slice2, g_internal, z-1, surface, helper); + + /* switch the input slices */ + hs = slice1; slice1 = slice2; slice2 = hs; + + /* switch the vertex lookup tables */ + helper_advance (helper); + + ++z; + } + + free_helper(helper); + free_slice(slice1); + free_slice(slice2); +} + +/** + * gts_isosurface_tetra_bcl: + * @surface: a #GtsSurface. + * @g: a #GtsCartesianGrid. + * @f: a #GtsIsoCartesianFunc. + * @data: user data to be passed to @f. + * @iso: isosurface value. + * + * Adds to @surface new faces defining the isosurface f(x,y,z) = + * @iso. By convention, the normals to the surface are pointing toward + * the positive values of f(x,y,z) - @iso. + * + * The user function @f is called successively for each value of the z + * coordinate defined by @g. It must fill the corresponding (x,y) + * plane with the values of the function for which the isosurface is + * to be computed. + * + * This version produces the dual "body-centered" faces relative to + * the faces produced by gts_isosurface_tetra(). + */ +void gts_isosurface_tetra_bcl (GtsSurface * surface, + GtsCartesianGrid g, + GtsIsoCartesianFunc f, + gpointer data, + gdouble iso) +{ + slice_t *slice1, *slice2, *slice3; + helper_bcl *helper; + gint z; + GtsCartesianGrid g_internal; + + g_return_if_fail (surface != NULL); + g_return_if_fail (f != NULL); + g_return_if_fail (g.nx > 1); + g_return_if_fail (g.ny > 1); + g_return_if_fail (g.nz > 1); + + memcpy (&g_internal, &g, sizeof (GtsCartesianGrid)); + + /* create the helper slices */ + slice1 = new_slice (g.nx, g.ny); + slice2 = new_slice (g.nx, g.ny); + slice3 = new_slice (g.nx, g.ny); + + /* create the helper for vertex-lookup */ + helper = init_helper_bcl (); + + z = 0; + f (slice1->data, g, z, data); + iso_sub (slice1, iso); + + z++; + g.z += g.dz; + + f (slice2->data, g, z, data); + iso_sub (slice1, iso); + + z++; + g.z += g.dz; + + /* go slicewise through the data */ + while (z < g.nz) { + slice_t * hs; + + /* request slice */ + f (slice3->data, g, z, data); + iso_sub (slice3, iso); + + g.z += g.dz; + + /* triangulate */ + iso_slice_evaluate_bcl (slice1, slice2, slice3, g_internal, z-2, + surface, helper); + + /* switch the input slices */ + hs = slice1; slice1 = slice2; slice2 = slice3; slice3 = hs; + + /* switch the vertex lookup tables */ + helper_advance_bcl (helper); + + ++z; + } + + free_helper_bcl(helper); + free_slice(slice1); + free_slice(slice2); + free_slice(slice3); +} Index: trunk/src_3rd/gts/kdtree.c =================================================================== --- trunk/src_3rd/gts/kdtree.c (nonexistent) +++ trunk/src_3rd/gts/kdtree.c (revision 1065) @@ -0,0 +1,152 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "gts.h" + + +static int compare_x (const void * p1, const void * p2) { + GtsPoint + * pp1 = *((gpointer *) p1), + * pp2 = *((gpointer *) p2); + if (pp1->x > pp2->x) + return 1; + return -1; +} + +static int compare_y (const void * p1, const void * p2) { + GtsPoint + * pp1 = *((gpointer *) p1), + * pp2 = *((gpointer *) p2); + if (pp1->y > pp2->y) + return 1; + return -1; +} + +static int compare_z (const void * p1, const void * p2) { + GtsPoint + * pp1 = *((gpointer *) p1), + * pp2 = *((gpointer *) p2); + if (pp1->z > pp2->z) + return 1; + return -1; +} + +/** + * gts_kdtree_new: + * @points: an array of #GtsPoint. + * @compare: always %NULL. + * + * Note that the order of the points in array @points is modified by this + * function. + * + * Returns: a new 3D tree for @points. + */ +GNode * gts_kdtree_new (GPtrArray * points, + int (*compare) (const void *, const void *)) +{ + guint middle; + GPtrArray array; + GNode * node; + GtsPoint * point; + + g_return_val_if_fail (points != NULL, NULL); + g_return_val_if_fail (points->len > 0, NULL); + + /* sort the points */ + if (compare == compare_x) compare = compare_y; + else if (compare == compare_y) compare = compare_z; + else compare = compare_x; + qsort (points->pdata, points->len, sizeof (gpointer), compare); + + middle = (points->len - 1)/2; + point = points->pdata[middle]; + node = g_node_new (point); + + if (points->len > 1) { + array.len = middle; + if (array.len > 0) { + array.pdata = points->pdata; + g_node_prepend (node, gts_kdtree_new (&array, compare)); + } + else + g_node_prepend (node, g_node_new (NULL)); + + array.len = points->len - middle - 1; + if (array.len > 0) { + array.pdata = &(points->pdata[middle + 1]); + g_node_prepend (node, gts_kdtree_new (&array, compare)); + } + else + g_node_prepend (node, g_node_new (NULL)); + } + + return node; +} + +/** + * gts_kdtree_range: + * @tree: a 3D tree. + * @bbox: a #GtsBBox. + * @compare: always %NULL. + * + * Returns: a list of #GtsPoint belonging to @tree which are inside @bbox. + */ +GSList * gts_kdtree_range (GNode * tree_3d, + GtsBBox * bbox, + int (*compare) (const void *, const void *)) +{ + GSList * list = NULL; + GtsPoint * p; + gdouble left, right, v; + GNode * node; + + g_return_val_if_fail (tree_3d != NULL, NULL); + g_return_val_if_fail (bbox != NULL, NULL); + + p = tree_3d->data; + if (p == NULL) + return NULL; + + if (gts_bbox_point_is_inside (bbox, p)) + list = g_slist_prepend (list, p); + + if (compare == compare_x) { + left = bbox->y1; right = bbox->y2; v = p->y; + compare = compare_y; + } + else if (compare == compare_y) { + left = bbox->z1; right = bbox->z2; v = p->z; + compare = compare_z; + } + else { + left = bbox->x1; right = bbox->x2; v = p->x; + compare = compare_x; + } + + if ((node = tree_3d->children)) { + if (right >= v) + list = g_slist_concat (list, gts_kdtree_range (node, bbox, compare)); + node = node->next; + if (left <= v) + list = g_slist_concat (list, gts_kdtree_range (node, bbox, compare)); + } + return list; +} + Index: trunk/src_3rd/gts/matrix.c =================================================================== --- trunk/src_3rd/gts/matrix.c (nonexistent) +++ trunk/src_3rd/gts/matrix.c (revision 1065) @@ -0,0 +1,725 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "gts.h" + +/** + * gts_matrix_new: + * @a00: element [0][0]. + * @a01: element [0][1]. + * @a02: element [0][2]. + * @a03: element [0][3]. + * @a10: element [1][0]. + * @a11: element [1][1]. + * @a12: element [1][2]. + * @a13: element [1][3]. + * @a20: element [2][0]. + * @a21: element [2][1]. + * @a22: element [2][2]. + * @a23: element [2][3]. + * @a30: element [3][0]. + * @a31: element [3][1]. + * @a32: element [3][2]. + * @a33: element [3][3]. + * + * Allocates memory and initializes a new #GtsMatrix. + * + * Returns: a pointer to the newly created #GtsMatrix. + */ +GtsMatrix * gts_matrix_new (gdouble a00, gdouble a01, gdouble a02, gdouble a03, + gdouble a10, gdouble a11, gdouble a12, gdouble a13, + gdouble a20, gdouble a21, gdouble a22, gdouble a23, + gdouble a30, gdouble a31, gdouble a32, gdouble a33) +{ + GtsMatrix * m; + + m = g_malloc (4*sizeof (GtsVector4)); + + m[0][0] = a00; m[1][0] = a10; m[2][0] = a20; m[3][0] = a30; + m[0][1] = a01; m[1][1] = a11; m[2][1] = a21; m[3][1] = a31; + m[0][2] = a02; m[1][2] = a12; m[2][2] = a22; m[3][2] = a32; + m[0][3] = a03; m[1][3] = a13; m[2][3] = a23; m[3][3] = a33; + + return m; +} + +/** + * gts_matrix_assign: + * @m: a #GtsMatrix. + * @a00: element [0][0]. + * @a01: element [0][1]. + * @a02: element [0][2]. + * @a03: element [0][3]. + * @a10: element [1][0]. + * @a11: element [1][1]. + * @a12: element [1][2]. + * @a13: element [1][3]. + * @a20: element [2][0]. + * @a21: element [2][1]. + * @a22: element [2][2]. + * @a23: element [2][3]. + * @a30: element [3][0]. + * @a31: element [3][1]. + * @a32: element [3][2]. + * @a33: element [3][3]. + * + * Set values of matrix elements. + */ +void gts_matrix_assign (GtsMatrix * m, + gdouble a00, gdouble a01, gdouble a02, gdouble a03, + gdouble a10, gdouble a11, gdouble a12, gdouble a13, + gdouble a20, gdouble a21, gdouble a22, gdouble a23, + gdouble a30, gdouble a31, gdouble a32, gdouble a33) +{ + g_return_if_fail (m != NULL); + + m[0][0] = a00; m[1][0] = a10; m[2][0] = a20; m[3][0] = a30; + m[0][1] = a01; m[1][1] = a11; m[2][1] = a21; m[3][1] = a31; + m[0][2] = a02; m[1][2] = a12; m[2][2] = a22; m[3][2] = a32; + m[0][3] = a03; m[1][3] = a13; m[2][3] = a23; m[3][3] = a33; +} + +/** + * gts_matrix_projection: + * @t: a #GtsTriangle. + * + * Creates a new #GtsMatrix representing the projection onto a plane of normal + * given by @t. + * + * Returns: a pointer to the newly created #GtsMatrix. + */ +GtsMatrix * gts_matrix_projection (GtsTriangle * t) +{ + GtsVertex * v1, * v2, * v3; + GtsEdge * e1, * e2, * e3; + GtsMatrix * m; + gdouble x1, y1, z1, x2, y2, z2, x3, y3, z3, l; + + g_return_val_if_fail (t != NULL, NULL); + + m = g_malloc (4*sizeof (GtsVector4)); + gts_triangle_vertices_edges (t, NULL, &v1, &v2, &v3, &e1, &e2, &e3); + + x1 = GTS_POINT (v2)->x - GTS_POINT (v1)->x; + y1 = GTS_POINT (v2)->y - GTS_POINT (v1)->y; + z1 = GTS_POINT (v2)->z - GTS_POINT (v1)->z; + x2 = GTS_POINT (v3)->x - GTS_POINT (v1)->x; + y2 = GTS_POINT (v3)->y - GTS_POINT (v1)->y; + z2 = GTS_POINT (v3)->z - GTS_POINT (v1)->z; + x3 = y1*z2 - z1*y2; y3 = z1*x2 - x1*z2; z3 = x1*y2 - y1*x2; + x2 = y3*z1 - z3*y1; y2 = z3*x1 - x3*z1; z2 = x3*y1 - y3*x1; + + g_assert ((l = sqrt (x1*x1 + y1*y1 + z1*z1)) > 0.0); + m[0][0] = x1/l; m[1][0] = y1/l; m[2][0] = z1/l; m[3][0] = 0.; + g_assert ((l = sqrt (x2*x2 + y2*y2 + z2*z2)) > 0.0); + m[0][1] = x2/l; m[1][1] = y2/l; m[2][1] = z2/l; m[3][1] = 0.; + g_assert ((l = sqrt (x3*x3 + y3*y3 + z3*z3)) > 0.0); + m[0][2] = x3/l; m[1][2] = y3/l; m[2][2] = z3/l; m[3][2] = 0.; + m[0][3] = 0; m[1][3] = 0.; m[2][3] = 0.; m[3][3] = 1.; + + return m; +} + +/** + * gts_matrix_transpose: + * @m: a #GtsMatrix. + * + * Returns: a pointer to a newly created #GtsMatrix transposed of @m. + */ +GtsMatrix * gts_matrix_transpose (GtsMatrix * m) +{ + GtsMatrix * mi; + + g_return_val_if_fail (m != NULL, NULL); + + mi = g_malloc (4*sizeof (GtsVector4)); + + mi[0][0] = m[0][0]; mi[1][0] = m[0][1]; + mi[2][0] = m[0][2]; mi[3][0] = m[0][3]; + mi[0][1] = m[1][0]; mi[1][1] = m[1][1]; + mi[2][1] = m[1][2]; mi[3][1] = m[1][3]; + mi[0][2] = m[2][0]; mi[1][2] = m[2][1]; + mi[2][2] = m[2][2]; mi[3][2] = m[2][3]; + mi[0][3] = m[3][0]; mi[1][3] = m[3][1]; + mi[2][3] = m[3][2]; mi[3][3] = m[3][3]; + + return mi; +} + +/* + * calculate the determinant of a 2x2 matrix. + * + * Adapted from: + * Matrix Inversion + * by Richard Carling + * from "Graphics Gems", Academic Press, 1990 + */ +static gdouble det2x2 (gdouble a, gdouble b, gdouble c, gdouble d) +{ + gdouble ans2; + + ans2 = a*d - b*c; + return ans2; +} + +/* + * calculate the determinant of a 3x3 matrix + * in the form + * + * | a1, b1, c1 | + * | a2, b2, c2 | + * | a3, b3, c3 | + * + * Adapted from: + * Matrix Inversion + * by Richard Carling + * from "Graphics Gems", Academic Press, 1990 + */ +static gdouble det3x3 (gdouble a1, gdouble a2, gdouble a3, + gdouble b1, gdouble b2, gdouble b3, + gdouble c1, gdouble c2, gdouble c3) +{ + gdouble ans3; + + ans3 = a1 * det2x2( b2, b3, c2, c3 ) + - b1 * det2x2( a2, a3, c2, c3 ) + + c1 * det2x2( a2, a3, b2, b3 ); + return ans3; +} + +/** + * gts_matrix_determinant: + * @m: a #GtsMatrix. + * + * Returns: the value of det(@m). + */ +gdouble gts_matrix_determinant (GtsMatrix * m) +{ + gdouble ans4; + gdouble a1, a2, a3, a4, b1, b2, b3, b4, c1, c2, c3, c4, d1, d2, d3, d4; + + g_return_val_if_fail (m != NULL, 0.0); + + a1 = m[0][0]; b1 = m[0][1]; + c1 = m[0][2]; d1 = m[0][3]; + + a2 = m[1][0]; b2 = m[1][1]; + c2 = m[1][2]; d2 = m[1][3]; + + a3 = m[2][0]; b3 = m[2][1]; + c3 = m[2][2]; d3 = m[2][3]; + + a4 = m[3][0]; b4 = m[3][1]; + c4 = m[3][2]; d4 = m[3][3]; + + ans4 = a1 * det3x3 (b2, b3, b4, c2, c3, c4, d2, d3, d4) + - b1 * det3x3 (a2, a3, a4, c2, c3, c4, d2, d3, d4) + + c1 * det3x3 (a2, a3, a4, b2, b3, b4, d2, d3, d4) + - d1 * det3x3 (a2, a3, a4, b2, b3, b4, c2, c3, c4); + + return ans4; +} + +/* + * adjoint( original_matrix, inverse_matrix ) + * + * calculate the adjoint of a 4x4 matrix + * + * Let a denote the minor determinant of matrix A obtained by + * ij + * + * deleting the ith row and jth column from A. + * + * i+j + * Let b = (-1) a + * ij ji + * + * The matrix B = (b ) is the adjoint of A + * ij + */ +static GtsMatrix * adjoint (GtsMatrix * m) +{ + gdouble a1, a2, a3, a4, b1, b2, b3, b4; + gdouble c1, c2, c3, c4, d1, d2, d3, d4; + GtsMatrix * ma; + + a1 = m[0][0]; b1 = m[0][1]; + c1 = m[0][2]; d1 = m[0][3]; + + a2 = m[1][0]; b2 = m[1][1]; + c2 = m[1][2]; d2 = m[1][3]; + + a3 = m[2][0]; b3 = m[2][1]; + c3 = m[2][2]; d3 = m[2][3]; + + a4 = m[3][0]; b4 = m[3][1]; + c4 = m[3][2]; d4 = m[3][3]; + + ma = g_malloc (4*sizeof (GtsVector4)); + + /* row column labeling reversed since we transpose rows & columns */ + + ma[0][0] = det3x3 (b2, b3, b4, c2, c3, c4, d2, d3, d4); + ma[1][0] = - det3x3 (a2, a3, a4, c2, c3, c4, d2, d3, d4); + ma[2][0] = det3x3 (a2, a3, a4, b2, b3, b4, d2, d3, d4); + ma[3][0] = - det3x3 (a2, a3, a4, b2, b3, b4, c2, c3, c4); + + ma[0][1] = - det3x3 (b1, b3, b4, c1, c3, c4, d1, d3, d4); + ma[1][1] = det3x3 (a1, a3, a4, c1, c3, c4, d1, d3, d4); + ma[2][1] = - det3x3 (a1, a3, a4, b1, b3, b4, d1, d3, d4); + ma[3][1] = det3x3 (a1, a3, a4, b1, b3, b4, c1, c3, c4); + + ma[0][2] = det3x3 (b1, b2, b4, c1, c2, c4, d1, d2, d4); + ma[1][2] = - det3x3 (a1, a2, a4, c1, c2, c4, d1, d2, d4); + ma[2][2] = det3x3 (a1, a2, a4, b1, b2, b4, d1, d2, d4); + ma[3][2] = - det3x3 (a1, a2, a4, b1, b2, b4, c1, c2, c4); + + ma[0][3] = - det3x3 (b1, b2, b3, c1, c2, c3, d1, d2, d3); + ma[1][3] = det3x3 (a1, a2, a3, c1, c2, c3, d1, d2, d3); + ma[2][3] = - det3x3 (a1, a2, a3, b1, b2, b3, d1, d2, d3); + ma[3][3] = det3x3 (a1, a2, a3, b1, b2, b3, c1, c2, c3); + + return ma; +} + + +/** + * gts_matrix_inverse: + * @m: a #GtsMatrix. + * + * Returns: a pointer to a newly created #GtsMatrix inverse of @m or %NULL + * if @m is not invertible. + */ +GtsMatrix * gts_matrix_inverse (GtsMatrix * m) +{ + GtsMatrix * madj; + gdouble det; + gint i, j; + + g_return_val_if_fail (m != NULL, NULL); + + det = gts_matrix_determinant (m); + if (det == 0.) + return NULL; + + madj = adjoint (m); + for (i = 0; i < 4; i++) + for(j = 0; j < 4; j++) + madj[i][j] /= det; + + return madj; +} + +/** + * gts_matrix3_inverse: + * @m: a 3x3 #GtsMatrix. + * + * Returns: a pointer to a newly created 3x3 #GtsMatrix inverse of @m or %NULL + * if @m is not invertible. + */ +GtsMatrix * gts_matrix3_inverse (GtsMatrix * m) +{ + GtsMatrix * mi; + gdouble det; + + g_return_val_if_fail (m != NULL, NULL); + + det = (m[0][0]*(m[1][1]*m[2][2] - m[2][1]*m[1][2]) - + m[0][1]*(m[1][0]*m[2][2] - m[2][0]*m[1][2]) + + m[0][2]*(m[1][0]*m[2][1] - m[2][0]*m[1][1])); + if (det == 0.0) + return NULL; + + mi = g_malloc0 (4*sizeof (GtsVector)); + + mi[0][0] = (m[1][1]*m[2][2] - m[1][2]*m[2][1])/det; + mi[0][1] = (m[2][1]*m[0][2] - m[0][1]*m[2][2])/det; + mi[0][2] = (m[0][1]*m[1][2] - m[1][1]*m[0][2])/det; + mi[1][0] = (m[1][2]*m[2][0] - m[1][0]*m[2][2])/det; + mi[1][1] = (m[0][0]*m[2][2] - m[2][0]*m[0][2])/det; + mi[1][2] = (m[1][0]*m[0][2] - m[0][0]*m[1][2])/det; + mi[2][0] = (m[1][0]*m[2][1] - m[2][0]*m[1][1])/det; + mi[2][1] = (m[2][0]*m[0][1] - m[0][0]*m[2][1])/det; + mi[2][2] = (m[0][0]*m[1][1] - m[0][1]*m[1][0])/det; + + return mi; +} + +/** + * gts_matrix_print: + * @m: a #GtsMatrix. + * @fptr: a file descriptor. + * + * Print @m to file @fptr. + */ +void gts_matrix_print (GtsMatrix * m, FILE * fptr) +{ + g_return_if_fail (m != NULL); + g_return_if_fail (fptr != NULL); + + fprintf (fptr, + "[[%15.7g %15.7g %15.7g %15.7g]\n" + " [%15.7g %15.7g %15.7g %15.7g]\n" + " [%15.7g %15.7g %15.7g %15.7g]\n" + " [%15.7g %15.7g %15.7g %15.7g]]\n", + m[0][0], m[0][1], m[0][2], m[0][3], + m[1][0], m[1][1], m[1][2], m[1][3], + m[2][0], m[2][1], m[2][2], m[2][3], + m[3][0], m[3][1], m[3][2], m[3][3]); +} + +/** + * gts_vector_print: + * @v: a #GtsVector. + * @fptr: a file descriptor. + * + * Print @s to file @fptr. + */ +void gts_vector_print (GtsVector v, FILE * fptr) +{ + g_return_if_fail (fptr != NULL); + + fprintf (fptr, + "[%15.7g %15.7g %15.7g ]\n", + v[0], v[1], v[2]); +} + +/** + * gts_vector4_print: + * @v: a #GtsVector4. + * @fptr: a file descriptor. + * + * Print @v to file @fptr. + */ +void gts_vector4_print (GtsVector4 v, FILE * fptr) +{ + g_return_if_fail (fptr != NULL); + + fprintf (fptr, + "[%15.7g %15.7g %15.7g %15.7g]\n", + v[0], v[1], v[2], v[3]); +} + +/* [cos(alpha)]^2 */ +#define COSALPHA2 0.999695413509 /* alpha = 1 degree */ +/* [sin(alpha)]^2 */ +#define SINALPHA2 3.04586490453e-4 /* alpha = 1 degree */ + +/** + * gts_matrix_compatible_row: + * @A: a #GtsMatrix. + * @b: a #GtsVector. + * @n: the number of previous constraints of @A.x=@b. + * @A1: a #GtsMatrix. + * @b1: a #GtsVector. + * + * Given a system of @n constraints @A.x=@b adds to it the compatible + * constraints defined by @A1.x=@b1. The compatibility is determined + * by insuring that the resulting system is well-conditioned (see + * Lindstrom and Turk (1998, 1999)). + * + * Returns: the number of constraints of the resulting system. + */ +guint gts_matrix_compatible_row (GtsMatrix * A, + GtsVector b, + guint n, + GtsVector A1, + gdouble b1) +{ + gdouble na1; + + g_return_val_if_fail (A != NULL, 0); + + na1 = gts_vector_scalar (A1, A1); + if (na1 == 0.0) + return n; + + /* normalize row */ + na1 = sqrt (na1); + A1[0] /= na1; A1[1] /= na1; A1[2] /= na1; b1 /= na1; + + if (n == 1) { + gdouble a0a1 = gts_vector_scalar (A[0], A1); + if (a0a1*a0a1 >= COSALPHA2) + return 1; + } + else if (n == 2) { + GtsVector V; + gdouble s; + + gts_vector_cross (V, A[0], A[1]); + s = gts_vector_scalar (V, A1); + if (s*s <= gts_vector_scalar (V, V)*SINALPHA2) + return 2; + } + + A[n][0] = A1[0]; A[n][1] = A1[1]; A[n][2] = A1[2]; b[n] = b1; + return n + 1; +} + +/** + * gts_matrix_quadratic_optimization: + * @A: a #GtsMatrix. + * @b: a #GtsVector. + * @n: the number of constraints (must be smaller than 3). + * @H: a symmetric positive definite Hessian. + * @c: a #GtsVector. + * + * Solve a quadratic optimization problem: Given a quadratic objective function + * f which can be written as: f(x) = x^t.@H.x + @c^t.x + k, where @H is the + * symmetric positive definite Hessian of f and k is a constant, find the + * minimum of f subject to the set of @n prior linear constraints, defined by + * the first @n rows of @A and @b (@A.x = @b). The new constraints given by + * the minimization are added to @A and @b only if they are linearly + * independent as determined by gts_matrix_compatible_row(). + * + * Returns: the new number of constraints defined by @A and @b. + */ +guint gts_matrix_quadratic_optimization (GtsMatrix * A, + GtsVector b, + guint n, + GtsMatrix * H, + GtsVector c) +{ + g_return_val_if_fail (A != NULL, 0); + g_return_val_if_fail (b != NULL, 0); + g_return_val_if_fail (n < 3, 0); + g_return_val_if_fail (H != NULL, 0); + + switch (n) { + case 0: { + n = gts_matrix_compatible_row (A, b, n, H[0], - c[0]); + n = gts_matrix_compatible_row (A, b, n, H[1], - c[1]); + n = gts_matrix_compatible_row (A, b, n, H[2], - c[2]); + return n; + } + case 1: { + GtsVector Q0 = {0., 0., 0.}; + GtsVector Q1 = {0., 0., 0.}; + GtsVector A1; + gdouble max = A[0][0]*A[0][0]; + guint d = 0; + + /* build a vector orthogonal to the constraint */ + if (A[0][1]*A[0][1] > max) { max = A[0][1]*A[0][1]; d = 1; } + if (A[0][2]*A[0][2] > max) { max = A[0][2]*A[0][2]; d = 2; } + switch (d) { + case 0: Q0[0] = - A[0][2]/A[0][0]; Q0[2] = 1.0; break; + case 1: Q0[1] = - A[0][2]/A[0][1]; Q0[2] = 1.0; break; + case 2: Q0[2] = - A[0][0]/A[0][2]; Q0[0] = 1.0; break; + } + + /* build a second vector orthogonal to the first and to the constraint */ + gts_vector_cross (Q1, A[0], Q0); + + A1[0] = gts_vector_scalar (Q0, H[0]); + A1[1] = gts_vector_scalar (Q0, H[1]); + A1[2] = gts_vector_scalar (Q0, H[2]); + + n = gts_matrix_compatible_row (A, b, n, A1, - gts_vector_scalar (Q0, c)); + + A1[0] = gts_vector_scalar (Q1, H[0]); + A1[1] = gts_vector_scalar (Q1, H[1]); + A1[2] = gts_vector_scalar (Q1, H[2]); + + n = gts_matrix_compatible_row (A, b, n, A1, - gts_vector_scalar (Q1, c)); + + return n; + } + case 2: { + /* build a vector orthogonal to the two constraints */ + GtsVector A1, Q; + + gts_vector_cross (Q, A[0], A[1]); + A1[0] = gts_vector_scalar (Q, H[0]); + A1[1] = gts_vector_scalar (Q, H[1]); + A1[2] = gts_vector_scalar (Q, H[2]); + + n = gts_matrix_compatible_row (A, b, n, A1, - gts_vector_scalar (Q, c)); + + return n; + } + default: + g_assert_not_reached (); + } + return 0; +} + +/** + * gts_matrix_destroy: + * @m: a #GtsMatrix. + * + * Free all the memory allocated for @m. + */ +void gts_matrix_destroy (GtsMatrix * m) +{ + g_free (m); +} + +/** + * gts_matrix_product: + * @m1: a #GtsMatrix. + * @m2: another #GtsMatrix. + * + * Returns: a new #GtsMatrix, product of @m1 and @m2. + */ +GtsMatrix * gts_matrix_product (GtsMatrix * m1, GtsMatrix * m2) +{ + guint i, j; + GtsMatrix * m; + + g_return_val_if_fail (m1 != NULL, NULL); + g_return_val_if_fail (m2 != NULL, NULL); + g_return_val_if_fail (m1 != m2, NULL); + + m = g_malloc (4*sizeof (GtsVector4)); + + for (i = 0; i < 4; i++) + for (j = 0; j < 4; j++) + m[i][j] = m1[i][0]*m2[0][j] + m1[i][1]*m2[1][j] + + m1[i][2]*m2[2][j] + m1[i][3]*m2[3][j]; + return m; +} + +/** + * gts_matrix_zero: + * @m: a #GtsMatrix or $NULL. + * + * Initializes @m to zeros. Allocates a matrix if @m is %NULL. + * + * Returns: the zero'ed matrix. + */ +GtsMatrix * gts_matrix_zero (GtsMatrix * m) +{ + if (m == NULL) + m = g_malloc0 (4*sizeof (GtsVector4)); + else { + m[0][0] = m[1][0] = m[2][0] = m[3][0] = 0.; + m[0][1] = m[1][1] = m[2][1] = m[3][1] = 0.; + m[0][2] = m[1][2] = m[2][2] = m[3][2] = 0.; + m[0][3] = m[1][3] = m[2][3] = m[3][3] = 0.; + } + return m; +} + +/** + * gts_matrix_identity: + * @m: a #GtsMatrix or %NULL. + * + * Initializes @m to an identity matrix. Allocates a matrix if @m is %NULL. + * + * Returns: the identity matrix. + */ +GtsMatrix * gts_matrix_identity (GtsMatrix * m) +{ + m = gts_matrix_zero (m); + m[0][0] = m[1][1] = m[2][2] = m[3][3] = 1.; + return m; +} + +/** + * gts_matrix_scale: + * @m: a #GtsMatrix or %NULL. + * @s: the scaling vector. + * + * Initializes @m to a scaling matrix for @s. Allocates a matrix if @m + * is %NULL. + * + * Returns: the scaling matrix. + */ +GtsMatrix * gts_matrix_scale (GtsMatrix * m, GtsVector s) +{ + m = gts_matrix_zero (m); + m[0][0] = s[0]; + m[1][1] = s[1]; + m[2][2] = s[2]; + m[3][3] = 1.; + return m; +} + +/** + * gts_matrix_translate: + * @m: a #GtsMatrix or %NULL. + * @t: the translation vector. + * + * Initializes @m to a translation matrix for @t. Allocates a new + * matrix if @m is %NULL. + * + * Returns: the translation matix. + */ +GtsMatrix * gts_matrix_translate (GtsMatrix * m, GtsVector t) +{ + m = gts_matrix_zero (m); + m[0][3] = t[0]; + m[1][3] = t[1]; + m[2][3] = t[2]; + m[3][3] = 1.; + m[0][0] = m[1][1] = m[2][2] = 1.; + return m; +} + +/** + * gts_matrix_rotate: + * @m: a #GtsMatrix or %NULL. + * @r: the rotation axis. + * @angle: the angle (in radians) to rotate by. + * + * Initializes @m to a rotation matrix around @r by @angle. + * Allocates a new matrix if @m is %NULL. + * + * Returns: the rotation matrix. + */ +GtsMatrix * gts_matrix_rotate (GtsMatrix * m, + GtsVector r, + gdouble angle) +{ + gdouble c, c1, s; + + gts_vector_normalize (r); + + c = cos (angle); + c1 = 1. - c; + s = sin (angle); + + if (m == NULL) + m = g_malloc (4*sizeof (GtsVector4)); + + m[0][0] = r[0]*r[0]*c1 + c; + m[0][1] = r[0]*r[1]*c1 - r[2]*s; + m[0][2] = r[0]*r[2]*c1 + r[1]*s; + m[0][3] = 0.; + + m[1][0] = r[1]*r[0]*c1 + r[2]*s; + m[1][1] = r[1]*r[1]*c1 + c; + m[1][2] = r[1]*r[2]*c1 - r[0]*s; + m[1][3] = 0.; + + m[2][0] = r[2]*r[0]*c1 - r[1]*s; + m[2][1] = r[2]*r[1]*c1 + r[0]*s; + m[2][2] = r[2]*r[2]*c1 + c; + m[2][3] = 0.; + + m[3][0] = 0.; + m[3][1] = 0.; + m[3][2] = 0.; + m[3][3] = 1.; + + return m; +} Index: trunk/src_3rd/gts/misc.c =================================================================== --- trunk/src_3rd/gts/misc.c (nonexistent) +++ trunk/src_3rd/gts/misc.c (revision 1065) @@ -0,0 +1,692 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include + +#include "gts.h" +#include "gts-private.h" +#include "config.h" + +const guint gts_major_version = GTS_MAJOR_VERSION; +const guint gts_minor_version = GTS_MINOR_VERSION; +const guint gts_micro_version = GTS_MICRO_VERSION; +const guint gts_interface_age = 1; +const guint gts_binary_age = 1; + +static gboolean char_in_string (char c, const char * s) +{ + while (*s != '\0') + if (*(s++) == c) + return TRUE; + return FALSE; +} + +static GtsFile * file_new (void) +{ + GtsFile * f; + + f = g_malloc (sizeof (GtsFile)); + f->fp = NULL; + f->s = f->s1 = NULL; + f->curline = 1; + f->curpos = 1; + f->token = g_string_new (""); + f->type = '\0'; + f->error = NULL; + f->next_token = '\0'; + + f->scope = f->scope_max = 0; + f->delimiters = g_strdup (" \t"); + f->comments = g_strdup (GTS_COMMENTS); + f->tokens = g_strdup ("\n{}()="); + + return f; +} + +/** + * gts_file_new: + * @fp: a file pointer. + * + * Returns: a new #GtsFile. + */ +GtsFile * gts_file_new (FILE * fp) +{ + GtsFile * f; + + g_return_val_if_fail (fp != NULL, NULL); + + f = file_new (); + f->fp = fp; + gts_file_next_token (f); + + return f; +} + +/** + * gts_file_new_from_string: + * @s: a string. + * + * Returns: a new #GtsFile. + */ +GtsFile * gts_file_new_from_string (const gchar * s) +{ + GtsFile * f; + + g_return_val_if_fail (s != NULL, NULL); + + f = file_new (); + f->s1 = f->s = g_strdup (s); + gts_file_next_token (f); + + return f; +} + +/** + * gts_file_destroy: + * @f: a #GtsFile. + * + * Frees all the memory allocated for @f. + */ +void gts_file_destroy (GtsFile * f) +{ + g_return_if_fail (f != NULL); + + g_free (f->delimiters); + g_free (f->comments); + g_free (f->tokens); + if (f->error) + g_free (f->error); + if (f->s1) + g_free (f->s1); + g_string_free (f->token, TRUE); + g_free (f); +} + +/** + * gts_file_verror: + * @f: a @GtsFile. + * @format: the standard sprintf() format string. + * @args: the list of parameters to insert into the format string. + * + * Sets the @error field of @f using g_strdup_vprintf(). + * + * This function can be called only once and disables any other + * operation on @f (gts_file_close() excepted). + */ +void gts_file_verror (GtsFile * f, + const gchar * format, + va_list args) +{ + g_return_if_fail (f != NULL); + g_return_if_fail (format != NULL); + + g_assert (f->type != GTS_ERROR); + f->error = g_strdup_vprintf (format, args); + f->type = GTS_ERROR; +} + +/** + * gts_file_error: + * @f: a @GtsFile. + * @format: the standard sprintf() format string. + * @...: the parameters to insert into the format string. + * + * Sets the @error field of @f using gts_file_verror(). + * + * This function can be called only once and disables any other + * operation on @f (gts_file_close() excepted). + */ +void gts_file_error (GtsFile * f, + const gchar * format, + ...) +{ + va_list args; + + g_return_if_fail (f != NULL); + g_return_if_fail (format != NULL); + + va_start (args, format); + gts_file_verror (f, format, args); + va_end (args); +} + +static gint next_char (GtsFile * f) +{ + if (f->fp) + return fgetc (f->fp); + else if (*f->s == '\0') + return EOF; + return *(f->s++); +} + +/** + * gts_file_getc : + * @f: a #GtsFile. + * + * Returns: the next character in @f or EOF if the end of the file is + * reached or if an error occured. + */ +gint gts_file_getc (GtsFile * f) +{ + gint c; + + g_return_val_if_fail (f != NULL, EOF); + + if (f->type == GTS_ERROR) + return EOF; + + c = next_char (f); + f->curpos++; + while (char_in_string (c, f->comments)) { + while (c != EOF && c != '\n') + c = next_char (f); + if (c == '\n') { + f->curline++; + f->curpos = 1; + c = next_char (f); + } + } + switch (c) { + case '\n': + f->curline++; + f->curpos = 1; + break; + case '{': + f->scope++; + break; + case '}': + if (f->scope == 0) { + f->line = f->curline; + f->pos = f->curpos - 1; + gts_file_error (f, "no matching opening brace"); + c = EOF; + } + else + f->scope--; + } + return c; +} + +/** + * gts_file_read: + * @f: a #GtsFile. + * @ptr: a pointer. + * @size: size of an element. + * @nmemb: number of elements. + * + * Reads @nmemb elements of data, each @size bytes long, from @f, + * storing them at the location given by @ptr. + * + * Returns: the number of elements read. + */ +guint gts_file_read (GtsFile * f, gpointer ptr, guint size, guint nmemb) +{ + guint i, n; + gchar * p; + + g_return_val_if_fail (f != NULL, 0); + g_return_val_if_fail (ptr != NULL, 0); + g_return_val_if_fail (f->fp != NULL, 0); + + if (f->type == GTS_ERROR) + return 0; + + n = fread (ptr, size, nmemb, f->fp); + for (i = 0, p = ptr; i < n*size; i++, p++) { + f->curpos++; + if (*p == '\n') { + f->curline++; + f->curpos = 1; + } + } + return n; +} + +/** + * gts_file_getc_scope : + * @f: a #GtsFile. + * + * Returns: the next character in @f in the scope defined by + * @f->scope_max or EOF if the end of the file is reached or if an + * error occured. + */ +gint gts_file_getc_scope (GtsFile * f) +{ + gint c; + + g_return_val_if_fail (f != NULL, EOF); + + if (f->type == GTS_ERROR) + return EOF; + + if (f->scope <= f->scope_max) + c = gts_file_getc (f); + else { + c = gts_file_getc (f); + while (c != EOF && f->scope > f->scope_max) + c = gts_file_getc (f); + } + return c; +} + +/** + * gts_file_next_token: + * @f: a #GtsFile. + * + * Reads next token from @f and updates its @token and @delim fields. + */ +void gts_file_next_token (GtsFile * f) +{ + gint c; + gboolean in_string = FALSE; + + g_return_if_fail (f != NULL); + + if (f->type == GTS_ERROR) + return; + f->token->str[0] = '\0'; + f->token->len = 0; + if (f->next_token != '\0') { + if (char_in_string (f->next_token, f->tokens)) { + f->line = f->curline; + f->pos = f->curpos - 1; + g_string_append_c (f->token, f->next_token); + f->type = f->next_token; + f->next_token = '\0'; + return; + } + else { + c = f->next_token; + f->next_token = '\0'; + } + } + else + c = gts_file_getc_scope (f); + f->type = GTS_NONE; + while (c != EOF && (!in_string || !char_in_string (c, f->delimiters))) { + if (in_string) { + if (char_in_string (c, f->tokens)) { + f->next_token = c; + break; + } + g_string_append_c (f->token, c); + } + else if (!char_in_string (c, f->delimiters)) { + in_string = TRUE; + f->line = f->curline; + f->pos = f->curpos - 1; + g_string_append_c (f->token, c); + if (char_in_string (c, f->tokens)) { + f->type = c; + break; + } + } + c = gts_file_getc_scope (f); + } + if (f->type == GTS_NONE && f->token->len > 0) { + gchar * a; + + a = f->token->str; + while (*a != '\0' && char_in_string (*a, "+-")) a++; + if (*a == '\0') { + f->type = GTS_STRING; + return; + } + a = f->token->str; + while (*a != '\0' && char_in_string (*a, "+-0123456789")) a++; + if (*a == '\0') { + f->type = GTS_INT; + return; + } + a = f->token->str; + while (*a != '\0' && char_in_string (*a, "+-eE.")) a++; + if (*a == '\0') { + f->type = GTS_STRING; + return; + } + a = f->token->str; + while (*a != '\0' && char_in_string (*a, "+-0123456789eE.")) a++; + if (*a == '\0') { + f->type = GTS_FLOAT; + return; + } + a = f->token->str; + if (!strncmp (a, "0x", 2) || + !strncmp (a, "-0x", 3) || + !strncmp (a, "+0x", 3)) { + while (*a != '\0' && char_in_string (*a, "+-0123456789abcdefx")) a++; + if (*a == '\0') { + f->type = GTS_INT; + return; + } + a = f->token->str; + while (*a != '\0' && char_in_string (*a, "+-0123456789abcdefx.p")) a++; + if (*a == '\0') { + f->type = GTS_FLOAT; + return; + } + } + f->type = GTS_STRING; + } +} + +/** + * gts_file_first_token_after: + * @f: a #GtsFile. + * @type: a #GtsTokenType. + * + * Finds and sets the first token of a type different from @type + * occuring after a token of type @type. + */ +void gts_file_first_token_after (GtsFile * f, GtsTokenType type) +{ + g_return_if_fail (f != NULL); + + while (f->type != GTS_ERROR && + f->type != GTS_NONE && + f->type != type) + gts_file_next_token (f); + while (f->type == type) + gts_file_next_token (f); +} + +/** + * gts_file_assign_start: + * @f: a #GtsFile. + * @vars: a %GTS_NONE terminated array of #GtsFileVariable. + * + * Opens a block delimited by braces to read a list of optional + * arguments specified by @vars. + * + * If an error is encountered the @error field of @f is set. + */ +void gts_file_assign_start (GtsFile * f, GtsFileVariable * vars) +{ + GtsFileVariable * var; + + g_return_if_fail (f != NULL); + g_return_if_fail (vars != NULL); + + var = vars; + while (var->type != GTS_NONE) + (var++)->set = FALSE; + + if (f->type != '{') { + gts_file_error (f, "expecting an opening brace"); + return; + } + + f->scope_max++; + gts_file_next_token (f); +} + +/** + * gts_file_assign_next: + * @f: a #GtsFile. + * @vars: a %GTS_NONE terminated array of #GtsFileVariable. + * + * Assigns the next optional argument of @vars read from @f. + * + * Returns: the variable of @vars which has been assigned or %NULL if + * no variable has been assigned (if an error has been encountered the + * @error field of @f is set). + */ +GtsFileVariable * gts_file_assign_next (GtsFile * f, GtsFileVariable * vars) +{ + GtsFileVariable * var; + gboolean found = FALSE; + + g_return_val_if_fail (f != NULL, NULL); + g_return_val_if_fail (vars != NULL, NULL); + + while (f->type == '\n') + gts_file_next_token (f); + if (f->type == '}') { + f->scope_max--; + gts_file_next_token (f); + return NULL; + } + if (f->type == GTS_ERROR) + return NULL; + + var = vars; + while (f->type != GTS_ERROR && var->type != GTS_NONE && !found) { + if (!strcmp (var->name, f->token->str)) { + found = TRUE; + if (var->unique && var->set) + gts_file_error (f, "variable `%s' was already set at line %d:%d", + var->name, var->line, var->pos); + else { + var->line = f->line; + var->pos = f->pos; + gts_file_next_token (f); + if (f->type != '=') + gts_file_error (f, "expecting `='"); + else { + var->set = TRUE; + switch (var->type) { + case GTS_FILE: + break; + case GTS_INT: + gts_file_next_token (f); + if (f->type != GTS_INT) { + gts_file_error (f, "expecting an integer"); + var->set = FALSE; + } + else if (var->data) + *((gint *) var->data) = atoi (f->token->str); + break; + case GTS_UINT: + gts_file_next_token (f); + if (f->type != GTS_INT) { + gts_file_error (f, "expecting an integer"); + var->set = FALSE; + } + else if (var->data) + *((guint *) var->data) = atoi (f->token->str); + break; + case GTS_FLOAT: + gts_file_next_token (f); + if (f->type != GTS_INT && f->type != GTS_FLOAT) { + gts_file_error (f, "expecting a number"); + var->set = FALSE; + } + else if (var->data) + *((gfloat *) var->data) = atof (f->token->str); + break; + case GTS_DOUBLE: + gts_file_next_token (f); + if (f->type != GTS_INT && f->type != GTS_FLOAT) { + gts_file_error (f, "expecting a number"); + var->set = FALSE; + } + else if (var->data) + *((gdouble *) var->data) = atof (f->token->str); + break; + case GTS_STRING: + gts_file_next_token (f); + if (f->type != GTS_INT && + f->type != GTS_FLOAT && + f->type != GTS_STRING) { + gts_file_error (f, "expecting a string"); + var->set = FALSE; + } + else if (var->data) + *((gchar **) var->data) = g_strdup (f->token->str); + break; + default: + g_assert_not_reached (); + } + } + } + } + else + var++; + } + if (!found) + gts_file_error (f, "unknown identifier `%s'", f->token->str); + else if (f->type != GTS_ERROR) { + g_assert (var->set); + gts_file_next_token (f); + return var; + } + return NULL; +} + +/** + * gts_file_assign_variables: + * @f: a #GtsFile. + * @vars: an array of #GtsFileVariable. + * + * Assigns all the variables belonging to @vars found in @f. + * + * If an error is encountered the @error field of @f is set. + */ +void gts_file_assign_variables (GtsFile * f, GtsFileVariable * vars) +{ + g_return_if_fail (f != NULL); + g_return_if_fail (vars != NULL); + + gts_file_assign_start (f, vars); + while (gts_file_assign_next (f, vars)) + ; +} + +/** + * gts_file_variable_error: + * @f: a #GtsFile. + * @vars: an array of #GtsFileVariable. + * @name: the name of a variable in @vars. + * @format: the standard sprintf() format string. + * @...: the parameters to insert into the format string. + * + * Sets the @error field of @f using gts_file_verror(). + * + * String @name must match one of the variable names in @vars. + * + * If variable @name has been assigned (using gts_file_assign_variables()) + * sets the @line and @pos fields of @f to the line and position where + * it has been assigned. + */ +void gts_file_variable_error (GtsFile * f, + GtsFileVariable * vars, + const gchar * name, + const gchar * format, + ...) +{ + va_list args; + GtsFileVariable * var; + + g_return_if_fail (f != NULL); + g_return_if_fail (vars != NULL); + g_return_if_fail (name != NULL); + g_return_if_fail (format != NULL); + + var = vars; + while (var->type != GTS_NONE && strcmp (var->name, name)) + var++; + + g_return_if_fail (var->type != GTS_NONE); /* @name not found in @vars */ + + if (var->set) { + f->line = var->line; + f->pos = var->pos; + } + + va_start (args, format); + gts_file_verror (f, format, args); + va_end (args); +} + +#ifdef DEBUG_FUNCTIONS +static GHashTable * ids = NULL; +static guint next_id = 1; + +guint id (gpointer p) +{ + g_return_val_if_fail (p != NULL, 0); + g_return_val_if_fail (ids != NULL, 0); + g_assert (g_hash_table_lookup (ids, p)); + return GPOINTER_TO_UINT (g_hash_table_lookup (ids, p)); +} + +void id_insert (gpointer p) +{ + g_return_if_fail (p != NULL); + if (ids == NULL) ids = g_hash_table_new (NULL, NULL); + g_assert (g_hash_table_lookup (ids, p) == NULL); + g_hash_table_insert (ids, p, GUINT_TO_POINTER (next_id++)); +} + +void id_remove (gpointer p) +{ + g_assert (g_hash_table_lookup (ids, p)); + g_hash_table_remove (ids, p); +} + +void gts_write_triangle (GtsTriangle * t, + GtsPoint * o, + FILE * fptr) +{ + gdouble xo = o ? o->x : 0.0; + gdouble yo = o ? o->y : 0.0; + gdouble zo = o ? o->z : 0.0; + + g_return_if_fail (t != NULL && fptr != NULL); + + fprintf (fptr, "(hdefine geometry \"t%d\" { =\n", id (t)); + fprintf (fptr, "OFF 3 1 0\n" + "%g %g %g\n%g %g %g\n%g %g %g\n3 0 1 2\n})\n" + "(geometry \"t%d\" { : \"t%d\"})\n" + "(normalization \"t%d\" none)\n", + GTS_POINT (GTS_SEGMENT (t->e1)->v1)->x - xo, + GTS_POINT (GTS_SEGMENT (t->e1)->v1)->y - yo, + GTS_POINT (GTS_SEGMENT (t->e1)->v1)->z - zo, + GTS_POINT (GTS_SEGMENT (t->e1)->v2)->x - xo, + GTS_POINT (GTS_SEGMENT (t->e1)->v2)->y - yo, + GTS_POINT (GTS_SEGMENT (t->e1)->v2)->z - zo, + GTS_POINT (gts_triangle_vertex (t))->x - xo, + GTS_POINT (gts_triangle_vertex (t))->y - yo, + GTS_POINT (gts_triangle_vertex (t))->z - zo, + id (t), id (t), id (t)); +} + +void gts_write_segment (GtsSegment * s, + GtsPoint * o, + FILE * fptr) +{ + gdouble xo = o ? o->x : 0.0; + gdouble yo = o ? o->y : 0.0; + gdouble zo = o ? o->z : 0.0; + + g_return_if_fail (s != NULL && fptr != NULL); + + fprintf (fptr, "(geometry \"s%d\" { =\n", id (s)); + fprintf (fptr, "VECT 1 2 0 2 0 %g %g %g %g %g %g })\n" + "(normalization \"s%d\" none)\n", + GTS_POINT (s->v1)->x - xo, + GTS_POINT (s->v1)->y - yo, + GTS_POINT (s->v1)->z - zo, + GTS_POINT (s->v2)->x - xo, + GTS_POINT (s->v2)->y - yo, + GTS_POINT (s->v2)->z - zo, + id (s)); +} +#endif /* DEBUG_FUNCTIONS */ Index: trunk/src_3rd/gts/named.c =================================================================== --- trunk/src_3rd/gts/named.c (nonexistent) +++ trunk/src_3rd/gts/named.c (revision 1065) @@ -0,0 +1,188 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "gts.h" + +static void nvertex_read (GtsObject ** po, GtsFile * fp) +{ + if ((*po)->klass->parent_class->read) + (* (*po)->klass->parent_class->read) (po, fp); + + if (fp->type != '\n' && fp->type != GTS_ERROR) { + strncpy (GTS_NVERTEX (*po)->name, fp->token->str, GTS_NAME_LENGTH); + gts_file_next_token (fp); + } +} + +static void nvertex_write (GtsObject * o, FILE * fptr) +{ + GtsNVertex * nv = GTS_NVERTEX (o); + + (* o->klass->parent_class->write) (o, fptr); + if (nv->name[0] != '\0') + fprintf (fptr, " %s", nv->name); +} + +static void nvertex_class_init (GtsNVertexClass * klass) +{ + GTS_OBJECT_CLASS (klass)->read = nvertex_read; + GTS_OBJECT_CLASS (klass)->write = nvertex_write; +} + +static void nvertex_init (GtsNVertex * nvertex) +{ + nvertex->name[0] = '\0'; +} + +/** + * gts_nvertex_class: + * + * Returns: the #GtsNVertexClass. + */ +GtsNVertexClass * gts_nvertex_class (void) +{ + static GtsNVertexClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo nvertex_info = { + "GtsNVertex", + sizeof (GtsNVertex), + sizeof (GtsNVertexClass), + (GtsObjectClassInitFunc) nvertex_class_init, + (GtsObjectInitFunc) nvertex_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_vertex_class ()), + &nvertex_info); + } + + return klass; +} + +static void nedge_read (GtsObject ** po, GtsFile * fp) +{ + if (fp->type != GTS_STRING) { + gts_file_error (fp, "expecting a string (name)"); + return; + } + strncpy (GTS_NEDGE (*po)->name, fp->token->str, GTS_NAME_LENGTH); + gts_file_next_token (fp); +} + +static void nedge_write (GtsObject * o, FILE * fptr) +{ + GtsNEdge * ne = GTS_NEDGE (o); + + if (ne->name[0] != '\0') + fprintf (fptr, " %s", ne->name); +} + +static void nedge_class_init (GtsNEdgeClass * klass) +{ + GTS_OBJECT_CLASS (klass)->read = nedge_read; + GTS_OBJECT_CLASS (klass)->write = nedge_write; +} + +static void nedge_init (GtsNEdge * nedge) +{ + nedge->name[0] = '\0'; +} + +/** + * gts_nedge_class: + * + * Returns: the #GtsNEdgeClass. + */ +GtsNEdgeClass * gts_nedge_class (void) +{ + static GtsNEdgeClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo nedge_info = { + "GtsNEdge", + sizeof (GtsNEdge), + sizeof (GtsNEdgeClass), + (GtsObjectClassInitFunc) nedge_class_init, + (GtsObjectInitFunc) nedge_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_edge_class ()), + &nedge_info); + } + + return klass; +} + +static void nface_read (GtsObject ** po, GtsFile * fp) +{ + if (fp->type != GTS_STRING) { + gts_file_error (fp, "expecting a string (name)"); + return; + } + strncpy (GTS_NFACE (*po)->name, fp->token->str, GTS_NAME_LENGTH); + gts_file_next_token (fp); +} + +static void nface_write (GtsObject * o, FILE * fptr) +{ + GtsNFace * nf = GTS_NFACE (o); + + if (nf->name[0] != '\0') + fprintf (fptr, " %s", GTS_NFACE (o)->name); +} + +static void nface_class_init (GtsNFaceClass * klass) +{ + GTS_OBJECT_CLASS (klass)->read = nface_read; + GTS_OBJECT_CLASS (klass)->write = nface_write; +} + +static void nface_init (GtsNFace * nface) +{ + nface->name[0] = '\0'; +} + +/** + * gts_nface_class: + * + * Returns: the #GtsNFaceClass. + */ +GtsNFaceClass * gts_nface_class (void) +{ + static GtsNFaceClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo nface_info = { + "GtsNFace", + sizeof (GtsNFace), + sizeof (GtsNFaceClass), + (GtsObjectClassInitFunc) nface_class_init, + (GtsObjectInitFunc) nface_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_face_class ()), + &nface_info); + } + + return klass; +} Index: trunk/src_3rd/gts/object.c =================================================================== --- trunk/src_3rd/gts/object.c (nonexistent) +++ trunk/src_3rd/gts/object.c (revision 1065) @@ -0,0 +1,345 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "gts.h" +#include "gts-private.h" + +static GHashTable * class_table = NULL; + +static void gts_object_class_init (GtsObjectClass * klass, + GtsObjectClass * parent_class) +{ + if (parent_class) { + gts_object_class_init (klass, parent_class->parent_class); + if (parent_class->info.class_init_func) + (*parent_class->info.class_init_func) (klass); + } +} + +/** + * gts_object_class_new: + * @parent_class: a #GtsObjectClass. + * @info: a #GtsObjectClassInfo, description of the new class to create. + * + * Returns: a new #GtsObjectClass derived from @parent_class and described by + * @info. + */ +gpointer gts_object_class_new (GtsObjectClass * parent_class, + GtsObjectClassInfo * info) +{ + GtsObjectClass * klass; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (parent_class == NULL || + info->object_size >= parent_class->info.object_size, + NULL); + g_return_val_if_fail (parent_class == NULL || + info->class_size >= parent_class->info.class_size, + NULL); + + klass = g_malloc0 (info->class_size); + klass->info = *info; + klass->parent_class = parent_class; + gts_object_class_init (klass, klass); + + if (!class_table) + class_table = g_hash_table_new (g_str_hash, g_str_equal); + g_hash_table_insert (class_table, klass->info.name, klass); + + return klass; +} + +/** + * gts_object_class_from_name: + * @name: the name of a #GtsObjectClass. + * + * Returns: the #GtsObjectClass with name @name or %NULL if it hasn't been + * instantiated yet. + */ +GtsObjectClass * gts_object_class_from_name (const gchar * name) +{ + g_return_val_if_fail (name != NULL, NULL); + + if (!class_table) + return NULL; + return g_hash_table_lookup (class_table, name); +} + +static void object_destroy (GtsObject * object) +{ +#ifdef DEBUG_IDENTITY +#ifdef DEBUG_LEAKS + fprintf (stderr, "destroy %s %p->%d\n", + object->klass->info.name, + object, + id (object)); +#endif + id_remove (object); +#endif + object->klass = NULL; + g_free (object); +} + +static void object_clone (GtsObject * clone, GtsObject * object) +{ + memcpy (clone, object, object->klass->info.object_size); + clone->reserved = NULL; +} + +static void object_class_init (GtsObjectClass * klass) +{ + klass->clone = object_clone; + klass->destroy = object_destroy; + klass->read = NULL; + klass->write = NULL; + klass->color = NULL; + klass->attributes = NULL; +} + +static void object_init (GtsObject * object) +{ + object->reserved = NULL; + object->flags = 0; +} + +/** + * gts_object_class: + * + * Returns: the #GtsObjectClass. + */ +GtsObjectClass * gts_object_class (void) +{ + static GtsObjectClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo object_info = { + "GtsObject", + sizeof (GtsObject), + sizeof (GtsObjectClass), + (GtsObjectClassInitFunc) object_class_init, + (GtsObjectInitFunc) object_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (NULL, &object_info); + } + + return klass; +} + +/** + * gts_object_check_cast: + * @object: a #GtsObject. + * @klass: a #GtsObjectClass. + * + * Returns: @object while emitting warnings if @object is not of class @klass. + */ +gpointer gts_object_check_cast (gpointer object, + gpointer klass) +{ + if (!object) { + g_warning ("invalid cast from (NULL) pointer to `%s'", + GTS_OBJECT_CLASS (klass)->info.name); + return object; + } + if (!((GtsObject *) object)->klass) { + g_warning ("invalid unclassed pointer in cast to `%s'", + GTS_OBJECT_CLASS (klass)->info.name); + return object; + } + if (!gts_object_is_from_class (object, klass)) { + g_warning ("invalid cast from `%s' to `%s'", + ((GtsObject *) object)->klass->info.name, + GTS_OBJECT_CLASS (klass)->info.name); + return object; + } + return object; +} + +/** + * gts_object_class_check_cast: + * @klass: a #GtsObjectClass. + * @from: a #GtsObjectClass. + * + * Returns: @klass while emitting warnings if @klass is not derived from + * @from. + */ +gpointer gts_object_class_check_cast (gpointer klass, + gpointer from) +{ + if (!klass) { + g_warning ("invalid cast from (NULL) pointer to `%s'", + GTS_OBJECT_CLASS (from)->info.name); + return klass; + } + if (!gts_object_class_is_from_class (klass, from)) { + g_warning ("invalid cast from `%s' to `%s'", + GTS_OBJECT_CLASS (klass)->info.name, + GTS_OBJECT_CLASS (from)->info.name); + return klass; + } + return klass; +} + +/** + * gts_object_init: + * @object: a #GtsObject. + * @klass: a #GtsObjectClass. + * + * Calls the init method of @klass with @object as argument. This is done + * recursively in the correct order (from the base class to the top). You + * should rarely need this function as it is called automatically by the + * constructor for each class. + */ +void gts_object_init (GtsObject * object, GtsObjectClass * klass) +{ + GtsObjectClass * parent_class; + + g_return_if_fail (object != NULL); + g_return_if_fail (klass != NULL); + + parent_class = klass->parent_class; + if (parent_class) + gts_object_init (object, parent_class); + if (klass->info.object_init_func) + (*klass->info.object_init_func) (object); +} + +/** + * gts_object_new: + * @klass: a #GtsObjectClass. + * + * Returns: a new initialized object of class @klass. + */ +GtsObject * gts_object_new (GtsObjectClass * klass) +{ + GtsObject * object; + + g_return_val_if_fail (klass != NULL, NULL); + + object = g_malloc0 (klass->info.object_size); + object->klass = klass; + gts_object_init (object, klass); + +#ifdef DEBUG_IDENTITY + id_insert (object); +#ifdef DEBUG_LEAKS + fprintf (stderr, "new %s %p->%d\n", klass->info.name, + object, + id (object)); +#endif +#endif + + return object; +} + +/** + * gts_object_clone: + * @object: a #GtsObject. + * + * Calls the clone method of @object. The call to this function will fail + * if no clone method exists for the given object. + * + * Returns: a new object clone of @object. + */ +GtsObject * gts_object_clone (GtsObject * object) +{ + GtsObject * clone; + + g_return_val_if_fail (object != NULL, NULL); + g_return_val_if_fail (object->klass->clone, NULL); + + clone = g_malloc0 (object->klass->info.object_size); + clone->klass = object->klass; + object_init (clone); + (* object->klass->clone) (clone, object); + +#ifdef DEBUG_IDENTITY + id_insert (clone); +#ifdef DEBUG_LEAKS + fprintf (stderr, "clone %s %p->%d\n", clone->klass->info.name, + clone, + id (clone)); +#endif +#endif + + return clone; +} + +/** + * gts_object_destroy: + * @object: a #GtsObject. + * + * Calls the destroy method of @object, freeing all memory allocated for it. + */ +void gts_object_destroy (GtsObject * object) +{ + g_assert (object->klass->destroy); + GTS_OBJECT_SET_FLAGS (object, GTS_DESTROYED); + (* object->klass->destroy) (object); +} + +/** + * gts_object_reset_reserved: + * @object: a #GtsObject. + * + * Reset the reserved field of @object. + */ +void gts_object_reset_reserved (GtsObject * object) +{ + g_return_if_fail (object != NULL); + + object->reserved = NULL; +} + +/** + * gts_object_attributes: + * @object: a #GtsObject. + * @from: a #GtsObject. + * + * Calls the attributes() method of @object using @from as source. + */ +void gts_object_attributes (GtsObject * object, GtsObject * from) +{ + g_return_if_fail (object != NULL); + + if (object->klass->attributes) + (* object->klass->attributes) (object, from); +} + +static void free_class (gchar * name, GtsObjectClass * klass) +{ + g_free (klass); +} + +/** + * gts_finalize: + * + * Free all the memory allocated by the object system of GTS. No other + * GTS function can be called after this function has been called. + */ +void gts_finalize (void) +{ + if (class_table) { + g_hash_table_foreach (class_table, (GHFunc) free_class, NULL); + g_hash_table_destroy (class_table); + class_table = NULL; + } +} Index: trunk/src_3rd/gts/oocs.c =================================================================== --- trunk/src_3rd/gts/oocs.c (nonexistent) +++ trunk/src_3rd/gts/oocs.c (revision 1065) @@ -0,0 +1,387 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "gts.h" + +static void cluster_destroy (GtsObject * object) +{ + GtsCluster * c = GTS_CLUSTER (object); + + if (c->v && gts_vertex_is_unattached (c->v)) + gts_object_destroy (GTS_OBJECT (c->v)); + + /* do not forget to call destroy method of the parent */ + (* GTS_OBJECT_CLASS (gts_cluster_class ())->parent_class->destroy) (object); +} + +static void cluster_add (GtsCluster * c, GtsPoint * p, gpointer data) +{ + GtsPoint * cp; + + g_return_if_fail (c != NULL); + g_return_if_fail (c->v != NULL); + g_return_if_fail (p != NULL); + + cp = GTS_POINT (c->v); + + cp->x += p->x; + cp->y += p->y; + cp->z += p->z; + c->n++; +} + +static void cluster_update (GtsCluster * c) +{ + GtsPoint * p; + + g_return_if_fail (c != NULL); + g_return_if_fail (c->v != NULL); + + if (c->n > 1) { + p = GTS_POINT (c->v); + p->x /= c->n; + p->y /= c->n; + p->z /= c->n; + } +} + +static void cluster_class_init (GtsClusterClass * klass) +{ + klass->add = cluster_add; + klass->update = cluster_update; + + GTS_OBJECT_CLASS (klass)->destroy = cluster_destroy; +} + +static void cluster_init (GtsCluster * c) +{ + c->v = NULL; + c->n = 0; +} + +/** + * gts_cluster_class: + * + * Returns: the #GtsClusterClass. + */ +GtsClusterClass * gts_cluster_class (void) +{ + static GtsClusterClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo cluster_info = { + "GtsCluster", + sizeof (GtsCluster), + sizeof (GtsClusterClass), + (GtsObjectClassInitFunc) cluster_class_init, + (GtsObjectInitFunc) cluster_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (gts_object_class (), &cluster_info); + } + + return klass; +} + +/** + * gts_cluster_new: + * @klass: a #GtsClusterClass. + * @id: the id of the new cluster. + * @vklass: a #GtsVertexClass for the representative vertex of the cluster. + * + * Returns: a new #GtsCluster. + */ +GtsCluster * gts_cluster_new (GtsClusterClass * klass, + GtsClusterId id, + GtsVertexClass * vklass) +{ + GtsCluster * c; + + c = GTS_CLUSTER (gts_object_new (GTS_OBJECT_CLASS (klass))); + c->id = id; + c->v = gts_vertex_new (vklass, 0., 0., 0.); + + return c; +} + +/** + * gts_cluster_add: + * @c: a #GtsCluster. + * @p: a #GtsPoint. + * @data: data to pass to the add() virtual method of #GtsClusterClass. + * + * Adds point @p to cluster @c. + */ +void gts_cluster_add (GtsCluster * c, GtsPoint * p, gpointer data) +{ + g_return_if_fail (c != NULL); + g_return_if_fail (p != NULL); + + (* GTS_CLUSTER_CLASS (GTS_OBJECT (c)->klass)->add) (c, p, data); +} + +/** + * gts_cluster_update: + * @c: a #GtsCluster. + * + * Updates the position of the vertex representative of all the + * vertices added to @c. + */ +void gts_cluster_update (GtsCluster * c) +{ + g_return_if_fail (c != NULL); + + (* GTS_CLUSTER_CLASS (GTS_OBJECT (c)->klass)->update) (c); +} + +static void destroy_cluster (GtsClusterId * id, GtsObject * cluster) +{ + gts_object_destroy (cluster); +} + +static void cluster_grid_destroy (GtsObject * object) +{ + GtsClusterGrid * cluster_grid = GTS_CLUSTER_GRID (object); + + g_hash_table_foreach (cluster_grid->clusters, + (GHFunc) destroy_cluster, NULL); + g_hash_table_destroy (cluster_grid->clusters); + + (* GTS_OBJECT_CLASS (gts_cluster_grid_class ())->parent_class->destroy) + (object); +} + +static void cluster_grid_class_init (GtsClusterGridClass * klass) +{ + GTS_OBJECT_CLASS (klass)->destroy = cluster_grid_destroy; +} + +static gint cluster_id_equal (gconstpointer v1, + gconstpointer v2) +{ + const GtsClusterId * id1 = (const GtsClusterId *) v1; + const GtsClusterId * id2 = (const GtsClusterId *) v2; + return ((id1->x == id2->x) && (id1->y == id2->y) && (id1->z == id2->z)); +} + +static guint cluster_id_hash (gconstpointer key) +{ + const GtsClusterId * id = (const GtsClusterId *) key; + return id->x + id->y + id->z; +} + +static void cluster_grid_init (GtsClusterGrid * cluster_grid) +{ + cluster_grid->surface = NULL; + cluster_grid->bbox = NULL; + cluster_grid->cluster_class = gts_cluster_class (); + cluster_grid->clusters = g_hash_table_new (cluster_id_hash, + cluster_id_equal); +} + +/** + * gts_cluster_grid_class: + * + * Returns: the #GtsClusterGridClass. + */ +GtsClusterGridClass * gts_cluster_grid_class (void) +{ + static GtsClusterGridClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo cluster_grid_info = { + "GtsClusterGrid", + sizeof (GtsClusterGrid), + sizeof (GtsClusterGridClass), + (GtsObjectClassInitFunc) cluster_grid_class_init, + (GtsObjectInitFunc) cluster_grid_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (gts_object_class (), &cluster_grid_info); + } + + return klass; +} + +/** + * gts_cluster_grid_new: + * @klass: a #GtsClusterGridClass. + * @cluster_class: the klass to be used for the vertex clusters. + * @s: the simplified surface. + * @bbox: bounding box of the surface to be simplified. + * @delta: the size of one grid cell of the simplification grid. + * + * Returns: a new #GtsClusterGrid. + */ +GtsClusterGrid * gts_cluster_grid_new (GtsClusterGridClass * klass, + GtsClusterClass * cluster_class, + GtsSurface * s, + GtsBBox * bbox, + gdouble delta) +{ + GtsClusterGrid * cluster_grid; + GtsVector size; + + g_return_val_if_fail (klass != NULL, NULL); + g_return_val_if_fail (cluster_class != NULL, NULL); + g_return_val_if_fail (s != NULL, NULL); + g_return_val_if_fail (bbox != NULL, NULL); + g_return_val_if_fail (delta > 0., NULL); + + size[0] = ceil ((bbox->x2 - bbox->x1)/delta); + size[1] = ceil ((bbox->y2 - bbox->y1)/delta); + size[2] = ceil ((bbox->z2 - bbox->z1)/delta); + g_return_val_if_fail (size[0] <= 2.*G_MAXINT + 2. && + size[1] <= 2.*G_MAXINT + 2. && + size[2] <= 2.*G_MAXINT + 2., NULL); + cluster_grid = + GTS_CLUSTER_GRID (gts_object_new (GTS_OBJECT_CLASS (klass))); + cluster_grid->cluster_class = cluster_class; + cluster_grid->surface = s; + cluster_grid->bbox = bbox; + cluster_grid->size[0] = size[0]; + cluster_grid->size[1] = size[1]; + cluster_grid->size[2] = size[2]; + + return cluster_grid; +} + +static GtsClusterId cluster_index (GtsPoint * p, + GtsBBox * bb, + GtsVector n) +{ + GtsClusterId id = {0, 0, 0}; + + g_return_val_if_fail (p->x >= bb->x1 && p->x <= bb->x2, id); + g_return_val_if_fail (p->y >= bb->y1 && p->y <= bb->y2, id); + g_return_val_if_fail (p->z >= bb->z1 && p->z <= bb->z2, id); + + id.x = (guint) (p->x == bb->x2 ? n[0] - 1. : n[0]*(p->x - bb->x1)/(bb->x2 - bb->x1)); + id.y = (guint) (p->y == bb->y2 ? n[1] - 1. : n[1]*(p->y - bb->y1)/(bb->y2 - bb->y1)); + id.z = (guint) (p->z == bb->z2 ? n[2] - 1. : n[2]*(p->z - bb->z1)/(bb->z2 - bb->z1)); + + return id; +} + +static GtsCluster * cluster_grid_add_point (GtsClusterGrid * cluster_grid, + GtsPoint * p, + gpointer data) +{ + GtsClusterId id = cluster_index (p, + cluster_grid->bbox, + cluster_grid->size); + GtsCluster * c = g_hash_table_lookup (cluster_grid->clusters, &id); + + if (c == NULL) { + c = gts_cluster_new (cluster_grid->cluster_class, id, + cluster_grid->surface->vertex_class); + g_hash_table_insert (cluster_grid->clusters, &c->id, c); + } + + gts_cluster_add (c, p, data); + + return c; +} + +/** + * gts_cluster_grid_add_triangle: + * @cluster_grid: a #GtsClusterGrid. + * @p1: a #GtsPoint. + * @p2: a #GtsPoint. + * @p3: a #GtsPoint. + * @data: user data to pass to the cluster add() method. + * + * Adds the triangle defined by @p1, @p2 and @p3 to the respective clusters + * of @cluster_grid. + */ +void gts_cluster_grid_add_triangle (GtsClusterGrid * cluster_grid, + GtsPoint * p1, + GtsPoint * p2, + GtsPoint * p3, + gpointer data) +{ + GtsCluster * c1, * c2, * c3; + + g_return_if_fail (cluster_grid != NULL); + g_return_if_fail (p1 != NULL); + g_return_if_fail (p2 != NULL); + g_return_if_fail (p3 != NULL); + g_return_if_fail (cluster_grid->surface != NULL); + + c1 = cluster_grid_add_point (cluster_grid, p1, data); + c2 = cluster_grid_add_point (cluster_grid, p2, data); + c3 = cluster_grid_add_point (cluster_grid, p3, data); + + if (c1 != c2 && c2 != c3 && c3 != c1) { + GtsVertex * v1, * v2, * v3; + GtsEdge * e1, * e2, * e3; + gboolean new_edge = FALSE; + + v1 = c1->v; v2 = c2->v; v3 = c3->v; + + if ((e1 = GTS_EDGE (gts_vertices_are_connected (v1, v2))) == NULL) { + e1 = gts_edge_new (cluster_grid->surface->edge_class, v1, v2); + new_edge = TRUE; + } + if ((e2 = GTS_EDGE (gts_vertices_are_connected (v2, v3))) == NULL) { + e2 = gts_edge_new (cluster_grid->surface->edge_class, v2, v3); + new_edge = TRUE; + } + if ((e3 = GTS_EDGE (gts_vertices_are_connected (v3, v1))) == NULL) { + e3 = gts_edge_new (cluster_grid->surface->edge_class, v3, v1); + new_edge = TRUE; + } + if (new_edge || !gts_triangle_use_edges (e1, e2, e3)) + gts_surface_add_face (cluster_grid->surface, + gts_face_new (cluster_grid->surface->face_class, + e1, e2, e3)); + } +} + +static void update_cluster (gint * id, GtsCluster * cluster, GtsRange * stats) +{ + gts_cluster_update (cluster); + gts_range_add_value (stats, cluster->n); +} + +/** + * gts_cluster_grid_update: + * @cluster_grid: a #GtsClusterGrid. + * + * Updates the representative vertices of all the clusters of @cluster_grid. + * + * Returns: a #GtsRange describing the statistics for the number of vertices + * added to each cluster of @cluster_grid. + */ +GtsRange gts_cluster_grid_update (GtsClusterGrid * cluster_grid) +{ + GtsRange stats; + + gts_range_init (&stats); + g_return_val_if_fail (cluster_grid != NULL, stats); + + g_hash_table_foreach (cluster_grid->clusters, + (GHFunc) update_cluster, &stats); + gts_range_update (&stats); + + return stats; +} Index: trunk/src_3rd/gts/partition.c =================================================================== --- trunk/src_3rd/gts/partition.c (nonexistent) +++ trunk/src_3rd/gts/partition.c (revision 1065) @@ -0,0 +1,1219 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + +#include "gts.h" + +/* #define DEBUG */ + +/* Graph partition */ + +/** + * gts_graph_partition_edges_cut: + * @partition: a list of @GtsGraph representing a partition. + * + * Returns: the number of edges cut by the partition. + */ +guint gts_graph_partition_edges_cut (GSList * partition) +{ + guint cuts = 0; + + while (partition) { + cuts += gts_graph_edges_cut (partition->data); + partition = partition->next; + } + + return cuts/2; +} + +/** + * gts_graph_partition_edges_cut_weight: + * @partition: a list of @GtsGraph representing a partition. + * + * Returns: the total weight of the edges cut by the partition. + */ +gfloat gts_graph_partition_edges_cut_weight (GSList * partition) +{ + gfloat weight = 0.; + + while (partition) { + weight += gts_graph_edges_cut_weight (partition->data); + partition = partition->next; + } + + return weight/2.; +} + +/** + * gts_graph_partition_print_stats: + * @partition: a list of @GtsGraph representing a partition. + * @fp: a file pointer. + * + * Writes to @fp a summary of the properties of @partition. + */ +void gts_graph_partition_print_stats (GSList * partition, + FILE * fp) +{ + GtsRange weight; + GSList * i; + + g_return_if_fail (partition != NULL); + g_return_if_fail (fp != NULL); + + gts_range_init (&weight); + i = partition; + while (i) { + gts_range_add_value (&weight, gts_graph_weight (i->data)); + i = i->next; + } + gts_range_update (&weight); + + fprintf (fp, + "# parts: %d\n" + "# edge cuts: %5d edge cuts weight: %5g\n" + "# weight: ", + g_slist_length (partition), + gts_graph_partition_edges_cut (partition), + gts_graph_partition_edges_cut_weight (partition)); + gts_range_print (&weight, fp); + fputc ('\n', fp); +} + +/** + * gts_graph_partition_balance: + * @partition: a list of @GtsGraph representing a partition. + * + * Returns: the difference between the maximum and the minimum weight + * of the graphs in @partition. + */ +gfloat gts_graph_partition_balance (GSList * partition) +{ + gfloat wmin = G_MAXFLOAT; + gfloat wmax = - G_MAXFLOAT; + + g_return_val_if_fail (partition != NULL, 0.); + + while (partition) { + gfloat weight = gts_graph_weight (partition->data); + if (weight < wmin) + wmin = weight; + if (weight > wmax) + wmax = weight; + partition = partition->next; + } + return wmax - wmin; +} + +/** + * gts_graph_partition_clone: + * @partition: a list of @GtsGraph representing a partition. + * + * Returns: a new partition clone of @partition (i.e. a list of new + * graphs clones of the graphs in @partition). + */ +GSList * gts_graph_partition_clone (GSList * partition) +{ + GSList * cparts = NULL; + + while (partition) { + cparts = + g_slist_prepend (cparts, + gts_object_clone (GTS_OBJECT (partition->data))); + partition = partition->next; + } + return cparts; +} + +/** + * gts_graph_partition_destroy: + * @partition: a list of @GtsGraph representing a partition. + * + * Destroys all the graphs in @partition and frees @partition. + */ +void gts_graph_partition_destroy (GSList * partition) +{ + GSList * i = partition; + + while (i) { + gts_object_destroy (GTS_OBJECT (i->data)); + i = i->next; + } + g_slist_free (partition); +} + +static void find_smallest_degree (GtsGNode * n, gpointer * data) +{ + GtsGNode ** nmin = data[0]; + GtsGraph * g = data[1]; + guint * min = data[2]; + guint degree = gts_gnode_degree (n, g); + + if (degree < *min) { + *min = degree; + *nmin = n; + } +} + +static gint graph_comp_weight (GtsGraph * g1, GtsGraph * g2) +{ + if (gts_graph_weight (g1) > gts_graph_weight (g2)) + return 1; + return -1; +} + +static void partition_update (GSList * list, GtsGraph * g) +{ + GSList * i; + GtsGraph * g1; + GtsHeap * size_heap; + gboolean reinit = TRUE; + + /* initialize traversals */ + i = list; + while (i) { + GtsGNode * seed = GTS_OBJECT (i->data)->reserved; + GTS_OBJECT (seed)->reserved = + gts_graph_traverse_new (g, seed, GTS_BREADTH_FIRST, reinit); + reinit = FALSE; + i = i->next; + } + + size_heap = gts_heap_new ((GCompareFunc) graph_comp_weight); + i = list; + while (i) { + gts_heap_insert (size_heap, i->data); + i = i->next; + } + while ((g1 = gts_heap_remove_top (size_heap))) { + GtsGraphTraverse * t = GTS_OBJECT (GTS_OBJECT (g1)->reserved)->reserved; + GtsGNode * n = gts_graph_traverse_next (t); + if (n) { + gts_container_add (GTS_CONTAINER (g1), GTS_CONTAINEE (n)); + gts_heap_insert (size_heap, g1); + } + } + gts_heap_destroy (size_heap); + + /* destroy traversals */ + i = list; + while (i) { + GtsGNode * seed = GTS_OBJECT (i->data)->reserved; + gts_graph_traverse_destroy (GTS_OBJECT (seed)->reserved); + GTS_OBJECT (seed)->reserved = NULL; + i = i->next; + } +} + +static void better_seed (GtsGNode * n, gpointer * data) +{ + guint * sum = data[0]; + GtsGNode ** seed = data[1]; + GtsGraph * g = data[2]; + guint sum1 = gts_graph_distance_sum (g, n); + + if (sum1 < *sum) { + *sum = sum1; + *seed = n; + } +} + +static GtsGNode * graph_new_seed (GtsGraph * g, GtsGNode * seed) +{ + guint sum = gts_graph_distance_sum (g, seed); + gpointer data[3]; + GtsGNode * new_seed = seed; + + data[0] = ∑ + data[1] = &new_seed; + data[2] = g; + gts_gnode_foreach_neighbor (seed, g, (GtsFunc) better_seed, data); + + return new_seed; +} + +/** + * gts_graph_bubble_partition: + * @g: a #GtsGraph. + * @np: number of partitions. + * @niter: the maximum number of iterations. + * @step_info: a #GtsFunc or %NULL. + * @data: user data to pass to @step_info. + * + * An implementation of the "bubble partitioning algorithm" of + * Diekmann, Preis, Schlimbach and Walshaw (2000). The maximum number + * of iteration on the positions of the graph growing seeds is + * controlled by @niter. + * + * If not %NULL @step_info is called after each iteration on the seeds + * positions passing the partition (a GSList) as argument. + * + * Returns: a list of @np new #GtsGraph representing the partition. + */ +GSList * gts_graph_bubble_partition (GtsGraph * g, + guint np, + guint niter, + GtsFunc step_info, + gpointer data) +{ + GSList * list = NULL, * seeds = NULL; + GtsGNode * seed = NULL; + guint min = G_MAXINT/2 - 1; + gpointer info[3]; + GtsGraph * g1; + gboolean changed = TRUE; + + g_return_val_if_fail (g != NULL, NULL); + g_return_val_if_fail (np > 0, NULL); + + info[0] = &seed; + info[1] = g; + info[2] = &min; + gts_container_foreach (GTS_CONTAINER (g), + (GtsFunc) find_smallest_degree, + info); + if (seed == NULL) + return NULL; + + g1 = GTS_GRAPH (gts_object_new (GTS_OBJECT (g)->klass)); + gts_container_add (GTS_CONTAINER (g1), GTS_CONTAINEE (seed)); + list = g_slist_prepend (list, g1); + GTS_OBJECT (g1)->reserved = seed; + seeds = g_slist_prepend (seeds, seed); + + while (--np && seed) + if ((seed = gts_graph_farthest (g, seeds))) { + g1 = GTS_GRAPH (gts_object_new (GTS_OBJECT (g)->klass)); + gts_container_add (GTS_CONTAINER (g1), GTS_CONTAINEE (seed)); + list = g_slist_prepend (list, g1); + GTS_OBJECT (g1)->reserved = seed; + seeds = g_slist_prepend (seeds, seed); + } + g_slist_free (seeds); + + partition_update (list, g); + + while (changed && niter--) { + GSList * i; + + changed = FALSE; + i = list; + while (i) { + GtsGraph * g1 = i->data; + GtsGNode * seed = GTS_OBJECT (g1)->reserved; + GtsGNode * new_seed = graph_new_seed (g1, seed); + if (new_seed != seed) { + changed = TRUE; + GTS_OBJECT (g1)->reserved = new_seed; + } + i = i->next; + } + + if (changed) { + i = list; + while (i) { + GtsGraph * g1 = i->data; + GtsGNode * seed = GTS_OBJECT (g1)->reserved; + + gts_object_destroy (GTS_OBJECT (g1)); + i->data = g1 = GTS_GRAPH (gts_object_new (GTS_OBJECT (g)->klass)); + gts_container_add (GTS_CONTAINER (g1), GTS_CONTAINEE (seed)); + GTS_OBJECT (g1)->reserved = seed; + i = i->next; + } + partition_update (list, g); + if (step_info) + (* step_info) (list, data); + } + } + g_slist_foreach (list, (GFunc) gts_object_reset_reserved, NULL); + return list; +} + +/* Graph bisection */ + +static gdouble node_cost (GtsGNode * n, gpointer * data) +{ + GtsGraph * g = data[0]; + GtsGraph * g1 = data[1]; + GSList * i = GTS_SLIST_CONTAINER (n)->items; + gdouble cost = 0.; + + while (i) { + GtsGEdge * e = i->data; + GtsGNode * n1 = GTS_GNODE_NEIGHBOR (n, e); + + if (gts_containee_is_contained (GTS_CONTAINEE (n1), GTS_CONTAINER (g))) { + if (gts_containee_is_contained (GTS_CONTAINEE (n1), GTS_CONTAINER (g1))) + cost -= gts_gedge_weight (e); + else + cost += gts_gedge_weight (e); + } + i = i->next; + } + + return cost; +} + +static void add_neighbor (GtsGNode * n, GtsEHeap * heap) +{ + if (GTS_OBJECT (n)->reserved == n) + return; + if (GTS_OBJECT (n)->reserved) + gts_eheap_remove (heap, GTS_OBJECT (n)->reserved); + GTS_OBJECT (n)->reserved = gts_eheap_insert (heap, n); +} + +static void add_unused (GtsGNode * n, GtsGraph * g2) +{ + if (GTS_OBJECT (n)->reserved) + GTS_OBJECT (n)->reserved = NULL; + else + gts_container_add (GTS_CONTAINER (g2), GTS_CONTAINEE (n)); +} + +static gdouble degree_cost (GtsGNode * n, GtsGraph * g) +{ + return gts_gnode_degree (n, g); +} + +static void add_seed (GtsGNode * n, GtsEHeap * heap) +{ + gts_eheap_insert (heap, n); +} + +static void boundary_node1 (GtsGNode * n, GtsGraphBisection * bg) +{ + GSList * i = GTS_SLIST_CONTAINER (n)->items; + + while (i) { + GtsGNode * n1 = GTS_GNODE_NEIGHBOR (n, i->data); + if (gts_containee_is_contained (GTS_CONTAINEE (n1), + GTS_CONTAINER (bg->g2))) { + g_hash_table_insert (bg->bg1, n, n1); + return; + } + i = i->next; + } +} + +static void boundary_node2 (GtsGNode * n, GtsGraphBisection * bg) +{ + GSList * i = GTS_SLIST_CONTAINER (n)->items; + + while (i) { + GtsGNode * n1 = GTS_GNODE_NEIGHBOR (n, i->data); + if (gts_containee_is_contained (GTS_CONTAINEE (n1), + GTS_CONTAINER (bg->g1))) { + g_hash_table_insert (bg->bg2, n, n1); + return; + } + i = i->next; + } +} + +static void check_bg (GtsGNode * n, gpointer * data) +{ + GHashTable * bg = data[0]; + GtsGraph * g = data[1]; + gboolean * ok = data[2]; + guint * nb = data[3]; + guint nn = gts_gnode_degree (n, g); + + if (nn > 0) + (*nb)++; + if ((nn > 0 && !g_hash_table_lookup (bg, n)) || + (nn == 0 && g_hash_table_lookup (bg, n))) { + g_warning ("nn: %d lookup: %p\n", + nn, g_hash_table_lookup (bg, n)); + *ok = FALSE; + } +} + +/** + * gts_graph_bisection_check: + * @bg: a #GtsGraphBisection. + * + * Checks that the boundary of @bg is correctly defined (used for + * debugging purposes). + * + * Returns: %TRUE if @bg is ok, %FALSE otherwise. + */ +gboolean gts_graph_bisection_check (GtsGraphBisection * bg) +{ + gboolean ok = TRUE; + guint nb; + gpointer data[4]; + + g_return_val_if_fail (bg != NULL, FALSE); + + nb = 0; + data[0] = bg->bg1; + data[1] = bg->g2; + data[2] = &ok; + data[3] = &nb; + gts_container_foreach (GTS_CONTAINER (bg->g1), (GtsFunc) check_bg, data); + g_return_val_if_fail (g_hash_table_size (bg->bg1) == nb, FALSE); + + nb = 0; + data[0] = bg->bg2; + data[1] = bg->g1; + gts_container_foreach (GTS_CONTAINER (bg->g2), (GtsFunc) check_bg, data); + g_return_val_if_fail (g_hash_table_size (bg->bg2) == nb, FALSE); + + return ok; +} + +/** + * gts_graph_ggg_bisection: + * @g: a #GtsGraph. + * @ntry: the number of randomly selected initial seeds. + * + * An implementation of the "Greedy Graph Growing" algorithm of + * Karypis and Kumar (1997). + * + * @ntry randomly chosen seeds are used and the best partition is retained. + * + * Returns: a new #GtsGraphBisection of @g. + */ +GtsGraphBisection * gts_graph_ggg_bisection (GtsGraph * g, guint ntry) +{ + gfloat size, bestcost = G_MAXFLOAT, smin; + GtsGraph * bestg1 = NULL, * bestg2 = NULL; + gboolean balanced = FALSE; + GtsEHeap * degree_heap; + GtsGNode * seed; + GtsGraphBisection * bg; + + g_return_val_if_fail (g != NULL, NULL); + + bg = g_malloc (sizeof (GtsGraphBisection)); + bg->g = g; + + size = gts_graph_weight (g)/2.; + smin = 0.9*size; + + degree_heap = gts_eheap_new ((GtsKeyFunc) degree_cost, g); + gts_eheap_freeze (degree_heap); + gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) add_seed, degree_heap); + gts_eheap_thaw (degree_heap); + + while (ntry && ((seed = gts_eheap_remove_top (degree_heap, NULL)))) { + GtsGraph * g1, * g2; + GtsGNode * n; + gdouble cost; + gpointer data[2]; + GtsEHeap * heap; + + g1 = gts_graph_new (GTS_GRAPH_CLASS (GTS_OBJECT (g)->klass), + g->node_class, g->edge_class); + g2 = gts_graph_new (GTS_GRAPH_CLASS (GTS_OBJECT (g)->klass), + g->node_class, g->edge_class); + + data[0] = g; + data[1] = g1; + heap = gts_eheap_new ((GtsKeyFunc) node_cost, data); + + gts_container_add (GTS_CONTAINER (g1), GTS_CONTAINEE (seed)); + GTS_OBJECT (seed)->reserved = seed; + gts_gnode_foreach_neighbor (seed, g, (GtsFunc) add_neighbor, heap); + + while ((n = gts_eheap_remove_top (heap, &cost))) + if (gts_graph_weight (g1) + gts_gnode_weight (n) <= size) { + gts_container_add (GTS_CONTAINER (g1), GTS_CONTAINEE (n)); + GTS_OBJECT (n)->reserved = n; + gts_gnode_foreach_neighbor (n, g, (GtsFunc) add_neighbor, heap); + } + else + GTS_OBJECT (n)->reserved = NULL; + gts_eheap_destroy (heap); + + gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) add_unused, g2); + + cost = gts_graph_edges_cut_weight (g1); + if (!bestg1 || + (!balanced && gts_graph_weight (g1) >= smin) || + (cost < bestcost && gts_graph_weight (g1) >= smin)) { + if (bestg1) + bestcost = cost; + if (bestg1) + gts_object_destroy (GTS_OBJECT (bestg1)); + if (bestg2) + gts_object_destroy (GTS_OBJECT (bestg2)); + bestg1 = g1; + bestg2 = g2; + if (gts_graph_weight (g1) >= smin) + balanced = TRUE; + } + else { + gts_object_destroy (GTS_OBJECT (g1)); + gts_object_destroy (GTS_OBJECT (g2)); + } + + ntry--; + } + gts_eheap_destroy (degree_heap); + +#ifdef DEBUG + fprintf (stderr, "bestcost: %5g g1: %5g|%5d g2: %5g|%5d\n", + bestcost, + gts_graph_weight (bestg1), + gts_container_size (GTS_CONTAINER (bestg1)), + gts_graph_weight (bestg2), + gts_container_size (GTS_CONTAINER (bestg2))); +#endif + + g_assert (bestg1 != NULL); + bg->g1 = bestg1; + g_assert (bestg2 != NULL); + bg->g2 = bestg2; + + /* boundary nodes */ + bg->bg1 = g_hash_table_new (NULL, NULL); + gts_container_foreach (GTS_CONTAINER (bg->g1), (GtsFunc) boundary_node1, bg); + bg->bg2 = g_hash_table_new (NULL, NULL); + gts_container_foreach (GTS_CONTAINER (bg->g2), (GtsFunc) boundary_node2, bg); + + return bg; +} + +/** + * gts_graph_bfgg_bisection: + * @g: a #GtsGraph. + * @ntry: the number of randomly selected initial seeds. + * + * An implementation of a "Breadth-First Graph Growing" algorithm. + * + * @ntry randomly chosen seeds are used and the best partition is retained. + * + * Returns: a new #GtsGraphBisection of @g. + */ +GtsGraphBisection * gts_graph_bfgg_bisection (GtsGraph * g, guint ntry) +{ + gfloat size, bestcost = G_MAXFLOAT, smin; + GtsGraph * bestg1 = NULL, * bestg2 = NULL; + GtsEHeap * degree_heap; + GtsGNode * seed; + GtsGraphBisection * bg; + + g_return_val_if_fail (g != NULL, NULL); + + bg = g_malloc (sizeof (GtsGraphBisection)); + bg->g = g; + + size = gts_graph_weight (g)/2.; + smin = 0.9*size; + + degree_heap = gts_eheap_new ((GtsKeyFunc) degree_cost, g); + gts_eheap_freeze (degree_heap); + gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) add_seed, degree_heap); + gts_eheap_thaw (degree_heap); + + while (ntry && ((seed = gts_eheap_remove_top (degree_heap, NULL)))) { + GtsGraph * g1, * g2; + GtsGNode * n; + gdouble cost; + GtsGraphTraverse * t = gts_graph_traverse_new (g, seed, + GTS_BREADTH_FIRST, TRUE); + + g1 = gts_graph_new (GTS_GRAPH_CLASS (GTS_OBJECT (g)->klass), + g->node_class, g->edge_class); + g2 = gts_graph_new (GTS_GRAPH_CLASS (GTS_OBJECT (g)->klass), + g->node_class, g->edge_class); + + while ((n = gts_graph_traverse_next (t))) + if (gts_graph_weight (g1) + gts_gnode_weight (n) <= size) { + gts_container_add (GTS_CONTAINER (g1), GTS_CONTAINEE (n)); + GTS_OBJECT (n)->reserved = n; + } + gts_graph_traverse_destroy (t); + + gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) add_unused, g2); + + cost = gts_graph_edges_cut_weight (g1); + if (!bestg1 || (cost < bestcost && gts_graph_weight (g1) >= smin)) { + if (bestg1) + bestcost = cost; + if (bestg1) + gts_object_destroy (GTS_OBJECT (bestg1)); + if (bestg2) + gts_object_destroy (GTS_OBJECT (bestg2)); + bestg1 = g1; + bestg2 = g2; + } + else { + gts_object_destroy (GTS_OBJECT (g1)); + gts_object_destroy (GTS_OBJECT (g2)); + } + + ntry--; + } + gts_eheap_destroy (degree_heap); + +#ifdef DEBUG + fprintf (stderr, "bestcost: %5g g1: %5g|%5d g2: %5g|%5d\n", + bestcost, + gts_graph_weight (bestg1), + gts_container_size (GTS_CONTAINER (bestg1)), + gts_graph_weight (bestg2), + gts_container_size (GTS_CONTAINER (bestg2))); +#endif + + bg->g1 = bestg1; + bg->g2 = bestg2; + + /* boundary nodes */ + bg->bg1 = g_hash_table_new (NULL, NULL); + gts_container_foreach (GTS_CONTAINER (bg->g1), (GtsFunc) boundary_node1, bg); + bg->bg2 = g_hash_table_new (NULL, NULL); + gts_container_foreach (GTS_CONTAINER (bg->g2), (GtsFunc) boundary_node2, bg); + + return bg; +} + +static gdouble node_move_cost1 (GtsGNode * n, GtsGraphBisection * bg) +{ + return gts_gnode_move_cost (n, bg->g1, bg->g2); +} + +static gdouble node_move_cost2 (GtsGNode * n, GtsGraphBisection * bg) +{ + return gts_gnode_move_cost (n, bg->g2, bg->g1); +} + +static void build_heap (GtsGNode * n, GtsEHeap * heap) +{ + GTS_OBJECT (n)->reserved = gts_eheap_insert (heap, n); +} + +/** + * gts_graph_bisection_kl_refine: + * @bg: a #GtsGraphBisection. + * @mmax: the maximum number of unsuccessful successive moves. + * + * An implementation of the simplified Kernighan-Lin algorithm for + * graph bisection refinement as described in Karypis and Kumar + * (1997). + * + * The algorithm stops if @mmax consecutive modes do not lead to a + * decrease in the number of edges cut. This last @mmax moves are + * undone. + * + * Returns: the decrease in the weight of the edges cut by the bisection. + */ +gdouble gts_graph_bisection_kl_refine (GtsGraphBisection * bg, + guint mmax) +{ + GtsEHeap * h1, * h2; + GtsGNode * n; + guint nm = 0, i; + GtsGNode ** moves; + gdouble bestcost = 0., totalcost = 0., best_balance; + + g_return_val_if_fail (bg != NULL, 0.); + g_return_val_if_fail (mmax > 0, 0.); + + h1 = gts_eheap_new ((GtsKeyFunc) node_move_cost1, bg); + gts_eheap_freeze (h1); + gts_container_foreach (GTS_CONTAINER (bg->g1), (GtsFunc) build_heap, h1); + gts_eheap_thaw (h1); + + h2 = gts_eheap_new ((GtsKeyFunc) node_move_cost2, bg); + gts_eheap_freeze (h2); + gts_container_foreach (GTS_CONTAINER (bg->g2), (GtsFunc) build_heap, h2); + gts_eheap_thaw (h2); + + moves = g_malloc (sizeof (GtsGNode *)*mmax); + best_balance = fabs (gts_graph_weight (bg->g1) - gts_graph_weight (bg->g2)); + + do { + GtsGraph * g1, * g2; + gdouble cost; + + if (gts_graph_weight (bg->g1) > gts_graph_weight (bg->g2)) { + n = gts_eheap_remove_top (h1, &cost); + g1 = bg->g1; + g2 = bg->g2; + } + else { + n = gts_eheap_remove_top (h2, &cost); + g1 = bg->g2; + g2 = bg->g1; + } + if (n) { + GSList * i; + + GTS_OBJECT (n)->reserved = NULL; + gts_container_add (GTS_CONTAINER (g2), GTS_CONTAINEE (n)); + gts_container_remove (GTS_CONTAINER (g1), GTS_CONTAINEE (n)); + + totalcost += cost; + if (totalcost < bestcost) { + bestcost = totalcost; + nm = 0; + } + else if (totalcost == bestcost) { + gdouble balance = fabs (gts_graph_weight (g1) - gts_graph_weight (g2)); + + if (balance < best_balance) { + best_balance = balance; + nm = 0; + } + } + else + moves[nm++] = n; + + i = GTS_SLIST_CONTAINER (n)->items; + while (i) { + GtsGNode * n1 = GTS_GNODE_NEIGHBOR (n, i->data); + if (GTS_OBJECT (n1)->reserved && + gts_containee_is_contained (GTS_CONTAINEE (n1), + GTS_CONTAINER (bg->g))) { + GtsEHeap * h = + gts_containee_is_contained (GTS_CONTAINEE (n1), + GTS_CONTAINER (bg->g1)) ? h1 : h2; + gts_eheap_remove (h, GTS_OBJECT (n1)->reserved); + GTS_OBJECT (n1)->reserved = gts_eheap_insert (h, n1); + } + i = i->next; + } + } + } while (n && nm < mmax); + + gts_eheap_foreach (h1, (GFunc) gts_object_reset_reserved, NULL); + gts_eheap_foreach (h2, (GFunc) gts_object_reset_reserved, NULL); + gts_eheap_destroy (h1); + gts_eheap_destroy (h2); + + /* undo last nm moves */ + for (i = 0; i < nm; i++) { + GtsGNode * n = moves[i]; + GtsGraph * g1 = + gts_containee_is_contained (GTS_CONTAINEE (n), + GTS_CONTAINER (bg->g1)) ? bg->g1 : bg->g2; + GtsGraph * g2 = g1 == bg->g1 ? bg->g2 : bg->g1; + + gts_container_add (GTS_CONTAINER (g2), GTS_CONTAINEE (n)); + gts_container_remove (GTS_CONTAINER (g1), GTS_CONTAINEE (n)); + } + g_free (moves); + + return bestcost; +} + +static void build_bheap (GtsGNode * n, GtsGNode * n1, GtsEHeap * heap) +{ + GTS_OBJECT (n)->reserved = gts_eheap_insert (heap, n); +} + +static void update_neighbors (GtsGNode * n, GtsGraphBisection * bg, + GtsEHeap * h1, GtsEHeap * h2) +{ + GSList * i; + + i = GTS_SLIST_CONTAINER (n)->items; + while (i) { + GtsGNode * n1 = GTS_GNODE_NEIGHBOR (n, i->data); + if (gts_containee_is_contained (GTS_CONTAINEE (n1), + GTS_CONTAINER (bg->g))) { + GtsEHeap * h; + GtsGraph /* * g1,*/ * g2; + GHashTable * bg1; + + if (gts_containee_is_contained (GTS_CONTAINEE (n1), + GTS_CONTAINER (bg->g1))) { + h = h1; + //g1 = bg->g1; + g2 = bg->g2; + bg1 = bg->bg1; + } + else { + h = h2; + //g1 = bg->g2; + g2 = bg->g1; + bg1 = bg->bg2; + } + g_hash_table_remove (bg1, n1); + if (h && GTS_OBJECT (n1)->reserved && GTS_OBJECT (n1)->reserved != n1) { + gts_eheap_remove (h, GTS_OBJECT (n1)->reserved); + GTS_OBJECT (n1)->reserved = NULL; + } + if (gts_gnode_degree (n1, g2)) { + g_hash_table_insert (bg1, n1, n1); + if (h && GTS_OBJECT (n1)->reserved != n1) + GTS_OBJECT (n1)->reserved = gts_eheap_insert (h, n1); + } + } + i = i->next; + } +} + +/** + * gts_graph_bisection_bkl_refine: + * @bg: a #GtsGraphBisection. + * @mmax: the maximum number of unsuccessful successive moves. + * @imbalance: the maximum relative imbalance allowed between the + * weights of both halves of the partition. + * + * An implementation of the simplified boundary Kernighan-Lin + * algorithm for graph bisection refinement as described in Karypis + * and Kumar (1997). + * + * The algorithm stops if @mmax consecutive modes do not lead to a + * decrease in the number of edges cut. This last @mmax moves are + * undone. + * + * Returns: the decrease in the weight of the edges cut by the bisection. + */ +gdouble gts_graph_bisection_bkl_refine (GtsGraphBisection * bg, + guint mmax, + gfloat imbalance) +{ + GtsEHeap * h1, * h2; + GtsGNode * n; + guint nm = 0, i; + GtsGNode ** moves; + gdouble bestcost = 0., totalcost = 0., best_balance; + gboolean balanced = FALSE; + + g_return_val_if_fail (bg != NULL, 0.); + g_return_val_if_fail (mmax > 0, 0.); + g_return_val_if_fail (imbalance >= 0. && imbalance <= 1., 0.); + + h1 = gts_eheap_new ((GtsKeyFunc) node_move_cost1, bg); + gts_eheap_freeze (h1); + g_hash_table_foreach (bg->bg1, (GHFunc) build_bheap, h1); + gts_eheap_thaw (h1); + + h2 = gts_eheap_new ((GtsKeyFunc) node_move_cost2, bg); + gts_eheap_freeze (h2); + g_hash_table_foreach (bg->bg2, (GHFunc) build_bheap, h2); + gts_eheap_thaw (h2); + + moves = g_malloc (sizeof (GtsGNode *)*mmax); + imbalance *= gts_graph_weight (bg->g); + best_balance = fabs (gts_graph_weight (bg->g1) - gts_graph_weight (bg->g2)); + if (best_balance <= imbalance) + balanced = TRUE; + + do { + GtsGraph * g1, * g2; + GHashTable * bg1, * bg2; + gdouble cost; + + if (gts_graph_weight (bg->g1) > gts_graph_weight (bg->g2)) { + n = gts_eheap_remove_top (h1, &cost); + g1 = bg->g1; + g2 = bg->g2; + bg1 = bg->bg1; + bg2 = bg->bg2; + } + else { + n = gts_eheap_remove_top (h2, &cost); + g1 = bg->g2; + g2 = bg->g1; + bg1 = bg->bg2; + bg2 = bg->bg1; + } + if (n) { + gdouble balance; + + GTS_OBJECT (n)->reserved = n; + gts_container_add (GTS_CONTAINER (g2), GTS_CONTAINEE (n)); + gts_container_remove (GTS_CONTAINER (g1), GTS_CONTAINEE (n)); + g_hash_table_remove (bg1, n); + if (gts_gnode_degree (n, g1)) + g_hash_table_insert (bg2, n, n); + + update_neighbors (n, bg, h1, h2); + + totalcost += cost; + balance = fabs (gts_graph_weight (g1) - gts_graph_weight (g2)); + + if (!balanced && balance <= imbalance) { + bestcost = totalcost; + best_balance = balance; + balanced = TRUE; + nm = 0; + } + else if (totalcost < bestcost && + (balance < best_balance || balance <= imbalance)) { + bestcost = totalcost; + best_balance = balance; + nm = 0; + } + else if (totalcost == bestcost && balance < best_balance) { + best_balance = balance; + nm = 0; + } + else + moves[nm++] = n; + } + } while (n && nm < mmax); + + gts_container_foreach (GTS_CONTAINER (bg->g), + (GtsFunc) gts_object_reset_reserved, NULL); + gts_eheap_destroy (h1); + gts_eheap_destroy (h2); + + /* undo last nm moves */ + for (i = 0; i < nm; i++) { + GtsGNode * n = moves[i]; + GtsGraph * g1, * g2; + GHashTable * bg1, * bg2; + + if (gts_containee_is_contained (GTS_CONTAINEE (n), + GTS_CONTAINER (bg->g1))) { + g1 = bg->g1; + g2 = bg->g2; + bg1 = bg->bg1; + bg2 = bg->bg2; + } + else { + g1 = bg->g2; + g2 = bg->g1; + bg1 = bg->bg2; + bg2 = bg->bg1; + } + + gts_container_add (GTS_CONTAINER (g2), GTS_CONTAINEE (n)); + gts_container_remove (GTS_CONTAINER (g1), GTS_CONTAINEE (n)); + g_hash_table_remove (bg1, n); + if (gts_gnode_degree (n, g1)) + g_hash_table_insert (bg2, n, n); + + update_neighbors (n, bg, NULL, NULL); + } + g_free (moves); + + return bestcost; +} + +/* Multilevel partitioning */ + +static void bisection_children (GtsGNodeSplit * ns, GtsGraphBisection * bg) +{ + GtsGraph * g, * g1; + GHashTable * bbg; + GtsGNode * n1 = GTS_GNODE_SPLIT_N1 (ns); + GtsGNode * n2 = GTS_GNODE_SPLIT_N2 (ns); + + if (gts_containee_is_contained (GTS_CONTAINEE (ns->n), + GTS_CONTAINER (bg->g1))) { + g = bg->g1; + g1 = bg->g2; + bbg = bg->bg1; + } + else { + g = bg->g2; + g1 = bg->g1; + bbg = bg->bg2; + } + + gts_allow_floating_gnodes = TRUE; + gts_container_remove (GTS_CONTAINER (g), GTS_CONTAINEE (ns->n)); + gts_allow_floating_gnodes = FALSE; + gts_container_add (GTS_CONTAINER (g), GTS_CONTAINEE (n1)); + gts_container_add (GTS_CONTAINER (g), GTS_CONTAINEE (n2)); + + if (g_hash_table_lookup (bbg, ns->n)) { + g_hash_table_remove (bbg, ns->n); + if (gts_gnode_degree (n1, g1) > 0) + g_hash_table_insert (bbg, n1, n1); + if (gts_gnode_degree (n2, g1) > 0) + g_hash_table_insert (bbg, n2, n2); + } +} + +/** + * gts_graph_bisection_new: + * @wg: a #GtsWGraph. + * @ntry: the number of tries for the graph growing algorithm. + * @mmax: the number of unsucessful moves for the refinement algorithm. + * @nmin: the minimum number of nodes of the coarsest graph. + * @imbalance: the maximum relative imbalance allowed between the + * weights of both halves of the partition. + * + * An implementation of a multilevel bisection algorithm as presented + * in Karypis and Kumar (1997). A multilevel hierarchy of graphs is + * created using the #GtsPGraph object. The bisection of the coarsest + * graph is created using the gts_graph_ggg_bisection() function. The + * graph is then uncoarsened using gts_pgraph_down() and at each level + * the bisection is refined using gts_graph_bisection_bkl_refine(). + * + * Returns: a new #GtsGraphBisection of @wg. + */ +GtsGraphBisection * gts_graph_bisection_new (GtsWGraph * wg, + guint ntry, + guint mmax, + guint nmin, + gfloat imbalance) +{ + GtsGraph * g; + GtsPGraph * pg; + GtsGraphBisection * bg; + gdouble cost; + + g_return_val_if_fail (wg != NULL, NULL); + + g = GTS_GRAPH (wg); + pg = gts_pgraph_new (gts_pgraph_class (), g, + gts_gnode_split_class (), + gts_wgnode_class (), + gts_wgedge_class (), + nmin); + + bg = gts_graph_ggg_bisection (g, ntry); +#ifdef DEBUG + fprintf (stderr, "before size: %5d weight: %5g cuts: %5d cweight: %5g\n", + gts_container_size (GTS_CONTAINER (bg->g1)), + gts_graph_weight (bg->g1), + gts_graph_edges_cut (bg->g1), + gts_graph_edges_cut_weight (bg->g1)); + g_assert (gts_graph_bisection_check (bg)); +#endif + while ((cost = gts_graph_bisection_bkl_refine (bg, mmax, imbalance))) { +#ifdef DEBUG + fprintf (stderr, " cost: %g\n", cost); + g_assert (gts_graph_bisection_check (bg)); +#endif + } +#ifdef DEBUG + fprintf (stderr, "after size: %5d weight: %5g cuts: %5d cweight: %5g\n", + gts_container_size (GTS_CONTAINER (bg->g1)), + gts_graph_weight (bg->g1), + gts_graph_edges_cut (bg->g1), + gts_graph_edges_cut_weight (bg->g1)); +#endif + while (gts_pgraph_down (pg, (GtsFunc) bisection_children, bg)) { +#ifdef DEBUG + fprintf (stderr, "before size: %5d weight: %5g cuts: %5d cweight: %5g\n", + gts_container_size (GTS_CONTAINER (bg->g1)), + gts_graph_weight (bg->g1), + gts_graph_edges_cut (bg->g1), + gts_graph_edges_cut_weight (bg->g1)); +#endif + while ((cost = gts_graph_bisection_bkl_refine (bg, mmax, imbalance))) { +#ifdef DEBUG + fprintf (stderr, " cost: %g\n", cost); + g_assert (gts_graph_bisection_check (bg)); +#endif + } +#ifdef DEBUG + fprintf (stderr, "after size: %5d weight: %5g cuts: %5d cweight: %5g\n", + gts_container_size (GTS_CONTAINER (bg->g1)), + gts_graph_weight (bg->g1), + gts_graph_edges_cut (bg->g1), + gts_graph_edges_cut_weight (bg->g1)); +#endif + } + gts_object_destroy (GTS_OBJECT (pg)); + + return bg; +} + +/** + * gts_graph_bisection_destroy: + * @bg: a #GtsGraphBisection. + * @destroy_graphs: controls graph destruction. + * + * Frees all the memory allocated for @bg. If @destroy_graphs is %TRUE + * the graphs created by @bg are destroyed. + */ +void gts_graph_bisection_destroy (GtsGraphBisection * bg, + gboolean destroy_graphs) +{ + g_return_if_fail (bg != NULL); + + g_hash_table_destroy (bg->bg1); + g_hash_table_destroy (bg->bg2); + + if (destroy_graphs) { + gts_object_destroy (GTS_OBJECT (bg->g1)); + gts_object_destroy (GTS_OBJECT (bg->g2)); + } + + g_free (bg); +} + +static void recursive_bisection (GtsWGraph * wg, + guint np, + guint ntry, + guint mmax, + guint nmin, + gfloat imbalance, + GSList ** list) +{ + if (np == 0) + *list = g_slist_prepend (*list, wg); + else { + GtsGraphBisection * bg = + gts_graph_bisection_new (wg, ntry, mmax, nmin, imbalance); + GtsGraph * g1 = bg->g1; + GtsGraph * g2 = bg->g2; + + gts_object_destroy (GTS_OBJECT (wg)); + gts_graph_bisection_destroy (bg, FALSE); + recursive_bisection (GTS_WGRAPH (g1), np - 1, ntry, mmax, nmin, imbalance, + list); + recursive_bisection (GTS_WGRAPH (g2), np - 1, ntry, mmax, nmin, imbalance, + list); + } +} + +/** + * gts_graph_recursive_bisection: + * @wg: a #GtsWGraph. + * @n: the number of bisection levels. + * @ntry: the number of tries for the graph growing algorithm. + * @mmax: the number of unsucessful moves for the refinement algorithm. + * @nmin: the minimum number of nodes of the coarsest graph. + * @imbalance: the maximum relative imbalance allowed between the + * weights of both halves of the partition. + * + * Calls gts_graph_bisection_new() recursively in order to obtain a + * 2^@n partition of @wg. + * + * Returns: a list of 2^@n new #GtsGraph representing the partition. + */ +GSList * gts_graph_recursive_bisection (GtsWGraph * wg, + guint n, + guint ntry, + guint mmax, + guint nmin, + gfloat imbalance) +{ + GtsGraphBisection * bg; + GtsGraph * g1, * g2; + GSList * list = NULL; + + g_return_val_if_fail (wg != NULL, NULL); + g_return_val_if_fail (n > 0, NULL); + + bg = gts_graph_bisection_new (wg, ntry, mmax, nmin, imbalance); + g1 = bg->g1; + g2 = bg->g2; + gts_graph_bisection_destroy (bg, FALSE); + recursive_bisection (GTS_WGRAPH (g1), n - 1, ntry, mmax, nmin, imbalance, + &list); + recursive_bisection (GTS_WGRAPH (g2), n - 1, ntry, mmax, nmin, imbalance, + &list); + + return list; +} Index: trunk/src_3rd/gts/pgraph.c =================================================================== --- trunk/src_3rd/gts/pgraph.c (nonexistent) +++ trunk/src_3rd/gts/pgraph.c (revision 1065) @@ -0,0 +1,584 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "gts.h" + +/* GtsGNodeSplit */ + +static void gnode_split_destroy (GtsObject * object) +{ + GtsGNodeSplit * ns = GTS_GNODE_SPLIT (object); + + if (gts_container_size (GTS_CONTAINER (ns->n)) == 0) { + g_assert (GTS_SLIST_CONTAINEE (ns->n)->containers == NULL); + gts_object_destroy (GTS_OBJECT (ns->n)); + } + else { + /* GtsGNode * n1 = GTS_GNODE_SPLIT_N1 (ns); */ + /* GtsGNode * n2 = GTS_GNODE_SPLIT_N2 (ns); */ + + g_warning ("Memory deallocation for GtsGNodeSplit not fully implemented yet: memory leak!"); + } + + (* GTS_OBJECT_CLASS (gts_gnode_split_class ())->parent_class->destroy) + (object); +} + +static void gnode_split_class_init (GtsGNodeSplitClass * klass) +{ + GTS_OBJECT_CLASS (klass)->destroy = gnode_split_destroy; +} + +static void gnode_split_init (GtsGNodeSplit * ns) +{ + ns->n = NULL; + ns->n1 = ns->n2 = NULL; +} + +/** + * gts_gnode_split_class: + * + * Returns: the #GtsGNodeSplitClass. + */ +GtsGNodeSplitClass * gts_gnode_split_class (void) +{ + static GtsGNodeSplitClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo gnode_split_info = { + "GtsGNodeSplit", + sizeof (GtsGNodeSplit), + sizeof (GtsGNodeSplitClass), + (GtsObjectClassInitFunc) gnode_split_class_init, + (GtsObjectInitFunc) gnode_split_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (gts_object_class (), &gnode_split_info); + } + + return klass; +} + +/** + * gts_gnode_split_new: + * @klass: a #GtsGNodeSplitClass. + * @n: a #GtsGNode. + * @n1: a #GtsGNodeSplit or #GtsGNode. + * @n2: a #GtsGNodeSplit or #GtsGNode. + * + * Creates a new #GtsGNodeSplit which would collapse @n1 and @n2 into + * @n. The collapse itself is not performed. + * + * Returns: the new #GtsGNodeSplit. + */ +GtsGNodeSplit * gts_gnode_split_new (GtsGNodeSplitClass * klass, + GtsGNode * n, + GtsObject * n1, + GtsObject * n2) +{ + GtsGNodeSplit * ns; + + g_return_val_if_fail (klass != NULL, NULL); + g_return_val_if_fail (n != NULL, NULL); + g_return_val_if_fail (GTS_IS_GNODE_SPLIT (n1) || GTS_IS_GNODE (n1), NULL); + g_return_val_if_fail (GTS_IS_GNODE_SPLIT (n2) || GTS_IS_GNODE (n2), NULL); + + ns = GTS_GNODE_SPLIT (gts_object_new (GTS_OBJECT_CLASS (klass))); + ns->n = n; + ns->n1 = n1; + ns->n2 = n2; + + return ns; +} + +static void connect_edge (GtsGEdge * e, gpointer * data) +{ + GtsGNode * n = data[0]; + GtsGNode * n1 = data[1]; + GtsGNode * n2 = data[2]; + + if (GTS_OBJECT (e)->reserved || /* edge is disconnected */ + gts_gedge_connects (e, n1, n2)) + return; + if (e->n1 == n1 || e->n1 == n2) + e->n1 = n; + else if (e->n2 == n1 || e->n2 == n2) + e->n2 = n; + else + g_assert_not_reached (); + gts_container_add (GTS_CONTAINER (n), GTS_CONTAINEE (e)); +} + +/** + * gts_gnode_split_collapse: + * @ns: a #GtsGNodeSplit. + * @g: a #GtsGraph. + * @klass: a #GtsWGEdgeClass. + * + * Collapses the node split @ns. Any new edge created during the + * process will be of class @klass. + */ +void gts_gnode_split_collapse (GtsGNodeSplit * ns, + GtsGraph * g, + GtsWGEdgeClass * klass) +{ + GtsGNode * n1, * n2; + GSList * i; + gpointer data[3]; + + g_return_if_fail (ns != NULL); + g_return_if_fail (g != NULL); + g_return_if_fail (gts_container_size (GTS_CONTAINER (ns->n)) == 0); + + n1 = GTS_GNODE_SPLIT_N1 (ns); + n2 = GTS_GNODE_SPLIT_N2 (ns); + + /* look for triangles */ + i = GTS_SLIST_CONTAINER (n1)->items; + while (i) { + GtsGEdge * e13 = i->data; + GtsGNode * n3 = GTS_GNODE_NEIGHBOR (n1, e13); + if (n3 != n2) { + GSList * j = GTS_SLIST_CONTAINER (n3)->items; + while (j) { + GtsGEdge * e32 = j->data; + GSList * next = j->next; + GtsGNode * n4 = GTS_GNODE_NEIGHBOR (n3, e32); + if (n4 == n2) { /* found triangle n1 (e13) n3 (e32) n2 */ + gts_wgedge_new (klass, ns->n, n3, + gts_gedge_weight (e13) + gts_gedge_weight (e32)); + GTS_OBJECT (e13)->reserved = n3; + GTS_OBJECT (e32)->reserved = n3; + GTS_SLIST_CONTAINER (n3)->items = + g_slist_remove (GTS_SLIST_CONTAINER (n3)->items, e32); + } + j = next; + } + if (GTS_OBJECT (e13)->reserved == n3) + GTS_SLIST_CONTAINER (n3)->items = + g_slist_remove (GTS_SLIST_CONTAINER (n3)->items, e13); + } + i = i->next; + } + + /* connect edges to new node */ + data[0] = ns->n; + data[1] = n1; + data[2] = n2; + gts_container_foreach (GTS_CONTAINER (n1), (GtsFunc) connect_edge, data); + gts_container_foreach (GTS_CONTAINER (n2), (GtsFunc) connect_edge, data); + + gts_allow_floating_gnodes = TRUE; + gts_container_remove (GTS_CONTAINER (g), GTS_CONTAINEE (n1)); + gts_container_remove (GTS_CONTAINER (g), GTS_CONTAINEE (n2)); + gts_allow_floating_gnodes = FALSE; + gts_container_add (GTS_CONTAINER (g), GTS_CONTAINEE (ns->n)); +} + +static void restore_edge (GtsGEdge * e, gpointer * data) +{ + GtsGNode * n = data[0]; + GtsGNode * n1 = data[1]; + GtsGNode * n2 = data[2]; + GtsGNode * n3 = GTS_OBJECT (e)->reserved; + + if (n3) { /* e is a disconnected edge */ + GTS_OBJECT (e)->reserved = NULL; + gts_container_add (GTS_CONTAINER (n3), GTS_CONTAINEE (e)); + return; + } + + if (gts_gedge_connects (e, n1, n2)) + return; + + if (e->n1 == n) + e->n1 = n1; + else if (e->n2 == n) + e->n2 = n1; + else + g_assert_not_reached (); + GTS_SLIST_CONTAINER (n)->items = + g_slist_remove (GTS_SLIST_CONTAINER (n)->items, e); +} + +/** + * gts_gnode_split_expand: + * @ns: a #GtsGNodeSplit. + * @g: a #GtsGraph. + * + * Expands the node split ns adding the new nodes to @g. + */ +void gts_gnode_split_expand (GtsGNodeSplit * ns, + GtsGraph * g) +{ + GtsGNode * n1, * n2; + gpointer data[3]; + GSList * i; + + g_return_if_fail (ns != NULL); + g_return_if_fail (g != NULL); + g_return_if_fail (gts_containee_is_contained (GTS_CONTAINEE (ns->n), + GTS_CONTAINER (g))); + + n1 = GTS_GNODE_SPLIT_N1 (ns); + n2 = GTS_GNODE_SPLIT_N2 (ns); + + data[0] = ns->n; + data[1] = n1; + data[2] = n2; + gts_container_foreach (GTS_CONTAINER (n1), (GtsFunc) restore_edge, data); + data[1] = n2; + data[2] = n1; + gts_container_foreach (GTS_CONTAINER (n2), (GtsFunc) restore_edge, data); + + i = GTS_SLIST_CONTAINER (ns->n)->items; + while (i) { + GSList * next = i->next; + gts_container_remove (GTS_CONTAINER (ns->n), GTS_CONTAINEE (i->data)); + i = next; + } + g_assert (gts_container_size (GTS_CONTAINER (ns->n)) == 0); + + gts_allow_floating_gnodes = TRUE; + gts_container_remove (GTS_CONTAINER (g), GTS_CONTAINEE (ns->n)); + gts_allow_floating_gnodes = FALSE; + + gts_container_add (GTS_CONTAINER (g), GTS_CONTAINEE (n1)); + gts_container_add (GTS_CONTAINER (g), GTS_CONTAINEE (n2)); +} + +/* GtsPGraph */ + +static void pgraph_destroy (GtsObject * object) +{ + GtsPGraph * pg = GTS_PGRAPH (object); + guint i; + + for (i = 0; i < pg->split->len; i++) + gts_object_destroy (GTS_OBJECT (g_ptr_array_index (pg->split, i))); + g_ptr_array_free (pg->split, TRUE); + g_array_free (pg->levels, TRUE); + + (* GTS_OBJECT_CLASS (gts_pgraph_class ())->parent_class->destroy) (object); +} + +static void pgraph_class_init (GtsPGraphClass * klass) +{ + GTS_OBJECT_CLASS (klass)->destroy = pgraph_destroy; +} + +static void pgraph_init (GtsPGraph * pg) +{ + pg->g = NULL; + pg->split = g_ptr_array_new (); + pg->levels = g_array_new (FALSE, FALSE, sizeof (guint)); + pg->level = 0; + pg->split_class = gts_gnode_split_class (); + pg->edge_class = gts_wgedge_class (); + pg->pos = pg->min = 0; +} + +/** + * gts_pgraph_class: + * + * Returns: the #GtsPGraphClass. + */ +GtsPGraphClass * gts_pgraph_class (void) +{ + static GtsPGraphClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo pgraph_info = { + "GtsPGraph", + sizeof (GtsPGraph), + sizeof (GtsPGraphClass), + (GtsObjectClassInitFunc) pgraph_class_init, + (GtsObjectInitFunc) pgraph_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (gts_object_class (), &pgraph_info); + } + + return klass; +} + +static void match_neighbor (GtsGNode * n, gpointer * data) +{ + if (!GTS_OBJECT (n)->reserved) { + GtsGraph * g = data[0]; + GSList ** list = data[1]; + GSList * i = GTS_SLIST_CONTAINER (n)->items; + gfloat wmax = - G_MAXFLOAT; + GtsGEdge * emax = NULL; + + while (i) { + GtsGNode * n1 = GTS_GNODE_NEIGHBOR (n, i->data); + if (!GTS_OBJECT (n1)->reserved && + gts_gedge_weight (i->data) > wmax && + gts_containee_is_contained (GTS_CONTAINEE (n1), GTS_CONTAINER (g))) { + emax = i->data; + wmax = gts_gedge_weight (emax); + } + i = i->next; + } + if (emax) { + GtsGNode * n1 = GTS_GNODE_NEIGHBOR (n, emax); + + GTS_OBJECT (n1)->reserved = n; + GTS_OBJECT (n)->reserved = n1; + *list = g_slist_prepend (*list, emax); + } + } +} + +static GSList * maximal_matching (GtsGraph * g) +{ + GSList * list = NULL; + gpointer data[2]; + + data[0] = g; + data[1] = &list; + gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) match_neighbor, data); + gts_container_foreach (GTS_CONTAINER (g), + (GtsFunc) gts_object_reset_reserved, + NULL); + + return list; +} + +/** + * gts_pgraph_new: + * @klass: a #GtsPGraphClass. + * @g: a #GtsGraph. + * @split_class: a #GtsGNodeSplitClass. + * @node_class: a #GtsWGNodeClass. + * @edge_class: a #GtsWGEdgeClass. + * @min: the minimum number of nodes. + * + * Creates a new multilevel approximation of graph @g. At each level a + * maximal matching is created using the Heavy Edge Matching (HEM) + * technique of Karypis and Kumar (1997). The newly created nodes are + * of type @node_class and their weight is set to the sum of the + * weights of their children. The newly created edges are of type + * @edge_class and their weight is set to the sum of the weight of the + * collapsed edges. The last level is reached when the maximal + * matching obtained would lead to a graph with less than @min nodes. + * + * Returns: the new #GtsPGraph containing the multilevel + * representation of @g. + */ +GtsPGraph * gts_pgraph_new (GtsPGraphClass * klass, + GtsGraph * g, + GtsGNodeSplitClass * split_class, + GtsWGNodeClass * node_class, + GtsWGEdgeClass * edge_class, + guint min) +{ + GtsPGraph * pg; + GSList * matching; + + g_return_val_if_fail (klass != NULL, NULL); + g_return_val_if_fail (g != NULL, NULL); + g_return_val_if_fail (split_class != NULL, NULL); + g_return_val_if_fail (node_class != NULL, NULL); + g_return_val_if_fail (edge_class != NULL, NULL); + + pg = GTS_PGRAPH (gts_object_new (GTS_OBJECT_CLASS (klass))); + pg->g = g; + pg->split_class = split_class; + pg->edge_class = edge_class; + + while (gts_container_size (GTS_CONTAINER (g)) > min && + (matching = maximal_matching (g))) { + GSList * i = matching; + guint size = gts_container_size (GTS_CONTAINER (g)); + + g_array_append_val (pg->levels, size); + + while (i && gts_container_size (GTS_CONTAINER (g)) > min) { + GtsGEdge * e = i->data; + GtsGNode * n = GTS_GNODE (gts_wgnode_new (node_class, + gts_gnode_weight (e->n1) + + gts_gnode_weight (e->n2))); + GtsGNodeSplit * ns = gts_gnode_split_new (split_class, n, + GTS_OBJECT (e->n1), + GTS_OBJECT (e->n2)); + gts_gnode_split_collapse (ns, g, edge_class); + g_ptr_array_add (pg->split, ns); + i = i->next; + } + g_slist_free (matching); + } + + pg->pos = pg->split->len; + pg->min = gts_container_size (GTS_CONTAINER (g)); + pg->level = pg->levels->len; + + return pg; +} + +/** + * gts_pgraph_add_node: + * @pg: a #GtsPGraph. + * + * Adds one node to the multilevel graph @pg by expanding the next + * available #GtsGNodeSplit. + * + * Returns: the expanded #GtsGNodeSplit or #NULL if all the + * #GtsGNodeSplit have already been expanded. + */ +GtsGNodeSplit * gts_pgraph_add_node (GtsPGraph * pg) +{ + GtsGNodeSplit * ns; + + g_return_val_if_fail (pg != NULL, NULL); + + if (pg->pos == 0) + return NULL; + + ns = g_ptr_array_index (pg->split, --pg->pos); + gts_gnode_split_expand (ns, pg->g); + + return ns; +} + +/** + * gts_pgraph_remove_node: + * @pg: a #GtsPGraph. + * + * Removes one node from the multilevel graph @pg by collapsing the + * first available #GtsGNodeSplit. + * + * Returns: the collapsed #GtsGNodeSplit or %NULL if all the + * #GtsGNodeSplit have already been collapsed. + */ +GtsGNodeSplit * gts_pgraph_remove_node (GtsPGraph * pg) +{ + GtsGNodeSplit * ns; + + g_return_val_if_fail (pg != NULL, NULL); + + if (pg->pos == pg->split->len) + return NULL; + + ns = g_ptr_array_index (pg->split, pg->pos++); + gts_gnode_split_collapse (ns, pg->g, pg->edge_class); + + return ns; +} + +/** + * gts_pgraph_max_node_number: + * @pg: a #GtsPGraph. + * + * Returns: the maximum number of nodes of @pg i.e. the number of + * nodes if all the #GtsGNodeSplit were expanded. + */ +guint gts_pgraph_max_node_number (GtsPGraph * pg) +{ + g_return_val_if_fail (pg != NULL, 0); + + return pg->min + pg->split->len; +} + +/** + * gts_pgraph_min_node_number: + * @pg: a #GtsPGraph. + * + * Returns: the minimum number of nodes of @pg i.e. the number of + * nodes if all the #GtsGNodeSplit were collapsed. + */ +guint gts_pgraph_min_node_number (GtsPGraph * pg) +{ + g_return_val_if_fail (pg != NULL, 0); + + return pg->min; +} + +/** + * gts_pgraph_set_node_number: + * @pg: a #GtsPGraph. + * @n: a number of nodes. + * + * Performs the required number of collapses or expansions to set the + * number of nodes of @pg to @n. + */ +void gts_pgraph_set_node_number (GtsPGraph * pg, guint n) +{ + g_return_if_fail (pg != NULL); + + n = pg->min + pg->split->len - n; + while (pg->pos > n && gts_pgraph_add_node (pg)) + ; + while (pg->pos < n && gts_pgraph_remove_node (pg)) + ; +} + +/** + * gts_pgraph_get_node_number: + * @pg: a #GtsPGraph. + * + * Returns: the current number of nodes of @pg. + */ +guint gts_pgraph_get_node_number (GtsPGraph * pg) +{ + g_return_val_if_fail (pg != NULL, 0); + + return pg->min + pg->split->len - pg->pos; +} + +/** + * gts_pgraph_down: + * @pg: a #GtsPGraph. + * @func: a #GtsFunc or %NULL. + * @data: user data to pass to @func. + * + * Performs the required number of expansions to go from the current + * level to the level immediately below. + * + * If @func is not %NULL, it is called after each #GtsGNodeSplit has + * been expanded. + * + * Returns: %FALSE if it is not possible to go down one level, %TRUE + * otherwise. + */ +gboolean gts_pgraph_down (GtsPGraph * pg, + GtsFunc func, + gpointer data) +{ + guint size; + + g_return_val_if_fail (pg != NULL, FALSE); + + if (pg->level == 0) + return FALSE; + + size = g_array_index (pg->levels, guint, --(pg->level)); + while (gts_container_size (GTS_CONTAINER (pg->g)) < size) { + GtsGNodeSplit * ns = gts_pgraph_add_node (pg); + + g_assert (ns); + if (func) + (* func) (ns, data); + } + return TRUE; +} + Index: trunk/src_3rd/gts/point.c =================================================================== --- trunk/src_3rd/gts/point.c (nonexistent) +++ trunk/src_3rd/gts/point.c (revision 1065) @@ -0,0 +1,986 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include +#include "gts.h" +#include "gts-private.h" +#include "predicates.h" + +static void point_read (GtsObject ** o, GtsFile * f) +{ + GtsPoint * p = GTS_POINT (*o); + + if (GTS_POINT_CLASS ((*o)->klass)->binary) { + if (gts_file_read (f, &(p->x), sizeof (gdouble), 1) != 1) { + gts_file_error (f, "expecting a binary number (x coordinate)"); + return; + } + if (gts_file_read (f, &(p->y), sizeof (gdouble), 1) != 1) { + gts_file_error (f, "expecting a binary number (y coordinate)"); + return; + } + if (gts_file_read (f, &(p->z), sizeof (gdouble), 1) != 1) { + gts_file_error (f, "expecting a binary number (z coordinate)"); + return; + } + } + else { + if (f->type != GTS_INT && f->type != GTS_FLOAT) { + gts_file_error (f, "expecting a number (x coordinate)"); + return; + } + p->x = atof (f->token->str); + + gts_file_next_token (f); + if (f->type != GTS_INT && f->type != GTS_FLOAT) { + gts_file_error (f, "expecting a number (y coordinate)"); + return; + } + p->y = atof (f->token->str); + + gts_file_next_token (f); + if (f->type != GTS_INT && f->type != GTS_FLOAT) { + gts_file_error (f, "expecting a number (z coordinate)"); + return; + } + p->z = atof (f->token->str); + + gts_file_next_token (f); + } +} + +static void point_write (GtsObject * o, FILE * fptr) +{ + GtsPoint * p = GTS_POINT (o); + + if (GTS_POINT_CLASS ((o)->klass)->binary) { + fwrite (&(p->x), sizeof (gdouble), 1, fptr); + fwrite (&(p->y), sizeof (gdouble), 1, fptr); + fwrite (&(p->z), sizeof (gdouble), 1, fptr); + } + else + fprintf (fptr, "%.10g %.10g %.10g", p->x, p->y, p->z); +} + +static void point_class_init (GtsObjectClass * klass) +{ + klass->read = point_read; + klass->write = point_write; +} + +/** + * gts_point_class: + * + * Returns: the #GtsPointClass. + */ +GtsPointClass * gts_point_class (void) +{ + static GtsPointClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo point_info = { + "GtsPoint", + sizeof (GtsPoint), + sizeof (GtsPointClass), + (GtsObjectClassInitFunc) point_class_init, + (GtsObjectInitFunc) NULL, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (gts_object_class (), + &point_info); + } + + return klass; +} + +/** + * gts_point_new: + * @klass: a #GtsPointClass. + * @x: the x-coordinate. + * @y: the y-coordinate. + * @z: the z-coordinate. + * + * Returns: a new #GtsPoint. + */ +GtsPoint * gts_point_new (GtsPointClass * klass, + gdouble x, gdouble y, gdouble z) +{ + GtsPoint * p; + + p = GTS_POINT (gts_object_new (GTS_OBJECT_CLASS (klass))); + p->x = x; + p->y = y; + p->z = z; + + return p; +} + +/** + * gts_point_set: + * @p: a #GtsPoint. + * @x: the x-coordinate. + * @y: the y-coordinate. + * @z: the z-coordinate. + * + * Sets the coordinates of @p. + */ +void gts_point_set (GtsPoint * p, gdouble x, gdouble y, gdouble z) +{ + g_return_if_fail (p != NULL); + + p->x = x; + p->y = y; + p->z = z; +} + +/** + * gts_point_distance: + * @p1: a #GtsPoint. + * @p2: another #GtsPoint. + * + * Returns: the Euclidean distance between @p1 and @p2. + */ +gdouble gts_point_distance (GtsPoint * p1, GtsPoint * p2) +{ + g_return_val_if_fail (p1 != NULL && p2 != NULL, 0.0); + + return sqrt ((p1->x - p2->x)*(p1->x - p2->x) + + (p1->y - p2->y)*(p1->y - p2->y) + + (p1->z - p2->z)*(p1->z - p2->z)); +} + +/** + * gts_point_distance2: + * @p1: a #GtsPoint. + * @p2: another #GtsPoint. + * + * Returns: the square of the Euclidean distance between @p1 and @p2. + */ +gdouble gts_point_distance2 (GtsPoint * p1, GtsPoint * p2) +{ + g_return_val_if_fail (p1 != NULL && p2 != NULL, 0.0); + + return + (p1->x - p2->x)*(p1->x - p2->x) + + (p1->y - p2->y)*(p1->y - p2->y) + + (p1->z - p2->z)*(p1->z - p2->z); +} + +/** + * gts_point_orientation_3d: + * @p1: a #GtsPoint. + * @p2: a #GtsPoint. + * @p3: a #GtsPoint. + * @p4: a #GtsPoint. + * + * Checks if @p4 lies above, below or on the plane passing through the + * points @p1, @p2 and @p3. Below is defined so that @p1, @p2 and @p3 + * appear in counterclockwise order when viewed from above the + * plane. The returned value is an approximation of six times the + * signed volume of the tetrahedron defined by the four points. This + * function uses adaptive floating point arithmetic and is + * consequently geometrically robust. + * + * Returns: a positive value if @p4 lies below, a negative value if + * @p4 lies above the plane, zero if the four points are coplanar. + */ +gdouble gts_point_orientation_3d (GtsPoint * p1, + GtsPoint * p2, + GtsPoint * p3, + GtsPoint * p4) +{ + g_return_val_if_fail (p1 != NULL && p2 != NULL && + p3 != NULL && p4 != NULL, 0.0); + return orient3d ((gdouble *) &p1->x, + (gdouble *) &p2->x, + (gdouble *) &p3->x, + (gdouble *) &p4->x); +} + +/** + * gts_point_is_in_triangle: + * @p: a #GtsPoint. + * @t: a #GtsTriangle. + * + * Tests if the planar projection (x, y) of @p is inside, outside or + * on the boundary of the planar projection of @t. This function is + * geometrically robust. + * + * Returns: %GTS_IN if @p is inside @t, %GTS_ON if @p is on the boundary of + * @t, %GTS_OUT otherwise. + */ +GtsIntersect gts_point_is_in_triangle (GtsPoint * p, GtsTriangle * t) +{ + GtsVertex * v1, * v2, * v3; + gdouble d1, d2, d3; + + g_return_val_if_fail (p != NULL && t != NULL, FALSE); + + gts_triangle_vertices (t, &v1, &v2, &v3); + + d1 = gts_point_orientation (GTS_POINT (v1), GTS_POINT (v2), p); + if (d1 < 0.0) + return GTS_OUT; + d2 = gts_point_orientation (GTS_POINT (v2), GTS_POINT (v3), p); + if (d2 < 0.0) + return GTS_OUT; + d3 = gts_point_orientation (GTS_POINT (v3), GTS_POINT (v1), p); + if (d3 < 0.0) + return GTS_OUT; + if (d1 == 0.0 || d2 == 0.0 || d3 == 0.0) + return GTS_ON; + return GTS_IN; +} + +/** + * gts_point_in_triangle_circle: + * @p: a #GtsPoint. + * @t: a #GtsTriangle. + * + * Tests if the planar projection (x, y) of @p is inside or outside + * the circumcircle of the planar projection of @t. This function is + * geometrically robust. + * + * Returns: a positive number if @p lies inside, + * a negative number if @p lies outside and zero if @p lies on + * the circumcircle of @t. + */ +gdouble gts_point_in_triangle_circle (GtsPoint * p, GtsTriangle * t) +{ + GtsPoint * p1, * p2, * p3; + + g_return_val_if_fail (p != NULL && t != NULL, 0.0); + + gts_triangle_vertices (t, + (GtsVertex **) &p1, + (GtsVertex **) &p2, + (GtsVertex **) &p3); + + return incircle ((gdouble *) &p1->x, + (gdouble *) &p2->x, + (gdouble *) &p3->x, + (gdouble *) &p->x); +} + +/** + * gts_point_in_circle: + * @p: a #GtsPoint. + * @p1: a #GtsPoint. + * @p2: a #GtsPoint. + * @p3: a #GtsPoint. + * + * Tests if the planar projection (x, y) of @p is inside or outside the + * circle defined by the planar projection of @p1, @p2 and @p3. + * + * Returns: a positive number if @p lies inside, + * a negative number if @p lies outside and zero if @p lies on + * the circle. + */ +gdouble gts_point_in_circle (GtsPoint * p, + GtsPoint * p1, GtsPoint * p2, GtsPoint * p3) +{ + g_return_val_if_fail (p != NULL && p1 != NULL && p2 != NULL && p3 != NULL, + 0.0); + + return incircle ((gdouble *) &p1->x, + (gdouble *) &p2->x, + (gdouble *) &p3->x, + (gdouble *) &p->x); +} + +/** + * gts_point_in_sphere: + * @p: a #GtsPoint. + * @p1: a #GtsPoint. + * @p2: a #GtsPoint. + * @p3: a #GtsPoint. + * @p4: a #GtsPoint. + * + * Tests if @p is inside or outside the sphere defined by @p1, @p2, + * @p3 and @p4. + * + * Returns: a positive number if @p lies inside, + * a negative number if @p lies outside and zero if @p lies on + * the sphere. + */ +gdouble gts_point_in_sphere (GtsPoint * p, + GtsPoint * p1, GtsPoint * p2, GtsPoint * p3, GtsPoint * p4) +{ + g_return_val_if_fail (p != NULL && p1 != NULL && p2 != NULL && p3 != NULL && p4 != NULL, + 0.0); + + return insphere ((gdouble *) &p1->x, + (gdouble *) &p2->x, + (gdouble *) &p3->x, + (gdouble *) &p4->x, + (gdouble *) &p->x); +} + +/** + * gts_point_segment_distance2: + * @p: a #GtsPoint. + * @s: a #GtsSegment. + * + * Returns: the square of the minimun Euclidean distance between @p and @s. + */ +gdouble gts_point_segment_distance2 (GtsPoint * p, GtsSegment * s) +{ + gdouble t, ns2, x, y, z; + GtsPoint * p1, * p2; + + g_return_val_if_fail (p != NULL, 0.0); + g_return_val_if_fail (s != NULL, 0.0); + + p1 = GTS_POINT (s->v1); + p2 = GTS_POINT (s->v2); + ns2 = gts_point_distance2 (p1, p2); + if (ns2 == 0.0) + return gts_point_distance2 (p, p1); + t = ((p2->x - p1->x)*(p->x - p1->x) + + (p2->y - p1->y)*(p->y - p1->y) + + (p2->z - p1->z)*(p->z - p1->z))/ns2; + if (t > 1.0) + return gts_point_distance2 (p, p2); + if (t < 0.0) + return gts_point_distance2 (p, p1); + x = (1. - t)*p1->x + t*p2->x - p->x; + y = (1. - t)*p1->y + t*p2->y - p->y; + z = (1. - t)*p1->z + t*p2->z - p->z; + return x*x + y*y + z*z; +} + +/** + * gts_point_segment_distance: + * @p: a #GtsPoint. + * @s: a #GtsSegment. + * + * Returns: the minimun Euclidean distance between @p and @s. + */ +gdouble gts_point_segment_distance (GtsPoint * p, GtsSegment * s) +{ + g_return_val_if_fail (p != NULL, 0.0); + g_return_val_if_fail (s != NULL, 0.0); + + return sqrt (gts_point_segment_distance2 (p, s)); +} + +/** + * gts_point_segment_closest: + * @p: a #GtsPoint. + * @s: a #GtsSegment. + * @closest: a #GtsPoint. + * + * Set the coordinates of @closest to the coordinates of the point belonging + * to @s closest to @p. + */ +void gts_point_segment_closest (GtsPoint * p, + GtsSegment * s, + GtsPoint * closest) +{ + gdouble t, ns2; + GtsPoint * p1, * p2; + + g_return_if_fail (p != NULL); + g_return_if_fail (s != NULL); + g_return_if_fail (closest != NULL); + + p1 = GTS_POINT (s->v1); + p2 = GTS_POINT (s->v2); + ns2 = gts_point_distance2 (p1, p2); + + if (ns2 == 0.0) { + gts_point_set (closest, p1->x, p1->y, p1->z); + return; + } + + t = ((p2->x - p1->x)*(p->x - p1->x) + + (p2->y - p1->y)*(p->y - p1->y) + + (p2->z - p1->z)*(p->z - p1->z))/ns2; + + if (t > 1.0) + gts_point_set (closest, p2->x, p2->y, p2->z); + else if (t < 0.0) + gts_point_set (closest, p1->x, p1->y, p1->z); + else + gts_point_set (closest, + (1. - t)*p1->x + t*p2->x, + (1. - t)*p1->y + t*p2->y, + (1. - t)*p1->z + t*p2->z); +} + +/** + * gts_point_triangle_distance2: + * @p: a #GtsPoint. + * @t: a #GtsTriangle. + * + * Returns: the square of the minimun Euclidean distance between @p and @t. + */ +gdouble gts_point_triangle_distance2 (GtsPoint * p, GtsTriangle * t) +{ + GtsPoint * p1, * p2, * p3; + GtsEdge * e1, * e2, * e3; + GtsVector p1p2, p1p3, pp1; + gdouble A, B, C, D, E, det; + gdouble t1, t2; + gdouble x, y, z; + + g_return_val_if_fail (p != NULL, 0.0); + g_return_val_if_fail (t != NULL, 0.0); + + gts_triangle_vertices_edges (t, NULL, + (GtsVertex **) &p1, + (GtsVertex **) &p2, + (GtsVertex **) &p3, + &e1, &e2, &e3); + + gts_vector_init (p1p2, p1, p2); + gts_vector_init (p1p3, p1, p3); + gts_vector_init (pp1, p, p1); + + B = gts_vector_scalar (p1p3, p1p2); + E = gts_vector_scalar (p1p2, p1p2); + C = gts_vector_scalar (p1p3, p1p3); + + det = B*B - E*C; + if (det == 0.) { /* p1p2 and p1p3 are colinear */ + gdouble d1 = gts_point_segment_distance2 (p, GTS_SEGMENT (e1)); + gdouble d2 = gts_point_segment_distance2 (p, GTS_SEGMENT (e3)); + if (d1 < d2) + return d1; + return d2; + } + + A = gts_vector_scalar (p1p3, pp1); + D = gts_vector_scalar (p1p2, pp1); + + t1 = (D*C - A*B)/det; + t2 = (A*E - D*B)/det; + + if (t1 < 0.) + return gts_point_segment_distance2 (p, GTS_SEGMENT (e3)); + if (t2 < 0.) + return gts_point_segment_distance2 (p, GTS_SEGMENT (e1)); + if (t1 + t2 > 1.) + return gts_point_segment_distance2 (p, GTS_SEGMENT (e2)); + + x = pp1[0] + t1*p1p2[0] + t2*p1p3[0]; + y = pp1[1] + t1*p1p2[1] + t2*p1p3[1]; + z = pp1[2] + t1*p1p2[2] + t2*p1p3[2]; + + return x*x + y*y + z*z; +} + +/** + * gts_point_triangle_distance: + * @p: a #GtsPoint. + * @t: a #GtsTriangle. + * + * Returns: the minimun Euclidean distance between @p and @t. + */ +gdouble gts_point_triangle_distance (GtsPoint * p, GtsTriangle * t) +{ + g_return_val_if_fail (p != NULL, 0.0); + g_return_val_if_fail (t != NULL, 0.0); + + return sqrt (gts_point_triangle_distance2 (p, t)); +} + +/** + * gts_point_triangle_closest: + * @p: a #GtsPoint. + * @t: a #GtsTriangle. + * @closest: a #GtsPoint. + * + * Set the coordinates of @closest to those of the point belonging to @t and + * closest to @p. + */ +void gts_point_triangle_closest (GtsPoint * p, + GtsTriangle * t, + GtsPoint * closest) +{ + GtsPoint * p1, * p2, * p3; + GtsEdge * e1, * e2, * e3; + GtsVector p1p2, p1p3, pp1; + gdouble A, B, C, D, E, det; + gdouble t1, t2; + + g_return_if_fail (p != NULL); + g_return_if_fail (t != NULL); + g_return_if_fail (closest != NULL); + + gts_triangle_vertices_edges (t, NULL, + (GtsVertex **) &p1, + (GtsVertex **) &p2, + (GtsVertex **) &p3, + &e1, &e2, &e3); + + gts_vector_init (p1p2, p1, p2); + gts_vector_init (p1p3, p1, p3); + gts_vector_init (pp1, p, p1); + + B = gts_vector_scalar (p1p3, p1p2); + E = gts_vector_scalar (p1p2, p1p2); + C = gts_vector_scalar (p1p3, p1p3); + + det = B*B - E*C; + if (det == 0.) { /* p1p2 and p1p3 are colinear */ + GtsPoint * cp = + GTS_POINT (gts_object_new (GTS_OBJECT_CLASS (gts_point_class ()))); + gts_point_segment_closest (p, GTS_SEGMENT (e1), cp); + gts_point_segment_closest (p, GTS_SEGMENT (e3), closest); + + if (gts_point_distance2 (cp, p) < gts_point_distance2 (closest, p)) + gts_point_set (closest, cp->x, cp->y, cp->z); + gts_object_destroy (GTS_OBJECT (cp)); + return; + } + + A = gts_vector_scalar (p1p3, pp1); + D = gts_vector_scalar (p1p2, pp1); + + t1 = (D*C - A*B)/det; + t2 = (A*E - D*B)/det; + + if (t1 < 0.) + gts_point_segment_closest (p, GTS_SEGMENT (e3), closest); + else if (t2 < 0.) + gts_point_segment_closest (p, GTS_SEGMENT (e1), closest); + else if (t1 + t2 > 1.) + gts_point_segment_closest (p, GTS_SEGMENT (e2), closest); + else + gts_point_set (closest, + p1->x + t1*p1p2[0] + t2*p1p3[0], + p1->y + t1*p1p2[1] + t2*p1p3[1], + p1->z + t1*p1p2[2] + t2*p1p3[2]); +} + +/** + * gts_segment_triangle_intersection: + * @s: a #GtsSegment. + * @t: a #GtsTriangle. + * @boundary: if %TRUE, the boundary of @t is taken into account. + * @klass: a #GtsPointClass to be used for the new point. + * + * Checks if @s intersects @t. If this is the case, creates a new + * point pi intersection of @s with @t. + * + * This function is geometrically robust in the sense that it will not + * return a point if @s and @t do not intersect and will return a + * point if @s and @t do intersect. However, the point coordinates are + * subject to round-off errors. + * + * Note that this function will not return any point if @s is contained in + * the plane defined by @t. + * + * Returns: a summit of @t (if @boundary is set to %TRUE), one of the endpoints + * of @s or a new #GtsPoint, intersection of @s with @t or %NULL if @s + * and @t don't intersect. + */ +GtsPoint * gts_segment_triangle_intersection (GtsSegment * s, + GtsTriangle * t, + gboolean boundary, + GtsPointClass * klass) +{ + GtsPoint * A, * B, * C, * D, * E, * I; + gdouble ABCE, ABCD, ADCE, ABDE, BCDE; + gdouble c; + + g_return_val_if_fail (s != NULL, NULL); + g_return_val_if_fail (t != NULL, NULL); + g_return_val_if_fail (klass != NULL, NULL); + + A = GTS_POINT (GTS_SEGMENT (t->e1)->v1); + B = GTS_POINT (GTS_SEGMENT (t->e1)->v2); + C = GTS_POINT (gts_triangle_vertex (t)); + D = GTS_POINT (s->v1); + E = GTS_POINT (s->v2); + + ABCE = gts_point_orientation_3d (A, B, C, E); + ABCD = gts_point_orientation_3d (A, B, C, D); + if (ABCE < 0.0 || ABCD > 0.0) { + GtsPoint * tmpp; + gdouble tmp; + tmpp = E; E = D; D = tmpp; + tmp = ABCE; ABCE = ABCD; ABCD = tmp; + } + if (ABCE < 0.0 || ABCD > 0.0) + return NULL; + ADCE = gts_point_orientation_3d (A, D, C, E); + if ((boundary && ADCE < 0.) || (!boundary && ADCE <= 0.)) + return NULL; + ABDE = gts_point_orientation_3d (A, B, D, E); + if ((boundary && ABDE < 0.) || (!boundary && ABDE <= 0.)) + return NULL; + BCDE = gts_point_orientation_3d (B, C, D, E); + if ((boundary && BCDE < 0.) || (!boundary && BCDE <= 0.)) + return NULL; + if (ABCE == 0.0) { + if (ABCD == 0.0) + /* s is contained in the plane defined by t*/ + return NULL; + return E; + } + if (ABCD == 0.0) + return D; + if (boundary) { /* corners of @t */ + if (ABDE == 0.) { + if (ADCE == 0.) + return A; + if (BCDE == 0.) + return B; + } + else if (BCDE == 0. && ADCE == 0.) + return C; + } + c = ABCE/(ABCE - ABCD); + I = GTS_POINT (gts_object_new (GTS_OBJECT_CLASS (klass))); + gts_point_set (I, + E->x + c*(D->x - E->x), + E->y + c*(D->y - E->y), + E->z + c*(D->z - E->z)); + return I; +} + +/** + * gts_point_transform: + * @p: a #GtsPoint. + * @m: the #GtsMatrix representing the transformation to + * apply to the coordinates of @p. + * + * Transform the coordinates of @p according to @m. (p[] becomes m[][].p[]). + */ +void gts_point_transform (GtsPoint * p, GtsMatrix * m) +{ + gdouble x, y, z; + g_return_if_fail (p != NULL && m != NULL); + x = m[0][0]*p->x + m[0][1]*p->y + m[0][2]*p->z + m[0][3]; + y = m[1][0]*p->x + m[1][1]*p->y + m[1][2]*p->z + m[1][3]; + z = m[2][0]*p->x + m[2][1]*p->y + m[2][2]*p->z + m[2][3]; + p->x = x; p->y = y; p->z = z; +} + +/** + * gts_point_orientation: + * @p1: a #GtsPoint. + * @p2: a #GtsPoint. + * @p3: a #GtsPoint. + * + * Checks for orientation of the projection of three points on the + * (x,y) plane. The result is also an approximation of twice the + * signed area of the triangle defined by the three points. This + * function uses adaptive floating point arithmetic and is + * consequently geometrically robust. + * + * Returns: a positive value if @p1, @p2 and @p3 appear in + * counterclockwise order, a negative value if they appear in + * clockwise order and zero if they are colinear. + */ +gdouble gts_point_orientation (GtsPoint * p1, GtsPoint * p2, GtsPoint * p3) +{ + g_return_val_if_fail (p1 != NULL && p2 != NULL && p3 != NULL, 0.0); + + return orient2d ((gdouble *) &p1->x, + (gdouble *) &p2->x, + (gdouble *) &p3->x); +} + +static gboolean ray_intersects_triangle (GtsPoint * D, GtsPoint * E, + GtsTriangle * t) +{ + GtsPoint * A, * B, * C; + gint ABCE, ABCD, ADCE, ABDE, BCDE; + + gts_triangle_vertices (t, (GtsVertex **) &A, + (GtsVertex **) &B, + (GtsVertex **) &C); + + ABCE = gts_point_orientation_3d_sos (A, B, C, E); + ABCD = gts_point_orientation_3d_sos (A, B, C, D); + if (ABCE < 0 || ABCD > 0) { + GtsPoint * tmpp; + gint tmp; + + tmpp = E; E = D; D = tmpp; + tmp = ABCE; ABCE = ABCD; ABCD = tmp; + } + if (ABCE < 0 || ABCD > 0) + return FALSE; + ADCE = gts_point_orientation_3d_sos (A, D, C, E); + if (ADCE < 0) + return FALSE; + ABDE = gts_point_orientation_3d_sos (A, B, D, E); + if (ABDE < 0) + return FALSE; + BCDE = gts_point_orientation_3d_sos (B, C, D, E); + if (BCDE < 0) + return FALSE; + return TRUE; +} + +/** + * gts_point_is_inside_surface: + * @p: a #GtsPoint. + * @tree: a bounding box tree of the faces of a closed, orientable + * surface (see gts_bb_tree_surface()). + * @is_open: %TRUE if the surface defined by @tree is "open" i.e. its volume + * is negative, %FALSE otherwise. + * + * Returns: %TRUE if @p is inside the surface defined by @tree, %FALSE + * otherwise. + */ +gboolean gts_point_is_inside_surface (GtsPoint * p, + GNode * tree, + gboolean is_open) +{ + GSList * list, * i; + guint nc = 0; + GtsPoint * p1; + GtsBBox * bb; + + g_return_val_if_fail (p != NULL, FALSE); + g_return_val_if_fail (tree != NULL, FALSE); + + bb = tree->data; + p1 = gts_point_new (gts_point_class (), bb->x2 + fabs (bb->x2)/10., p->y, p->z); + i = list = gts_bb_tree_stabbed (tree, p); + while (i) { + GtsTriangle * t = GTS_TRIANGLE (GTS_BBOX (i->data)->bounded); + + if (ray_intersects_triangle (p, p1, t)) + nc++; + i = i->next; + } + g_slist_free (list); + gts_object_destroy (GTS_OBJECT (p1)); + + return is_open ? (nc % 2 == 0) : (nc % 2 != 0); +} + +#define SIGN(x) ((x) > 0. ? 1 : -1) +#define ORIENT1D(a,b) ((a) > (b) ? 1 : (a) < (b) ? -1 : 0) + +static gint sortp (gpointer * p, guint n) +{ + gint sign = 1; + guint i, j; + + for (i = 0; i < n - 1; i++) + for (j = 0; j < n - 1 - i; j++) + if (GPOINTER_TO_UINT (p[j+1]) < GPOINTER_TO_UINT (p[j])) { + gpointer tmp = p[j]; + + p[j] = p[j+1]; + p[j+1] = tmp; + sign = - sign; + } + return sign; +} + +/** + * gts_point_orientation_3d_sos: + * @p1: a #GtsPoint. + * @p2: a #GtsPoint. + * @p3: a #GtsPoint. + * @p4: a #GtsPoint. + * + * Checks if @p4 lies above or below the plane passing through the + * points @p1, @p2 and @p3. Below is defined so that @p1, @p2 and @p3 + * appear in counterclockwise order when viewed from above the + * plane. This function uses adaptive floating point arithmetic and is + * consequently geometrically robust. + * + * Simulation of Simplicity (SoS) is used to break ties when the + * orientation is degenerate (i.e. @p4 lies on the plane defined by + * @p1, @p2 and @p3). + * + * Returns: +1 if @p4 lies below, -1 if @p4 lies above the plane. + */ +gint gts_point_orientation_3d_sos (GtsPoint * p1, + GtsPoint * p2, + GtsPoint * p3, + GtsPoint * p4) +{ + gdouble o; + + g_return_val_if_fail (p1 != NULL && p2 != NULL && + p3 != NULL && p4 != NULL, 0); + + o = orient3d ((gdouble *) &p1->x, + (gdouble *) &p2->x, + (gdouble *) &p3->x, + (gdouble *) &p4->x); + if (o != 0.) + return SIGN (o); + else { + GtsPoint * p[4]; + gdouble a[2], b[2], c[2]; + gint sign; + + p[0] = p1; p[1] = p2; p[2] = p3; p[3] = p4; + sign = sortp ((gpointer *) p, 4); + + /* epsilon^1/8 */ + a[0] = p[1]->x; a[1] = p[1]->y; + b[0] = p[2]->x; b[1] = p[2]->y; + c[0] = p[3]->x; c[1] = p[3]->y; + o = orient2d (a, b, c); + if (o != 0.) + return SIGN (o)*sign; + + /* epsilon^1/4 */ + a[0] = p[1]->x; a[1] = p[1]->z; + b[0] = p[2]->x; b[1] = p[2]->z; + c[0] = p[3]->x; c[1] = p[3]->z; + o = orient2d (a, b, c); + if (o != 0.) + return - SIGN (o)*sign; + + /* epsilon^1/2 */ + a[0] = p[1]->y; a[1] = p[1]->z; + b[0] = p[2]->y; b[1] = p[2]->z; + c[0] = p[3]->y; c[1] = p[3]->z; + o = orient2d (a, b, c); + if (o != 0.) + return SIGN (o)*sign; + + /* epsilon */ + a[0] = p[0]->x; a[1] = p[0]->y; + b[0] = p[2]->x; b[1] = p[2]->y; + c[0] = p[3]->x; c[1] = p[3]->y; + o = orient2d (a, b, c); + if (o != 0.) + return - SIGN (o)*sign; + + /* epsilon^5/4 */ + o = ORIENT1D (p[2]->x, p[3]->x); + if (o != 0.) + return SIGN (o)*sign; + + /* epsilon^3/2 */ + o = ORIENT1D (p[2]->y, p[3]->y); + if (o != 0.) + return - SIGN (o)*sign; + + /* epsilon^2 */ + a[0] = p[0]->x; a[1] = p[0]->z; + b[0] = p[2]->x; b[1] = p[2]->z; + c[0] = p[3]->x; c[1] = p[3]->z; + o = orient2d (a, b, c); + if (o != 0.) + return SIGN (o)*sign; + + /* epsilon^5/2 */ + o = ORIENT1D (p[2]->z, p[3]->z); + if (o != 0.) + return SIGN (o)*sign; + + /* epsilon^4 */ + a[0] = p[0]->y; a[1] = p[0]->z; + b[0] = p[2]->y; b[1] = p[2]->z; + c[0] = p[3]->y; c[1] = p[3]->z; + o = orient2d (a, b, c); + if (o != 0.) + return - SIGN (o)*sign; + + /* epsilon^8 */ + a[0] = p[0]->x; a[1] = p[0]->y; + b[0] = p[1]->x; b[1] = p[1]->y; + c[0] = p[3]->x; c[1] = p[3]->y; + o = orient2d (a, b, c); + if (o != 0.) + return SIGN (o)*sign; + + /* epsilon^33/4 */ + o = ORIENT1D (p[1]->x, p[3]->x); + if (o != 0.) + return - SIGN (o)*sign; + + /* epsilon^17/2 */ + o = ORIENT1D (p[1]->y, p[3]->y); + if (o != 0.) + return SIGN (o)*sign; + + /* epsilon^10 */ + o = ORIENT1D (p[0]->x, p[3]->x); + if (o != 0.) + return SIGN (o)*sign; + + /* epsilon^21/2 */ + return sign; + } +} + +/** + * gts_point_orientation_sos: + * @p1: a #GtsPoint. + * @p2: a #GtsPoint. + * @p3: a #GtsPoint. + * + * Checks for orientation of the projection of three points on the + * (x,y) plane. + * + * Simulation of Simplicity (SoS) is used to break ties when the + * orientation is degenerate (i.e. @p3 lies on the line defined by + * @p1 and @p2). + * + * Returns: a positive value if @p1, @p2 and @p3 appear in + * counterclockwise order or a negative value if they appear in + * clockwise order. + */ +gint gts_point_orientation_sos (GtsPoint * p1, + GtsPoint * p2, + GtsPoint * p3) +{ + gdouble o; + + g_return_val_if_fail (p1 != NULL && p2 != NULL && p3 != NULL, 0); + + o = orient2d ((gdouble *) &p1->x, + (gdouble *) &p2->x, + (gdouble *) &p3->x); + if (o != 0.) + return SIGN (o); + else { + GtsPoint * p[3]; + gint sign; + + p[0] = p1; p[1] = p2; p[2] = p3; + sign = sortp ((gpointer *) p, 3); + + /* epsilon^1/4 */ + o = ORIENT1D (p[1]->x, p[2]->x); + if (o != 0.) + return - SIGN (o)*sign; + + /* epsilon^1/2 */ + o = ORIENT1D (p[1]->y, p[2]->y); + if (o != 0.) + return SIGN (o)*sign; + + /* epsilon */ + o = ORIENT1D (p[0]->x, p[2]->x); + if (o != 0.) + return SIGN (o)*sign; + + /* epsilon^3/2 */ + return sign; + } +} Index: trunk/src_3rd/gts/predicates.c =================================================================== --- trunk/src_3rd/gts/predicates.c (nonexistent) +++ trunk/src_3rd/gts/predicates.c (revision 1065) @@ -0,0 +1,2742 @@ +/*****************************************************************************/ +/* */ +/* Routines for Arbitrary Precision Floating-point Arithmetic */ +/* and Fast Robust Geometric Predicates */ +/* (predicates.c) */ +/* */ +/* May 18, 1996 */ +/* */ +/* Placed in the public domain by */ +/* Jonathan Richard Shewchuk */ +/* School of Computer Science */ +/* Carnegie Mellon University */ +/* 5000 Forbes Avenue */ +/* Pittsburgh, Pennsylvania 15213-3891 */ +/* jrs@cs.cmu.edu */ +/* */ +/* This file contains C implementation of algorithms for exact addition */ +/* and multiplication of floating-point numbers, and predicates for */ +/* robustly performing the orientation and incircle tests used in */ +/* computational geometry. The algorithms and underlying theory are */ +/* described in Jonathan Richard Shewchuk. "Adaptive Precision Floating- */ +/* Point Arithmetic and Fast Robust Geometric Predicates." Technical */ +/* Report CMU-CS-96-140, School of Computer Science, Carnegie Mellon */ +/* University, Pittsburgh, Pennsylvania, May 1996. (Submitted to */ +/* Discrete & Computational Geometry.) */ +/* */ +/* This file, the paper listed above, and other information are available */ +/* from the Web page http://www.cs.cmu.edu/~quake/robust.html . */ +/* */ +/*****************************************************************************/ + +/*****************************************************************************/ +/* */ +/* Using this code: */ +/* */ +/* First, read the short or long version of the paper (from the Web page */ +/* above). */ +/* */ +/* Be sure to call exactinit() once, before calling any of the arithmetic */ +/* functions or geometric predicates. Also be sure to turn on the */ +/* optimizer when compiling this file. */ +/* */ +/* */ +/* Several geometric predicates are defined. Their parameters are all */ +/* points. Each point is an array of two or three floating-point */ +/* numbers. The geometric predicates, described in the papers, are */ +/* */ +/* orient2d(pa, pb, pc) */ +/* orient2dfast(pa, pb, pc) */ +/* orient3d(pa, pb, pc, pd) */ +/* orient3dfast(pa, pb, pc, pd) */ +/* incircle(pa, pb, pc, pd) */ +/* incirclefast(pa, pb, pc, pd) */ +/* insphere(pa, pb, pc, pd, pe) */ +/* inspherefast(pa, pb, pc, pd, pe) */ +/* */ +/* Those with suffix "fast" are approximate, non-robust versions. Those */ +/* without the suffix are adaptive precision, robust versions. There */ +/* are also versions with the suffices "exact" and "slow", which are */ +/* non-adaptive, exact arithmetic versions, which I use only for timings */ +/* in my arithmetic papers. */ +/* */ +/* */ +/* An expansion is represented by an array of floating-point numbers, */ +/* sorted from smallest to largest magnitude (possibly with interspersed */ +/* zeros). The length of each expansion is stored as a separate integer, */ +/* and each arithmetic function returns an integer which is the length */ +/* of the expansion it created. */ +/* */ +/* Several arithmetic functions are defined. Their parameters are */ +/* */ +/* e, f Input expansions */ +/* elen, flen Lengths of input expansions (must be >= 1) */ +/* h Output expansion */ +/* b Input scalar */ +/* */ +/* The arithmetic functions are */ +/* */ +/* grow_expansion(elen, e, b, h) */ +/* grow_expansion_zeroelim(elen, e, b, h) */ +/* expansion_sum(elen, e, flen, f, h) */ +/* expansion_sum_zeroelim1(elen, e, flen, f, h) */ +/* expansion_sum_zeroelim2(elen, e, flen, f, h) */ +/* fast_expansion_sum(elen, e, flen, f, h) */ +/* fast_expansion_sum_zeroelim(elen, e, flen, f, h) */ +/* linear_expansion_sum(elen, e, flen, f, h) */ +/* linear_expansion_sum_zeroelim(elen, e, flen, f, h) */ +/* scale_expansion(elen, e, b, h) */ +/* scale_expansion_zeroelim(elen, e, b, h) */ +/* compress(elen, e, h) */ +/* */ +/* All of these are described in the long version of the paper; some are */ +/* described in the short version. All return an integer that is the */ +/* length of h. Those with suffix _zeroelim perform zero elimination, */ +/* and are recommended over their counterparts. The procedure */ +/* fast_expansion_sum_zeroelim() (or linear_expansion_sum_zeroelim() on */ +/* processors that do not use the round-to-even tiebreaking rule) is */ +/* recommended over expansion_sum_zeroelim(). Each procedure has a */ +/* little note next to it (in the code below) that tells you whether or */ +/* not the output expansion may be the same array as one of the input */ +/* expansions. */ +/* */ +/* */ +/* If you look around below, you'll also find macros for a bunch of */ +/* simple unrolled arithmetic operations, and procedures for printing */ +/* expansions (commented out because they don't work with all C */ +/* compilers) and for generating random floating-point numbers whose */ +/* significand bits are all random. Most of the macros have undocumented */ +/* requirements that certain of their parameters should not be the same */ +/* variable; for safety, better to make sure all the parameters are */ +/* distinct variables. Feel free to send email to jrs@cs.cmu.edu if you */ +/* have questions. */ +/* */ +/*****************************************************************************/ + +#include +#include +#include +#include "predicates.h" + +/* Use header file generated automatically by predicates_init. */ +//#define USE_PREDICATES_INIT + +#ifdef USE_PREDICATES_INIT +#include "predicates_init.h" +#endif /* USE_PREDICATES_INIT */ + +/* FPU control. We MUST have only double precision (not extended precision) */ +#include "rounding.h" + +/* On some machines, the exact arithmetic routines might be defeated by the */ +/* use of internal extended precision floating-point registers. Sometimes */ +/* this problem can be fixed by defining certain values to be volatile, */ +/* thus forcing them to be stored to memory and rounded off. This isn't */ +/* a great solution, though, as it slows the arithmetic down. */ +/* */ +/* To try this out, write "#define INEXACT volatile" below. Normally, */ +/* however, INEXACT should be defined to be nothing. ("#define INEXACT".) */ + +#define INEXACT /* Nothing */ +/* #define INEXACT volatile */ + +#define REAL double /* float or double */ +#define REALPRINT doubleprint +#define REALRAND doublerand +#define NARROWRAND narrowdoublerand +#define UNIFORMRAND uniformdoublerand + +/* Which of the following two methods of finding the absolute values is */ +/* fastest is compiler-dependent. A few compilers can inline and optimize */ +/* the fabs() call; but most will incur the overhead of a function call, */ +/* which is disastrously slow. A faster way on IEEE machines might be to */ +/* mask the appropriate bit, but that's difficult to do in C. */ + +#define Absolute(a) ((a) >= 0.0 ? (a) : -(a)) +/* #define Absolute(a) fabs(a) */ + +/* Many of the operations are broken up into two pieces, a main part that */ +/* performs an approximate operation, and a "tail" that computes the */ +/* roundoff error of that operation. */ +/* */ +/* The operations Fast_Two_Sum(), Fast_Two_Diff(), Two_Sum(), Two_Diff(), */ +/* Split(), and Two_Product() are all implemented as described in the */ +/* reference. Each of these macros requires certain variables to be */ +/* defined in the calling routine. The variables `bvirt', `c', `abig', */ +/* `_i', `_j', `_k', `_l', `_m', and `_n' are declared `INEXACT' because */ +/* they store the result of an operation that may incur roundoff error. */ +/* The input parameter `x' (or the highest numbered `x_' parameter) must */ +/* also be declared `INEXACT'. */ + +#define Fast_Two_Sum_Tail(a, b, x, y) \ + bvirt = x - a; \ + y = b - bvirt + +#define Fast_Two_Sum(a, b, x, y) \ + x = (REAL) (a + b); \ + Fast_Two_Sum_Tail(a, b, x, y) + +#define Fast_Two_Diff_Tail(a, b, x, y) \ + bvirt = a - x; \ + y = bvirt - b + +#define Fast_Two_Diff(a, b, x, y) \ + x = (REAL) (a - b); \ + Fast_Two_Diff_Tail(a, b, x, y) + +#define Two_Sum_Tail(a, b, x, y) \ + bvirt = (REAL) (x - a); \ + avirt = x - bvirt; \ + bround = b - bvirt; \ + around = a - avirt; \ + y = around + bround + +#define Two_Sum(a, b, x, y) \ + x = (REAL) (a + b); \ + Two_Sum_Tail(a, b, x, y) + +#define Two_Diff_Tail(a, b, x, y) \ + bvirt = (REAL) (a - x); \ + avirt = x + bvirt; \ + bround = bvirt - b; \ + around = a - avirt; \ + y = around + bround + +#define Two_Diff(a, b, x, y) \ + x = (REAL) (a - b); \ + Two_Diff_Tail(a, b, x, y) + +#define Split(a, ahi, alo) \ + c = (REAL) (splitter * a); \ + abig = (REAL) (c - a); \ + ahi = c - abig; \ + alo = a - ahi + +#define Two_Product_Tail(a, b, x, y) \ + Split(a, ahi, alo); \ + Split(b, bhi, blo); \ + err1 = x - (ahi * bhi); \ + err2 = err1 - (alo * bhi); \ + err3 = err2 - (ahi * blo); \ + y = (alo * blo) - err3 + +#define Two_Product(a, b, x, y) \ + x = (REAL) (a * b); \ + Two_Product_Tail(a, b, x, y) + +/* Two_Product_Presplit() is Two_Product() where one of the inputs has */ +/* already been split. Avoids redundant splitting. */ + +#define Two_Product_Presplit(a, b, bhi, blo, x, y) \ + x = (REAL) (a * b); \ + Split(a, ahi, alo); \ + err1 = x - (ahi * bhi); \ + err2 = err1 - (alo * bhi); \ + err3 = err2 - (ahi * blo); \ + y = (alo * blo) - err3 + +/* Two_Product_2Presplit() is Two_Product() where both of the inputs have */ +/* already been split. Avoids redundant splitting. */ + +#define Two_Product_2Presplit(a, ahi, alo, b, bhi, blo, x, y) \ + x = (REAL) (a * b); \ + err1 = x - (ahi * bhi); \ + err2 = err1 - (alo * bhi); \ + err3 = err2 - (ahi * blo); \ + y = (alo * blo) - err3 + +/* Square() can be done more quickly than Two_Product(). */ + +#define Square_Tail(a, x, y) \ + Split(a, ahi, alo); \ + err1 = x - (ahi * ahi); \ + err3 = err1 - ((ahi + ahi) * alo); \ + y = (alo * alo) - err3 + +#define Square(a, x, y) \ + x = (REAL) (a * a); \ + Square_Tail(a, x, y) + +/* Macros for summing expansions of various fixed lengths. These are all */ +/* unrolled versions of Expansion_Sum(). */ + +#define Two_One_Sum(a1, a0, b, x2, x1, x0) \ + Two_Sum(a0, b , _i, x0); \ + Two_Sum(a1, _i, x2, x1) + +#define Two_One_Diff(a1, a0, b, x2, x1, x0) \ + Two_Diff(a0, b , _i, x0); \ + Two_Sum( a1, _i, x2, x1) + +#define Two_Two_Sum(a1, a0, b1, b0, x3, x2, x1, x0) \ + Two_One_Sum(a1, a0, b0, _j, _0, x0); \ + Two_One_Sum(_j, _0, b1, x3, x2, x1) + +#define Two_Two_Diff(a1, a0, b1, b0, x3, x2, x1, x0) \ + Two_One_Diff(a1, a0, b0, _j, _0, x0); \ + Two_One_Diff(_j, _0, b1, x3, x2, x1) + +#define Four_One_Sum(a3, a2, a1, a0, b, x4, x3, x2, x1, x0) \ + Two_One_Sum(a1, a0, b , _j, x1, x0); \ + Two_One_Sum(a3, a2, _j, x4, x3, x2) + +#define Four_Two_Sum(a3, a2, a1, a0, b1, b0, x5, x4, x3, x2, x1, x0) \ + Four_One_Sum(a3, a2, a1, a0, b0, _k, _2, _1, _0, x0); \ + Four_One_Sum(_k, _2, _1, _0, b1, x5, x4, x3, x2, x1) + +#define Four_Four_Sum(a3, a2, a1, a0, b4, b3, b1, b0, x7, x6, x5, x4, x3, x2, \ + x1, x0) \ + Four_Two_Sum(a3, a2, a1, a0, b1, b0, _l, _2, _1, _0, x1, x0); \ + Four_Two_Sum(_l, _2, _1, _0, b4, b3, x7, x6, x5, x4, x3, x2) + +#define Eight_One_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b, x8, x7, x6, x5, x4, \ + x3, x2, x1, x0) \ + Four_One_Sum(a3, a2, a1, a0, b , _j, x3, x2, x1, x0); \ + Four_One_Sum(a7, a6, a5, a4, _j, x8, x7, x6, x5, x4) + +#define Eight_Two_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b1, b0, x9, x8, x7, \ + x6, x5, x4, x3, x2, x1, x0) \ + Eight_One_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b0, _k, _6, _5, _4, _3, _2, \ + _1, _0, x0); \ + Eight_One_Sum(_k, _6, _5, _4, _3, _2, _1, _0, b1, x9, x8, x7, x6, x5, x4, \ + x3, x2, x1) + +#define Eight_Four_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b4, b3, b1, b0, x11, \ + x10, x9, x8, x7, x6, x5, x4, x3, x2, x1, x0) \ + Eight_Two_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b1, b0, _l, _6, _5, _4, _3, \ + _2, _1, _0, x1, x0); \ + Eight_Two_Sum(_l, _6, _5, _4, _3, _2, _1, _0, b4, b3, x11, x10, x9, x8, \ + x7, x6, x5, x4, x3, x2) + +/* Macros for multiplying expansions of various fixed lengths. */ + +#define Two_One_Product(a1, a0, b, x3, x2, x1, x0) \ + Split(b, bhi, blo); \ + Two_Product_Presplit(a0, b, bhi, blo, _i, x0); \ + Two_Product_Presplit(a1, b, bhi, blo, _j, _0); \ + Two_Sum(_i, _0, _k, x1); \ + Fast_Two_Sum(_j, _k, x3, x2) + +#define Four_One_Product(a3, a2, a1, a0, b, x7, x6, x5, x4, x3, x2, x1, x0) \ + Split(b, bhi, blo); \ + Two_Product_Presplit(a0, b, bhi, blo, _i, x0); \ + Two_Product_Presplit(a1, b, bhi, blo, _j, _0); \ + Two_Sum(_i, _0, _k, x1); \ + Fast_Two_Sum(_j, _k, _i, x2); \ + Two_Product_Presplit(a2, b, bhi, blo, _j, _0); \ + Two_Sum(_i, _0, _k, x3); \ + Fast_Two_Sum(_j, _k, _i, x4); \ + Two_Product_Presplit(a3, b, bhi, blo, _j, _0); \ + Two_Sum(_i, _0, _k, x5); \ + Fast_Two_Sum(_j, _k, x7, x6) + +#define Two_Two_Product(a1, a0, b1, b0, x7, x6, x5, x4, x3, x2, x1, x0) \ + Split(a0, a0hi, a0lo); \ + Split(b0, bhi, blo); \ + Two_Product_2Presplit(a0, a0hi, a0lo, b0, bhi, blo, _i, x0); \ + Split(a1, a1hi, a1lo); \ + Two_Product_2Presplit(a1, a1hi, a1lo, b0, bhi, blo, _j, _0); \ + Two_Sum(_i, _0, _k, _1); \ + Fast_Two_Sum(_j, _k, _l, _2); \ + Split(b1, bhi, blo); \ + Two_Product_2Presplit(a0, a0hi, a0lo, b1, bhi, blo, _i, _0); \ + Two_Sum(_1, _0, _k, x1); \ + Two_Sum(_2, _k, _j, _1); \ + Two_Sum(_l, _j, _m, _2); \ + Two_Product_2Presplit(a1, a1hi, a1lo, b1, bhi, blo, _j, _0); \ + Two_Sum(_i, _0, _n, _0); \ + Two_Sum(_1, _0, _i, x2); \ + Two_Sum(_2, _i, _k, _1); \ + Two_Sum(_m, _k, _l, _2); \ + Two_Sum(_j, _n, _k, _0); \ + Two_Sum(_1, _0, _j, x3); \ + Two_Sum(_2, _j, _i, _1); \ + Two_Sum(_l, _i, _m, _2); \ + Two_Sum(_1, _k, _i, x4); \ + Two_Sum(_2, _i, _k, x5); \ + Two_Sum(_m, _k, x7, x6) + +/* An expansion of length two can be squared more quickly than finding the */ +/* product of two different expansions of length two, and the result is */ +/* guaranteed to have no more than six (rather than eight) components. */ + +#define Two_Square(a1, a0, x5, x4, x3, x2, x1, x0) \ + Square(a0, _j, x0); \ + _0 = a0 + a0; \ + Two_Product(a1, _0, _k, _1); \ + Two_One_Sum(_k, _1, _j, _l, _2, x1); \ + Square(a1, _j, _1); \ + Two_Two_Sum(_j, _1, _l, _2, x5, x4, x3, x2) + +#ifndef USE_PREDICATES_INIT + +static REAL splitter; /* = 2^ceiling(p / 2) + 1. Used to split floats in half. */ +/* A set of coefficients used to calculate maximum roundoff errors. */ +static REAL resulterrbound; +static REAL ccwerrboundA, ccwerrboundB, ccwerrboundC; +static REAL o3derrboundA, o3derrboundB, o3derrboundC; +static REAL iccerrboundA, iccerrboundB, iccerrboundC; +static REAL isperrboundA, isperrboundB, isperrboundC; + +void +gts_predicates_init() +{ + double half = 0.5; + double check = 1.0, lastcheck; + int every_other = 1; + /* epsilon = 2^(-p). Used to estimate roundoff errors. */ + double epsilon = 1.0; + + FPU_ROUND_DOUBLE; + + splitter = 1.; + + /* Repeatedly divide `epsilon' by two until it is too small to add to */ + /* one without causing roundoff. (Also check if the sum is equal to */ + /* the previous sum, for machines that round up instead of using exact */ + /* rounding. Not that this library will work on such machines anyway). */ + do { + lastcheck = check; + epsilon *= half; + if (every_other) { + splitter *= 2.0; + } + every_other = !every_other; + check = 1.0 + epsilon; + } while ((check != 1.0) && (check != lastcheck)); + splitter += 1.0; + /* Error bounds for orientation and incircle tests. */ + resulterrbound = (3.0 + 8.0 * epsilon) * epsilon; + ccwerrboundA = (3.0 + 16.0 * epsilon) * epsilon; + ccwerrboundB = (2.0 + 12.0 * epsilon) * epsilon; + ccwerrboundC = (9.0 + 64.0 * epsilon) * epsilon * epsilon; + o3derrboundA = (7.0 + 56.0 * epsilon) * epsilon; + o3derrboundB = (3.0 + 28.0 * epsilon) * epsilon; + o3derrboundC = (26.0 + 288.0 * epsilon) * epsilon * epsilon; + iccerrboundA = (10.0 + 96.0 * epsilon) * epsilon; + iccerrboundB = (4.0 + 48.0 * epsilon) * epsilon; + iccerrboundC = (44.0 + 576.0 * epsilon) * epsilon * epsilon; + isperrboundA = (16.0 + 224.0 * epsilon) * epsilon; + isperrboundB = (5.0 + 72.0 * epsilon) * epsilon; + isperrboundC = (71.0 + 1408.0 * epsilon) * epsilon * epsilon; + + + FPU_RESTORE; +} + +#endif /* USE_PREDICATES_INIT */ + +/*****************************************************************************/ +/* */ +/* doubleprint() Print the bit representation of a double. */ +/* */ +/* Useful for debugging exact arithmetic routines. */ +/* */ +/*****************************************************************************/ + +/* +void doubleprint(number) +double number; +{ + unsigned long long no; + unsigned long long sign, expo; + int exponent; + int i, bottomi; + + no = *(unsigned long long *) &number; + sign = no & 0x8000000000000000ll; + expo = (no >> 52) & 0x7ffll; + exponent = (int) expo; + exponent = exponent - 1023; + if (sign) { + printf("-"); + } else { + printf(" "); + } + if (exponent == -1023) { + printf( + "0.0000000000000000000000000000000000000000000000000000_ ( )"); + } else { + printf("1."); + bottomi = -1; + for (i = 0; i < 52; i++) { + if (no & 0x0008000000000000ll) { + printf("1"); + bottomi = i; + } else { + printf("0"); + } + no <<= 1; + } + printf("_%d (%d)", exponent, exponent - 1 - bottomi); + } +} +*/ + +/*****************************************************************************/ +/* */ +/* floatprint() Print the bit representation of a float. */ +/* */ +/* Useful for debugging exact arithmetic routines. */ +/* */ +/*****************************************************************************/ + +/* +void floatprint(number) +float number; +{ + unsigned no; + unsigned sign, expo; + int exponent; + int i, bottomi; + + no = *(unsigned *) &number; + sign = no & 0x80000000; + expo = (no >> 23) & 0xff; + exponent = (int) expo; + exponent = exponent - 127; + if (sign) { + printf("-"); + } else { + printf(" "); + } + if (exponent == -127) { + printf("0.00000000000000000000000_ ( )"); + } else { + printf("1."); + bottomi = -1; + for (i = 0; i < 23; i++) { + if (no & 0x00400000) { + printf("1"); + bottomi = i; + } else { + printf("0"); + } + no <<= 1; + } + printf("_%3d (%3d)", exponent, exponent - 1 - bottomi); + } +} +*/ + +/*****************************************************************************/ +/* */ +/* expansion_print() Print the bit representation of an expansion. */ +/* */ +/* Useful for debugging exact arithmetic routines. */ +/* */ +/*****************************************************************************/ + +/* +void expansion_print(elen, e) +int elen; +REAL *e; +{ + int i; + + for (i = elen - 1; i >= 0; i--) { + REALPRINT(e[i]); + if (i > 0) { + printf(" +\n"); + } else { + printf("\n"); + } + } +} +*/ + +/*****************************************************************************/ +/* */ +/* doublerand() Generate a double with random 53-bit significand and a */ +/* random exponent in [0, 511]. */ +/* */ +/*****************************************************************************/ + +/* +static double doublerand() +{ + double result; + double expo; + long a, b, c; + long i; + + a = random(); + b = random(); + c = random(); + result = (double) (a - 1073741824) * 8388608.0 + (double) (b >> 8); + for (i = 512, expo = 2; i <= 131072; i *= 2, expo = expo * expo) { + if (c & i) { + result *= expo; + } + } + return result; +} +*/ + +/*****************************************************************************/ +/* */ +/* narrowdoublerand() Generate a double with random 53-bit significand */ +/* and a random exponent in [0, 7]. */ +/* */ +/*****************************************************************************/ + +/* +static double narrowdoublerand() +{ + double result; + double expo; + long a, b, c; + long i; + + a = random(); + b = random(); + c = random(); + result = (double) (a - 1073741824) * 8388608.0 + (double) (b >> 8); + for (i = 512, expo = 2; i <= 2048; i *= 2, expo = expo * expo) { + if (c & i) { + result *= expo; + } + } + return result; +} +*/ + +/*****************************************************************************/ +/* */ +/* uniformdoublerand() Generate a double with random 53-bit significand. */ +/* */ +/*****************************************************************************/ + +/* +static double uniformdoublerand() +{ + double result; + long a, b; + + a = random(); + b = random(); + result = (double) (a - 1073741824) * 8388608.0 + (double) (b >> 8); + return result; +} +*/ + +/*****************************************************************************/ +/* */ +/* floatrand() Generate a float with random 24-bit significand and a */ +/* random exponent in [0, 63]. */ +/* */ +/*****************************************************************************/ + +/* +static float floatrand() +{ + float result; + float expo; + long a, c; + long i; + + a = random(); + c = random(); + result = (float) ((a - 1073741824) >> 6); + for (i = 512, expo = 2; i <= 16384; i *= 2, expo = expo * expo) { + if (c & i) { + result *= expo; + } + } + return result; +} +*/ + +/*****************************************************************************/ +/* */ +/* narrowfloatrand() Generate a float with random 24-bit significand and */ +/* a random exponent in [0, 7]. */ +/* */ +/*****************************************************************************/ + +/* +static float narrowfloatrand() +{ + float result; + float expo; + long a, c; + long i; + + a = random(); + c = random(); + result = (float) ((a - 1073741824) >> 6); + for (i = 512, expo = 2; i <= 2048; i *= 2, expo = expo * expo) { + if (c & i) { + result *= expo; + } + } + return result; +} +*/ + +/*****************************************************************************/ +/* */ +/* uniformfloatrand() Generate a float with random 24-bit significand. */ +/* */ +/*****************************************************************************/ + +/* +static float uniformfloatrand() +{ + float result; + long a; + + a = random(); + result = (float) ((a - 1073741824) >> 6); + return result; +} +*/ + +/*****************************************************************************/ +/* */ +/* fast_expansion_sum_zeroelim() Sum two expansions, eliminating zero */ +/* components from the output expansion. */ +/* */ +/* Sets h = e + f. See the long version of my paper for details. */ +/* */ +/* If round-to-even is used (as with IEEE 754), maintains the strongly */ +/* nonoverlapping property. (That is, if e is strongly nonoverlapping, h */ +/* will be also.) Does NOT maintain the nonoverlapping or nonadjacent */ +/* properties. */ +/* */ +/*****************************************************************************/ + +static int fast_expansion_sum_zeroelim(int elen, REAL *e, + int flen, REAL *f, REAL *h) + /* h cannot be e or f. */ +{ + REAL Q; + INEXACT REAL Qnew; + INEXACT REAL hh; + INEXACT REAL bvirt; + REAL avirt, bround, around; + int eindex, findex, hindex; + REAL enow, fnow; + + enow = e[0]; + fnow = f[0]; + eindex = findex = 0; + if ((fnow > enow) == (fnow > -enow)) { + Q = enow; + enow = e[++eindex]; + } else { + Q = fnow; + fnow = f[++findex]; + } + hindex = 0; + if ((eindex < elen) && (findex < flen)) { + if ((fnow > enow) == (fnow > -enow)) { + Fast_Two_Sum(enow, Q, Qnew, hh); + enow = e[++eindex]; + } else { + Fast_Two_Sum(fnow, Q, Qnew, hh); + fnow = f[++findex]; + } + Q = Qnew; + if (hh != 0.0) { + h[hindex++] = hh; + } + while ((eindex < elen) && (findex < flen)) { + if ((fnow > enow) == (fnow > -enow)) { + Two_Sum(Q, enow, Qnew, hh); + enow = e[++eindex]; + } else { + Two_Sum(Q, fnow, Qnew, hh); + fnow = f[++findex]; + } + Q = Qnew; + if (hh != 0.0) { + h[hindex++] = hh; + } + } + } + while (eindex < elen) { + Two_Sum(Q, enow, Qnew, hh); + enow = e[++eindex]; + Q = Qnew; + if (hh != 0.0) { + h[hindex++] = hh; + } + } + while (findex < flen) { + Two_Sum(Q, fnow, Qnew, hh); + fnow = f[++findex]; + Q = Qnew; + if (hh != 0.0) { + h[hindex++] = hh; + } + } + if ((Q != 0.0) || (hindex == 0)) { + h[hindex++] = Q; + } + return hindex; +} + +/*****************************************************************************/ +/* */ +/* scale_expansion_zeroelim() Multiply an expansion by a scalar, */ +/* eliminating zero components from the */ +/* output expansion. */ +/* */ +/* Sets h = be. See either version of my paper for details. */ +/* */ +/* Maintains the nonoverlapping property. If round-to-even is used (as */ +/* with IEEE 754), maintains the strongly nonoverlapping and nonadjacent */ +/* properties as well. (That is, if e has one of these properties, so */ +/* will h.) */ +/* */ +/*****************************************************************************/ + +static int scale_expansion_zeroelim(int elen, REAL *e, REAL b, REAL *h) + /* e and h cannot be the same. */ +{ + INEXACT REAL Q, sum; + REAL hh; + INEXACT REAL product1; + REAL product0; + int eindex, hindex; + REAL enow; + INEXACT REAL bvirt; + REAL avirt, bround, around; + INEXACT REAL c; + INEXACT REAL abig; + REAL ahi, alo, bhi, blo; + REAL err1, err2, err3; + + Split(b, bhi, blo); + Two_Product_Presplit(e[0], b, bhi, blo, Q, hh); + hindex = 0; + if (hh != 0) { + h[hindex++] = hh; + } + for (eindex = 1; eindex < elen; eindex++) { + enow = e[eindex]; + Two_Product_Presplit(enow, b, bhi, blo, product1, product0); + Two_Sum(Q, product0, sum, hh); + if (hh != 0) { + h[hindex++] = hh; + } + Fast_Two_Sum(product1, sum, Q, hh); + if (hh != 0) { + h[hindex++] = hh; + } + } + if ((Q != 0.0) || (hindex == 0)) { + h[hindex++] = Q; + } + return hindex; +} + +/*****************************************************************************/ +/* */ +/* estimate() Produce a one-word estimate of an expansion's value. */ +/* */ +/* See either version of my paper for details. */ +/* */ +/*****************************************************************************/ + +static REAL estimate(int elen, REAL *e) +{ + REAL Q; + int eindex; + + Q = e[0]; + for (eindex = 1; eindex < elen; eindex++) { + Q += e[eindex]; + } + return Q; +} + +/*****************************************************************************/ +/* */ +/* orient2dfast() Approximate 2D orientation test. Nonrobust. */ +/* orient2dexact() Exact 2D orientation test. Robust. */ +/* orient2dslow() Another exact 2D orientation test. Robust. */ +/* orient2d() Adaptive exact 2D orientation test. Robust. */ +/* */ +/* Return a positive value if the points pa, pb, and pc occur */ +/* in counterclockwise order; a negative value if they occur */ +/* in clockwise order; and zero if they are collinear. The */ +/* result is also a rough approximation of twice the signed */ +/* area of the triangle defined by the three points. */ +/* */ +/* Only the first and last routine should be used; the middle two are for */ +/* timings. */ +/* */ +/* The last three use exact arithmetic to ensure a correct answer. The */ +/* result returned is the determinant of a matrix. In orient2d() only, */ +/* this determinant is computed adaptively, in the sense that exact */ +/* arithmetic is used only to the degree it is needed to ensure that the */ +/* returned value has the correct sign. Hence, orient2d() is usually quite */ +/* fast, but will run more slowly when the input points are collinear or */ +/* nearly so. */ +/* */ +/*****************************************************************************/ + +static REAL orient2dadapt(REAL *pa, REAL *pb, REAL *pc, REAL detsum) +{ + INEXACT REAL acx, acy, bcx, bcy; + REAL acxtail, acytail, bcxtail, bcytail; + INEXACT REAL detleft, detright; + REAL detlefttail, detrighttail; + REAL det, errbound; + REAL B[4], C1[8], C2[12], D[16]; + INEXACT REAL B3; + int C1length, C2length, Dlength; + REAL u[4]; + INEXACT REAL u3; + INEXACT REAL s1, t1; + REAL s0, t0; + + INEXACT REAL bvirt; + REAL avirt, bround, around; + INEXACT REAL c; + INEXACT REAL abig; + REAL ahi, alo, bhi, blo; + REAL err1, err2, err3; + INEXACT REAL _i, _j; + REAL _0; + + acx = (REAL) (pa[0] - pc[0]); + bcx = (REAL) (pb[0] - pc[0]); + acy = (REAL) (pa[1] - pc[1]); + bcy = (REAL) (pb[1] - pc[1]); + + Two_Product(acx, bcy, detleft, detlefttail); + Two_Product(acy, bcx, detright, detrighttail); + + Two_Two_Diff(detleft, detlefttail, detright, detrighttail, + B3, B[2], B[1], B[0]); + B[3] = B3; + + det = estimate(4, B); + errbound = ccwerrboundB * detsum; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Diff_Tail(pa[0], pc[0], acx, acxtail); + Two_Diff_Tail(pb[0], pc[0], bcx, bcxtail); + Two_Diff_Tail(pa[1], pc[1], acy, acytail); + Two_Diff_Tail(pb[1], pc[1], bcy, bcytail); + + if ((acxtail == 0.0) && (acytail == 0.0) + && (bcxtail == 0.0) && (bcytail == 0.0)) { + return det; + } + + errbound = ccwerrboundC * detsum + resulterrbound * Absolute(det); + det += (acx * bcytail + bcy * acxtail) + - (acy * bcxtail + bcx * acytail); + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Product(acxtail, bcy, s1, s0); + Two_Product(acytail, bcx, t1, t0); + Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]); + u[3] = u3; + C1length = fast_expansion_sum_zeroelim(4, B, 4, u, C1); + + Two_Product(acx, bcytail, s1, s0); + Two_Product(acy, bcxtail, t1, t0); + Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]); + u[3] = u3; + C2length = fast_expansion_sum_zeroelim(C1length, C1, 4, u, C2); + + Two_Product(acxtail, bcytail, s1, s0); + Two_Product(acytail, bcxtail, t1, t0); + Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]); + u[3] = u3; + Dlength = fast_expansion_sum_zeroelim(C2length, C2, 4, u, D); + + return(D[Dlength - 1]); +} + +REAL orient2d(pa, pb, pc) +REAL *pa; +REAL *pb; +REAL *pc; +{ + REAL detleft, detright, det; + REAL detsum, errbound; + REAL orient; + + FPU_ROUND_DOUBLE; + + detleft = (pa[0] - pc[0]) * (pb[1] - pc[1]); + detright = (pa[1] - pc[1]) * (pb[0] - pc[0]); + det = detleft - detright; + + if (detleft > 0.0) { + if (detright <= 0.0) { + FPU_RESTORE; + return det; + } else { + detsum = detleft + detright; + } + } else if (detleft < 0.0) { + if (detright >= 0.0) { + FPU_RESTORE; + return det; + } else { + detsum = -detleft - detright; + } + } else { + FPU_RESTORE; + return det; + } + + errbound = ccwerrboundA * detsum; + if ((det >= errbound) || (-det >= errbound)) { + FPU_RESTORE; + return det; + } + + orient = orient2dadapt(pa, pb, pc, detsum); + FPU_RESTORE; + return orient; +} + +/*****************************************************************************/ +/* */ +/* orient3dfast() Approximate 3D orientation test. Nonrobust. */ +/* orient3dexact() Exact 3D orientation test. Robust. */ +/* orient3dslow() Another exact 3D orientation test. Robust. */ +/* orient3d() Adaptive exact 3D orientation test. Robust. */ +/* */ +/* Return a positive value if the point pd lies below the */ +/* plane passing through pa, pb, and pc; "below" is defined so */ +/* that pa, pb, and pc appear in counterclockwise order when */ +/* viewed from above the plane. Returns a negative value if */ +/* pd lies above the plane. Returns zero if the points are */ +/* coplanar. The result is also a rough approximation of six */ +/* times the signed volume of the tetrahedron defined by the */ +/* four points. */ +/* */ +/* Only the first and last routine should be used; the middle two are for */ +/* timings. */ +/* */ +/* The last three use exact arithmetic to ensure a correct answer. The */ +/* result returned is the determinant of a matrix. In orient3d() only, */ +/* this determinant is computed adaptively, in the sense that exact */ +/* arithmetic is used only to the degree it is needed to ensure that the */ +/* returned value has the correct sign. Hence, orient3d() is usually quite */ +/* fast, but will run more slowly when the input points are coplanar or */ +/* nearly so. */ +/* */ +/*****************************************************************************/ + +static REAL orient3dadapt(REAL *pa, REAL *pb, REAL *pc, REAL *pd, + REAL permanent) +{ + INEXACT REAL adx, bdx, cdx, ady, bdy, cdy, adz, bdz, cdz; + REAL det, errbound; + + INEXACT REAL bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1; + REAL bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0; + REAL bc[4], ca[4], ab[4]; + INEXACT REAL bc3, ca3, ab3; + REAL adet[8], bdet[8], cdet[8]; + int alen, blen, clen; + REAL abdet[16]; + int ablen; + REAL *finnow, *finother, *finswap; + REAL fin1[192], fin2[192]; + int finlength; + + REAL adxtail, bdxtail, cdxtail; + REAL adytail, bdytail, cdytail; + REAL adztail, bdztail, cdztail; + INEXACT REAL at_blarge, at_clarge; + INEXACT REAL bt_clarge, bt_alarge; + INEXACT REAL ct_alarge, ct_blarge; + REAL at_b[4], at_c[4], bt_c[4], bt_a[4], ct_a[4], ct_b[4]; + int at_blen, at_clen, bt_clen, bt_alen, ct_alen, ct_blen; + INEXACT REAL bdxt_cdy1, cdxt_bdy1, cdxt_ady1; + INEXACT REAL adxt_cdy1, adxt_bdy1, bdxt_ady1; + REAL bdxt_cdy0, cdxt_bdy0, cdxt_ady0; + REAL adxt_cdy0, adxt_bdy0, bdxt_ady0; + INEXACT REAL bdyt_cdx1, cdyt_bdx1, cdyt_adx1; + INEXACT REAL adyt_cdx1, adyt_bdx1, bdyt_adx1; + REAL bdyt_cdx0, cdyt_bdx0, cdyt_adx0; + REAL adyt_cdx0, adyt_bdx0, bdyt_adx0; + REAL bct[8], cat[8], abt[8]; + int bctlen, catlen, abtlen; + INEXACT REAL bdxt_cdyt1, cdxt_bdyt1, cdxt_adyt1; + INEXACT REAL adxt_cdyt1, adxt_bdyt1, bdxt_adyt1; + REAL bdxt_cdyt0, cdxt_bdyt0, cdxt_adyt0; + REAL adxt_cdyt0, adxt_bdyt0, bdxt_adyt0; + REAL u[4], v[12], w[16]; + INEXACT REAL u3; + int vlength, wlength; + REAL negate; + + INEXACT REAL bvirt; + REAL avirt, bround, around; + INEXACT REAL c; + INEXACT REAL abig; + REAL ahi, alo, bhi, blo; + REAL err1, err2, err3; + INEXACT REAL _i, _j, _k; + REAL _0; + + adx = (REAL) (pa[0] - pd[0]); + bdx = (REAL) (pb[0] - pd[0]); + cdx = (REAL) (pc[0] - pd[0]); + ady = (REAL) (pa[1] - pd[1]); + bdy = (REAL) (pb[1] - pd[1]); + cdy = (REAL) (pc[1] - pd[1]); + adz = (REAL) (pa[2] - pd[2]); + bdz = (REAL) (pb[2] - pd[2]); + cdz = (REAL) (pc[2] - pd[2]); + + Two_Product(bdx, cdy, bdxcdy1, bdxcdy0); + Two_Product(cdx, bdy, cdxbdy1, cdxbdy0); + Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]); + bc[3] = bc3; + alen = scale_expansion_zeroelim(4, bc, adz, adet); + + Two_Product(cdx, ady, cdxady1, cdxady0); + Two_Product(adx, cdy, adxcdy1, adxcdy0); + Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]); + ca[3] = ca3; + blen = scale_expansion_zeroelim(4, ca, bdz, bdet); + + Two_Product(adx, bdy, adxbdy1, adxbdy0); + Two_Product(bdx, ady, bdxady1, bdxady0); + Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]); + ab[3] = ab3; + clen = scale_expansion_zeroelim(4, ab, cdz, cdet); + + ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet); + finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1); + + det = estimate(finlength, fin1); + errbound = o3derrboundB * permanent; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Diff_Tail(pa[0], pd[0], adx, adxtail); + Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail); + Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail); + Two_Diff_Tail(pa[1], pd[1], ady, adytail); + Two_Diff_Tail(pb[1], pd[1], bdy, bdytail); + Two_Diff_Tail(pc[1], pd[1], cdy, cdytail); + Two_Diff_Tail(pa[2], pd[2], adz, adztail); + Two_Diff_Tail(pb[2], pd[2], bdz, bdztail); + Two_Diff_Tail(pc[2], pd[2], cdz, cdztail); + + if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0) + && (adytail == 0.0) && (bdytail == 0.0) && (cdytail == 0.0) + && (adztail == 0.0) && (bdztail == 0.0) && (cdztail == 0.0)) { + return det; + } + + errbound = o3derrboundC * permanent + resulterrbound * Absolute(det); + det += (adz * ((bdx * cdytail + cdy * bdxtail) + - (bdy * cdxtail + cdx * bdytail)) + + adztail * (bdx * cdy - bdy * cdx)) + + (bdz * ((cdx * adytail + ady * cdxtail) + - (cdy * adxtail + adx * cdytail)) + + bdztail * (cdx * ady - cdy * adx)) + + (cdz * ((adx * bdytail + bdy * adxtail) + - (ady * bdxtail + bdx * adytail)) + + cdztail * (adx * bdy - ady * bdx)); + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + finnow = fin1; + finother = fin2; + + if (adxtail == 0.0) { + if (adytail == 0.0) { + at_b[0] = 0.0; + at_blen = 1; + at_c[0] = 0.0; + at_clen = 1; + } else { + negate = -adytail; + Two_Product(negate, bdx, at_blarge, at_b[0]); + at_b[1] = at_blarge; + at_blen = 2; + Two_Product(adytail, cdx, at_clarge, at_c[0]); + at_c[1] = at_clarge; + at_clen = 2; + } + } else { + if (adytail == 0.0) { + Two_Product(adxtail, bdy, at_blarge, at_b[0]); + at_b[1] = at_blarge; + at_blen = 2; + negate = -adxtail; + Two_Product(negate, cdy, at_clarge, at_c[0]); + at_c[1] = at_clarge; + at_clen = 2; + } else { + Two_Product(adxtail, bdy, adxt_bdy1, adxt_bdy0); + Two_Product(adytail, bdx, adyt_bdx1, adyt_bdx0); + Two_Two_Diff(adxt_bdy1, adxt_bdy0, adyt_bdx1, adyt_bdx0, + at_blarge, at_b[2], at_b[1], at_b[0]); + at_b[3] = at_blarge; + at_blen = 4; + Two_Product(adytail, cdx, adyt_cdx1, adyt_cdx0); + Two_Product(adxtail, cdy, adxt_cdy1, adxt_cdy0); + Two_Two_Diff(adyt_cdx1, adyt_cdx0, adxt_cdy1, adxt_cdy0, + at_clarge, at_c[2], at_c[1], at_c[0]); + at_c[3] = at_clarge; + at_clen = 4; + } + } + if (bdxtail == 0.0) { + if (bdytail == 0.0) { + bt_c[0] = 0.0; + bt_clen = 1; + bt_a[0] = 0.0; + bt_alen = 1; + } else { + negate = -bdytail; + Two_Product(negate, cdx, bt_clarge, bt_c[0]); + bt_c[1] = bt_clarge; + bt_clen = 2; + Two_Product(bdytail, adx, bt_alarge, bt_a[0]); + bt_a[1] = bt_alarge; + bt_alen = 2; + } + } else { + if (bdytail == 0.0) { + Two_Product(bdxtail, cdy, bt_clarge, bt_c[0]); + bt_c[1] = bt_clarge; + bt_clen = 2; + negate = -bdxtail; + Two_Product(negate, ady, bt_alarge, bt_a[0]); + bt_a[1] = bt_alarge; + bt_alen = 2; + } else { + Two_Product(bdxtail, cdy, bdxt_cdy1, bdxt_cdy0); + Two_Product(bdytail, cdx, bdyt_cdx1, bdyt_cdx0); + Two_Two_Diff(bdxt_cdy1, bdxt_cdy0, bdyt_cdx1, bdyt_cdx0, + bt_clarge, bt_c[2], bt_c[1], bt_c[0]); + bt_c[3] = bt_clarge; + bt_clen = 4; + Two_Product(bdytail, adx, bdyt_adx1, bdyt_adx0); + Two_Product(bdxtail, ady, bdxt_ady1, bdxt_ady0); + Two_Two_Diff(bdyt_adx1, bdyt_adx0, bdxt_ady1, bdxt_ady0, + bt_alarge, bt_a[2], bt_a[1], bt_a[0]); + bt_a[3] = bt_alarge; + bt_alen = 4; + } + } + if (cdxtail == 0.0) { + if (cdytail == 0.0) { + ct_a[0] = 0.0; + ct_alen = 1; + ct_b[0] = 0.0; + ct_blen = 1; + } else { + negate = -cdytail; + Two_Product(negate, adx, ct_alarge, ct_a[0]); + ct_a[1] = ct_alarge; + ct_alen = 2; + Two_Product(cdytail, bdx, ct_blarge, ct_b[0]); + ct_b[1] = ct_blarge; + ct_blen = 2; + } + } else { + if (cdytail == 0.0) { + Two_Product(cdxtail, ady, ct_alarge, ct_a[0]); + ct_a[1] = ct_alarge; + ct_alen = 2; + negate = -cdxtail; + Two_Product(negate, bdy, ct_blarge, ct_b[0]); + ct_b[1] = ct_blarge; + ct_blen = 2; + } else { + Two_Product(cdxtail, ady, cdxt_ady1, cdxt_ady0); + Two_Product(cdytail, adx, cdyt_adx1, cdyt_adx0); + Two_Two_Diff(cdxt_ady1, cdxt_ady0, cdyt_adx1, cdyt_adx0, + ct_alarge, ct_a[2], ct_a[1], ct_a[0]); + ct_a[3] = ct_alarge; + ct_alen = 4; + Two_Product(cdytail, bdx, cdyt_bdx1, cdyt_bdx0); + Two_Product(cdxtail, bdy, cdxt_bdy1, cdxt_bdy0); + Two_Two_Diff(cdyt_bdx1, cdyt_bdx0, cdxt_bdy1, cdxt_bdy0, + ct_blarge, ct_b[2], ct_b[1], ct_b[0]); + ct_b[3] = ct_blarge; + ct_blen = 4; + } + } + + bctlen = fast_expansion_sum_zeroelim(bt_clen, bt_c, ct_blen, ct_b, bct); + wlength = scale_expansion_zeroelim(bctlen, bct, adz, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, + finother); + finswap = finnow; finnow = finother; finother = finswap; + + catlen = fast_expansion_sum_zeroelim(ct_alen, ct_a, at_clen, at_c, cat); + wlength = scale_expansion_zeroelim(catlen, cat, bdz, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, + finother); + finswap = finnow; finnow = finother; finother = finswap; + + abtlen = fast_expansion_sum_zeroelim(at_blen, at_b, bt_alen, bt_a, abt); + wlength = scale_expansion_zeroelim(abtlen, abt, cdz, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, + finother); + finswap = finnow; finnow = finother; finother = finswap; + + if (adztail != 0.0) { + vlength = scale_expansion_zeroelim(4, bc, adztail, v); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (bdztail != 0.0) { + vlength = scale_expansion_zeroelim(4, ca, bdztail, v); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (cdztail != 0.0) { + vlength = scale_expansion_zeroelim(4, ab, cdztail, v); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + + if (adxtail != 0.0) { + if (bdytail != 0.0) { + Two_Product(adxtail, bdytail, adxt_bdyt1, adxt_bdyt0); + Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdz, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + if (cdztail != 0.0) { + Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdztail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + if (cdytail != 0.0) { + negate = -adxtail; + Two_Product(negate, cdytail, adxt_cdyt1, adxt_cdyt0); + Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdz, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + if (bdztail != 0.0) { + Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdztail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + } + if (bdxtail != 0.0) { + if (cdytail != 0.0) { + Two_Product(bdxtail, cdytail, bdxt_cdyt1, bdxt_cdyt0); + Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adz, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + if (adztail != 0.0) { + Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adztail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + if (adytail != 0.0) { + negate = -bdxtail; + Two_Product(negate, adytail, bdxt_adyt1, bdxt_adyt0); + Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdz, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + if (cdztail != 0.0) { + Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdztail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + } + if (cdxtail != 0.0) { + if (adytail != 0.0) { + Two_Product(cdxtail, adytail, cdxt_adyt1, cdxt_adyt0); + Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdz, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + if (bdztail != 0.0) { + Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdztail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + if (bdytail != 0.0) { + negate = -cdxtail; + Two_Product(negate, bdytail, cdxt_bdyt1, cdxt_bdyt0); + Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adz, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + if (adztail != 0.0) { + Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adztail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + } + + if (adztail != 0.0) { + wlength = scale_expansion_zeroelim(bctlen, bct, adztail, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (bdztail != 0.0) { + wlength = scale_expansion_zeroelim(catlen, cat, bdztail, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (cdztail != 0.0) { + wlength = scale_expansion_zeroelim(abtlen, abt, cdztail, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + + return finnow[finlength - 1]; +} + +REAL orient3d(pa, pb, pc, pd) +REAL *pa; +REAL *pb; +REAL *pc; +REAL *pd; +{ + REAL adx, bdx, cdx, ady, bdy, cdy, adz, bdz, cdz; + REAL bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady; + REAL det; + REAL permanent, errbound; + REAL orient; + + FPU_ROUND_DOUBLE; + + adx = pa[0] - pd[0]; + bdx = pb[0] - pd[0]; + cdx = pc[0] - pd[0]; + ady = pa[1] - pd[1]; + bdy = pb[1] - pd[1]; + cdy = pc[1] - pd[1]; + adz = pa[2] - pd[2]; + bdz = pb[2] - pd[2]; + cdz = pc[2] - pd[2]; + + bdxcdy = bdx * cdy; + cdxbdy = cdx * bdy; + + cdxady = cdx * ady; + adxcdy = adx * cdy; + + adxbdy = adx * bdy; + bdxady = bdx * ady; + + det = adz * (bdxcdy - cdxbdy) + + bdz * (cdxady - adxcdy) + + cdz * (adxbdy - bdxady); + + permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * Absolute(adz) + + (Absolute(cdxady) + Absolute(adxcdy)) * Absolute(bdz) + + (Absolute(adxbdy) + Absolute(bdxady)) * Absolute(cdz); + errbound = o3derrboundA * permanent; + if ((det > errbound) || (-det > errbound)) { + FPU_RESTORE; + return det; + } + + orient = orient3dadapt(pa, pb, pc, pd, permanent); + FPU_RESTORE; + return orient; +} + +/*****************************************************************************/ +/* */ +/* incirclefast() Approximate 2D incircle test. Nonrobust. */ +/* incircleexact() Exact 2D incircle test. Robust. */ +/* incircleslow() Another exact 2D incircle test. Robust. */ +/* incircle() Adaptive exact 2D incircle test. Robust. */ +/* */ +/* Return a positive value if the point pd lies inside the */ +/* circle passing through pa, pb, and pc; a negative value if */ +/* it lies outside; and zero if the four points are cocircular.*/ +/* The points pa, pb, and pc must be in counterclockwise */ +/* order, or the sign of the result will be reversed. */ +/* */ +/* Only the first and last routine should be used; the middle two are for */ +/* timings. */ +/* */ +/* The last three use exact arithmetic to ensure a correct answer. The */ +/* result returned is the determinant of a matrix. In incircle() only, */ +/* this determinant is computed adaptively, in the sense that exact */ +/* arithmetic is used only to the degree it is needed to ensure that the */ +/* returned value has the correct sign. Hence, incircle() is usually quite */ +/* fast, but will run more slowly when the input points are cocircular or */ +/* nearly so. */ +/* */ +/*****************************************************************************/ + +static REAL incircleadapt(REAL *pa, REAL *pb, REAL *pc, REAL *pd, + REAL permanent) +{ + INEXACT REAL adx, bdx, cdx, ady, bdy, cdy; + REAL det, errbound; + + INEXACT REAL bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1; + REAL bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0; + REAL bc[4], ca[4], ab[4]; + INEXACT REAL bc3, ca3, ab3; + REAL axbc[8], axxbc[16], aybc[8], ayybc[16], adet[32]; + int axbclen, axxbclen, aybclen, ayybclen, alen; + REAL bxca[8], bxxca[16], byca[8], byyca[16], bdet[32]; + int bxcalen, bxxcalen, bycalen, byycalen, blen; + REAL cxab[8], cxxab[16], cyab[8], cyyab[16], cdet[32]; + int cxablen, cxxablen, cyablen, cyyablen, clen; + REAL abdet[64]; + int ablen; + REAL fin1[1152], fin2[1152]; + REAL *finnow, *finother, *finswap; + int finlength; + + REAL adxtail, bdxtail, cdxtail, adytail, bdytail, cdytail; + INEXACT REAL adxadx1, adyady1, bdxbdx1, bdybdy1, cdxcdx1, cdycdy1; + REAL adxadx0, adyady0, bdxbdx0, bdybdy0, cdxcdx0, cdycdy0; + REAL aa[4], bb[4], cc[4]; + INEXACT REAL aa3, bb3, cc3; + INEXACT REAL ti1, tj1; + REAL ti0, tj0; + REAL u[4], v[4]; + INEXACT REAL u3, v3; + REAL temp8[8], temp16a[16], temp16b[16], temp16c[16]; + REAL temp32a[32], temp32b[32], temp48[48], temp64[64]; + int temp8len, temp16alen, temp16blen, temp16clen; + int temp32alen, temp32blen, temp48len, temp64len; + REAL axtbb[8], axtcc[8], aytbb[8], aytcc[8]; + int axtbblen, axtcclen, aytbblen, aytcclen; + REAL bxtaa[8], bxtcc[8], bytaa[8], bytcc[8]; + int bxtaalen, bxtcclen, bytaalen, bytcclen; + REAL cxtaa[8], cxtbb[8], cytaa[8], cytbb[8]; + int cxtaalen, cxtbblen, cytaalen, cytbblen; + REAL axtbc[8], aytbc[8], bxtca[8], bytca[8], cxtab[8], cytab[8]; + int axtbclen = 0, aytbclen = 0; + int bxtcalen = 0, bytcalen = 0; + int cxtablen = 0, cytablen = 0; + REAL axtbct[16], aytbct[16], bxtcat[16], bytcat[16], cxtabt[16], cytabt[16]; + int axtbctlen, aytbctlen, bxtcatlen, bytcatlen, cxtabtlen, cytabtlen; + REAL axtbctt[8], aytbctt[8], bxtcatt[8]; + REAL bytcatt[8], cxtabtt[8], cytabtt[8]; + int axtbcttlen, aytbcttlen, bxtcattlen, bytcattlen, cxtabttlen, cytabttlen; + REAL abt[8], bct[8], cat[8]; + int abtlen, bctlen, catlen; + REAL abtt[4], bctt[4], catt[4]; + int abttlen, bcttlen, cattlen; + INEXACT REAL abtt3, bctt3, catt3; + REAL negate; + + INEXACT REAL bvirt; + REAL avirt, bround, around; + INEXACT REAL c; + INEXACT REAL abig; + REAL ahi, alo, bhi, blo; + REAL err1, err2, err3; + INEXACT REAL _i, _j; + REAL _0; + + adx = (REAL) (pa[0] - pd[0]); + bdx = (REAL) (pb[0] - pd[0]); + cdx = (REAL) (pc[0] - pd[0]); + ady = (REAL) (pa[1] - pd[1]); + bdy = (REAL) (pb[1] - pd[1]); + cdy = (REAL) (pc[1] - pd[1]); + + Two_Product(bdx, cdy, bdxcdy1, bdxcdy0); + Two_Product(cdx, bdy, cdxbdy1, cdxbdy0); + Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]); + bc[3] = bc3; + axbclen = scale_expansion_zeroelim(4, bc, adx, axbc); + axxbclen = scale_expansion_zeroelim(axbclen, axbc, adx, axxbc); + aybclen = scale_expansion_zeroelim(4, bc, ady, aybc); + ayybclen = scale_expansion_zeroelim(aybclen, aybc, ady, ayybc); + alen = fast_expansion_sum_zeroelim(axxbclen, axxbc, ayybclen, ayybc, adet); + + Two_Product(cdx, ady, cdxady1, cdxady0); + Two_Product(adx, cdy, adxcdy1, adxcdy0); + Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]); + ca[3] = ca3; + bxcalen = scale_expansion_zeroelim(4, ca, bdx, bxca); + bxxcalen = scale_expansion_zeroelim(bxcalen, bxca, bdx, bxxca); + bycalen = scale_expansion_zeroelim(4, ca, bdy, byca); + byycalen = scale_expansion_zeroelim(bycalen, byca, bdy, byyca); + blen = fast_expansion_sum_zeroelim(bxxcalen, bxxca, byycalen, byyca, bdet); + + Two_Product(adx, bdy, adxbdy1, adxbdy0); + Two_Product(bdx, ady, bdxady1, bdxady0); + Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]); + ab[3] = ab3; + cxablen = scale_expansion_zeroelim(4, ab, cdx, cxab); + cxxablen = scale_expansion_zeroelim(cxablen, cxab, cdx, cxxab); + cyablen = scale_expansion_zeroelim(4, ab, cdy, cyab); + cyyablen = scale_expansion_zeroelim(cyablen, cyab, cdy, cyyab); + clen = fast_expansion_sum_zeroelim(cxxablen, cxxab, cyyablen, cyyab, cdet); + + ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet); + finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1); + + det = estimate(finlength, fin1); + errbound = iccerrboundB * permanent; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Diff_Tail(pa[0], pd[0], adx, adxtail); + Two_Diff_Tail(pa[1], pd[1], ady, adytail); + Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail); + Two_Diff_Tail(pb[1], pd[1], bdy, bdytail); + Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail); + Two_Diff_Tail(pc[1], pd[1], cdy, cdytail); + if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0) + && (adytail == 0.0) && (bdytail == 0.0) && (cdytail == 0.0)) { + return det; + } + + errbound = iccerrboundC * permanent + resulterrbound * Absolute(det); + det += ((adx * adx + ady * ady) * ((bdx * cdytail + cdy * bdxtail) + - (bdy * cdxtail + cdx * bdytail)) + + 2.0 * (adx * adxtail + ady * adytail) * (bdx * cdy - bdy * cdx)) + + ((bdx * bdx + bdy * bdy) * ((cdx * adytail + ady * cdxtail) + - (cdy * adxtail + adx * cdytail)) + + 2.0 * (bdx * bdxtail + bdy * bdytail) * (cdx * ady - cdy * adx)) + + ((cdx * cdx + cdy * cdy) * ((adx * bdytail + bdy * adxtail) + - (ady * bdxtail + bdx * adytail)) + + 2.0 * (cdx * cdxtail + cdy * cdytail) * (adx * bdy - ady * bdx)); + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + finnow = fin1; + finother = fin2; + + if ((bdxtail != 0.0) || (bdytail != 0.0) + || (cdxtail != 0.0) || (cdytail != 0.0)) { + Square(adx, adxadx1, adxadx0); + Square(ady, adyady1, adyady0); + Two_Two_Sum(adxadx1, adxadx0, adyady1, adyady0, aa3, aa[2], aa[1], aa[0]); + aa[3] = aa3; + } + if ((cdxtail != 0.0) || (cdytail != 0.0) + || (adxtail != 0.0) || (adytail != 0.0)) { + Square(bdx, bdxbdx1, bdxbdx0); + Square(bdy, bdybdy1, bdybdy0); + Two_Two_Sum(bdxbdx1, bdxbdx0, bdybdy1, bdybdy0, bb3, bb[2], bb[1], bb[0]); + bb[3] = bb3; + } + if ((adxtail != 0.0) || (adytail != 0.0) + || (bdxtail != 0.0) || (bdytail != 0.0)) { + Square(cdx, cdxcdx1, cdxcdx0); + Square(cdy, cdycdy1, cdycdy0); + Two_Two_Sum(cdxcdx1, cdxcdx0, cdycdy1, cdycdy0, cc3, cc[2], cc[1], cc[0]); + cc[3] = cc3; + } + + if (adxtail != 0.0) { + axtbclen = scale_expansion_zeroelim(4, bc, adxtail, axtbc); + temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, 2.0 * adx, + temp16a); + + axtcclen = scale_expansion_zeroelim(4, cc, adxtail, axtcc); + temp16blen = scale_expansion_zeroelim(axtcclen, axtcc, bdy, temp16b); + + axtbblen = scale_expansion_zeroelim(4, bb, adxtail, axtbb); + temp16clen = scale_expansion_zeroelim(axtbblen, axtbb, -cdy, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (adytail != 0.0) { + aytbclen = scale_expansion_zeroelim(4, bc, adytail, aytbc); + temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, 2.0 * ady, + temp16a); + + aytbblen = scale_expansion_zeroelim(4, bb, adytail, aytbb); + temp16blen = scale_expansion_zeroelim(aytbblen, aytbb, cdx, temp16b); + + aytcclen = scale_expansion_zeroelim(4, cc, adytail, aytcc); + temp16clen = scale_expansion_zeroelim(aytcclen, aytcc, -bdx, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (bdxtail != 0.0) { + bxtcalen = scale_expansion_zeroelim(4, ca, bdxtail, bxtca); + temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, 2.0 * bdx, + temp16a); + + bxtaalen = scale_expansion_zeroelim(4, aa, bdxtail, bxtaa); + temp16blen = scale_expansion_zeroelim(bxtaalen, bxtaa, cdy, temp16b); + + bxtcclen = scale_expansion_zeroelim(4, cc, bdxtail, bxtcc); + temp16clen = scale_expansion_zeroelim(bxtcclen, bxtcc, -ady, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (bdytail != 0.0) { + bytcalen = scale_expansion_zeroelim(4, ca, bdytail, bytca); + temp16alen = scale_expansion_zeroelim(bytcalen, bytca, 2.0 * bdy, + temp16a); + + bytcclen = scale_expansion_zeroelim(4, cc, bdytail, bytcc); + temp16blen = scale_expansion_zeroelim(bytcclen, bytcc, adx, temp16b); + + bytaalen = scale_expansion_zeroelim(4, aa, bdytail, bytaa); + temp16clen = scale_expansion_zeroelim(bytaalen, bytaa, -cdx, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (cdxtail != 0.0) { + cxtablen = scale_expansion_zeroelim(4, ab, cdxtail, cxtab); + temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, 2.0 * cdx, + temp16a); + + cxtbblen = scale_expansion_zeroelim(4, bb, cdxtail, cxtbb); + temp16blen = scale_expansion_zeroelim(cxtbblen, cxtbb, ady, temp16b); + + cxtaalen = scale_expansion_zeroelim(4, aa, cdxtail, cxtaa); + temp16clen = scale_expansion_zeroelim(cxtaalen, cxtaa, -bdy, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (cdytail != 0.0) { + cytablen = scale_expansion_zeroelim(4, ab, cdytail, cytab); + temp16alen = scale_expansion_zeroelim(cytablen, cytab, 2.0 * cdy, + temp16a); + + cytaalen = scale_expansion_zeroelim(4, aa, cdytail, cytaa); + temp16blen = scale_expansion_zeroelim(cytaalen, cytaa, bdx, temp16b); + + cytbblen = scale_expansion_zeroelim(4, bb, cdytail, cytbb); + temp16clen = scale_expansion_zeroelim(cytbblen, cytbb, -adx, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + + if ((adxtail != 0.0) || (adytail != 0.0)) { + if ((bdxtail != 0.0) || (bdytail != 0.0) + || (cdxtail != 0.0) || (cdytail != 0.0)) { + Two_Product(bdxtail, cdy, ti1, ti0); + Two_Product(bdx, cdytail, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]); + u[3] = u3; + negate = -bdy; + Two_Product(cdxtail, negate, ti1, ti0); + negate = -bdytail; + Two_Product(cdx, negate, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]); + v[3] = v3; + bctlen = fast_expansion_sum_zeroelim(4, u, 4, v, bct); + + Two_Product(bdxtail, cdytail, ti1, ti0); + Two_Product(cdxtail, bdytail, tj1, tj0); + Two_Two_Diff(ti1, ti0, tj1, tj0, bctt3, bctt[2], bctt[1], bctt[0]); + bctt[3] = bctt3; + bcttlen = 4; + } else { + bct[0] = 0.0; + bctlen = 1; + bctt[0] = 0.0; + bcttlen = 1; + } + + if (adxtail != 0.0) { + temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, adxtail, temp16a); + axtbctlen = scale_expansion_zeroelim(bctlen, bct, adxtail, axtbct); + temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, 2.0 * adx, + temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + if (bdytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, cc, adxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail, + temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, + temp16a, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (cdytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, bb, -adxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail, + temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, + temp16a, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + + temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, adxtail, + temp32a); + axtbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adxtail, axtbctt); + temp16alen = scale_expansion_zeroelim(axtbcttlen, axtbctt, 2.0 * adx, + temp16a); + temp16blen = scale_expansion_zeroelim(axtbcttlen, axtbctt, adxtail, + temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, + temp64, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (adytail != 0.0) { + temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, adytail, temp16a); + aytbctlen = scale_expansion_zeroelim(bctlen, bct, adytail, aytbct); + temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, 2.0 * ady, + temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + + + temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, adytail, + temp32a); + aytbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adytail, aytbctt); + temp16alen = scale_expansion_zeroelim(aytbcttlen, aytbctt, 2.0 * ady, + temp16a); + temp16blen = scale_expansion_zeroelim(aytbcttlen, aytbctt, adytail, + temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, + temp64, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + if ((bdxtail != 0.0) || (bdytail != 0.0)) { + if ((cdxtail != 0.0) || (cdytail != 0.0) + || (adxtail != 0.0) || (adytail != 0.0)) { + Two_Product(cdxtail, ady, ti1, ti0); + Two_Product(cdx, adytail, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]); + u[3] = u3; + negate = -cdy; + Two_Product(adxtail, negate, ti1, ti0); + negate = -cdytail; + Two_Product(adx, negate, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]); + v[3] = v3; + catlen = fast_expansion_sum_zeroelim(4, u, 4, v, cat); + + Two_Product(cdxtail, adytail, ti1, ti0); + Two_Product(adxtail, cdytail, tj1, tj0); + Two_Two_Diff(ti1, ti0, tj1, tj0, catt3, catt[2], catt[1], catt[0]); + catt[3] = catt3; + cattlen = 4; + } else { + cat[0] = 0.0; + catlen = 1; + catt[0] = 0.0; + cattlen = 1; + } + + if (bdxtail != 0.0) { + temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, bdxtail, temp16a); + bxtcatlen = scale_expansion_zeroelim(catlen, cat, bdxtail, bxtcat); + temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, 2.0 * bdx, + temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + if (cdytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, aa, bdxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail, + temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, + temp16a, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (adytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, cc, -bdxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail, + temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, + temp16a, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + + temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, bdxtail, + temp32a); + bxtcattlen = scale_expansion_zeroelim(cattlen, catt, bdxtail, bxtcatt); + temp16alen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, 2.0 * bdx, + temp16a); + temp16blen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, bdxtail, + temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, + temp64, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (bdytail != 0.0) { + temp16alen = scale_expansion_zeroelim(bytcalen, bytca, bdytail, temp16a); + bytcatlen = scale_expansion_zeroelim(catlen, cat, bdytail, bytcat); + temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, 2.0 * bdy, + temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + + + temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, bdytail, + temp32a); + bytcattlen = scale_expansion_zeroelim(cattlen, catt, bdytail, bytcatt); + temp16alen = scale_expansion_zeroelim(bytcattlen, bytcatt, 2.0 * bdy, + temp16a); + temp16blen = scale_expansion_zeroelim(bytcattlen, bytcatt, bdytail, + temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, + temp64, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + if ((cdxtail != 0.0) || (cdytail != 0.0)) { + if ((adxtail != 0.0) || (adytail != 0.0) + || (bdxtail != 0.0) || (bdytail != 0.0)) { + Two_Product(adxtail, bdy, ti1, ti0); + Two_Product(adx, bdytail, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]); + u[3] = u3; + negate = -ady; + Two_Product(bdxtail, negate, ti1, ti0); + negate = -adytail; + Two_Product(bdx, negate, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]); + v[3] = v3; + abtlen = fast_expansion_sum_zeroelim(4, u, 4, v, abt); + + Two_Product(adxtail, bdytail, ti1, ti0); + Two_Product(bdxtail, adytail, tj1, tj0); + Two_Two_Diff(ti1, ti0, tj1, tj0, abtt3, abtt[2], abtt[1], abtt[0]); + abtt[3] = abtt3; + abttlen = 4; + } else { + abt[0] = 0.0; + abtlen = 1; + abtt[0] = 0.0; + abttlen = 1; + } + + if (cdxtail != 0.0) { + temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, cdxtail, temp16a); + cxtabtlen = scale_expansion_zeroelim(abtlen, abt, cdxtail, cxtabt); + temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, 2.0 * cdx, + temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + if (adytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, bb, cdxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail, + temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, + temp16a, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (bdytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, aa, -cdxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail, + temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, + temp16a, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + + temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, cdxtail, + temp32a); + cxtabttlen = scale_expansion_zeroelim(abttlen, abtt, cdxtail, cxtabtt); + temp16alen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, 2.0 * cdx, + temp16a); + temp16blen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, cdxtail, + temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, + temp64, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (cdytail != 0.0) { + temp16alen = scale_expansion_zeroelim(cytablen, cytab, cdytail, temp16a); + cytabtlen = scale_expansion_zeroelim(abtlen, abt, cdytail, cytabt); + temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, 2.0 * cdy, + temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + + + temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, cdytail, + temp32a); + cytabttlen = scale_expansion_zeroelim(abttlen, abtt, cdytail, cytabtt); + temp16alen = scale_expansion_zeroelim(cytabttlen, cytabtt, 2.0 * cdy, + temp16a); + temp16blen = scale_expansion_zeroelim(cytabttlen, cytabtt, cdytail, + temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, + temp64, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + + return finnow[finlength - 1]; +} + +REAL incircle(pa, pb, pc, pd) +REAL *pa; +REAL *pb; +REAL *pc; +REAL *pd; +{ + REAL adx, bdx, cdx, ady, bdy, cdy; + REAL bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady; + REAL alift, blift, clift; + REAL det; + REAL permanent, errbound; + REAL inc; + + FPU_ROUND_DOUBLE; + + adx = pa[0] - pd[0]; + bdx = pb[0] - pd[0]; + cdx = pc[0] - pd[0]; + ady = pa[1] - pd[1]; + bdy = pb[1] - pd[1]; + cdy = pc[1] - pd[1]; + + bdxcdy = bdx * cdy; + cdxbdy = cdx * bdy; + alift = adx * adx + ady * ady; + + cdxady = cdx * ady; + adxcdy = adx * cdy; + blift = bdx * bdx + bdy * bdy; + + adxbdy = adx * bdy; + bdxady = bdx * ady; + clift = cdx * cdx + cdy * cdy; + + det = alift * (bdxcdy - cdxbdy) + + blift * (cdxady - adxcdy) + + clift * (adxbdy - bdxady); + + permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * alift + + (Absolute(cdxady) + Absolute(adxcdy)) * blift + + (Absolute(adxbdy) + Absolute(bdxady)) * clift; + errbound = iccerrboundA * permanent; + if ((det > errbound) || (-det > errbound)) { + FPU_RESTORE; + return det; + } + + inc = incircleadapt(pa, pb, pc, pd, permanent); + FPU_RESTORE; + return inc; +} + +/*****************************************************************************/ +/* */ +/* inspherefast() Approximate 3D insphere test. Nonrobust. */ +/* insphereexact() Exact 3D insphere test. Robust. */ +/* insphereslow() Another exact 3D insphere test. Robust. */ +/* insphere() Adaptive exact 3D insphere test. Robust. */ +/* */ +/* Return a positive value if the point pe lies inside the */ +/* sphere passing through pa, pb, pc, and pd; a negative value */ +/* if it lies outside; and zero if the five points are */ +/* cospherical. The points pa, pb, pc, and pd must be ordered */ +/* so that they have a positive orientation (as defined by */ +/* orient3d()), or the sign of the result will be reversed. */ +/* */ +/* Only the first and last routine should be used; the middle two are for */ +/* timings. */ +/* */ +/* The last three use exact arithmetic to ensure a correct answer. The */ +/* result returned is the determinant of a matrix. In insphere() only, */ +/* this determinant is computed adaptively, in the sense that exact */ +/* arithmetic is used only to the degree it is needed to ensure that the */ +/* returned value has the correct sign. Hence, insphere() is usually quite */ +/* fast, but will run more slowly when the input points are cospherical or */ +/* nearly so. */ +/* */ +/*****************************************************************************/ + +static REAL insphereexact(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe) +{ + INEXACT REAL axby1, bxcy1, cxdy1, dxey1, exay1; + INEXACT REAL bxay1, cxby1, dxcy1, exdy1, axey1; + INEXACT REAL axcy1, bxdy1, cxey1, dxay1, exby1; + INEXACT REAL cxay1, dxby1, excy1, axdy1, bxey1; + REAL axby0, bxcy0, cxdy0, dxey0, exay0; + REAL bxay0, cxby0, dxcy0, exdy0, axey0; + REAL axcy0, bxdy0, cxey0, dxay0, exby0; + REAL cxay0, dxby0, excy0, axdy0, bxey0; + REAL ab[4], bc[4], cd[4], de[4], ea[4]; + REAL ac[4], bd[4], ce[4], da[4], eb[4]; + REAL temp8a[8], temp8b[8], temp16[16]; + int temp8alen, temp8blen, temp16len; + REAL abc[24], bcd[24], cde[24], dea[24], eab[24]; + REAL abd[24], bce[24], cda[24], deb[24], eac[24]; + int abclen, bcdlen, cdelen, dealen, eablen; + int abdlen, bcelen, cdalen, deblen, eaclen; + REAL temp48a[48], temp48b[48]; + int temp48alen, temp48blen; + REAL abcd[96], bcde[96], cdea[96], deab[96], eabc[96]; + int abcdlen, bcdelen, cdealen, deablen, eabclen; + REAL temp192[192]; + REAL det384x[384], det384y[384], det384z[384]; + int xlen, ylen, zlen; + REAL detxy[768]; + int xylen; + REAL adet[1152], bdet[1152], cdet[1152], ddet[1152], edet[1152]; + int alen, blen, clen, dlen, elen; + REAL abdet[2304], cddet[2304], cdedet[3456]; + int ablen, cdlen; + REAL deter[5760]; + int deterlen; + int i; + + INEXACT REAL bvirt; + REAL avirt, bround, around; + INEXACT REAL c; + INEXACT REAL abig; + REAL ahi, alo, bhi, blo; + REAL err1, err2, err3; + INEXACT REAL _i, _j; + REAL _0; + + Two_Product(pa[0], pb[1], axby1, axby0); + Two_Product(pb[0], pa[1], bxay1, bxay0); + Two_Two_Diff(axby1, axby0, bxay1, bxay0, ab[3], ab[2], ab[1], ab[0]); + + Two_Product(pb[0], pc[1], bxcy1, bxcy0); + Two_Product(pc[0], pb[1], cxby1, cxby0); + Two_Two_Diff(bxcy1, bxcy0, cxby1, cxby0, bc[3], bc[2], bc[1], bc[0]); + + Two_Product(pc[0], pd[1], cxdy1, cxdy0); + Two_Product(pd[0], pc[1], dxcy1, dxcy0); + Two_Two_Diff(cxdy1, cxdy0, dxcy1, dxcy0, cd[3], cd[2], cd[1], cd[0]); + + Two_Product(pd[0], pe[1], dxey1, dxey0); + Two_Product(pe[0], pd[1], exdy1, exdy0); + Two_Two_Diff(dxey1, dxey0, exdy1, exdy0, de[3], de[2], de[1], de[0]); + + Two_Product(pe[0], pa[1], exay1, exay0); + Two_Product(pa[0], pe[1], axey1, axey0); + Two_Two_Diff(exay1, exay0, axey1, axey0, ea[3], ea[2], ea[1], ea[0]); + + Two_Product(pa[0], pc[1], axcy1, axcy0); + Two_Product(pc[0], pa[1], cxay1, cxay0); + Two_Two_Diff(axcy1, axcy0, cxay1, cxay0, ac[3], ac[2], ac[1], ac[0]); + + Two_Product(pb[0], pd[1], bxdy1, bxdy0); + Two_Product(pd[0], pb[1], dxby1, dxby0); + Two_Two_Diff(bxdy1, bxdy0, dxby1, dxby0, bd[3], bd[2], bd[1], bd[0]); + + Two_Product(pc[0], pe[1], cxey1, cxey0); + Two_Product(pe[0], pc[1], excy1, excy0); + Two_Two_Diff(cxey1, cxey0, excy1, excy0, ce[3], ce[2], ce[1], ce[0]); + + Two_Product(pd[0], pa[1], dxay1, dxay0); + Two_Product(pa[0], pd[1], axdy1, axdy0); + Two_Two_Diff(dxay1, dxay0, axdy1, axdy0, da[3], da[2], da[1], da[0]); + + Two_Product(pe[0], pb[1], exby1, exby0); + Two_Product(pb[0], pe[1], bxey1, bxey0); + Two_Two_Diff(exby1, exby0, bxey1, bxey0, eb[3], eb[2], eb[1], eb[0]); + + temp8alen = scale_expansion_zeroelim(4, bc, pa[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, ac, -pb[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, + temp16); + temp8alen = scale_expansion_zeroelim(4, ab, pc[2], temp8a); + abclen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, + abc); + + temp8alen = scale_expansion_zeroelim(4, cd, pb[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, bd, -pc[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, + temp16); + temp8alen = scale_expansion_zeroelim(4, bc, pd[2], temp8a); + bcdlen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, + bcd); + + temp8alen = scale_expansion_zeroelim(4, de, pc[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, ce, -pd[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, + temp16); + temp8alen = scale_expansion_zeroelim(4, cd, pe[2], temp8a); + cdelen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, + cde); + + temp8alen = scale_expansion_zeroelim(4, ea, pd[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, da, -pe[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, + temp16); + temp8alen = scale_expansion_zeroelim(4, de, pa[2], temp8a); + dealen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, + dea); + + temp8alen = scale_expansion_zeroelim(4, ab, pe[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, eb, -pa[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, + temp16); + temp8alen = scale_expansion_zeroelim(4, ea, pb[2], temp8a); + eablen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, + eab); + + temp8alen = scale_expansion_zeroelim(4, bd, pa[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, da, pb[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, + temp16); + temp8alen = scale_expansion_zeroelim(4, ab, pd[2], temp8a); + abdlen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, + abd); + + temp8alen = scale_expansion_zeroelim(4, ce, pb[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, eb, pc[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, + temp16); + temp8alen = scale_expansion_zeroelim(4, bc, pe[2], temp8a); + bcelen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, + bce); + + temp8alen = scale_expansion_zeroelim(4, da, pc[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, ac, pd[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, + temp16); + temp8alen = scale_expansion_zeroelim(4, cd, pa[2], temp8a); + cdalen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, + cda); + + temp8alen = scale_expansion_zeroelim(4, eb, pd[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, bd, pe[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, + temp16); + temp8alen = scale_expansion_zeroelim(4, de, pb[2], temp8a); + deblen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, + deb); + + temp8alen = scale_expansion_zeroelim(4, ac, pe[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, ce, pa[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, + temp16); + temp8alen = scale_expansion_zeroelim(4, ea, pc[2], temp8a); + eaclen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, + eac); + + temp48alen = fast_expansion_sum_zeroelim(cdelen, cde, bcelen, bce, temp48a); + temp48blen = fast_expansion_sum_zeroelim(deblen, deb, bcdlen, bcd, temp48b); + for (i = 0; i < temp48blen; i++) { + temp48b[i] = -temp48b[i]; + } + bcdelen = fast_expansion_sum_zeroelim(temp48alen, temp48a, + temp48blen, temp48b, bcde); + xlen = scale_expansion_zeroelim(bcdelen, bcde, pa[0], temp192); + xlen = scale_expansion_zeroelim(xlen, temp192, pa[0], det384x); + ylen = scale_expansion_zeroelim(bcdelen, bcde, pa[1], temp192); + ylen = scale_expansion_zeroelim(ylen, temp192, pa[1], det384y); + zlen = scale_expansion_zeroelim(bcdelen, bcde, pa[2], temp192); + zlen = scale_expansion_zeroelim(zlen, temp192, pa[2], det384z); + xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy); + alen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, adet); + + temp48alen = fast_expansion_sum_zeroelim(dealen, dea, cdalen, cda, temp48a); + temp48blen = fast_expansion_sum_zeroelim(eaclen, eac, cdelen, cde, temp48b); + for (i = 0; i < temp48blen; i++) { + temp48b[i] = -temp48b[i]; + } + cdealen = fast_expansion_sum_zeroelim(temp48alen, temp48a, + temp48blen, temp48b, cdea); + xlen = scale_expansion_zeroelim(cdealen, cdea, pb[0], temp192); + xlen = scale_expansion_zeroelim(xlen, temp192, pb[0], det384x); + ylen = scale_expansion_zeroelim(cdealen, cdea, pb[1], temp192); + ylen = scale_expansion_zeroelim(ylen, temp192, pb[1], det384y); + zlen = scale_expansion_zeroelim(cdealen, cdea, pb[2], temp192); + zlen = scale_expansion_zeroelim(zlen, temp192, pb[2], det384z); + xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy); + blen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, bdet); + + temp48alen = fast_expansion_sum_zeroelim(eablen, eab, deblen, deb, temp48a); + temp48blen = fast_expansion_sum_zeroelim(abdlen, abd, dealen, dea, temp48b); + for (i = 0; i < temp48blen; i++) { + temp48b[i] = -temp48b[i]; + } + deablen = fast_expansion_sum_zeroelim(temp48alen, temp48a, + temp48blen, temp48b, deab); + xlen = scale_expansion_zeroelim(deablen, deab, pc[0], temp192); + xlen = scale_expansion_zeroelim(xlen, temp192, pc[0], det384x); + ylen = scale_expansion_zeroelim(deablen, deab, pc[1], temp192); + ylen = scale_expansion_zeroelim(ylen, temp192, pc[1], det384y); + zlen = scale_expansion_zeroelim(deablen, deab, pc[2], temp192); + zlen = scale_expansion_zeroelim(zlen, temp192, pc[2], det384z); + xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy); + clen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, cdet); + + temp48alen = fast_expansion_sum_zeroelim(abclen, abc, eaclen, eac, temp48a); + temp48blen = fast_expansion_sum_zeroelim(bcelen, bce, eablen, eab, temp48b); + for (i = 0; i < temp48blen; i++) { + temp48b[i] = -temp48b[i]; + } + eabclen = fast_expansion_sum_zeroelim(temp48alen, temp48a, + temp48blen, temp48b, eabc); + xlen = scale_expansion_zeroelim(eabclen, eabc, pd[0], temp192); + xlen = scale_expansion_zeroelim(xlen, temp192, pd[0], det384x); + ylen = scale_expansion_zeroelim(eabclen, eabc, pd[1], temp192); + ylen = scale_expansion_zeroelim(ylen, temp192, pd[1], det384y); + zlen = scale_expansion_zeroelim(eabclen, eabc, pd[2], temp192); + zlen = scale_expansion_zeroelim(zlen, temp192, pd[2], det384z); + xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy); + dlen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, ddet); + + temp48alen = fast_expansion_sum_zeroelim(bcdlen, bcd, abdlen, abd, temp48a); + temp48blen = fast_expansion_sum_zeroelim(cdalen, cda, abclen, abc, temp48b); + for (i = 0; i < temp48blen; i++) { + temp48b[i] = -temp48b[i]; + } + abcdlen = fast_expansion_sum_zeroelim(temp48alen, temp48a, + temp48blen, temp48b, abcd); + xlen = scale_expansion_zeroelim(abcdlen, abcd, pe[0], temp192); + xlen = scale_expansion_zeroelim(xlen, temp192, pe[0], det384x); + ylen = scale_expansion_zeroelim(abcdlen, abcd, pe[1], temp192); + ylen = scale_expansion_zeroelim(ylen, temp192, pe[1], det384y); + zlen = scale_expansion_zeroelim(abcdlen, abcd, pe[2], temp192); + zlen = scale_expansion_zeroelim(zlen, temp192, pe[2], det384z); + xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy); + elen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, edet); + + ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet); + cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet); + cdelen = fast_expansion_sum_zeroelim(cdlen, cddet, elen, edet, cdedet); + deterlen = fast_expansion_sum_zeroelim(ablen, abdet, cdelen, cdedet, deter); + + return deter[deterlen - 1]; +} + +static REAL insphereadapt(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe, + REAL permanent) +{ + INEXACT REAL aex, bex, cex, dex, aey, bey, cey, dey, aez, bez, cez, dez; + REAL det, errbound; + + INEXACT REAL aexbey1, bexaey1, bexcey1, cexbey1; + INEXACT REAL cexdey1, dexcey1, dexaey1, aexdey1; + INEXACT REAL aexcey1, cexaey1, bexdey1, dexbey1; + REAL aexbey0, bexaey0, bexcey0, cexbey0; + REAL cexdey0, dexcey0, dexaey0, aexdey0; + REAL aexcey0, cexaey0, bexdey0, dexbey0; + REAL ab[4], bc[4], cd[4], da[4], ac[4], bd[4]; + INEXACT REAL ab3, bc3, cd3, da3, ac3, bd3; + REAL abeps, bceps, cdeps, daeps, aceps, bdeps; + REAL temp8a[8], temp8b[8], temp8c[8], temp16[16], temp24[24], temp48[48]; + int temp8alen, temp8blen, temp8clen, temp16len, temp24len, temp48len; + REAL xdet[96], ydet[96], zdet[96], xydet[192]; + int xlen, ylen, zlen, xylen; + REAL adet[288], bdet[288], cdet[288], ddet[288]; + int alen, blen, clen, dlen; + REAL abdet[576], cddet[576]; + int ablen, cdlen; + REAL fin1[1152]; + int finlength; + + REAL aextail, bextail, cextail, dextail; + REAL aeytail, beytail, ceytail, deytail; + REAL aeztail, beztail, ceztail, deztail; + + INEXACT REAL bvirt; + REAL avirt, bround, around; + INEXACT REAL c; + INEXACT REAL abig; + REAL ahi, alo, bhi, blo; + REAL err1, err2, err3; + INEXACT REAL _i, _j; + REAL _0; + + aex = (REAL) (pa[0] - pe[0]); + bex = (REAL) (pb[0] - pe[0]); + cex = (REAL) (pc[0] - pe[0]); + dex = (REAL) (pd[0] - pe[0]); + aey = (REAL) (pa[1] - pe[1]); + bey = (REAL) (pb[1] - pe[1]); + cey = (REAL) (pc[1] - pe[1]); + dey = (REAL) (pd[1] - pe[1]); + aez = (REAL) (pa[2] - pe[2]); + bez = (REAL) (pb[2] - pe[2]); + cez = (REAL) (pc[2] - pe[2]); + dez = (REAL) (pd[2] - pe[2]); + + Two_Product(aex, bey, aexbey1, aexbey0); + Two_Product(bex, aey, bexaey1, bexaey0); + Two_Two_Diff(aexbey1, aexbey0, bexaey1, bexaey0, ab3, ab[2], ab[1], ab[0]); + ab[3] = ab3; + + Two_Product(bex, cey, bexcey1, bexcey0); + Two_Product(cex, bey, cexbey1, cexbey0); + Two_Two_Diff(bexcey1, bexcey0, cexbey1, cexbey0, bc3, bc[2], bc[1], bc[0]); + bc[3] = bc3; + + Two_Product(cex, dey, cexdey1, cexdey0); + Two_Product(dex, cey, dexcey1, dexcey0); + Two_Two_Diff(cexdey1, cexdey0, dexcey1, dexcey0, cd3, cd[2], cd[1], cd[0]); + cd[3] = cd3; + + Two_Product(dex, aey, dexaey1, dexaey0); + Two_Product(aex, dey, aexdey1, aexdey0); + Two_Two_Diff(dexaey1, dexaey0, aexdey1, aexdey0, da3, da[2], da[1], da[0]); + da[3] = da3; + + Two_Product(aex, cey, aexcey1, aexcey0); + Two_Product(cex, aey, cexaey1, cexaey0); + Two_Two_Diff(aexcey1, aexcey0, cexaey1, cexaey0, ac3, ac[2], ac[1], ac[0]); + ac[3] = ac3; + + Two_Product(bex, dey, bexdey1, bexdey0); + Two_Product(dex, bey, dexbey1, dexbey0); + Two_Two_Diff(bexdey1, bexdey0, dexbey1, dexbey0, bd3, bd[2], bd[1], bd[0]); + bd[3] = bd3; + + temp8alen = scale_expansion_zeroelim(4, cd, bez, temp8a); + temp8blen = scale_expansion_zeroelim(4, bd, -cez, temp8b); + temp8clen = scale_expansion_zeroelim(4, bc, dez, temp8c); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, + temp8blen, temp8b, temp16); + temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c, + temp16len, temp16, temp24); + temp48len = scale_expansion_zeroelim(temp24len, temp24, aex, temp48); + xlen = scale_expansion_zeroelim(temp48len, temp48, -aex, xdet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, aey, temp48); + ylen = scale_expansion_zeroelim(temp48len, temp48, -aey, ydet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, aez, temp48); + zlen = scale_expansion_zeroelim(temp48len, temp48, -aez, zdet); + xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet); + alen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, adet); + + temp8alen = scale_expansion_zeroelim(4, da, cez, temp8a); + temp8blen = scale_expansion_zeroelim(4, ac, dez, temp8b); + temp8clen = scale_expansion_zeroelim(4, cd, aez, temp8c); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, + temp8blen, temp8b, temp16); + temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c, + temp16len, temp16, temp24); + temp48len = scale_expansion_zeroelim(temp24len, temp24, bex, temp48); + xlen = scale_expansion_zeroelim(temp48len, temp48, bex, xdet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, bey, temp48); + ylen = scale_expansion_zeroelim(temp48len, temp48, bey, ydet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, bez, temp48); + zlen = scale_expansion_zeroelim(temp48len, temp48, bez, zdet); + xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet); + blen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, bdet); + + temp8alen = scale_expansion_zeroelim(4, ab, dez, temp8a); + temp8blen = scale_expansion_zeroelim(4, bd, aez, temp8b); + temp8clen = scale_expansion_zeroelim(4, da, bez, temp8c); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, + temp8blen, temp8b, temp16); + temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c, + temp16len, temp16, temp24); + temp48len = scale_expansion_zeroelim(temp24len, temp24, cex, temp48); + xlen = scale_expansion_zeroelim(temp48len, temp48, -cex, xdet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, cey, temp48); + ylen = scale_expansion_zeroelim(temp48len, temp48, -cey, ydet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, cez, temp48); + zlen = scale_expansion_zeroelim(temp48len, temp48, -cez, zdet); + xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet); + clen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, cdet); + + temp8alen = scale_expansion_zeroelim(4, bc, aez, temp8a); + temp8blen = scale_expansion_zeroelim(4, ac, -bez, temp8b); + temp8clen = scale_expansion_zeroelim(4, ab, cez, temp8c); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, + temp8blen, temp8b, temp16); + temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c, + temp16len, temp16, temp24); + temp48len = scale_expansion_zeroelim(temp24len, temp24, dex, temp48); + xlen = scale_expansion_zeroelim(temp48len, temp48, dex, xdet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, dey, temp48); + ylen = scale_expansion_zeroelim(temp48len, temp48, dey, ydet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, dez, temp48); + zlen = scale_expansion_zeroelim(temp48len, temp48, dez, zdet); + xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet); + dlen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, ddet); + + ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet); + cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet); + finlength = fast_expansion_sum_zeroelim(ablen, abdet, cdlen, cddet, fin1); + + det = estimate(finlength, fin1); + errbound = isperrboundB * permanent; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Diff_Tail(pa[0], pe[0], aex, aextail); + Two_Diff_Tail(pa[1], pe[1], aey, aeytail); + Two_Diff_Tail(pa[2], pe[2], aez, aeztail); + Two_Diff_Tail(pb[0], pe[0], bex, bextail); + Two_Diff_Tail(pb[1], pe[1], bey, beytail); + Two_Diff_Tail(pb[2], pe[2], bez, beztail); + Two_Diff_Tail(pc[0], pe[0], cex, cextail); + Two_Diff_Tail(pc[1], pe[1], cey, ceytail); + Two_Diff_Tail(pc[2], pe[2], cez, ceztail); + Two_Diff_Tail(pd[0], pe[0], dex, dextail); + Two_Diff_Tail(pd[1], pe[1], dey, deytail); + Two_Diff_Tail(pd[2], pe[2], dez, deztail); + if ((aextail == 0.0) && (aeytail == 0.0) && (aeztail == 0.0) + && (bextail == 0.0) && (beytail == 0.0) && (beztail == 0.0) + && (cextail == 0.0) && (ceytail == 0.0) && (ceztail == 0.0) + && (dextail == 0.0) && (deytail == 0.0) && (deztail == 0.0)) { + return det; + } + + errbound = isperrboundC * permanent + resulterrbound * Absolute(det); + abeps = (aex * beytail + bey * aextail) + - (aey * bextail + bex * aeytail); + bceps = (bex * ceytail + cey * bextail) + - (bey * cextail + cex * beytail); + cdeps = (cex * deytail + dey * cextail) + - (cey * dextail + dex * ceytail); + daeps = (dex * aeytail + aey * dextail) + - (dey * aextail + aex * deytail); + aceps = (aex * ceytail + cey * aextail) + - (aey * cextail + cex * aeytail); + bdeps = (bex * deytail + dey * bextail) + - (bey * dextail + dex * beytail); + det += (((bex * bex + bey * bey + bez * bez) + * ((cez * daeps + dez * aceps + aez * cdeps) + + (ceztail * da3 + deztail * ac3 + aeztail * cd3)) + + (dex * dex + dey * dey + dez * dez) + * ((aez * bceps - bez * aceps + cez * abeps) + + (aeztail * bc3 - beztail * ac3 + ceztail * ab3))) + - ((aex * aex + aey * aey + aez * aez) + * ((bez * cdeps - cez * bdeps + dez * bceps) + + (beztail * cd3 - ceztail * bd3 + deztail * bc3)) + + (cex * cex + cey * cey + cez * cez) + * ((dez * abeps + aez * bdeps + bez * daeps) + + (deztail * ab3 + aeztail * bd3 + beztail * da3)))) + + 2.0 * (((bex * bextail + bey * beytail + bez * beztail) + * (cez * da3 + dez * ac3 + aez * cd3) + + (dex * dextail + dey * deytail + dez * deztail) + * (aez * bc3 - bez * ac3 + cez * ab3)) + - ((aex * aextail + aey * aeytail + aez * aeztail) + * (bez * cd3 - cez * bd3 + dez * bc3) + + (cex * cextail + cey * ceytail + cez * ceztail) + * (dez * ab3 + aez * bd3 + bez * da3))); + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + return insphereexact(pa, pb, pc, pd, pe); +} + +REAL insphere(pa, pb, pc, pd, pe) +REAL *pa; +REAL *pb; +REAL *pc; +REAL *pd; +REAL *pe; +{ + REAL aex, bex, cex, dex; + REAL aey, bey, cey, dey; + REAL aez, bez, cez, dez; + REAL aexbey, bexaey, bexcey, cexbey, cexdey, dexcey, dexaey, aexdey; + REAL aexcey, cexaey, bexdey, dexbey; + REAL alift, blift, clift, dlift; + REAL ab, bc, cd, da, ac, bd; + REAL abc, bcd, cda, dab; + REAL aezplus, bezplus, cezplus, dezplus; + REAL aexbeyplus, bexaeyplus, bexceyplus, cexbeyplus; + REAL cexdeyplus, dexceyplus, dexaeyplus, aexdeyplus; + REAL aexceyplus, cexaeyplus, bexdeyplus, dexbeyplus; + REAL det; + REAL permanent, errbound; + REAL ins; + + FPU_ROUND_DOUBLE; + + aex = pa[0] - pe[0]; + bex = pb[0] - pe[0]; + cex = pc[0] - pe[0]; + dex = pd[0] - pe[0]; + aey = pa[1] - pe[1]; + bey = pb[1] - pe[1]; + cey = pc[1] - pe[1]; + dey = pd[1] - pe[1]; + aez = pa[2] - pe[2]; + bez = pb[2] - pe[2]; + cez = pc[2] - pe[2]; + dez = pd[2] - pe[2]; + + aexbey = aex * bey; + bexaey = bex * aey; + ab = aexbey - bexaey; + bexcey = bex * cey; + cexbey = cex * bey; + bc = bexcey - cexbey; + cexdey = cex * dey; + dexcey = dex * cey; + cd = cexdey - dexcey; + dexaey = dex * aey; + aexdey = aex * dey; + da = dexaey - aexdey; + + aexcey = aex * cey; + cexaey = cex * aey; + ac = aexcey - cexaey; + bexdey = bex * dey; + dexbey = dex * bey; + bd = bexdey - dexbey; + + abc = aez * bc - bez * ac + cez * ab; + bcd = bez * cd - cez * bd + dez * bc; + cda = cez * da + dez * ac + aez * cd; + dab = dez * ab + aez * bd + bez * da; + + alift = aex * aex + aey * aey + aez * aez; + blift = bex * bex + bey * bey + bez * bez; + clift = cex * cex + cey * cey + cez * cez; + dlift = dex * dex + dey * dey + dez * dez; + + det = (dlift * abc - clift * dab) + (blift * cda - alift * bcd); + + aezplus = Absolute(aez); + bezplus = Absolute(bez); + cezplus = Absolute(cez); + dezplus = Absolute(dez); + aexbeyplus = Absolute(aexbey); + bexaeyplus = Absolute(bexaey); + bexceyplus = Absolute(bexcey); + cexbeyplus = Absolute(cexbey); + cexdeyplus = Absolute(cexdey); + dexceyplus = Absolute(dexcey); + dexaeyplus = Absolute(dexaey); + aexdeyplus = Absolute(aexdey); + aexceyplus = Absolute(aexcey); + cexaeyplus = Absolute(cexaey); + bexdeyplus = Absolute(bexdey); + dexbeyplus = Absolute(dexbey); + permanent = ((cexdeyplus + dexceyplus) * bezplus + + (dexbeyplus + bexdeyplus) * cezplus + + (bexceyplus + cexbeyplus) * dezplus) + * alift + + ((dexaeyplus + aexdeyplus) * cezplus + + (aexceyplus + cexaeyplus) * dezplus + + (cexdeyplus + dexceyplus) * aezplus) + * blift + + ((aexbeyplus + bexaeyplus) * dezplus + + (bexdeyplus + dexbeyplus) * aezplus + + (dexaeyplus + aexdeyplus) * bezplus) + * clift + + ((bexceyplus + cexbeyplus) * aezplus + + (cexaeyplus + aexceyplus) * bezplus + + (aexbeyplus + bexaeyplus) * cezplus) + * dlift; + errbound = isperrboundA * permanent; + if ((det > errbound) || (-det > errbound)) { + FPU_RESTORE; + return det; + } + + ins = insphereadapt(pa, pb, pc, pd, pe, permanent); + FPU_RESTORE; + return ins; +} Index: trunk/src_3rd/gts/predicates.h =================================================================== --- trunk/src_3rd/gts/predicates.h (nonexistent) +++ trunk/src_3rd/gts/predicates.h (revision 1065) @@ -0,0 +1,41 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/* Header file for robust predicates by Jonathan Richard Shewchuk */ + +#ifndef __PREDICATES_H__ +#define __PREDICATES_H__ + +double orient2d (double * pa, + double * pb, + double * pc); +double orient3d (double * pa, + double * pb, + double * pc, + double * pd); +double incircle (double * pa, + double * pb, + double * pc, + double * pd); +double insphere (double * pa, + double * pb, + double * pc, + double * pd, + double * pe); + +#endif /* __PREDICATES_H__ */ Index: trunk/src_3rd/gts/psurface.c =================================================================== --- trunk/src_3rd/gts/psurface.c (nonexistent) +++ trunk/src_3rd/gts/psurface.c (revision 1065) @@ -0,0 +1,471 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include +#include "gts.h" + +#define HEAP_INSERT_OBJECT(h, e) (GTS_OBJECT (e)->reserved =\ + gts_eheap_insert (h, e)) +#define HEAP_REMOVE_OBJECT(h, e) (gts_eheap_remove (h, GTS_OBJECT (e)->reserved),\ + GTS_OBJECT (e)->reserved = NULL) + +static void psurface_destroy (GtsObject * object) +{ + GtsPSurface * ps = GTS_PSURFACE (object); + guint i; + + if (!GTS_PSURFACE_IS_CLOSED (ps)) + gts_psurface_close (ps); + + for (i = 0; i < ps->split->len; i++) + if (g_ptr_array_index (ps->split, i)) + gts_object_destroy (GTS_OBJECT (g_ptr_array_index (ps->split, i))); + g_ptr_array_free (ps->split, TRUE); + + (* GTS_OBJECT_CLASS (gts_psurface_class ())->parent_class->destroy) (object); +} + +static void psurface_class_init (GtsObjectClass * klass) +{ + klass->destroy = psurface_destroy; +} + +static void psurface_init (GtsPSurface * psurface) +{ + psurface->s = NULL; + psurface->split = g_ptr_array_new (); + psurface->split_class = gts_split_class (); + psurface->pos = psurface->min = 0; + psurface->vertices = psurface->faces = NULL; +} + +/** + * gts_psurface_class: + * + * Returns: the #GtsPSurfaceClass. + */ +GtsPSurfaceClass * gts_psurface_class (void) +{ + static GtsPSurfaceClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo psurface_info = { + "GtsPSurface", + sizeof (GtsPSurface), + sizeof (GtsPSurfaceClass), + (GtsObjectClassInitFunc) psurface_class_init, + (GtsObjectInitFunc) psurface_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (gts_object_class (), + &psurface_info); + } + + return klass; +} + +static GtsVertex * edge_collapse (GtsPSurface * ps, + GtsEdge * e, + GtsEHeap * heap, + GtsCoarsenFunc coarsen_func, + gpointer coarsen_data, + gdouble maxcosine2) +{ + GtsVertex * v1 = GTS_SEGMENT (e)->v1, * v2 = GTS_SEGMENT (e)->v2, * mid; + GtsSplit * vs; + GtsObject * o1, * o2; + + /* if the edge is degenerate (i.e. v1 == v2), destroy and return */ + if (v1 == v2) { + gts_object_destroy (GTS_OBJECT (e)); + return NULL; + } + + if (!gts_edge_collapse_is_valid (e) || + /* check that a non-manifold edge is not a contact edge */ + (g_slist_length (e->triangles) > 2 && gts_edge_is_contact (e) > 1)) { + GTS_OBJECT (e)->reserved = + gts_eheap_insert_with_key (heap, e, G_MAXDOUBLE); + return NULL; + } + + mid = (*coarsen_func) (e, ps->s->vertex_class, coarsen_data); + + if (gts_edge_collapse_creates_fold (e, mid, maxcosine2)) { + GTS_OBJECT (e)->reserved = + gts_eheap_insert_with_key (heap, e, G_MAXDOUBLE); + gts_object_destroy (GTS_OBJECT (mid)); + return NULL; + } + + if (GTS_OBJECT (v1)->reserved) + o1 = GTS_OBJECT (v1)->reserved; + else + o1 = GTS_OBJECT (v1); + if (GTS_OBJECT (v2)->reserved) + o2 = GTS_OBJECT (v2)->reserved; + else + o2 = GTS_OBJECT (v2); + vs = gts_split_new (ps->split_class, mid, o1, o2); + gts_split_collapse (vs, ps->s->edge_class, heap); + GTS_OBJECT (vs->v)->reserved = vs; + g_ptr_array_add (ps->split, vs); + + return mid; +} + +static void update_2nd_closest_neighbors (GtsVertex * v, GtsEHeap * heap) +{ + GSList * i = v->segments; + GSList * list = NULL; + + while (i) { + GtsSegment * s = i->data; + if (GTS_IS_EDGE (s)) { + GtsVertex * v1 = s->v1 == v ? s->v2 : s->v1; + GSList * j = v1->segments; + while (j) { + GtsSegment * s1 = j->data; + if (GTS_IS_EDGE (s1) && !g_slist_find (list, s1)) + list = g_slist_prepend (list, s1); + j = j->next; + } + } + i = i->next; + } + + i = list; + while (i) { + GtsEdge * e = i->data; + if (GTS_OBJECT (e)->reserved) + HEAP_REMOVE_OBJECT (heap, e); + HEAP_INSERT_OBJECT (heap, e); + i = i->next; + } + + g_slist_free (list); +} + +static gdouble edge_length2 (GtsEdge * e) +{ + return gts_point_distance2 (GTS_POINT (GTS_SEGMENT (e)->v1), + GTS_POINT (GTS_SEGMENT (e)->v2)); +} + +static void create_heap_coarsen (GtsEdge * e, GtsEHeap * heap) +{ + HEAP_INSERT_OBJECT (heap, e); +} + +/* #define DEBUG_FOLD */ +/* #define DEBUG_CONTACT_VERTEX */ + +#ifdef DEBUG_FOLD +static void check_fold (GtsTriangle * t, gdouble * maxcosine2) +{ + GtsEdge * e1 = t->e1, * e2 = t->e2, * e3 = t->e3; + + + if (gts_triangles_are_folded (e1->triangles, + GTS_SEGMENT (e1)->v1, + GTS_SEGMENT (e1)->v2, + *maxcosine2) || + gts_triangles_are_folded (e2->triangles, + GTS_SEGMENT (e2)->v1, + GTS_SEGMENT (e2)->v2, + *maxcosine2) || + gts_triangles_are_folded (e3->triangles, + GTS_SEGMENT (e3)->v1, + GTS_SEGMENT (e3)->v2, + *maxcosine2)) { + fprintf (stderr, "triangle %p:(%p,%p,%p) is folded\n", t, e1, e2, e3); + g_assert_not_reached (); + } +} +#endif + +/** + * gts_psurface_new: + * @klass: a #GtsPSurfaceClass. + * @surface: a #GtsSurface. + * @split_class: a #GtsSplitClass to use for the new progressive surface. + * @cost_func: cost function for the edge collapse algorithm. + * @cost_data: data to pass to @cost_func. + * @coarsen_func: the function returning the vertex replacement for the edge + * collapse. + * @coarsen_data: data to pass to @coarsen_func. + * @stop_func: the function to call to decide whether to stop the coarsening + * process. + * @stop_data: data to pass to @stop_func. + * @minangle: the minimum angle allowable between two neighboring triangles. + * This is used to avoid introducing folds in the mesh during simplification. + * + * This function works in exactly the same way as the + * gts_surface_coarsen() function, except that the history of edge + * collapse is saved in an array of #GtsSplit objects. This allows for + * dynamic continuous multiresolution control of the input @surface. + * + * Returns: a new progressive surface. + */ +GtsPSurface * gts_psurface_new (GtsPSurfaceClass * klass, + GtsSurface * surface, + GtsSplitClass * split_class, + GtsKeyFunc cost_func, + gpointer cost_data, + GtsCoarsenFunc coarsen_func, + gpointer coarsen_data, + GtsStopFunc stop_func, + gpointer stop_data, + gdouble minangle) +{ + GtsPSurface * psurface; + GtsEHeap * heap; + GtsEdge * e; + gdouble top_cost, maxcosine2; + guint i; + + g_return_val_if_fail (klass != NULL, NULL); + g_return_val_if_fail (surface != NULL, NULL); + g_return_val_if_fail (split_class != NULL, NULL); + g_return_val_if_fail (stop_func != NULL, NULL); + + psurface = GTS_PSURFACE (gts_object_new (GTS_OBJECT_CLASS (klass))); + psurface->s = surface; + psurface->split_class = split_class; + + if (cost_func == NULL) + cost_func = (GtsKeyFunc) edge_length2; + if (coarsen_func == NULL) + coarsen_func = (GtsCoarsenFunc) gts_segment_midvertex; + + heap = gts_eheap_new (cost_func, cost_data); + maxcosine2 = cos (minangle); maxcosine2 *= maxcosine2; + + gts_eheap_freeze (heap); + gts_surface_foreach_edge (surface, (GtsFunc) create_heap_coarsen, heap); + gts_eheap_thaw (heap); + /* we want to control edge destruction manually */ + gts_allow_floating_edges = TRUE; + while ((e = gts_eheap_remove_top (heap, &top_cost)) && + (top_cost < G_MAXDOUBLE) && + !(*stop_func) (top_cost, gts_eheap_size (heap) - + gts_edge_face_number (e, surface), stop_data)) { + GtsVertex * v = edge_collapse (psurface, e, heap, + coarsen_func, coarsen_data, maxcosine2); + if (v != NULL) { + update_2nd_closest_neighbors (v, heap); +#ifdef DEBUG_FOLD + { + GSList * triangles = gts_vertex_triangles (v, NULL), * i; + fprintf (stderr, "\n---- Check for folds ----\n%p: ", v); + i = triangles; + while (i) { + GtsTriangle * t = i->data; + fprintf (stderr, "%p:(%p,%p,%p) ", t, t->e1, t->e2, t->e3); + i = i->next; + } + fprintf (stderr, "\n"); + g_slist_free (triangles); + gts_surface_foreach_face (surface, (GtsFunc) check_fold, &maxcosine2); + } +#endif +#ifdef DEBUG_CONTACT_VERTEX + if (gts_vertex_is_contact (v, FALSE) != 1) { + FILE * fptr = fopen ("after", "wt"); + GSList * triangles = gts_vertex_triangles (v, NULL), * i; + + fprintf (stderr, "collapse of %p created a contact vertex\n", e); + + fprintf (fptr, + "(geometry \"sphere\" { = SPHERE 0.1 0. 0. 0. })\n" + "(normalization \"sphere\" none)\n"); + i = triangles; + while (i) { + gts_write_triangle (i->data, GTS_POINT (v), fptr); + i = i->next; + } + g_assert_not_reached (); + } +#endif + } + } + gts_allow_floating_edges = FALSE; + + /* set reserved field of remaining edges back to NULL */ + if (e) GTS_OBJECT (e)->reserved = NULL; + gts_eheap_foreach (heap, (GFunc) gts_object_reset_reserved, NULL); + + gts_eheap_destroy (heap); + + psurface->pos = psurface->split->len; + psurface->min = gts_surface_vertex_number (psurface->s); + + /* set reserved field of vertices (used to build the hierarchy) + back to NULL */ + for (i = 0; i < psurface->split->len; i++) { + GtsSplit * vs = g_ptr_array_index (psurface->split, i); + gts_object_reset_reserved (GTS_OBJECT (vs->v)); + } + + return psurface; +} + +/** + * gts_psurface_add_vertex: + * @ps: a #GtsPSurface. + * + * Adds a vertex to the progressive surface @ps by expanding the next + * available #GtsSplit. + * + * Returns: the expanded #GtsSplit or %NULL if all the #GtsSplit have already + * been expanded. + */ +GtsSplit * gts_psurface_add_vertex (GtsPSurface * ps) +{ + GtsSplit * vs; + + g_return_val_if_fail (ps != NULL, NULL); + g_return_val_if_fail (GTS_PSURFACE_IS_CLOSED (ps), NULL); + + if (ps->pos == 0) + return NULL; + + vs = g_ptr_array_index (ps->split, --ps->pos); + gts_split_expand (vs, ps->s, ps->s->edge_class); + + return vs; +} + +/** + * gts_psurface_remove_vertex: + * @ps: a #GtsPSurface. + * + * Removes one vertex from the progressive surface @ps by collapsing the first + * available #GtsSplit. + * + * Returns: the collapsed #GtsSplit or %NULL if all the #GtsSplit have already + * been collapsed. + */ +GtsSplit * gts_psurface_remove_vertex (GtsPSurface * ps) +{ + GtsSplit * vs; + + g_return_val_if_fail (ps != NULL, NULL); + g_return_val_if_fail (GTS_PSURFACE_IS_CLOSED (ps), NULL); + + if (ps->pos == ps->split->len) + return NULL; + + vs = g_ptr_array_index (ps->split, ps->pos++); + gts_split_collapse (vs, ps->s->edge_class, NULL); + + return vs; +} + +/** + * gts_psurface_max_vertex_number: + * @ps: a #GtsPSurface. + * + * Returns: the maximum number of vertices of @ps i.e. the number of vertices + * if all the #GtsSplit were expanded. + */ +guint gts_psurface_max_vertex_number (GtsPSurface * ps) +{ + g_return_val_if_fail (ps != NULL, 0); + + return ps->min + ps->split->len; +} + +/** + * gts_psurface_min_vertex_number: + * @ps: a #GtsPSurface. + * + * Returns: the minimum number of vertices of @ps i.e. the number of vertices + * if all the #GtsSplit were collapsed. + */ +guint gts_psurface_min_vertex_number (GtsPSurface * ps) +{ + g_return_val_if_fail (ps != NULL, 0); + + return ps->min; +} + +/** + * gts_psurface_set_vertex_number: + * @ps: a #GtsPSurface. + * @n: a number of vertices. + * + * Performs the required number of collapses or expansions to set the number + * of vertices of @ps to @n. + */ +void gts_psurface_set_vertex_number (GtsPSurface * ps, guint n) +{ + g_return_if_fail (ps != NULL); + g_return_if_fail (GTS_PSURFACE_IS_CLOSED (ps)); + + n = ps->min + ps->split->len - n; + while (ps->pos > n && gts_psurface_add_vertex (ps)) + ; + while (ps->pos < n && gts_psurface_remove_vertex (ps)) + ; +} + +/** + * gts_psurface_get_vertex_number: + * @ps: a #GtsPSurface. + * + * Returns: the current number of vertices of @ps. + */ +guint gts_psurface_get_vertex_number (GtsPSurface * ps) +{ + g_return_val_if_fail (ps != NULL, 0); + + if (!GTS_PSURFACE_IS_CLOSED (ps)) + return ps->min + ps->pos; + else + return ps->min + ps->split->len - ps->pos; +} + +/** + * gts_psurface_foreach_vertex: + * @ps: a #GtsPSurface. + * @func: a function to call for each vertex of @ps. + * @data: data to be passed to @func. + * + * Calls @func for each (potential) vertex of @ps, whether actually used + * or not. The vertices are called in the order they were created during the + * edge collapse operation. + */ +void gts_psurface_foreach_vertex (GtsPSurface * ps, + GtsFunc func, + gpointer data) +{ + guint i; + + g_return_if_fail (ps != NULL); + g_return_if_fail (func != NULL); + g_return_if_fail (GTS_PSURFACE_IS_CLOSED (ps)); + + for (i = 0; i < ps->split->len; i++) { + GtsSplit * vs = g_ptr_array_index (ps->split, i); + (*func) (vs->v, data); + } +} Index: trunk/src_3rd/gts/refine.c =================================================================== --- trunk/src_3rd/gts/refine.c (nonexistent) +++ trunk/src_3rd/gts/refine.c (revision 1065) @@ -0,0 +1,418 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "gts.h" + +/** + * gts_vertex_encroaches_edge: + * @v: a #GtsVertex. + * @e: a #GtsEdge. + * + * Returns: %TRUE if @v is strictly contained in the diametral circle of @e, + * %FALSE otherwise. + */ +gboolean gts_vertex_encroaches_edge (GtsVertex * v, GtsEdge * e) +{ + GtsPoint * p, * p1, * p2; + + g_return_val_if_fail (v != NULL, FALSE); + g_return_val_if_fail (e != NULL, FALSE); + + p = GTS_POINT (v); + p1 = GTS_POINT (GTS_SEGMENT (e)->v1); + p2 = GTS_POINT (GTS_SEGMENT (e)->v2); + + if ((p1->x - p->x)*(p2->x - p->x) + (p1->y - p->y)*(p2->y - p->y) < 0.0) + return TRUE; + return FALSE; +} + +/** + * gts_edge_is_encroached: + * @e: a #GtsEdge. + * @s: a #GtsSurface describing a (constrained) Delaunay triangulation. + * @encroaches: a #GtsEncroachFunc. + * @data: user data to be passed to @encroaches. + * + * Returns: a #GtsVertex belonging to @s and encroaching upon @e + * (as defined by @encroaches) or %NULL if there is none. + */ +GtsVertex * gts_edge_is_encroached (GtsEdge * e, + GtsSurface * s, + GtsEncroachFunc encroaches, + gpointer data) +{ + GSList * i; + + g_return_val_if_fail (e != NULL, NULL); + g_return_val_if_fail (s != NULL, NULL); + g_return_val_if_fail (encroaches != NULL, NULL); + + i = e->triangles; + while (i) { + GtsFace * f = i->data; + if (GTS_IS_FACE (f) && gts_face_has_parent_surface (f, s)) { + GtsVertex * v = gts_triangle_vertex_opposite (GTS_TRIANGLE (f), e); + if ((* encroaches) (v, e, s, data)) + return v; + } + i = i->next; + } + + return NULL; +} + +#define ALREADY_ENCROACHED(c) (GTS_OBJECT (c)->reserved) + +static void vertex_encroaches (GtsVertex * v, + GtsSurface * surface, + GtsFifo * encroached, + GtsEncroachFunc encroaches, + gpointer data) +{ + GSList * triangles, * i; + + g_return_if_fail (v != NULL); + g_return_if_fail (surface != NULL); + g_return_if_fail (encroached != NULL); + g_return_if_fail (encroaches != NULL); + + i = triangles = gts_vertex_triangles (v, NULL); + while (i) { + GtsFace * f = i->data; + if (GTS_IS_FACE (f) && gts_face_has_parent_surface (f, surface)) { + GtsEdge * e = gts_triangle_edge_opposite (i->data, v); + if (!ALREADY_ENCROACHED (e) && + GTS_IS_CONSTRAINT (e) && + (* encroaches) (v, e, surface, data)) { + gts_fifo_push (encroached, e); + ALREADY_ENCROACHED (e) = encroached; + } + } + i = i->next; + } + g_slist_free (triangles); +} + +static void make_encroached_fifo (GtsEdge * e, gpointer * datas) +{ + GtsFifo * fifo = datas[0]; + GtsSurface * s = datas[1]; + GtsEncroachFunc encroaches = (GtsEncroachFunc) datas[2]; + gpointer data = datas[3]; + + if (GTS_IS_CONSTRAINT (e) && + gts_edge_is_encroached (e, s, encroaches, data)) { + gts_fifo_push (fifo, e); + ALREADY_ENCROACHED (e) = fifo; + } +} + +#define SQUARE_ROOT_TWO 1.41421356237309504880168872420969807856967187 +#define DISTANCE_2D(v1, v2) (sqrt ((GTS_POINT (v2)->x - GTS_POINT (v1)->x)*\ + (GTS_POINT (v2)->x - GTS_POINT (v1)->x) +\ + (GTS_POINT (v2)->y - GTS_POINT (v1)->y)*\ + (GTS_POINT (v2)->y - GTS_POINT (v1)->y))) + +/* finds where to split the given edge to avoid infinite cycles. (see + Shewchuk's thesis for details */ +static GtsVertex * split_edge (GtsEdge * e, + GtsSurface * surface) +{ + GSList * i = e->triangles; + GtsEdge * c = NULL; + + /* look for constraints touching e */ + while (i && !c) { + GtsTriangle * t = i->data; + if (GTS_IS_FACE (t) && + gts_face_has_parent_surface (GTS_FACE (t), surface)) { + GtsEdge * e1, * e2; + if (t->e1 == e) { e1 = t->e2; e2 = t->e3; } + else if (t->e2 == e) { e1 = t->e1; e2 = t->e3; } + else { e1 = t->e1; e2 = t->e2; } + if (GTS_IS_CONSTRAINT (e1) && !GTS_IS_CONSTRAINT (e2)) + c = e1; + else if (GTS_IS_CONSTRAINT (e2) && !GTS_IS_CONSTRAINT (e1)) + c = e2; + } + i = i->next; + } + if (c) { + /* use power of two concentric shells */ + GtsVertex * v1 = GTS_SEGMENT (e)->v1; + GtsVertex * v2 = GTS_SEGMENT (e)->v2; + gdouble l = DISTANCE_2D (v1, v2); + gdouble nearestpower = 1., split; + + while (l > SQUARE_ROOT_TWO*nearestpower) + nearestpower *= 2.; + while (l < SQUARE_ROOT_TWO*nearestpower/2.) + nearestpower /= 2.; + split = nearestpower/l/2.; + + if (GTS_SEGMENT (c)->v1 == v2 || GTS_SEGMENT (c)->v2 == v2) + split = 1. - split; + return gts_vertex_new (surface->vertex_class, + (1. - split)*GTS_POINT (v1)->x + + split*GTS_POINT (v2)->x, + (1. - split)*GTS_POINT (v1)->y + + split*GTS_POINT (v2)->y, + (1. - split)*GTS_POINT (v1)->z + + split*GTS_POINT (v2)->z); + } + else + return gts_segment_midvertex (GTS_SEGMENT (e), surface->vertex_class); +} + +static gint split_encroached (GtsSurface * surface, + GtsFifo * encroached, + gint steiner_max, + GtsEncroachFunc encroaches, + gpointer data) +{ + GtsSegment * s; + + while (steiner_max-- != 0 && (s = gts_fifo_pop (encroached))) { + GtsVertex * v = split_edge (GTS_EDGE (s), surface); + GtsFace * boundary = gts_edge_is_boundary (GTS_EDGE (s), surface); + GtsFace * f = boundary; +#if 1 + GtsEdge * e1 = GTS_EDGE (gts_object_clone (GTS_OBJECT (s))); + GtsEdge * e2 = GTS_EDGE (gts_object_clone (GTS_OBJECT (s))); + + GTS_SEGMENT (e1)->v1 = s->v1; + s->v1->segments = g_slist_prepend (s->v1->segments, e1); + GTS_SEGMENT (e1)->v2 = v; + v->segments = g_slist_prepend (v->segments, e1); + + GTS_SEGMENT (e2)->v1 = v; + v->segments = g_slist_prepend (v->segments, e2); + GTS_SEGMENT (e2)->v2 = s->v2; + s->v2->segments = g_slist_prepend (s->v2->segments, e2); +#else + GtsEdge * e1 = gts_edge_new (GTS_EDGE_CLASS (GTS_OBJECT (s)->klass), + s->v1, v); + GtsEdge * e2 = gts_edge_new (GTS_EDGE_CLASS (GTS_OBJECT (s)->klass), + v, s->v2); +#endif + + GTS_OBJECT (s)->klass = GTS_OBJECT_CLASS (surface->edge_class); + + if (f == NULL) + g_assert ((f = gts_edge_has_parent_surface (GTS_EDGE (s), surface))); + g_assert (gts_delaunay_add_vertex_to_face (surface, v, f) == NULL); + + if (boundary) + gts_object_destroy (GTS_OBJECT (s)); + + vertex_encroaches (v, surface, encroached, encroaches, data); + + if (gts_edge_is_encroached (e1, surface, encroaches, data)) { + gts_fifo_push (encroached, e1); + ALREADY_ENCROACHED (e1) = encroached; + } + if (gts_edge_is_encroached (e2, surface, encroaches, data)) { + gts_fifo_push (encroached, e2); + ALREADY_ENCROACHED (e2) = encroached; + } + } + + return steiner_max; +} + +/** + * gts_delaunay_conform: + * @surface: a #GtsSurface describing a constrained Delaunay triangulation. + * @steiner_max: maximum number of Steiner points. + * @encroaches: a #GtsEncroachFunc. + * @data: user-data to pass to @encroaches. + * + * Recursively split constraints of @surface which are encroached by + * vertices of @surface (see Shewchuk 96 for details). The split + * constraints are destroyed and replaced by a set of new constraints + * of the same class. If gts_vertex_encroaches_edge() is used for + * @encroaches, the resulting surface will be Delaunay conforming. + * + * If @steiner_max is positive or nul, the recursive splitting + * procedure will stop when this maximum number of Steiner points is + * reached. In that case the resulting surface will not necessarily be + * Delaunay conforming. + * + * Returns: the number of remaining encroached edges. If @steiner_max + * is set to a negative value and gts_vertex_encroaches_edge() is used + * for @encroaches this should always be zero. + */ +guint gts_delaunay_conform (GtsSurface * surface, + gint steiner_max, + GtsEncroachFunc encroaches, + gpointer data) +{ + GtsFifo * encroached; + gpointer datas[4]; + guint encroached_number; + + g_return_val_if_fail (surface != NULL, 0); + g_return_val_if_fail (surface != NULL, 0); + g_return_val_if_fail (encroaches != NULL, 0); + + datas[0] = encroached = gts_fifo_new (); + datas[1] = surface; + datas[2] = encroaches; + datas[3] = data; + gts_surface_foreach_edge (surface, (GtsFunc) make_encroached_fifo, datas); + + split_encroached (surface, + encroached, + steiner_max, + encroaches, data); + gts_fifo_foreach (encroached, (GtsFunc) gts_object_reset_reserved, NULL); + encroached_number = gts_fifo_size (encroached); + gts_fifo_destroy (encroached); + return encroached_number; +} + +#define EHEAP_PAIR(f) (GTS_OBJECT (f)->reserved) + +static void heap_surface_add_face (GtsSurface * s, GtsFace * f) +{ + GtsEHeap * heap = GTS_OBJECT (s)->reserved; + gdouble key = gts_eheap_key (heap, f); + + if (key != 0.) + EHEAP_PAIR (f) = gts_eheap_insert_with_key (heap, f, key); + + if (GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass->parent_class)->add_face) + (* GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass->parent_class)->add_face) + (s, f); +} + +static void heap_surface_remove_face (GtsSurface * s, GtsFace * f) +{ + GtsEHeap * heap = GTS_OBJECT (s)->reserved; + + if (EHEAP_PAIR (f)) + gts_eheap_remove (heap, EHEAP_PAIR (f)); + + if (GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass->parent_class)->remove_face) + (* GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass->parent_class)->remove_face) + (s, f); +} + +static void heap_surface_class_init (GtsSurfaceClass * klass) +{ + klass->add_face = heap_surface_add_face; + klass->remove_face = heap_surface_remove_face; +} + +static GtsObjectClass * heap_surface_class_new (GtsObjectClass * parent_class) +{ + GtsObjectClassInfo heap_surface_info; + + heap_surface_info = parent_class->info; + heap_surface_info.class_init_func = (GtsObjectClassInitFunc) + heap_surface_class_init; + return gts_object_class_new (parent_class, + &heap_surface_info); +} + +static void make_face_heap (GtsFace * f, GtsEHeap * heap) +{ + gdouble key = gts_eheap_key (heap, f); + + if (key != 0.) + EHEAP_PAIR (f) = gts_eheap_insert_with_key (heap, f, key); +} + +/** + * gts_delaunay_refine: + * @surface: a #GtsSurface describing a conforming Delaunay triangulation. + * @steiner_max: maximum number of Steiner points. + * @encroaches: a #GtsEncroachFunc. + * @encroach_data: user-data to pass to @encroaches. + * @cost: a #GtsKeyFunc used to sort the faces during refinement. + * @cost_data: user-data to pass to @cost. + * + * An implementation of the refinement algorithm described in Ruppert + * (1995) and Shewchuk (1996). + * + * Returns: the number of unrefined faces of @surface left. Should be zero + * if @steiner_max is set to a negative value. + */ +guint gts_delaunay_refine (GtsSurface * surface, + gint steiner_max, + GtsEncroachFunc encroaches, + gpointer encroach_data, + GtsKeyFunc cost, + gpointer cost_data) +{ + GtsObjectClass * heap_surface_class; + GtsObjectClass * original_class; + GtsEHeap * heap; + GtsFifo * encroached; + GtsFace * f; + guint unrefined_number; + + g_return_val_if_fail (surface != NULL, 0); + g_return_val_if_fail (encroaches != NULL, 0); + g_return_val_if_fail (cost != NULL, 0); + + original_class = GTS_OBJECT (surface)->klass; + heap_surface_class = heap_surface_class_new (original_class); + GTS_OBJECT (surface)->klass = heap_surface_class; + + heap = gts_eheap_new (cost, cost_data); + gts_surface_foreach_face (surface, (GtsFunc) make_face_heap, heap); + encroached = gts_fifo_new (); + + GTS_OBJECT (surface)->reserved = heap; + + while (steiner_max-- != 0 && (f = gts_eheap_remove_top (heap, NULL))) { + GtsVertex * c = + GTS_VERTEX (gts_triangle_circumcircle_center (GTS_TRIANGLE (f), + GTS_POINT_CLASS (surface->vertex_class))); + EHEAP_PAIR (f) = NULL; + g_assert (c != NULL); + g_assert (gts_delaunay_add_vertex (surface, c, f) == NULL); + + vertex_encroaches (c, surface, encroached, encroaches, encroach_data); + if (!gts_fifo_is_empty (encroached)) { + gts_delaunay_remove_vertex (surface, c); + steiner_max = split_encroached (surface, + encroached, + steiner_max, + encroaches, + encroach_data); + } + } + + unrefined_number = gts_eheap_size (heap); + gts_eheap_foreach (heap, (GFunc) gts_object_reset_reserved, NULL); + gts_eheap_destroy (heap); + + gts_fifo_foreach (encroached, (GtsFunc) gts_object_reset_reserved, NULL); + gts_fifo_destroy (encroached); + + GTS_OBJECT (surface)->klass = original_class; + GTS_OBJECT (surface)->reserved = NULL; + g_free (heap_surface_class); + + return unrefined_number; +} Index: trunk/src_3rd/gts/rounding.h =================================================================== --- trunk/src_3rd/gts/rounding.h (nonexistent) +++ trunk/src_3rd/gts/rounding.h (revision 1065) @@ -0,0 +1,85 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#ifdef HAVE_FPU_CONTROL_H +# include +# ifdef _FPU_EXTENDED +# if !defined(__alpha__) || !defined(__GLIBC__) +# if defined(__arm__) + static fpu_control_t fpu_round_double = _FPU_DEFAULT; +# else + static fpu_control_t fpu_round_double = + (_FPU_DEFAULT & ~ _FPU_EXTENDED)|_FPU_DOUBLE; +# endif + static fpu_control_t fpu_init; +# define FPU_ROUND_DOUBLE { _FPU_GETCW(fpu_init);\ + _FPU_SETCW(fpu_round_double); } +# define FPU_RESTORE {_FPU_SETCW(fpu_init);} +# else /* __alpha__ && __GLIBC__ */ +# define FPU_ROUND_DOUBLE +# define FPU_RESTORE +# endif /* __alpha__ && __GLIBC__ */ +# else /* not FPU_EXTENDED */ +# define FPU_ROUND_DOUBLE +# define FPU_RESTORE +# endif /* not FPU_EXTENDED */ +#else /* not HAVE_FPU_CONTROL_H */ +# ifdef __FreeBSD__ +# include +# define FPU_ROUND_DOUBLE (fpsetprec(FP_PD)) +# define FPU_RESTORE (fpsetprec(FP_PE)) +# else /* not __FreeBSD__ */ +# ifdef WIN32 +# ifdef _MSC_VER +# include + static unsigned int fpu_init; +# define FPU_ROUND_DOUBLE (fpu_init = _controlfp (0, 0),\ + _controlfp (_PC_53, MCW_PC)) +# define FPU_RESTORE (_controlfp (fpu_init, 0xfffff)) +# elif __MINGW32__ +# include + static unsigned int fpu_init; +# define FPU_ROUND_DOUBLE (fpu_init = _controlfp (0, 0),\ + _controlfp (_PC_53, _MCW_PC)) +# define FPU_RESTORE (_controlfp (fpu_init, 0xfffff)) +# else /* not _MSC_VER or __MINGW32__ */ +# error "You need MSVC or MinGW for the Win32 version" +# endif /* not _MSC_VER or __MINGW32__ */ +# else /* not WIN32 */ +# ifdef __CYGWIN__ + typedef unsigned int fpu_control_t __attribute__ ((__mode__ (__HI__))); + static fpu_control_t fpu_round_double = 0x027f; + static fpu_control_t fpu_init; +# define _FPU_GETCW(cw) __asm__ ("fnstcw %0" : "=m" (*&cw)) +# define _FPU_SETCW(cw) __asm__ ("fldcw %0" : : "m" (*&cw)) +# define FPU_ROUND_DOUBLE { _FPU_GETCW(fpu_init);\ + _FPU_SETCW(fpu_round_double); } +# define FPU_RESTORE { _FPU_SETCW(fpu_init);} +# else /* not __CYGWIN__ */ +# ifdef CPP_HAS_WARNING +# warning "Unknown CPU: assuming default double precision rounding" +# endif /* CPP_HAS_WARNING */ +# define FPU_ROUND_DOUBLE +# define FPU_RESTORE +# endif /* not __CYGWIN__ */ +# endif /* not WIN32 */ +# endif /* not __FreeBSD__ */ +#endif /* not HAVE_FPU_CONTROL_H */ Index: trunk/src_3rd/gts/segment.c =================================================================== --- trunk/src_3rd/gts/segment.c (nonexistent) +++ trunk/src_3rd/gts/segment.c (revision 1065) @@ -0,0 +1,233 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "gts.h" + +static void segment_destroy (GtsObject * object) +{ + GtsSegment * segment = GTS_SEGMENT (object); + GtsVertex * v1 = segment->v1; + GtsVertex * v2 = segment->v2; + + v1->segments = g_slist_remove (v1->segments, segment); + if (!GTS_OBJECT_DESTROYED (v1) && + !gts_allow_floating_vertices && v1->segments == NULL) + gts_object_destroy (GTS_OBJECT (v1)); + + v2->segments = g_slist_remove (v2->segments, segment); + if (!GTS_OBJECT_DESTROYED (v2) && + !gts_allow_floating_vertices && v2->segments == NULL) + gts_object_destroy (GTS_OBJECT (v2)); + + (* GTS_OBJECT_CLASS (gts_segment_class ())->parent_class->destroy) (object); +} + +static void segment_class_init (GtsObjectClass * klass) +{ + klass->destroy = segment_destroy; +} + +static void segment_init (GtsSegment * segment) +{ + segment->v1 = segment->v2 = NULL; +} + +/** + * gts_segment_class: + * + * Returns: the #GtsSegmentClass. + */ +GtsSegmentClass * gts_segment_class (void) +{ + static GtsSegmentClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo segment_info = { + "GtsSegment", + sizeof (GtsSegment), + sizeof (GtsSegmentClass), + (GtsObjectClassInitFunc) segment_class_init, + (GtsObjectInitFunc) segment_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (gts_object_class (), + &segment_info); + } + + return klass; +} + +/** + * gts_segment_new: + * @klass: a #GtsSegmentClass. + * @v1: a #GtsVertex. + * @v2: another #GtsVertex different from @v1. + * + * Returns: a new #GtsSegment linking @v1 and @v2. + */ +GtsSegment * gts_segment_new (GtsSegmentClass * klass, + GtsVertex * v1, GtsVertex * v2) +{ + GtsSegment * s; + + g_return_val_if_fail (v1 != NULL, NULL); + g_return_val_if_fail (v2 != NULL, NULL); + g_return_val_if_fail (v1 != v2, NULL); + + s = GTS_SEGMENT (gts_object_new (GTS_OBJECT_CLASS (klass))); + s->v1 = v1; + s->v2 = v2; + v1->segments = g_slist_prepend (v1->segments, s); + v2->segments = g_slist_prepend (v2->segments, s); + + return s; +} + +/** + * gts_segment_is_duplicate: + * @s: a #GtsSegment. + * + * Returns: the first #GtsSegment different from @s which shares the + * same endpoints or %NULL if there is none. + */ +GtsSegment * gts_segment_is_duplicate (GtsSegment * s) +{ + GSList * i; + GtsVertex * v2; + + g_return_val_if_fail (s != NULL, NULL); + + v2 = s->v2; + i = s->v1->segments; + if (s->v1 == v2) /* s is degenerate: special treatment */ + while (i) { + GtsSegment * s1 = i->data; + if (s1 != s && s1->v1 == v2 && s1->v2 == v2) + return s1; + i = i->next; + } + else /* s is not degenerate */ + while (i) { + GtsSegment * s1 = i->data; + if (s1 != s && (s1->v1 == v2 || s1->v2 == v2)) + return s1; + i = i->next; + } + return NULL; +} + +/** + * gts_segments_are_intersecting: + * @s1: a #GtsSegment. + * @s2: a #GtsSegment. + * + * Returns: %GTS_IN if @s1 and @s2 are intersecting, %GTS_ON if one of the + * endpoints of @s1 (resp. @s2) lies on @s2 (resp. @s1), %GTS_OUT otherwise. + */ +GtsIntersect gts_segments_are_intersecting (GtsSegment * s1, GtsSegment * s2) +{ + GtsPoint * p1, * p2, * p3, * p4; + gdouble d1, d2, d3, d4; + + g_return_val_if_fail (s1 != NULL && s2 != NULL, FALSE); + + p1 = GTS_POINT (s1->v1); p2 = GTS_POINT (s1->v2); + p3 = GTS_POINT (s2->v1); p4 = GTS_POINT (s2->v2); + d1 = gts_point_orientation (p1, p2, p3); + d2 = gts_point_orientation (p1, p2, p4); + if ((d1 > 0.0 && d2 > 0.0) || + (d1 < 0.0 && d2 < 0.0)) + return GTS_OUT; + d3 = gts_point_orientation (p3, p4, p1); + d4 = gts_point_orientation (p3, p4, p2); + if ((d3 > 0.0 && d4 > 0.0) || + (d3 < 0.0 && d4 < 0.0)) + return GTS_OUT; + if (d1 == 0.0 || d2 == 0.0 || d3 == 0.0 || d4 == 0.0) + return GTS_ON; + return GTS_IN; +} + +/** + * gts_segment_midvertex: + * @s: a #GtsSegment. + * @klass: a #GtsVertexClass to be used for the new vertex. + * + * Returns: a new #GtsVertex, midvertex of @s. + */ +GtsVertex * gts_segment_midvertex (GtsSegment * s, GtsVertexClass * klass) +{ + GtsPoint * p1, * p2; + + g_return_val_if_fail (s != NULL, NULL); + g_return_val_if_fail (klass != NULL, NULL); + + p1 = GTS_POINT (s->v1); p2 = GTS_POINT (s->v2); + return gts_vertex_new (klass, + (p1->x + p2->x)/2., + (p1->y + p2->y)/2., + (p1->z + p2->z)/2.); +} + +/** + * gts_segments_from_vertices: + * @vertices: a list of #GtsVertex. + * + * Returns: a list of unique #GtsSegment which have one of their vertices in + * @vertices. + */ +GSList * gts_segments_from_vertices (GSList * vertices) +{ + GHashTable * hash; + GSList * segments = NULL, * i; + + hash = g_hash_table_new (NULL, NULL); + i = vertices; + while (i) { + GSList * j = GTS_VERTEX (i->data)->segments; + while (j) { + GtsSegment * s = j->data; + if (g_hash_table_lookup (hash, s) == NULL) { + segments = g_slist_prepend (segments, s); + g_hash_table_insert (hash, s, i); + } + j = j->next; + } + i = i->next; + } + g_hash_table_destroy (hash); + return segments; +} + +/** + * gts_segment_is_ok: + * @s: a #GtsSegment. + * + * Returns: %TRUE if @s is not degenerate (i.e. @s->v1 != @s->v2) and not + * duplicate, %FALSE otherwise. + */ +gboolean gts_segment_is_ok (GtsSegment * s) +{ + g_return_val_if_fail (s != NULL, FALSE); + g_return_val_if_fail (s->v1 != s->v2, FALSE); + g_return_val_if_fail (!gts_segment_is_duplicate (s), FALSE); + g_return_val_if_fail (GTS_OBJECT (s)->reserved == NULL, FALSE); + return TRUE; +} Index: trunk/src_3rd/gts/split.c =================================================================== --- trunk/src_3rd/gts/split.c (nonexistent) +++ trunk/src_3rd/gts/split.c (revision 1065) @@ -0,0 +1,1840 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include +#include "gts.h" + +#define DYNAMIC_SPLIT +#define NEW + +/* #define DEBUG + #define DEBUG_HEXPAND + #define DEBUG_EXPAND */ + +struct _GtsSplitCFace { + GtsFace * f; + GtsTriangle ** a1, ** a2; +}; + +typedef struct _CFace CFace; +typedef struct _CFaceClass CFaceClass; + +struct _CFace { + GtsObject object; + + GtsSplit * parent_split; + GtsTriangle * t; + guint flags; +}; +/* the size of the CFace structure must be smaller or equal to the size + of the GtsFace structure as both structures use the same memory location */ + +struct _CFaceClass { + GtsObjectClass parent_class; +}; + +#define IS_CFACE(obj) (gts_object_is_from_class (obj, cface_class ())) +#define CFACE(obj) ((CFace *) obj) +#define CFACE_ORIENTATION(cf) ((cf)->flags & 0x1) +#define CFACE_ORIENTATION_DIRECT(cf) ((cf)->flags |= 0x1) +#define CFACE_VVS(cf) ((cf)->flags & 0x2) +#define CFACE_VVS_DIRECT(cf) ((cf)->flags |= 0x2) +#define CFACE_E1 0x4 +#define CFACE_E2 0x8 +#define CFACE_KEEP_VVS 0x10 + +#define ROTATE_ORIENT(e, e1, e2, e3) { if (e1 == e) { e1 = e2; e2 = e3; }\ + else if (e2 == e) { e2 = e1; e1 = e3; }\ + else g_assert (e3 == e); } +#define SEGMENT_USE_VERTEX(s, v) ((s)->v1 == v || (s)->v2 == v) +#define TRIANGLE_REPLACE_EDGE(t, e, with) { if ((t)->e1 == e)\ + (t)->e1 = with;\ + else if ((t)->e2 == e)\ + (t)->e2 = with;\ + else {\ + g_assert ((t)->e3 == e);\ + (t)->e3 = with;\ + }\ + } + +#define HEAP_INSERT_OBJECT(h, e) (GTS_OBJECT (e)->reserved =\ + gts_eheap_insert (h, e)) +#define HEAP_REMOVE_OBJECT(h, e) (gts_eheap_remove (h, GTS_OBJECT (e)->reserved),\ + GTS_OBJECT (e)->reserved = NULL) + +static GtsObjectClass * cface_class (void) +{ + static GtsObjectClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo cface_info = { + "GtsCFace", + sizeof (CFace), + sizeof (CFaceClass), + (GtsObjectClassInitFunc) NULL, + (GtsObjectInitFunc) NULL, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (gts_object_class (), &cface_info); + g_assert (sizeof (CFace) <= sizeof (GtsFace)); + } + + return klass; +} + +/* Replace @e with @with for all the triangles using @e but @f. + Destroys @e and removes it from @heap (if not %NULL). + Returns a triangle using e different from f or %NULL. */ +static GtsTriangle * replace_edge_collapse (GtsEdge * e, + GtsEdge * with, + CFace * cf, + GtsEHeap * heap +#ifdef DYNAMIC_SPLIT + , GtsTriangle *** a1 +#endif +#ifdef NEW + , guint edge_flag +#endif + ) +{ + GSList * i; + GtsTriangle * rt = NULL; +#ifdef DYNAMIC_SPLIT + guint size; + GtsTriangle ** a; +#endif + +#ifdef NEW + i = e->triangles; + e->triangles = NULL; + size = g_slist_length (i)*sizeof (GtsTriangle *); + *a1 = a = g_malloc (size > 0 ? size : sizeof (GtsTriangle *)); + while (i) { + GtsTriangle * t = i->data; + GSList * next = i->next; + if (t != ((GtsTriangle *) cf)) { + if (IS_CFACE (t)) { + i->next = e->triangles; + e->triangles = i; + /* set the edge given by edge_flag (CFACE_E1 or CFACE_E2) */ + GTS_OBJECT (t)->reserved = GUINT_TO_POINTER (edge_flag); + cf->flags |= CFACE_KEEP_VVS; + } + else { + TRIANGLE_REPLACE_EDGE (t, e, with); + i->next = with->triangles; + with->triangles = i; + rt = t; + *(a++) = t; + } + } + else + g_slist_free_1 (i); + i = next; + } + *a = NULL; + if (!e->triangles) { + if (heap) + HEAP_REMOVE_OBJECT (heap, e); + gts_object_destroy (GTS_OBJECT (e)); + } +#else /* not NEW */ + i = e->triangles; +#ifdef DYNAMIC_SPLIT + size = g_slist_length (i)*sizeof (GtsTriangle *); + *a1 = a = g_malloc (size > 0 ? size : sizeof (GtsTriangle *)); +#endif + while (i) { + GtsTriangle * t = i->data; + GSList * next = i->next; + if (t != ((GtsTriangle *) cf)) { + TRIANGLE_REPLACE_EDGE (t, e, with); + i->next = with->triangles; + with->triangles = i; + rt = t; +#ifdef DYNAMIC_SPLIT + *(a++) = t; +#endif + } + else + g_slist_free_1 (i); + i = next; + } +#ifdef DYNAMIC_SPLIT + *a = NULL; +#endif + if (heap) + HEAP_REMOVE_OBJECT (heap, e); + e->triangles = NULL; + gts_object_destroy (GTS_OBJECT (e)); +#endif /* NEW */ + + return rt; +} + +static CFace * cface_new (GtsFace * f, + GtsEdge * e, + GtsVertex * v1, + GtsVertex * v2, + GtsSplit * vs, + GtsEHeap * heap, + GtsEdgeClass * klass +#ifdef DYNAMIC_SPLIT + , GtsSplitCFace * scf +#endif + ) +{ + CFace * cf; + GtsVertex * v; + GtsEdge * e1, * e2, * e3, * vvs; + GSList * i; + GtsTriangle * t, * t1 = NULL, * t2 = NULL; + guint flags; + + g_return_val_if_fail (f != NULL, NULL); +#ifndef NEW + g_return_val_if_fail (GTS_IS_FACE (f), NULL); +#endif + g_return_val_if_fail (e != NULL, NULL); + g_return_val_if_fail (vs != NULL, NULL); + + t = ((GtsTriangle *) f); + if (heap) + g_return_val_if_fail (!gts_triangle_is_duplicate (t), NULL); + +#ifdef NEW + /* get CFACE_E1 and CFACE_E2 info */ + flags = GPOINTER_TO_UINT (GTS_OBJECT (f)->reserved); +#endif + GTS_OBJECT_SET_FLAGS (f, GTS_DESTROYED); + + i = f->surfaces; + while (i) { + GSList * next = i->next; + gts_surface_remove_face (i->data, f); + i = next; + } + g_slist_free (f->surfaces); + + e1 = t->e1; e2 = t->e2; e3 = t->e3; + ROTATE_ORIENT (e, e1, e2, e3); + + cf = (CFace *) f; +#ifndef NEW + GTS_OBJECT (cf)->klass = cface_class (); +#else + cf->flags = flags; +#endif + gts_object_init (GTS_OBJECT (cf), cface_class ()); + cf->parent_split = vs; + + if (SEGMENT_USE_VERTEX (GTS_SEGMENT (e1), v2)) { + CFACE_ORIENTATION_DIRECT (cf); /* v1->v2->v */ + e3 = e1; e1 = e2; e2 = e3; + } + v = GTS_SEGMENT (e1)->v1 == v1 ? + GTS_SEGMENT (e1)->v2 : GTS_SEGMENT (e1)->v1; +#ifdef NEW + if ((cf->flags & CFACE_E1) || (cf->flags & CFACE_E2)) + g_assert ((vvs = GTS_EDGE (gts_vertices_are_connected (vs->v, v)))); + else +#endif + vvs = gts_edge_new (klass, v, vs->v); + + t1 = replace_edge_collapse (e1, vvs, cf, heap +#ifdef DYNAMIC_SPLIT + , &scf->a1 +#endif +#ifdef NEW + , CFACE_E1 +#endif + ); + t2 = replace_edge_collapse (e2, vvs, cf, heap +#ifdef DYNAMIC_SPLIT + , &scf->a2 +#endif +#ifdef NEW + , CFACE_E2 +#endif + ); + t = cf->t = t1 ? t1 : t2; + g_assert (t); + + /* set up flags necessary to find vvs */ + if (t->e1 == vvs) e2 = t->e2; + else if (t->e2 == vvs) e2 = t->e3; + else { + g_assert (t->e3 == vvs); + e2 = t->e1; + } + if (SEGMENT_USE_VERTEX (GTS_SEGMENT (e2), v)) + CFACE_VVS_DIRECT (cf); + + return cf; +} + +static void find_vvs (GtsVertex * vs, + GtsTriangle * t, + GtsVertex ** v, GtsEdge ** vvs, + gboolean orientation) +{ + GtsEdge * e1 = t->e1, * e2 = t->e2, * e3 = t->e3, * tmp; + + if (SEGMENT_USE_VERTEX (GTS_SEGMENT (e2), vs)) { + tmp = e1; e1 = e2; e2 = e3; e3 = tmp; + } + else if (SEGMENT_USE_VERTEX (GTS_SEGMENT (e3), vs)) { + tmp = e1; e1 = e3; e3 = e2; e2 = tmp; + } + else + g_assert (SEGMENT_USE_VERTEX (GTS_SEGMENT (e1), vs)); + if (SEGMENT_USE_VERTEX (GTS_SEGMENT (e2), vs) || + !gts_segments_touch (GTS_SEGMENT (e1), GTS_SEGMENT (e2))) { + tmp = e1; e1 = e2; e2 = e3; e3 = tmp; + g_assert (gts_segments_touch (GTS_SEGMENT (e1), GTS_SEGMENT (e2))); + } + + *vvs = orientation ? e1 : e3; + + if (GTS_SEGMENT (*vvs)->v1 != vs) { + g_assert (GTS_SEGMENT (*vvs)->v2 == vs); + *v = GTS_SEGMENT (*vvs)->v1; + } + else + *v = GTS_SEGMENT (*vvs)->v2; +} + +static void replace_edge_expand (GtsEdge * e, + GtsEdge * with, + GtsTriangle ** a, + GtsVertex * v) +{ + GtsTriangle ** i = a, * t; + + while ((t = *(i++))) { +#ifdef DEBUG_EXPAND + g_assert (!IS_CFACE (t)); + fprintf (stderr, "replacing %p->%d: e: %p->%d with: %p->%d\n", + t, id (t), e, id (e), with, id (with)); +#endif + TRIANGLE_REPLACE_EDGE (t, e, with); + with->triangles = g_slist_prepend (with->triangles, t); + if (GTS_OBJECT (t)->reserved) { + /* apart from the triangles having e as an edge, t is the only + triangle using v */ + g_assert (GTS_OBJECT (t)->reserved == v); + GTS_OBJECT (t)->reserved = NULL; + } + else + GTS_OBJECT (t)->reserved = v; + } +} + +static void cface_expand (CFace * cf, + GtsTriangle ** a1, + GtsTriangle ** a2, + GtsEdge * e, + GtsVertex * v1, + GtsVertex * v2, + GtsVertex * vs, + GtsEdgeClass * klass) +{ + GtsVertex * v; + GtsEdge * e1, * e2, * vvs; + gboolean orientation; + guint flags; + + g_return_if_fail (cf != NULL); + g_return_if_fail (IS_CFACE (cf)); + g_return_if_fail (e != NULL); + g_return_if_fail (vs != NULL); + + flags = cf->flags; + orientation = CFACE_ORIENTATION (cf); + + find_vvs (vs, cf->t, &v, &vvs, CFACE_VVS (cf)); + +#ifdef NEW + if (flags & CFACE_E1) + e1 = GTS_EDGE (gts_vertices_are_connected (v1, v)); + else + e1 = gts_edge_new (klass, v, v1); + if (flags & CFACE_E2) + e2 = GTS_EDGE (gts_vertices_are_connected (v2, v)); + else + e2 = gts_edge_new (klass, v, v2); +#else + e1 = gts_edge_new (v, v1); + e2 = gts_edge_new (v, v2); +#endif + + replace_edge_expand (vvs, e1, a1, v1); + replace_edge_expand (vvs, e2, a2, v2); + +#ifdef NEW + if (!(flags & CFACE_KEEP_VVS)) { + g_slist_free (vvs->triangles); + vvs->triangles = NULL; + gts_object_destroy (GTS_OBJECT (vvs)); + } +#else + g_slist_free (vvs->triangles); + vvs->triangles = NULL; + gts_object_destroy (GTS_OBJECT (vvs)); +#endif + + /* gts_face_new : because I am "creating" a face */ + GTS_OBJECT (cf)->klass = GTS_OBJECT_CLASS (gts_face_class ()); + gts_object_init (GTS_OBJECT (cf), GTS_OBJECT (cf)->klass); + + if (orientation) + gts_triangle_set (GTS_TRIANGLE (cf), e, e2, e1); + else + gts_triangle_set (GTS_TRIANGLE (cf), e, e1, e2); +} + +static void split_destroy (GtsObject * object) +{ + GtsSplit * vs = GTS_SPLIT (object); + guint i = vs->ncf; + GtsSplitCFace * cf = vs->cfaces; + + while (i--) { + if (IS_CFACE (cf->f)) + gts_object_destroy (GTS_OBJECT (cf->f)); + g_free (cf->a1); + g_free (cf->a2); + cf++; + } + g_free (vs->cfaces); + + if (!gts_allow_floating_vertices && vs->v && vs->v->segments == NULL) + gts_object_destroy (GTS_OBJECT (vs->v)); + + (* GTS_OBJECT_CLASS (gts_split_class ())->parent_class->destroy) (object); +} + +static void split_class_init (GtsObjectClass * klass) +{ + klass->destroy = split_destroy; +} + +static void split_init (GtsSplit * split) +{ + split->v1 = split->v2 = NULL; + split->v = NULL; + split->cfaces = NULL; + split->ncf = 0; +} + +/** + * gts_split_class: + * + * Returns: the #GtsSplitClass. + */ +GtsSplitClass * gts_split_class (void) +{ + static GtsSplitClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo split_info = { + "GtsSplit", + sizeof (GtsSplit), + sizeof (GtsSplitClass), + (GtsObjectClassInitFunc) split_class_init, + (GtsObjectInitFunc) split_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (gts_object_class (), + &split_info); + } + + return klass; +} + +#ifdef DEBUG +static gboolean edge_collapse_is_valid (GtsEdge * e) +{ + GSList * i; + + g_return_val_if_fail (e != NULL, FALSE); + + if (gts_segment_is_duplicate (GTS_SEGMENT (e))) { + g_warning ("collapsing duplicate edge"); + return FALSE; + } + + i = GTS_SEGMENT (e)->v1->segments; + while (i) { + GtsEdge * e1 = i->data; + if (e1 != e && GTS_IS_EDGE (e1)) { + GtsEdge * e2 = NULL; + GSList * j = GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e)->v1 ? + GTS_SEGMENT (e1)->v2->segments : GTS_SEGMENT (e1)->v1->segments; + while (j && !e2) { + GtsEdge * e1 = j->data; + if (GTS_IS_EDGE (e1) && + (GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e)->v2 || + GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e)->v2)) + e2 = e1; + j = j->next; + } + if (e2 && !gts_triangle_use_edges (e, e1, e2)) { + g_warning ("collapsing empty triangle"); + return FALSE; + } + } + i = i->next; + } + + if (gts_edge_is_boundary (e, NULL)) { + GtsTriangle * t = e->triangles->data; + if (gts_edge_is_boundary (t->e1, NULL) && + gts_edge_is_boundary (t->e2, NULL) && + gts_edge_is_boundary (t->e3, NULL)) { + g_warning ("collapsing single triangle"); + return FALSE; + } + } + else { + if (gts_vertex_is_boundary (GTS_SEGMENT (e)->v1, NULL) && + gts_vertex_is_boundary (GTS_SEGMENT (e)->v2, NULL)) { + g_warning ("collapsing two sides of a strip"); + return FALSE; + } + if (gts_edge_belongs_to_tetrahedron (e)) { + g_warning ("collapsing tetrahedron"); + return FALSE; + } + } + + return TRUE; +} +#endif /* DEBUG */ + +/* Not currently used. May be useful for some debug code */ +#ifdef DEBUG +static void print_split (GtsSplit * vs, FILE * fptr) +{ + guint j; + GtsSplitCFace * cf; + + g_return_if_fail (vs != NULL); + g_return_if_fail (fptr != NULL); + + fprintf (fptr, "%p: v: %p v1: %p v2: %p ncf: %u cfaces: %p\n", + vs, vs->v, vs->v1, vs->v2, vs->ncf, vs->cfaces); + cf = vs->cfaces; + j = vs->ncf; + while (j--) { + fprintf (stderr, " f: %p a1: %p a2: %p\n", + cf->f, cf->a1, cf->a2); + cf++; + } +} +#endif + +/** + * gts_split_collapse: + * @vs: a #GtsSplit. + * @klass: a #GtsEdgeClass. + * @heap: a #GtsEHeap or %NULL. + * + * Collapses the vertex split @vs. Any new edge created during the process will + * be of class @klass. If heap is not %NULL, the new edges will be inserted + * into it and the destroyed edges will be removed from it. + */ +void gts_split_collapse (GtsSplit * vs, + GtsEdgeClass * klass, + GtsEHeap * heap) +{ + GtsEdge * e; + GtsVertex * v, * v1, * v2; + GSList * i, * end; +#ifdef DYNAMIC_SPLIT + GtsSplitCFace * cf; + guint j; +#endif +#ifdef DEBUG + gboolean invalid = FALSE; + static guint ninvalid = 0; +#endif + + g_return_if_fail (vs != NULL); + g_return_if_fail (klass != NULL); + + v = vs->v; + + g_return_if_fail (v->segments == NULL); + + /* we don't want to destroy vertices */ + gts_allow_floating_vertices = TRUE; + + v1 = GTS_SPLIT_V1 (vs); + v2 = GTS_SPLIT_V2 (vs); + g_assert ((e = GTS_EDGE (gts_vertices_are_connected (v1, v2)))); + +#ifdef DEBUG + fprintf (stderr, "collapsing %p: v1: %p v2: %p v: %p\n", vs, v1, v2, v); + if (!edge_collapse_is_valid (e)) { + char fname[80]; + FILE * fptr; + GSList * triangles, * i; + + g_warning ("invalid edge collapse"); + invalid = TRUE; + sprintf (fname, "invalid.%d", ninvalid); + fptr = fopen (fname, "wt"); + gts_write_segment (GTS_SEGMENT (e), GTS_POINT (v), fptr); + triangles = gts_vertex_triangles (v1, NULL); + triangles = gts_vertex_triangles (v2, triangles); + i = triangles; + while (i) { + gts_write_triangle (i->data, GTS_POINT (v), fptr); + i = i->next; + } + g_slist_free (triangles); + fclose (fptr); + } +#endif + + i = e->triangles; +#ifdef DYNAMIC_SPLIT + cf = vs->cfaces; + j = vs->ncf; + while (j--) { + g_free (cf->a1); + g_free (cf->a2); + cf++; + } + g_free (vs->cfaces); + + vs->ncf = g_slist_length (i); + g_assert (vs->ncf > 0); + cf = vs->cfaces = g_malloc (vs->ncf*sizeof (GtsSplitCFace)); +#endif /* DYNAMIC_SPLIT */ +#ifdef NEW + while (i) { + cf->f = i->data; + g_assert (GTS_IS_FACE (cf->f)); + GTS_OBJECT (cf->f)->klass = GTS_OBJECT_CLASS (cface_class ()); + cf++; + i = i->next; + } + i = e->triangles; + cf = vs->cfaces; + while (i) { + cface_new (i->data, e, v1, v2, vs, heap, klass, cf); +#ifdef DEBUG + fprintf (stderr, "cface: %p->%d t: %p->%d a1: ", + cf->f, id (cf->f), CFACE (cf->f)->t, id (CFACE (cf->f)->t)); + { + GtsTriangle * t, ** a; + a = cf->a1; + while ((t = *(a++))) + fprintf (stderr, "%p->%d ", t, id (t)); + fprintf (stderr, "a2: "); + a = cf->a2; + while ((t = *(a++))) + fprintf (stderr, "%p->%d ", t, id (t)); + fprintf (stderr, "\n"); + } +#endif + cf++; + i = i->next; + } +#else /* not NEW */ + while (i) { + cface_new (i->data, e, v1, v2, vs, heap +#ifdef DYNAMIC_SPLIT + , cf +#endif /* DYNAMIC_SPLIT */ + ); +#ifdef DYNAMIC_SPLIT + cf->f = i->data; + cf++; +#endif /* DYNAMIC_SPLIT */ + i = i->next; + } +#endif /* NEW */ + g_slist_free (e->triangles); + e->triangles = NULL; + gts_object_destroy (GTS_OBJECT (e)); + + gts_allow_floating_vertices = FALSE; + + end = NULL; + i = v1->segments; + while (i) { + GtsSegment * s = i->data; + if (s->v1 == v1) + s->v1 = v; + else + s->v2 = v; + end = i; + i = i->next; + } + if (end) { + end->next = v->segments; + v->segments = v1->segments; + v1->segments = NULL; + } + + end = NULL; + i = v2->segments; + while (i) { + GtsSegment * s = i->data; + if (s->v1 == v2) + s->v1 = v; + else + s->v2 = v; + end = i; + i = i->next; + } + if (end) { + end->next = v->segments; + v->segments = v2->segments; + v2->segments = NULL; + } + +#ifdef DEBUG + if (invalid) { + char fname[80]; + FILE * fptr; + GSList * triangles, * i; + GtsSurface * surface = NULL; + + sprintf (fname, "invalid_after.%d", ninvalid); + fptr = fopen (fname, "wt"); + triangles = gts_vertex_triangles (v, NULL); + i = triangles; + while (i) { + GtsTriangle * t = i->data; + fprintf (stderr, "checking %p->%d\n", t, id (t)); + g_assert (GTS_IS_FACE (t)); + gts_write_triangle (t, GTS_POINT (v), fptr); + surface = GTS_FACE (t)->surfaces->data; + if (gts_triangle_is_duplicate (t)) + fprintf (stderr, "%p->%d is duplicate\n", t, id (t)); + if (gts_segment_is_duplicate (GTS_SEGMENT (t->e1))) + fprintf (stderr, "e1 of %p->%d is duplicate\n", t, id (t)); + if (gts_segment_is_duplicate (GTS_SEGMENT (t->e2))) + fprintf (stderr, "e2 of %p->%d is duplicate\n", t, id (t)); + if (gts_segment_is_duplicate (GTS_SEGMENT (t->e3))) + fprintf (stderr, "e3 of %p->%d is duplicate\n", t, id (t)); + i = i->next; + } + fclose (fptr); + g_slist_free (triangles); +#if 0 + gts_split_expand (vs, surface); + + sprintf (fname, "invalid_after_after.%d", ninvalid); + fptr = fopen (fname, "wt"); + triangles = gts_vertex_triangles (v1, NULL); + triangles = gts_vertex_triangles (v2, triangles); + i = triangles; + while (i) { + GtsTriangle * t = i->data; + gts_write_triangle (t, GTS_POINT (v), fptr); + surface = GTS_FACE (t)->surfaces->data; + if (gts_triangle_is_duplicate (t)) + fprintf (stderr, "%p->%d is duplicate\n", t, id (t)); + if (gts_segment_is_duplicate (GTS_SEGMENT (t->e1))) + fprintf (stderr, "e1 of %p->%d is duplicate\n", t, id (t)); + if (gts_segment_is_duplicate (GTS_SEGMENT (t->e2))) + fprintf (stderr, "e2 of %p->%d is duplicate\n", t, id (t)); + if (gts_segment_is_duplicate (GTS_SEGMENT (t->e3))) + fprintf (stderr, "e3 of %p->%d is duplicate\n", t, id (t)); + i = i->next; + } + fclose (fptr); + g_slist_free (triangles); + + exit (1); +#endif + ninvalid++; + } +#endif +} + +/** + * gts_split_expand: + * @vs: a #GtsSplit. + * @s: a #GtsSurface. + * @klass: a #GtsEdgeClass. + * + * Expands the vertex split @vs adding the newly created faces to @s. Any + * new edge will be of class @klass. + */ +void gts_split_expand (GtsSplit * vs, + GtsSurface * s, + GtsEdgeClass * klass) +{ + GSList * i; + GtsEdge * e; + GtsVertex * v, * v1, * v2; + gboolean changed = FALSE; + GtsSplitCFace * cf; + guint j; + + g_return_if_fail (vs != NULL); + g_return_if_fail (s != NULL); + g_return_if_fail (klass != NULL); + + /* we don't want to destroy vertices */ + gts_allow_floating_vertices = TRUE; + + v1 = GTS_SPLIT_V1 (vs); + v2 = GTS_SPLIT_V2 (vs); + v = vs->v; +#ifdef DEBUG_EXPAND + fprintf (stderr, "expanding %p->%d: v1: %p->%d v2: %p->%d v: %p->%d\n", + vs, id (vs), v1, id (v1), v2, id (v2), v, id (v)); +#endif + e = gts_edge_new (klass, v1, v2); + cf = vs->cfaces; + j = vs->ncf; + while (j--) { + cface_expand (CFACE (cf->f), cf->a1, cf->a2, e, v1, v2, v, klass); + gts_surface_add_face (s, cf->f); + cf++; + } + + gts_allow_floating_vertices = FALSE; + + /* this part is described by figure "expand.fig" */ + i = v->segments; + while (i) { + GtsEdge * e1 = i->data; + GtsVertex * with = NULL; + GSList * j = e1->triangles, * next = i->next; + // fprintf (stderr, "e1: %p->%d\n", e1, id (e1)); + while (j && !with) { + with = GTS_OBJECT (j->data)->reserved; + j = j->next; + } + if (with) { + j = e1->triangles; + while (j) { + GtsTriangle * t = j->data; + if (GTS_OBJECT (t)->reserved) { + g_assert (GTS_OBJECT (t)->reserved == with); + GTS_OBJECT (t)->reserved = NULL; + } + else + GTS_OBJECT (t)->reserved = with; + j = j->next; + } + if (GTS_SEGMENT (e1)->v1 == v) + GTS_SEGMENT (e1)->v1 = with; + else + GTS_SEGMENT (e1)->v2 = with; + + v->segments = g_slist_remove_link (v->segments, i); + i->next = with->segments; + with->segments = i; + changed = TRUE; + } + if (next) + i = next; + else { + /* check for infinite loop (the crossed out case in + figure "expand.fig") */ + g_assert (changed); + changed = FALSE; + i = v->segments; + } + } +} + +#ifndef DYNAMIC_SPLIT +static void cface_neighbors (GtsSplitCFace * cf, + GtsEdge * e, + GtsVertex * v1, + GtsVertex * v2) +{ + GtsTriangle * t = GTS_TRIANGLE (cf->f), ** a; + GtsEdge * e1 = t->e1, * e2 = t->e2, * e3 = t->e3; + GSList * i; + guint size; + + ROTATE_ORIENT (e, e1, e2, e3); + if (SEGMENT_USE_VERTEX (GTS_SEGMENT (e1), v2)) { + e3 = e1; e1 = e2; e2 = e3; + } + + i = e1->triangles; + size = g_slist_length (i)*sizeof (GtsTriangle *); + a = cf->a1 = g_malloc (size > 0 ? size : sizeof (GtsTriangle *)); + while (i) { + if (i->data != t) + *(a++) = i->data; + i = i->next; + } + *a = NULL; + + i = e2->triangles; + size = g_slist_length (i)*sizeof (GtsTriangle *); + a = cf->a2 = g_malloc (size > 0 ? size : sizeof (GtsTriangle *)); + while (i) { + if (i->data != t) + *(a++) = i->data; + i = i->next; + } + *a = NULL; +} +#endif /*ifndef DYNAMIC_SPLIT */ + +/** + * gts_split_new: + * @klass: a #GtsSplitClass. + * @v: a #GtsVertex. + * @o1: either a #GtsVertex or a #GtsSplit. + * @o2: either a #GtsVertex or a #GtsSplit. + * + * Creates a new #GtsSplit which would collapse @o1 and @o2 into @v. The + * collapse itself is not performed. + * + * Returns: the new #GtsSplit. + */ +GtsSplit * gts_split_new (GtsSplitClass * klass, + GtsVertex * v, + GtsObject * o1, + GtsObject * o2) +{ + GtsSplit * vs; +#ifndef DYNAMIC_SPLIT + GtsVertex * v1, * v2; + GtsEdge * e; + GSList * i; + GtsSplitCFace * cf; +#endif + + g_return_val_if_fail (klass != NULL, NULL); + g_return_val_if_fail (v != NULL, NULL); + g_return_val_if_fail (GTS_IS_SPLIT (o1) || GTS_IS_VERTEX (o1), NULL); + g_return_val_if_fail (GTS_IS_SPLIT (o2) || GTS_IS_VERTEX (o2), NULL); + + vs = GTS_SPLIT (gts_object_new (GTS_OBJECT_CLASS (klass))); + vs->v = v; + vs->v1 = o1; + vs->v2 = o2; +#ifdef DYNAMIC_SPLIT + vs->ncf = 0; + vs->cfaces = NULL; +#else + v1 = GTS_SPLIT_V1 (vs); + v2 = GTS_SPLIT_V2 (vs); + g_assert ((e = GTS_EDGE (gts_vertices_are_connected (v1, v2)))); + i = e->triangles; + vs->ncf = g_slist_length (i); + g_assert (vs->ncf > 0); + cf = vs->cfaces = g_malloc (vs->ncf*sizeof (GtsSplitCFace)); + while (i) { + cf->f = i->data; + cface_neighbors (cf, e, v1, v2); + i = i->next; + cf++; + } +#endif + + return vs; +} + +static gboolean +split_traverse_pre_order (GtsSplit * vs, + GtsSplitTraverseFunc func, + gpointer data) +{ + if (func (vs, data)) + return TRUE; + if (GTS_IS_SPLIT (vs->v1) && + split_traverse_pre_order (GTS_SPLIT (vs->v1), func, data)) + return TRUE; + if (GTS_IS_SPLIT (vs->v2) && + split_traverse_pre_order (GTS_SPLIT (vs->v2), func, data)) + return TRUE; + return FALSE; +} + +static gboolean +split_depth_traverse_pre_order (GtsSplit * vs, + guint depth, + GtsSplitTraverseFunc func, + gpointer data) +{ + if (func (vs, data)) + return TRUE; + + depth--; + if (!depth) + return FALSE; + + if (GTS_IS_SPLIT (vs->v1) && + split_depth_traverse_pre_order (GTS_SPLIT (vs->v1), depth, func, data)) + return TRUE; + if (GTS_IS_SPLIT (vs->v2) && + split_depth_traverse_pre_order (GTS_SPLIT (vs->v2), depth, func, data)) + return TRUE; + return FALSE; +} + +static gboolean +split_traverse_post_order (GtsSplit * vs, + GtsSplitTraverseFunc func, + gpointer data) +{ + if (GTS_IS_SPLIT (vs->v1) && + split_traverse_post_order (GTS_SPLIT (vs->v1), func, data)) + return TRUE; + if (GTS_IS_SPLIT (vs->v2) && + split_traverse_post_order (GTS_SPLIT (vs->v2), func, data)) + return TRUE; + if (func (vs, data)) + return TRUE; + return FALSE; +} + +static gboolean +split_depth_traverse_post_order (GtsSplit * vs, + guint depth, + GtsSplitTraverseFunc func, + gpointer data) +{ + depth--; + if (depth) { + if (GTS_IS_SPLIT (vs->v1) && + split_depth_traverse_post_order (GTS_SPLIT (vs->v1), + depth, func, data)) + return TRUE; + if (GTS_IS_SPLIT (vs->v2) && + split_depth_traverse_post_order (GTS_SPLIT (vs->v2), + depth, func, data)) + return TRUE; + } + if (func (vs, data)) + return TRUE; + return FALSE; +} + +/** + * gts_split_traverse: + * @root: the #GtsSplit to start the traversal from. + * @order: the order in which nodes are visited - G_PRE_ORDER or G_POST_ORDER. + * @depth: the maximum depth of the traversal. Nodes below this depth + * will not be visited. If depth is -1 all nodes in the tree are + * visited. If depth is 1, only the root is visited. If depth is 2, + * the root and its children are visited. And so on. + * @func: the function to call for each visited #GtsHSplit. + * @data: user data to pass to the function. + * + * Traverses the #GtsSplit tree having @root as root. Calls @func for each + * #GtsSplit of the tree in the order specified by @order. If order is set + * to G_PRE_ORDER @func is called for the #GtsSplit then its children, if order + * is set to G_POST_ORDER @func is called for the children and then for the + * #GtsSplit. + */ +void gts_split_traverse (GtsSplit * root, + GTraverseType order, + gint depth, + GtsSplitTraverseFunc func, + gpointer data) +{ + g_return_if_fail (root != NULL); + g_return_if_fail (func != NULL); + g_return_if_fail (order < G_LEVEL_ORDER); + g_return_if_fail (depth == -1 || depth > 0); + + switch (order) { + case G_PRE_ORDER: + if (depth < 0) + split_traverse_pre_order (root, func, data); + else + split_depth_traverse_pre_order (root, depth, func, data); + break; + case G_POST_ORDER: + if (depth < 0) + split_traverse_post_order (root, func, data); + else + split_depth_traverse_post_order (root, depth, func, data); + break; + default: + g_assert_not_reached (); + } +} + +/** + * gts_split_height: + * @root: a #GtsSplit. + * + * Returns: the maximum height of the vertex split tree having @root as root. + */ +guint gts_split_height (GtsSplit * root) +{ + guint height = 0, tmp_height; + + g_return_val_if_fail (root != NULL, 0); + + if (GTS_IS_SPLIT (root->v1)) { + tmp_height = gts_split_height (GTS_SPLIT (root->v1)); + if (tmp_height > height) + height = tmp_height; + } + if (GTS_IS_SPLIT (root->v2)) { + tmp_height = gts_split_height (GTS_SPLIT (root->v2)); + if (tmp_height > height) + height = tmp_height; + } + + return height + 1; +} + +#ifndef DYNAMIC_SPLIT +static gboolean list_array_are_identical (GSList * list, + gpointer * array, + gpointer excluded) +{ + while (list) { + gpointer data = list->data; + if (data != excluded) { + gboolean found = FALSE; + gpointer * a = array; + + while (!found && *a) + if (*(a++) == data) + found = TRUE; + if (!found) + return FALSE; + } + list = list->next; + } + return TRUE; +} +#endif /* ifndef DYNAMIC_SPLIT */ + +#ifndef NEW +gboolean gts_split_is_collapsable (GtsSplit * vs) +{ + guint i; + GtsSplitCFace * cf; + GtsVertex * v1, * v2; + GtsEdge * e; + + g_return_val_if_fail (vs != NULL, FALSE); + + v1 = GTS_SPLIT_V1 (vs); + v2 = GTS_SPLIT_V2 (vs); + g_return_val_if_fail ((e = GTS_EDGE (gts_vertices_are_connected (v1, v2))), + FALSE); + +#ifdef DYNAMIC_SPLIT + if (!gts_edge_collapse_is_valid (e)) + return FALSE; +#else + i = vs->ncf; + cf = vs->cfaces; + while (i--) { + GtsTriangle * t = GTS_TRIANGLE (cf->f); + GtsEdge * e1 = t->e1, * e2 = t->e2, * e3 = t->e3; + + ROTATE_ORIENT (e, e1, e2, e3); + if (SEGMENT_USE_VERTEX (GTS_SEGMENT (e1), v2)) { + e3 = e1; e1 = e2; e2 = e3; + } + + if (!list_array_are_identical (e1->triangles, (gpointer *) cf->a1, t)) + return FALSE; + if (!list_array_are_identical (e2->triangles, (gpointer *) cf->a2, t)) + return FALSE; + + cf++; + } +#endif + return TRUE; +} +#endif /* not NEW */ + +#ifdef DEBUG_HEXPAND +static guint expand_level = 0; + +static void expand_indent (FILE * fptr) +{ + guint i = expand_level; + while (i--) + fputc (' ', fptr); +} +#endif + +/** + * gts_hsplit_force_expand: + * @hs: a #GtsHSplit. + * @hsurface: a #GtsHSurface. + * + * Forces the expansion of @hs by first expanding all its dependencies not + * already expanded. + */ +void gts_hsplit_force_expand (GtsHSplit * hs, + GtsHSurface * hsurface) +{ + guint i; + GtsSplitCFace * cf; + + g_return_if_fail (hs != NULL); + g_return_if_fail (hsurface != NULL); + g_return_if_fail (hs->nchild == 0); + +#ifdef DEBUG_HEXPAND + expand_level += 2; +#endif + + if (hs->parent && hs->parent->nchild == 0) { +#ifdef DEBUG_HEXPAND + expand_indent (stderr); + fprintf (stderr, "expand parent %p\n", hs->parent); +#endif + gts_hsplit_force_expand (hs->parent, hsurface); + } + + i = GTS_SPLIT (hs)->ncf; + cf = GTS_SPLIT (hs)->cfaces; + while (i--) { + GtsTriangle ** j, * t; + + j = cf->a1; + while ((t = *(j++))) + if (IS_CFACE (t)) { +#ifdef DEBUG_HEXPAND + expand_indent (stderr); + fprintf (stderr, "expand a1: cf->f: %p t: %p parent_split: %p\n", + cf->f, + t, + GTS_HSPLIT (CFACE (t)->parent_split)); +#endif + gts_hsplit_force_expand (GTS_HSPLIT (CFACE (t)->parent_split), + hsurface); +#ifdef DEBUG_HEXPAND + g_assert (!IS_CFACE (t)); +#endif + } + j = cf->a2; + while ((t = *(j++))) + if (IS_CFACE (t)) { +#ifdef DEBUG_HEXPAND + expand_indent (stderr); + fprintf (stderr, "expand a2: cf->f: %p t: %p parent_split: %p\n", + cf->f, + t, + GTS_HSPLIT (CFACE (t)->parent_split)); +#endif + gts_hsplit_force_expand (GTS_HSPLIT (CFACE (t)->parent_split), + hsurface); + } + cf++; + } + + gts_hsplit_expand (hs, hsurface); + +#ifdef DEBUG_HEXPAND + expand_level -= 2; + expand_indent (stderr); + fprintf (stderr, "%p expanded\n", hs); +#endif +} + +static void index_object (GtsObject * o, guint * n) +{ + o->reserved = GUINT_TO_POINTER ((*n)++); +} + +static void index_face (GtsFace * f, gpointer * data) +{ + guint * nf = data[1]; + + g_hash_table_insert (data[0], f, GUINT_TO_POINTER ((*nf)++)); +} + +/** + * gts_psurface_write: + * @ps: a #GtsPSurface. + * @fptr: a file pointer. + * + * Writes to @fptr a GTS progressive surface description. + */ +void gts_psurface_write (GtsPSurface * ps, FILE * fptr) +{ + guint nv = 1; + guint nf = 1; + GHashTable * hash; + gpointer data[2]; + + g_return_if_fail (ps != NULL); + g_return_if_fail (fptr != NULL); + g_return_if_fail (GTS_PSURFACE_IS_CLOSED (ps)); + + while (gts_psurface_remove_vertex (ps)) + ; + + GTS_POINT_CLASS (ps->s->vertex_class)->binary = FALSE; + gts_surface_write (ps->s, fptr); + + gts_surface_foreach_vertex (ps->s, (GtsFunc) index_object, &nv); + hash = g_hash_table_new (NULL, NULL); + data[0] = hash; + data[1] = &nf; + gts_surface_foreach_face (ps->s, (GtsFunc) index_face, data); + + fprintf (fptr, "%u\n", ps->split->len); + while (ps->pos) { + GtsSplit * vs = g_ptr_array_index (ps->split, --ps->pos); + GtsSplitCFace * scf = vs->cfaces; + GtsVertex * v1, * v2; + guint i = vs->ncf; + + fprintf (fptr, "%u %u", + GPOINTER_TO_UINT (GTS_OBJECT (vs->v)->reserved), + vs->ncf); + if (GTS_OBJECT (vs)->klass->write) + (*GTS_OBJECT (vs)->klass->write) (GTS_OBJECT (vs), fptr); + fputc ('\n', fptr); + + v1 = GTS_IS_SPLIT (vs->v1) ? GTS_SPLIT (vs->v1)->v : GTS_VERTEX (vs->v1); + GTS_OBJECT (v1)->reserved = GUINT_TO_POINTER (nv++); + v2 = GTS_IS_SPLIT (vs->v2) ? GTS_SPLIT (vs->v2)->v : GTS_VERTEX (vs->v2); + GTS_OBJECT (v2)->reserved = GUINT_TO_POINTER (nv++); + + (*GTS_OBJECT (v1)->klass->write) (GTS_OBJECT (v1), fptr); + fputc ('\n', fptr); + + (*GTS_OBJECT (v2)->klass->write) (GTS_OBJECT (v2), fptr); + fputc ('\n', fptr); + + while (i--) { + CFace * cf = CFACE (scf->f); + GtsTriangle ** a, * t; + + fprintf (fptr, "%u %u", + GPOINTER_TO_UINT (g_hash_table_lookup (hash, cf->t)), + cf->flags); + if (GTS_OBJECT_CLASS (ps->s->face_class)->write) + (*GTS_OBJECT_CLASS (ps->s->face_class)->write) (GTS_OBJECT (cf), fptr); + fputc ('\n', fptr); + + a = scf->a1; + while ((t = *(a++))) + fprintf (fptr, "%u ", + GPOINTER_TO_UINT (g_hash_table_lookup (hash, t))); + fprintf (fptr, "\n"); + + a = scf->a2; + while ((t = *(a++))) + fprintf (fptr, "%u ", + GPOINTER_TO_UINT (g_hash_table_lookup (hash, t))); + fprintf (fptr, "\n"); + + g_hash_table_insert (hash, cf, GUINT_TO_POINTER (nf++)); + + scf++; + } + + gts_split_expand (vs, ps->s, ps->s->edge_class); + } + + gts_surface_foreach_vertex (ps->s, + (GtsFunc) gts_object_reset_reserved, NULL); + g_hash_table_destroy (hash); +} + +static guint surface_read (GtsSurface * surface, + GtsFile * f, + GPtrArray * vertices, + GPtrArray * faces) +{ + GtsEdge ** edges; + guint n, nv, ne, nf; + + g_return_val_if_fail (surface != NULL, 1); + g_return_val_if_fail (f != NULL, 1); + + if (f->type != GTS_INT) { + gts_file_error (f, "expecting an integer (number of vertices)"); + return f->line; + } + nv = atoi (f->token->str); + + gts_file_next_token (f); + if (f->type != GTS_INT) { + gts_file_error (f, "expecting an integer (number of edges)"); + return f->line; + } + ne = atoi (f->token->str); + + gts_file_next_token (f); + if (f->type != GTS_INT) { + gts_file_error (f, "expecting an integer (number of faces)"); + return f->line; + } + nf = atoi (f->token->str); + + gts_file_next_token (f); + if (f->type == GTS_STRING) { + if (f->type != GTS_STRING) { + gts_file_error (f, "expecting a string (GtsSurfaceClass)"); + return f->line; + } + gts_file_next_token (f); + if (f->type != GTS_STRING) { + gts_file_error (f, "expecting a string (GtsFaceClass)"); + return f->line; + } + gts_file_next_token (f); + if (f->type != GTS_STRING) { + gts_file_error (f, "expecting a string (GtsEdgeClass)"); + return f->line; + } + gts_file_next_token (f); + if (f->type != GTS_STRING) { + gts_file_error (f, "expecting a string (GtsVertexClass)"); + return f->line; + } + if (!strcmp (f->token->str, "GtsVertexBinary")) + GTS_POINT_CLASS (surface->vertex_class)->binary = TRUE; + else + gts_file_first_token_after (f, '\n'); + } + else + gts_file_first_token_after (f, '\n'); + + g_ptr_array_set_size (vertices, nv); + g_ptr_array_set_size (faces, nf); + /* allocate nv + 1 just in case nv == 0 */ + edges = g_malloc ((ne + 1)*sizeof (GtsEdge *)); + + n = 0; + while (n < nv && f->type != GTS_ERROR) { + GtsObject * new_vertex = + gts_object_new (GTS_OBJECT_CLASS (surface->vertex_class)); + + (* GTS_OBJECT_CLASS (surface->vertex_class)->read) (&new_vertex, f); + if (f->type != GTS_ERROR) { + if (!GTS_POINT_CLASS (surface->vertex_class)->binary) + gts_file_first_token_after (f, '\n'); + g_ptr_array_index (vertices, n++) = new_vertex; + } + else + gts_object_destroy (new_vertex); + } + if (f->type == GTS_ERROR) + nv = n; + if (GTS_POINT_CLASS (surface->vertex_class)->binary) + gts_file_first_token_after (f, '\n'); + + n = 0; + while (n < ne && f->type != GTS_ERROR) { + guint p1, p2; + + if (f->type != GTS_INT) + gts_file_error (f, "expecting an integer (first vertex index)"); + else { + p1 = atoi (f->token->str); + if (p1 == 0 || p1 > nv) + gts_file_error (f, "vertex index `%d' is out of range `[1,%d]'", + p1, nv); + else { + gts_file_next_token (f); + if (f->type != GTS_INT) + gts_file_error (f, "expecting an integer (second vertex index)"); + else { + p2 = atoi (f->token->str); + if (p2 == 0 || p2 > nv) + gts_file_error (f, "vertex index `%d' is out of range `[1,%d]'", + p2, nv); + else { + GtsEdge * new_edge = + gts_edge_new (surface->edge_class, + g_ptr_array_index (vertices, p1 - 1), + g_ptr_array_index (vertices, p2 - 1)); + + gts_file_next_token (f); + if (f->type != '\n') + if (GTS_OBJECT_CLASS (surface->edge_class)->read) + (*GTS_OBJECT_CLASS (surface->edge_class)->read) + ((GtsObject **) &new_edge, f); + gts_file_first_token_after (f, '\n'); + edges[n++] = new_edge; + } + } + } + } + } + if (f->type == GTS_ERROR) + ne = n; + + n = 0; + while (n < nf && f->type != GTS_ERROR) { + guint s1, s2, s3; + + if (f->type != GTS_INT) + gts_file_error (f, "expecting an integer (first edge index)"); + else { + s1 = atoi (f->token->str); + if (s1 == 0 || s1 > ne) + gts_file_error (f, "edge index `%d' is out of range `[1,%d]'", + s1, ne); + else { + gts_file_next_token (f); + if (f->type != GTS_INT) + gts_file_error (f, "expecting an integer (second edge index)"); + else { + s2 = atoi (f->token->str); + if (s2 == 0 || s2 > ne) + gts_file_error (f, "edge index `%d' is out of range `[1,%d]'", + s2, ne); + else { + gts_file_next_token (f); + if (f->type != GTS_INT) + gts_file_error (f, "expecting an integer (third edge index)"); + else { + s3 = atoi (f->token->str); + if (s3 == 0 || s3 > ne) + gts_file_error (f, "edge index `%d' is out of range `[1,%d]'", + s3, ne); + else { + GtsFace * new_face = gts_face_new (surface->face_class, + edges[s1 - 1], + edges[s2 - 1], + edges[s3 - 1]); + + gts_file_next_token (f); + if (f->type != '\n') + if (GTS_OBJECT_CLASS (surface->face_class)->read) + (*GTS_OBJECT_CLASS (surface->face_class)->read) + ((GtsObject **) &new_face, f); + gts_file_first_token_after (f, '\n'); + gts_surface_add_face (surface, new_face); + g_ptr_array_index (faces, n++) = new_face; + } + } + } + } + } + } + } + + g_free (edges); + + if (f->type == GTS_ERROR) { + gts_allow_floating_vertices = TRUE; + while (nv) + gts_object_destroy (GTS_OBJECT (g_ptr_array_index (vertices, nv-- - 1))); + gts_allow_floating_vertices = FALSE; + return f->line; + } + + return 0; +} + +/** + * gts_psurface_open: + * @klass: a #GtsPSurfaceClass. + * @s: a #GtsSurface. + * @split_class: a #GtsSplitClass to use for the #GtsSplit. + * @f: a #GtsFile. + * + * Creates a new #GtsPSurface prepared for input from the file @f + * containing a valid GTS representation of a progressive surface. The initial + * shape of the progressive surface is loaded into @s. + * + * Before being usable as such this progressive surface must be closed using + * gts_psurface_close(). While open however, the functions + * gts_psurface_get_vertex_number(), gts_psurface_min_vertex_number() and + * gts_psurface_max_vertex_number() can still be used. + * + * Returns: a new #GtsPSurface or %NULL if there was a format error while + * reading the file, in which case @f contains information about the error. + */ +GtsPSurface * gts_psurface_open (GtsPSurfaceClass * klass, + GtsSurface * s, + GtsSplitClass * split_class, + GtsFile * f) +{ + GtsPSurface * ps; + + g_return_val_if_fail (klass != NULL, NULL); + g_return_val_if_fail (s != NULL, NULL); + g_return_val_if_fail (split_class != NULL, NULL); + g_return_val_if_fail (f != NULL, NULL); + + ps = GTS_PSURFACE (gts_object_new (GTS_OBJECT_CLASS (klass))); + ps->s = s; + ps->split_class = split_class; + + ps->vertices = g_ptr_array_new (); + ps->faces = g_ptr_array_new (); + + if (surface_read (s, f, ps->vertices, ps->faces)) { + ps->s = NULL; + gts_object_destroy (GTS_OBJECT (ps)); + return NULL; + } + + ps->min = gts_surface_vertex_number (ps->s); + ps->pos = 0; + + if (f->type == GTS_INT) { + gint ns = atoi (f->token->str); + + if (ns > 0) { + g_ptr_array_set_size (ps->split, ns); + gts_file_first_token_after (f, '\n'); + } + } + + return ps; +} + +/** + * gts_psurface_read_vertex: + * @ps: a #GtsPSurface prealably created with gts_psurface_open(). + * @fp: a #GtsFile. + * + * Reads in one vertex split operation from @fp and performs the expansion. + * + * If an error occurs while reading the file, the @error field of @fp is set. + * + * Returns: the newly created #GtsSplit or %NULL if no vertex split could be + * read from @fp. + */ +GtsSplit * gts_psurface_read_vertex (GtsPSurface * ps, GtsFile * fp) +{ + guint nv, ncf; + GtsSplit * vs, * parent; + GtsSplitCFace * scf; + + g_return_val_if_fail (ps != NULL, NULL); + g_return_val_if_fail (fp != NULL, NULL); + g_return_val_if_fail (!GTS_PSURFACE_IS_CLOSED (ps), NULL); + + if (ps->pos >= ps->split->len) + return NULL; + + if (fp->type == GTS_NONE) + return NULL; + if (fp->type != GTS_INT) { + gts_file_error (fp, "expecting an integer (vertex index)"); + return NULL; + } + nv = atoi (fp->token->str); + if (nv == 0 || nv > ps->vertices->len) { + gts_file_error (fp, "vertex index `%d' is out of range `[1,%d]'", + nv, ps->vertices->len); + return NULL; + } + + gts_file_next_token (fp); + if (fp->type != GTS_INT) { + gts_file_error (fp, "expecting an integer (ncf)"); + return NULL; + } + ncf = atoi (fp->token->str); + + vs = GTS_SPLIT (gts_object_new (GTS_OBJECT_CLASS (ps->split_class))); + + vs->v = g_ptr_array_index (ps->vertices, nv - 1); + vs->v1 = vs->v2 = NULL; + vs->cfaces = NULL; + vs->ncf = 0; + + gts_file_next_token (fp); + if (fp->type != '\n') + if (GTS_OBJECT (vs)->klass->read) + (* GTS_OBJECT (vs)->klass->read) ((GtsObject **) &vs, fp); + gts_file_first_token_after (fp, '\n'); + + if (fp->type != GTS_ERROR) { + vs->v1 = gts_object_new (GTS_OBJECT_CLASS (ps->s->vertex_class)); + (* GTS_OBJECT_CLASS (ps->s->vertex_class)->read) (&(vs->v1), fp); + if (fp->type != GTS_ERROR) { + vs->v1->reserved = vs; + g_ptr_array_add (ps->vertices, vs->v1); + + gts_file_first_token_after (fp, '\n'); + + vs->v2 = gts_object_new (GTS_OBJECT_CLASS (ps->s->vertex_class)); + (*GTS_OBJECT_CLASS (ps->s->vertex_class)->read) (&(vs->v2), fp); + if (fp->type != GTS_ERROR) { + vs->v2->reserved = vs; + g_ptr_array_add (ps->vertices, vs->v2); + gts_file_first_token_after (fp, '\n'); + } + } + } + + if (fp->type != GTS_ERROR) { + scf = vs->cfaces = g_malloc (sizeof (GtsSplitCFace)*ncf); + while (fp->type != GTS_ERROR && ncf--) { + guint it, flags; + GtsFace * f; + CFace * cf; + GPtrArray * a; + + if (fp->type != GTS_INT) + gts_file_error (fp, "expecting an integer (face index)"); + else { + it = atoi (fp->token->str); + if (it == 0 || it > ps->faces->len) + gts_file_error (fp, "face index `%d' is out of range `[1,%d]'", + it, ps->faces->len); + else { + gts_file_next_token (fp); + if (fp->type != GTS_INT) + gts_file_error (fp, "expecting an integer (flags)"); + else { + flags = atoi (fp->token->str); + f = + GTS_FACE (gts_object_new (GTS_OBJECT_CLASS (ps->s->face_class))); + + gts_file_next_token (fp); + if (fp->type != '\n') + if (GTS_OBJECT (f)->klass->read) + (*GTS_OBJECT (f)->klass->read) ((GtsObject **) &f, fp); + gts_file_first_token_after (fp, '\n'); + if (fp->type != GTS_ERROR) { + scf->f = f; + + cf = (CFace *) f; + GTS_OBJECT (cf)->klass = GTS_OBJECT_CLASS (cface_class ()); + cf->parent_split = vs; + cf->t = g_ptr_array_index (ps->faces, it - 1); + cf->flags = flags; + + a = g_ptr_array_new (); + do { + if (fp->type != GTS_INT) + gts_file_error (fp, "expecting an integer (face index)"); + else { + it = atoi (fp->token->str); + if (it > ps->faces->len) + gts_file_error (fp, + "face index `%d' is out of range `[1,%d]'", + it, ps->faces->len); + else { + g_ptr_array_add (a, g_ptr_array_index (ps->faces, + it - 1)); + gts_file_next_token (fp); + } + } + } while (fp->type != GTS_ERROR && fp->type != '\n'); + gts_file_first_token_after (fp, '\n'); + g_ptr_array_add (a, NULL); + scf->a1 = (GtsTriangle **) a->pdata; + g_ptr_array_free (a, FALSE); + + if (fp->type != GTS_ERROR) { + a = g_ptr_array_new (); + do { + if (fp->type != GTS_INT) + gts_file_error (fp, "expecting an integer (face index)"); + else { + it = atoi (fp->token->str); + if (it > ps->faces->len) + gts_file_error (fp, + "face index `%d' is out of range `[1,%d]'", + it, ps->faces->len); + else { + g_ptr_array_add (a, g_ptr_array_index (ps->faces, + it - 1)); + gts_file_next_token (fp); + } + } + } while (fp->type != GTS_ERROR && fp->type != '\n'); + gts_file_first_token_after (fp, '\n'); + g_ptr_array_add (a, NULL); + scf->a2 = (GtsTriangle **) a->pdata; + g_ptr_array_free (a, FALSE); + + g_ptr_array_add (ps->faces, f); + + vs->ncf++; + scf++; + } + } + } + } + } + } + } + + if (fp->type != GTS_ERROR) { + if ((parent = GTS_OBJECT (vs->v)->reserved)) { + GTS_OBJECT (vs->v)->reserved = NULL; + if (parent->v1 == GTS_OBJECT (vs->v)) + parent->v1 = GTS_OBJECT (vs); + else { + g_assert (parent->v2 == GTS_OBJECT (vs->v)); + parent->v2 = GTS_OBJECT (vs); + } + } + g_ptr_array_index (ps->split, ps->pos++) = vs; + gts_split_expand (vs, ps->s, ps->s->edge_class); + + return vs; + } + + if (vs->v1) gts_object_destroy (vs->v1); + if (vs->v2) gts_object_destroy (vs->v2); + gts_object_destroy (GTS_OBJECT (vs)); + + return NULL; +} + +/** + * gts_psurface_close: + * @ps: a #GtsPSurface prealably created with gts_psurface_open(). + * + * Closes a progressive surface. + */ +void gts_psurface_close (GtsPSurface * ps) +{ + g_return_if_fail (ps != NULL); + g_return_if_fail (!GTS_PSURFACE_IS_CLOSED (ps)); + + g_ptr_array_free (ps->vertices, TRUE); + g_ptr_array_free (ps->faces, TRUE); + ps->faces = ps->vertices = NULL; + + gts_surface_foreach_vertex (ps->s, + (GtsFunc) gts_object_reset_reserved, NULL); + if (ps->pos > 0) + g_ptr_array_set_size (ps->split, ps->pos); + if (ps->split->len > 1) { + guint i, half = ps->split->len/2, n = ps->split->len - 1; + + for (i = 0; i < half; i++) { + gpointer p1 = g_ptr_array_index (ps->split, i); + gpointer p2 = g_ptr_array_index (ps->split, n - i); + g_ptr_array_index (ps->split, n - i) = p1; + g_ptr_array_index (ps->split, i) = p2; + } + } + ps->pos = 0; +} Index: trunk/src_3rd/gts/stripe.c =================================================================== --- trunk/src_3rd/gts/stripe.c (nonexistent) +++ trunk/src_3rd/gts/stripe.c (revision 1065) @@ -0,0 +1,766 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999-2003 Wagner Toledo Correa, Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "gts.h" + +#define PRINT_HEAP_ELEMENTS 0 + +typedef struct { + GtsTriangle * t; + gboolean used; + GSList * neighbors; + GtsEHeapPair *pos; +} tri_data_t; + +typedef struct { + GHashTable * ht; +} map_t; + +typedef struct { + map_t * map; + GtsEHeap * heap; +} heap_t; + +static tri_data_t * tri_data_new (GtsTriangle * t); +static void tri_data_destroy (tri_data_t * td); +static guint tri_data_num_unused_neighbors2 (const tri_data_t * td, + const map_t * map); +static GHashTable * tri_data_unused_neighbors2 (const tri_data_t * td, + const map_t * map); + +static map_t * map_new (GtsSurface * s); +static void map_destroy (map_t * map); +static tri_data_t * map_lookup (const map_t * map, GtsTriangle * t); + + +static heap_t * heap_new (GtsSurface * s); +static void heap_destroy (heap_t * heap); +static gboolean heap_is_empty (const heap_t * heap); +static GtsTriangle * heap_top (const heap_t * heap); +static void heap_remove (heap_t * heap, GtsTriangle * t); + +/* helper functions */ + +static gboolean vertices_are_unique (GtsVertex * v1, + GtsVertex * v2, + GtsVertex * v3) +{ + g_assert (v1 && v2 && v3); + return (v1 != v2 && v1 != v3 && v2 != v3); +} + +static gboolean vertex_is_one_of (GtsVertex * v, + GtsVertex * v1, + GtsVertex * v2, + GtsVertex * v3) +{ + g_assert (v && v1 && v2 && v3); + return v == v1 || v == v2 || v == v3; +} + +static guint num_shared_vertices (GtsVertex * u1, + GtsVertex * u2, + GtsVertex * u3, + GtsVertex * v1, + GtsVertex * v2, + GtsVertex * v3) +{ + guint n = 0; + + g_assert (u1 && u2 && u3); + g_assert (v1 && v2 && v3); + g_assert (vertices_are_unique (u1, u2, u3)); + g_assert (vertices_are_unique (v1, v2, v3)); + + if (vertex_is_one_of (v1, u1, u2, u3)) + n++; + if (vertex_is_one_of (v2, u1, u2, u3)) + n++; + if (vertex_is_one_of (v3, u1, u2, u3)) + n++; + return n; +} + +static gboolean vertices_match (GtsVertex * v1, + GtsVertex * v2, + GtsVertex * v3, + GtsVertex ** v4, + GtsVertex ** v5, + GtsVertex ** v6) +{ + guint i; + + g_assert (v4 && v5 && v6); + g_assert (*v4 && *v5 && *v6); + g_assert (vertices_are_unique (*v4, *v5, *v6)); + + for (i = 0; i < 2; i++) { + if ((!v1 || (v1 == *v4)) && + (!v2 || (v2 == *v5)) && + (!v3 || (v3 == *v6))) + return TRUE; + else { + GtsVertex * v7 = * v4; + + *v4 = *v5; + *v5 = *v6; + *v6 = v7; + } + } + return ((!v1 || (v1 == *v4)) && + (!v2 || (v2 == *v5)) && + (!v3 || (v3 == *v6))); +} + +static GtsVertex * non_shared_vertex1 (GtsVertex * u1, + GtsVertex * u2, + GtsVertex * u3, + GtsVertex * v1, + GtsVertex * v2, + GtsVertex * v3) +{ + GtsVertex * u = NULL; + + g_assert (u1 && u2 && u3); + g_assert (v1 && v2 && v3); + g_assert (vertices_are_unique (u1, u2, u3)); + g_assert (vertices_are_unique (v1, v2, v3)); + g_assert (num_shared_vertices (u1, u2, u3, v1, v2, v3) == 2); + + if (!vertex_is_one_of (u1, v1, v2, v3)) { + g_assert (vertex_is_one_of (u2, v1, v2, v3)); + g_assert (vertex_is_one_of (u3, v1, v2, v3)); + u = u1; + } else if (!vertex_is_one_of (u2, v1, v2, v3)) { + g_assert (vertex_is_one_of (u1, v1, v2, v3)); + g_assert (vertex_is_one_of (u3, v1, v2, v3)); + u = u2; + } else if (!vertex_is_one_of (u3, v1, v2, v3)) { + g_assert (vertex_is_one_of (u1, v1, v2, v3)); + g_assert (vertex_is_one_of (u2, v1, v2, v3)); + u = u3; + } else + g_assert_not_reached (); + + return u; +} + +static void match_vertex (GtsVertex * v, + GtsVertex ** v1, + GtsVertex ** v2, + GtsVertex ** v3) +{ + g_assert (v && v1 && v2 && v3); + g_assert (*v1 && *v2 && *v3); + g_assert (vertex_is_one_of (v, *v1, *v2, *v3)); + while (*v1 != v) { + GtsVertex *v0 = *v1; + + *v1 = *v2; + *v2 = *v3; + *v3 = v0; + } +} + +/* tri_data_t functions */ + +static tri_data_t * tri_data_new (GtsTriangle * t) +{ + tri_data_t * td; + + td = g_malloc (sizeof (tri_data_t)); + td->t = t; + td->used = FALSE; + td->neighbors = gts_triangle_neighbors (t); + td->pos = NULL; + + return td; +} + +static void tri_data_destroy (tri_data_t * td) +{ + if (!td) + return; + g_slist_free (td->neighbors); + g_free (td); +} + +static guint tri_data_num_unused_neighbors2 (const tri_data_t * td, + const map_t * map) +{ + GHashTable *h; + guint n; + + g_assert (td); + g_assert (map); + h = tri_data_unused_neighbors2 (td, map); + n = g_hash_table_size (h); + g_hash_table_destroy (h); + return n; +} + +static void copy_key_to_array (gpointer key, + gpointer value, + gpointer user_data) +{ + GtsTriangle * t = key; + GtsTriangle *** p = user_data; + + (void) value; + g_assert (t); + g_assert (p && *p); + **p = t; + (*p)++; +} + +static gboolean are_neighbors_unique (GHashTable *h) +{ + GtsTriangle ** a; + GtsTriangle ** p; + gint i, j, n; /* guint won't work if n == 0 */ + + g_assert (h); + n = g_hash_table_size (h); +#ifdef DEBUG + if (n > 9) + g_warning ("triangle has %d 2-level neighbors", n); +#endif /* DEBUG */ + a = g_malloc(n*sizeof (GtsTriangle *)); + p = a; + g_hash_table_foreach (h, copy_key_to_array, &p); + for (i = 0; i < n - 1; i++) { + g_assert (a[i]); + for (j = i + 1; j < n; j++) { + g_assert (a[j]); + if (a[i] == a[j]) { + g_free (a); + return FALSE; + } + } + } + g_free (a); + return TRUE; +} + +static GHashTable * tri_data_unused_neighbors2 (const tri_data_t * td, + const map_t * map) +{ + GHashTable * h = g_hash_table_new (NULL, NULL); + GSList * li; + + g_assert (td); + g_assert (map); + for (li = td->neighbors; li != NULL; li = li->next) { + GtsTriangle * t2 = li->data; + tri_data_t * td2 = map_lookup (map, t2); + GSList * lj; + + g_assert (td2); + if (!td2->used) { + g_hash_table_insert (h, t2, td2); + for (lj = td2->neighbors; lj != NULL; lj = lj->next) { + GtsTriangle * t3 = lj->data; + tri_data_t * td3 = map_lookup (map, t3); + + g_assert (td3); + if (td3 != td && !td3->used) + g_hash_table_insert (h, t3, td3); + } + } + } + g_assert (are_neighbors_unique (h)); + return h; +} + +#if PRINT_HEAP_ELEMENTS +static void tri_data_print (const tri_data_t * td, FILE * fp) +{ + g_assert (td); + g_assert (fp); + fprintf(fp, "td=%p t=%p used=%d pos=%p key=%f\n", + td, td->t, td->used, td->pos, + td->pos ? td->pos->key : -1.0); +} +#endif /* PRINT_HEAP_ELEMENTS */ + +/* heap_t functions */ + +static gdouble triangle_priority (gpointer item, gpointer data) +{ + GtsTriangle * t = item; + map_t * map = data; + tri_data_t * td; + gdouble k; + + g_assert (t); + g_assert (map); + td = map_lookup (map, t); + g_assert (td); + k = tri_data_num_unused_neighbors2 (td, map); + return k; +} + +#if PRINT_HEAP_ELEMENTS +static void print_heap_element (gpointer data, gpointer user_data) +{ + GtsTriangle * t = data; + map_t * map = user_data; + tri_data_t * td; + + g_assert (t); + g_assert (map); + td = map_lookup (map, t); + g_assert (td); + g_assert (!td->used); + g_assert (td->pos); + tri_data_print (td, stderr); +} +#endif /* PRINT_HEAP_ELEMENTS */ + +static void insert_entry_into_heap (gpointer key, + gpointer value, + gpointer user_data) +{ + GtsTriangle * t = key; + tri_data_t * td = value; + GtsEHeap * heap = user_data; + + g_assert (!td->pos); + td->pos = gts_eheap_insert (heap, t); + g_assert (td->pos); +} + +static heap_t * heap_new (GtsSurface *s) +{ + heap_t * heap; + + g_assert (s); + heap = g_malloc (sizeof (heap_t)); + heap->map = map_new (s); + heap->heap = gts_eheap_new (triangle_priority, heap->map); + g_hash_table_foreach (heap->map->ht, + insert_entry_into_heap, + heap->heap); +#if PRINT_HEAP_ELEMENTS + gts_eheap_foreach (heap->heap, print_heap_element, heap->map); +#endif /* PRINT_HEAP_ELEMENTS */ + return heap; +} + +static void heap_destroy (heap_t * heap) +{ + if (!heap) + return; + map_destroy (heap->map); + gts_eheap_destroy (heap->heap); + g_free (heap); +} + +static gboolean heap_is_empty (const heap_t * heap) +{ + g_assert (heap); + g_assert (heap->heap); + return gts_eheap_size (heap->heap) == 0; +} + +typedef struct { + const heap_t * heap; + double min_key; +} min_key_t; + +static GtsTriangle * heap_top (const heap_t * heap) +{ + GtsTriangle * t; + + g_assert (heap); + g_assert (heap->heap); + t = gts_eheap_top (heap->heap, NULL); + return t; +} + +static void decrease_key (gpointer key, gpointer value, gpointer user_data) +{ + GtsTriangle * t = key; + tri_data_t * td = value; + heap_t *heap = user_data; + gdouble k; + + (void) t; + g_assert (heap); + g_assert (heap->map); + g_assert (heap->heap); + g_assert (td); + g_assert (!td->used); + g_assert (td->pos); + + k = tri_data_num_unused_neighbors2 (td, heap->map); + g_assert (k <= td->pos->key); +#ifdef DEBUG + if (k == td->pos->key) + g_warning ("same key: %f\n", k); +#endif /* DEBUG */ + if (k != td->pos->key) { + g_assert (k < td->pos->key); + g_assert (k >= 0.0); + gts_eheap_decrease_key (heap->heap, td->pos, k); + } +} + +static void heap_remove (heap_t * heap, GtsTriangle * t) +{ + tri_data_t * td; + GHashTable * h; + + g_assert (heap); + g_assert (t); + td = map_lookup (heap->map, t); + g_assert (td); + g_assert (!td->used); + g_assert (td->pos); + td->used = TRUE; + gts_eheap_remove (heap->heap, td->pos); + td->pos = NULL; + + /* fprintf(stderr, "td: %p\n", td); */ + h = tri_data_unused_neighbors2 (td, heap->map); + g_hash_table_foreach (h, decrease_key, heap); + g_hash_table_destroy (h); +} + +/* map_t functions */ + +static gint create_map_entry (gpointer item, gpointer data) +{ + GtsTriangle * t = item; + GHashTable * ht = data; + tri_data_t * td; + + g_assert (t); + g_assert (ht); + td = tri_data_new (t); + g_hash_table_insert (ht, t, td); + return 0; +} + +static void free_map_entry (gpointer key, gpointer value, gpointer user_data) +{ + GtsTriangle * t = key; + tri_data_t * td = value; + + (void) user_data; + g_assert (t); + g_assert (td); + g_assert (td->t == t); + tri_data_destroy (td); +} + +static map_t * map_new (GtsSurface * s) +{ + map_t * map; + + map = g_malloc (sizeof (map_t)); + map->ht = g_hash_table_new (NULL, NULL); + gts_surface_foreach_face (s, create_map_entry, map->ht); + return map; +} + +static void map_destroy (map_t * map) +{ + if (!map) + return; + g_hash_table_foreach (map->ht, free_map_entry, NULL); + g_hash_table_destroy (map->ht); + g_free (map); +} + +static tri_data_t * map_lookup (const map_t * map, GtsTriangle * t) +{ + tri_data_t * td; + + g_assert (map); + g_assert (map->ht); + g_assert (t); + td = g_hash_table_lookup (map->ht, t); + g_assert (td); + g_assert (td->t == t); + return td; +} + +/* other helper functions */ + +static GtsTriangle * find_min_neighbor (heap_t * heap, GtsTriangle * t) +{ + GtsTriangle * min_neighbor = NULL; + gdouble min_key = G_MAXDOUBLE; + tri_data_t * td; + GSList * li; + + g_assert (heap); + g_assert (t); + + td = map_lookup (heap->map, t); + for (li = td->neighbors; li != NULL; li = li->next) { + GtsTriangle * t2 = li->data; + tri_data_t * td2 = map_lookup (heap->map, t2); + gdouble k; + + g_assert (td2); + if (td2->used) + continue; + g_assert (td2->pos); + k = td2->pos->key; + if (k < min_key) { + min_key = k; + min_neighbor = t2; + } + } + return min_neighbor; +} + +static GtsTriangle * find_neighbor_forward (heap_t * heap, + GtsTriangle * t, + GtsVertex ** v1, + GtsVertex ** v2, + GtsVertex ** v3, + gboolean left_turn) +{ + GtsTriangle * neighbor = NULL; + tri_data_t * td; + GSList * li; + + g_assert (heap); + g_assert (t); + g_assert (v1 && v2 && v3); + g_assert (vertices_are_unique (*v1, *v2, *v3)); + + td = map_lookup (heap->map, t); + g_assert (td); + for (li = td->neighbors; li && !neighbor; li = li->next) { + GtsTriangle * t2 = li->data; + tri_data_t * td2 = map_lookup (heap->map, t2); + GtsVertex * v4, * v5, * v6; + + g_assert (td2); + if (t2 == t || td2->used) + continue; + gts_triangle_vertices (t2, &v4, &v5, &v6); + if (left_turn) { + if (!vertices_match (*v1, *v3, NULL, &v4, &v5, &v6)) + continue; + } else { + if (!vertices_match (*v3, *v2, NULL, &v4, &v5, &v6)) + continue; + } + neighbor = t2; + *v1 = v4; + *v2 = v5; + *v3 = v6; + } + return neighbor; +} + +static GtsTriangle * find_neighbor_backward (heap_t * heap, + GtsTriangle * t, + GtsVertex ** v1, + GtsVertex ** v2, + GtsVertex ** v3, + gboolean left_turn) +{ + GtsTriangle * neighbor = NULL; + tri_data_t * td; + GSList * li; + + g_assert (heap); + g_assert (t); + g_assert (v1 && v2 && v3); + g_assert (vertices_are_unique (*v1, *v2, *v3)); + + td = map_lookup (heap->map, t); + g_assert (td); + for (li = td->neighbors; li && !neighbor; li = li->next) { + GtsTriangle * t2 = li->data; + tri_data_t * td2 = map_lookup (heap->map, t2); + GtsVertex * v4, * v5, * v6; + + g_assert (td2); + if (t2 == t || td2->used) + continue; + gts_triangle_vertices (t2, &v4, &v5, &v6); + if (left_turn) { + if (!vertices_match (NULL, *v2, *v1, &v4, &v5, &v6)) + continue; + } else if (!vertices_match(*v1, NULL, *v2, &v4, &v5, &v6)) + continue; + neighbor = t2; + *v1 = v4; + *v2 = v5; + *v3 = v6; + } + return neighbor; +} + +static GSList * grow_strip_forward (heap_t * heap, + GSList * strip, + GtsTriangle * t, + GtsVertex * v1, + GtsVertex * v2, + GtsVertex * v3) +{ + gboolean left_turn; + + g_assert (heap); + g_assert (g_slist_length(strip) == 2); + g_assert (t); + g_assert (v1 && v2 && v3); + g_assert (vertices_are_unique (v1, v2, v3)); + + left_turn = TRUE; + while ((t = find_neighbor_forward (heap, t, &v1, &v2, &v3, + left_turn)) != NULL) { + heap_remove (heap, t); + strip = g_slist_prepend (strip, t); + left_turn = !left_turn; + } + return strip; +} + +static GSList * grow_strip_backward (heap_t * heap, + GSList * strip, + GtsTriangle * t, + GtsVertex * v1, + GtsVertex * v2, + GtsVertex * v3) +{ + /* we have to make sure we add an even number of triangles */ + GtsTriangle * t2; + + g_assert (heap); + g_assert (g_slist_length(strip) >= 2); + g_assert (t); + g_assert (v1 && v2 && v3); + g_assert (vertices_are_unique (v1, v2, v3)); + + while ((t2 = find_neighbor_backward (heap, t, &v1, &v2, &v3, + FALSE)) != NULL + && (t = find_neighbor_backward (heap, t2, &v1, &v2, &v3, + TRUE)) != NULL) { + heap_remove (heap, t2); + heap_remove (heap, t); + strip = g_slist_prepend (strip, t2); + strip = g_slist_prepend (strip, t); + } + return strip; +} + +static gboolean find_right_turn (GtsVertex ** v1, + GtsVertex ** v2, + GtsVertex ** v3, + GtsVertex ** v4, + GtsVertex ** v5, + GtsVertex ** v6) +{ + GtsVertex * v; + + g_assert (v1 && v2 && v3); + g_assert (v4 && v5 && v6); + g_assert (vertices_are_unique (*v1, *v2, *v3)); + g_assert (vertices_are_unique (*v4, *v5, *v6)); + g_assert (num_shared_vertices (*v1, *v2, *v3, *v4, *v5, *v6) == 2); + + v = non_shared_vertex1 (*v1, *v2, *v3, *v4, *v5, *v6); + match_vertex (v, v1, v2, v3); + match_vertex (*v3, v4, v5, v6); + + g_assert (v1 && v2 && v3); + g_assert (v4 && v5 && v6); + g_assert (*v4 == *v3); + + if (*v5 == *v2) { + g_assert (vertices_are_unique (*v1, *v2, *v3)); + g_assert (vertices_are_unique (*v4, *v5, *v6)); + g_assert (num_shared_vertices (*v1, *v2, *v3, + *v4, *v5, *v6) == 2); + return TRUE; + } else { +#ifdef DEBUG + g_warning ("couldn't find a right turn"); +#endif /* DEBUG */ + return FALSE; + } +} + +/** + * gts_surface_strip: + * @s: a #GtsSurface. + * + * Decompose @s into triangle strips for fast-rendering. + * + * Returns: a list of triangle strips containing all the triangles of @s. + * A triangle strip is itself a list of successive triangles having one edge + * in common. + */ +GSList * gts_surface_strip (GtsSurface *s) +{ + GSList * strips = NULL; + heap_t * heap; + + g_return_val_if_fail (s != NULL, NULL); + + heap = heap_new (s); + while (!heap_is_empty (heap)) { + GtsTriangle * t1, * t2; + GtsVertex * v1, * v2, * v3, * v4, * v5, * v6; + GSList * strip = NULL; + + /* remove heap top */ + t1 = heap_top (heap); + g_assert (t1); + heap_remove (heap, t1); + + /* start a new strip */ + strip = g_slist_prepend (strip, t1); + + /* find second triangle */ + t2 = find_min_neighbor (heap, t1); + if (t2) { + g_assert (t2 != t1); + + /* find right turn */ + gts_triangle_vertices (t1, &v1, &v2, &v3); + gts_triangle_vertices (t2, &v4, &v5, &v6); + if (find_right_turn (&v1, &v2, &v3, &v4, &v5, &v6)) { + heap_remove (heap, t2); + strip = g_slist_prepend (strip, t2); + + /* grow strip forward */ + strip = grow_strip_forward (heap, strip, t2, v4, v5, v6); + + strip = g_slist_reverse (strip); + + /* grow strip backward */ + strip = grow_strip_backward (heap, strip, t1, v1, v2, v3); + } + } + strips = g_slist_prepend (strips, strip); + } + strips = g_slist_reverse (strips); + heap_destroy (heap); + + return strips; +} Index: trunk/src_3rd/gts/surface.c =================================================================== --- trunk/src_3rd/gts/surface.c (nonexistent) +++ trunk/src_3rd/gts/surface.c (revision 1065) @@ -0,0 +1,2743 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include "gts.h" + +#include "gts-private.h" + +static void destroy_foreach_face (GtsFace * f, GtsSurface * s) +{ + f->surfaces = g_slist_remove (f->surfaces, s); + if (!GTS_OBJECT_DESTROYED (f) && + !gts_allow_floating_faces && f->surfaces == NULL) + gts_object_destroy (GTS_OBJECT (f)); +} + +static void surface_destroy (GtsObject * object) +{ + GtsSurface * surface = GTS_SURFACE (object); + + gts_surface_foreach_face (surface, (GtsFunc) destroy_foreach_face, surface); +#ifdef USE_SURFACE_BTREE + g_tree_destroy (surface->faces); +#else /* not USE_SURFACE_BTREE */ + g_hash_table_destroy (surface->faces); +#endif /* not USE_SURFACE_BTREE */ + + (* GTS_OBJECT_CLASS (gts_surface_class ())->parent_class->destroy) (object); +} + +static void surface_write (GtsObject * object, FILE * fptr) +{ + fprintf (fptr, " %s %s %s %s", + object->klass->info.name, + GTS_OBJECT_CLASS (GTS_SURFACE (object)->face_class)->info.name, + GTS_OBJECT_CLASS (GTS_SURFACE (object)->edge_class)->info.name, + GTS_POINT_CLASS (GTS_SURFACE (object)->vertex_class)->binary ? + "GtsVertexBinary" : + GTS_OBJECT_CLASS (GTS_SURFACE (object)->vertex_class)->info.name); +} + +static void surface_class_init (GtsSurfaceClass * klass) +{ + GTS_OBJECT_CLASS (klass)->destroy = surface_destroy; + GTS_OBJECT_CLASS (klass)->write = surface_write; + klass->add_face = NULL; + klass->remove_face = NULL; +} + +#ifdef USE_SURFACE_BTREE +static gint compare_pointers (gconstpointer a, gconstpointer b) +{ + if (GPOINTER_TO_UINT (a) < GPOINTER_TO_UINT (b)) + return -1; + if (GPOINTER_TO_UINT (a) > GPOINTER_TO_UINT (b)) + return 1; + return 0; +} +#endif /* USE_SURFACE_BTREE */ + +static void surface_init (GtsSurface * surface) +{ +#ifdef USE_SURFACE_BTREE + surface->faces = g_tree_new (compare_pointers); +#else /* not USE_SURFACE_BTREE */ + surface->faces = g_hash_table_new (NULL, NULL); +#endif /* not USE_SURFACE_BTREE */ + surface->vertex_class = gts_vertex_class (); + surface->edge_class = gts_edge_class (); + surface->face_class = gts_face_class (); + surface->keep_faces = FALSE; +} + +/** + * gts_surface_class: + * + * Returns: the #GtsSurfaceClass. + */ +GtsSurfaceClass * gts_surface_class (void) +{ + static GtsSurfaceClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo surface_info = { + "GtsSurface", + sizeof (GtsSurface), + sizeof (GtsSurfaceClass), + (GtsObjectClassInitFunc) surface_class_init, + (GtsObjectInitFunc) surface_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (gts_object_class (), &surface_info); + } + + return klass; +} + +/** + * gts_surface_new: + * @klass: a #GtsSurfaceClass. + * @face_class: a #GtsFaceClass. + * @edge_class: a #GtsEdgeClass. + * @vertex_class: a #GtsVertexClass. + * + * Returns: a new empty #GtsSurface. + */ +GtsSurface * gts_surface_new (GtsSurfaceClass * klass, + GtsFaceClass * face_class, + GtsEdgeClass * edge_class, + GtsVertexClass * vertex_class) +{ + GtsSurface * s; + + s = GTS_SURFACE (gts_object_new (GTS_OBJECT_CLASS (klass))); + s->vertex_class = vertex_class; + s->edge_class = edge_class; + s->face_class = face_class; + + return s; +} + +/** + * gts_surface_add_face: + * @s: a #GtsSurface. + * @f: a #GtsFace. + * + * Adds face @f to surface @s. + */ +void gts_surface_add_face (GtsSurface * s, GtsFace * f) +{ + g_return_if_fail (s != NULL); + g_return_if_fail (f != NULL); + + g_assert (s->keep_faces == FALSE); + +#ifdef USE_SURFACE_BTREE + if (!g_tree_lookup (s->faces, f)) { + f->surfaces = g_slist_prepend (f->surfaces, s); + g_tree_insert (s->faces, f, f); + } +#else /* not USE_SURFACE_BTREE */ + if (!g_hash_table_lookup (s->faces, f)) { + f->surfaces = g_slist_prepend (f->surfaces, s); + g_hash_table_insert (s->faces, f, f); + } +#endif /* not USE_SURFACE_BTREE */ + + if (GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass)->add_face) + (* GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass)->add_face) (s, f); +} + +/** + * gts_surface_remove_face: + * @s: a #GtsSurface. + * @f: a #GtsFace. + * + * Removes face @f from surface @s. + */ +void gts_surface_remove_face (GtsSurface * s, + GtsFace * f) +{ + g_return_if_fail (s != NULL); + g_return_if_fail (f != NULL); + + g_assert (s->keep_faces == FALSE); + +#ifdef USE_SURFACE_BTREE + g_tree_remove (s->faces, f); +#else /* not USE_SURFACE_BTREE */ + g_hash_table_remove (s->faces, f); +#endif /* not USE_SURFACE_BTREE */ + + f->surfaces = g_slist_remove (f->surfaces, s); + + if (GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass)->remove_face) + (* GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass)->remove_face) (s, f); + + if (!GTS_OBJECT_DESTROYED (f) && + !gts_allow_floating_faces && + f->surfaces == NULL) + gts_object_destroy (GTS_OBJECT (f)); +} + +/** + * gts_surface_read: + * @surface: a #GtsSurface. + * @f: a #GtsFile. + * + * Add to @surface the data read from @f. The format of the file pointed to + * by @f is as described in gts_surface_write(). + * + * Returns: 0 if successful or the line number at which the parsing + * stopped in case of error (in which case the @error field of @f is + * set to a description of the error which occured). + */ +/* Update split.c/surface_read() if modifying this function */ +guint gts_surface_read (GtsSurface * surface, GtsFile * f) +{ + GtsVertex ** vertices; + GtsEdge ** edges; + guint n, nv, ne, nf; + + g_return_val_if_fail (surface != NULL, 1); + g_return_val_if_fail (f != NULL, 1); + + if (f->type != GTS_INT) { + gts_file_error (f, "expecting an integer (number of vertices)"); + return f->line; + } + nv = atoi (f->token->str); + + gts_file_next_token (f); + if (f->type != GTS_INT) { + gts_file_error (f, "expecting an integer (number of edges)"); + return f->line; + } + ne = atoi (f->token->str); + + gts_file_next_token (f); + if (f->type != GTS_INT) { + gts_file_error (f, "expecting an integer (number of faces)"); + return f->line; + } + nf = atoi (f->token->str); + + gts_file_next_token (f); + if (f->type == GTS_STRING) { + if (f->type != GTS_STRING) { + gts_file_error (f, "expecting a string (GtsSurfaceClass)"); + return f->line; + } + gts_file_next_token (f); + if (f->type != GTS_STRING) { + gts_file_error (f, "expecting a string (GtsFaceClass)"); + return f->line; + } + gts_file_next_token (f); + if (f->type != GTS_STRING) { + gts_file_error (f, "expecting a string (GtsEdgeClass)"); + return f->line; + } + gts_file_next_token (f); + if (f->type != GTS_STRING) { + gts_file_error (f, "expecting a string (GtsVertexClass)"); + return f->line; + } + if (!strcmp (f->token->str, "GtsVertexBinary")) + GTS_POINT_CLASS (surface->vertex_class)->binary = TRUE; + else { + GTS_POINT_CLASS (surface->vertex_class)->binary = FALSE; + gts_file_first_token_after (f, '\n'); + } + } + else + gts_file_first_token_after (f, '\n'); + + if (nf <= 0) + return 0; + + /* allocate nv + 1 just in case nv == 0 */ + vertices = g_malloc ((nv + 1)*sizeof (GtsVertex *)); + edges = g_malloc ((ne + 1)*sizeof (GtsEdge *)); + + n = 0; + while (n < nv && f->type != GTS_ERROR) { + GtsObject * new_vertex = + gts_object_new (GTS_OBJECT_CLASS (surface->vertex_class)); + + (* GTS_OBJECT_CLASS (surface->vertex_class)->read) (&new_vertex, f); + if (f->type != GTS_ERROR) { + if (!GTS_POINT_CLASS (surface->vertex_class)->binary) + gts_file_first_token_after (f, '\n'); + vertices[n++] = GTS_VERTEX (new_vertex); + } + else + gts_object_destroy (new_vertex); + } + if (f->type == GTS_ERROR) + nv = n; + if (GTS_POINT_CLASS (surface->vertex_class)->binary) + gts_file_first_token_after (f, '\n'); + + n = 0; + while (n < ne && f->type != GTS_ERROR) { + guint p1, p2; + + if (f->type != GTS_INT) + gts_file_error (f, "expecting an integer (first vertex index)"); + else { + p1 = atoi (f->token->str); + if (p1 == 0 || p1 > nv) + gts_file_error (f, "vertex index `%d' is out of range `[1,%d]'", + p1, nv); + else { + gts_file_next_token (f); + if (f->type != GTS_INT) + gts_file_error (f, "expecting an integer (second vertex index)"); + else { + p2 = atoi (f->token->str); + if (p2 == 0 || p2 > nv) + gts_file_error (f, "vertex index `%d' is out of range `[1,%d]'", + p2, nv); + else { + GtsEdge * new_edge = + gts_edge_new (surface->edge_class, + vertices[p1 - 1], vertices[p2 - 1]); + + gts_file_next_token (f); + if (f->type != '\n') + if (GTS_OBJECT_CLASS (surface->edge_class)->read) + (*GTS_OBJECT_CLASS (surface->edge_class)->read) + ((GtsObject **) &new_edge, f); + gts_file_first_token_after (f, '\n'); + edges[n++] = new_edge; + } + } + } + } + } + if (f->type == GTS_ERROR) + ne = n; + + n = 0; + while (n < nf && f->type != GTS_ERROR) { + guint s1, s2, s3; + + if (f->type != GTS_INT) + gts_file_error (f, "expecting an integer (first edge index)"); + else { + s1 = atoi (f->token->str); + if (s1 == 0 || s1 > ne) + gts_file_error (f, "edge index `%d' is out of range `[1,%d]'", + s1, ne); + else { + gts_file_next_token (f); + if (f->type != GTS_INT) + gts_file_error (f, "expecting an integer (second edge index)"); + else { + s2 = atoi (f->token->str); + if (s2 == 0 || s2 > ne) + gts_file_error (f, "edge index `%d' is out of range `[1,%d]'", + s2, ne); + else { + gts_file_next_token (f); + if (f->type != GTS_INT) + gts_file_error (f, "expecting an integer (third edge index)"); + else { + s3 = atoi (f->token->str); + if (s3 == 0 || s3 > ne) + gts_file_error (f, "edge index `%d' is out of range `[1,%d]'", + s3, ne); + else { + GtsFace * new_face = gts_face_new (surface->face_class, + edges[s1 - 1], + edges[s2 - 1], + edges[s3 - 1]); + + gts_file_next_token (f); + if (f->type != '\n') + if (GTS_OBJECT_CLASS (surface->face_class)->read) + (*GTS_OBJECT_CLASS (surface->face_class)->read) + ((GtsObject **) &new_face, f); + gts_file_first_token_after (f, '\n'); + gts_surface_add_face (surface, new_face); + n++; + } + } + } + } + } + } + } + + if (f->type == GTS_ERROR) { + gts_allow_floating_vertices = TRUE; + while (nv) + gts_object_destroy (GTS_OBJECT (vertices[nv-- - 1])); + gts_allow_floating_vertices = FALSE; + } + + g_free (vertices); + g_free (edges); + + if (f->type == GTS_ERROR) + return f->line; + return 0; +} + +static void sum_area (GtsFace * f, gdouble * area) { + *area += gts_triangle_area (GTS_TRIANGLE (f)); +} + +/** + * gts_surface_area: + * @s: a #GtsSurface. + * + * Returns: the area of @s obtained as the sum of the signed areas of its + * faces. + */ +gdouble gts_surface_area (GtsSurface * s) +{ + gdouble area = 0.0; + gts_surface_foreach_face (s, (GtsFunc)sum_area, &area); + return area; +} + +/** + * gts_range_init: + * @r: a #GtsRange. + * + * Initializes a #GtsRange. + */ +void gts_range_init (GtsRange * r) +{ + g_return_if_fail (r != NULL); + + r->max = - G_MAXDOUBLE; + r->min = G_MAXDOUBLE; + r->sum = r->sum2 = 0.0; + r->n = 0; +} + +/** + * gts_range_reset: + * @r: a #GtsRange. + * + * Sets all the fields of @r to 0. + */ +void gts_range_reset (GtsRange * r) +{ + g_return_if_fail (r != NULL); + + r->max = 0.0; + r->min = 0.0; + r->sum = r->sum2 = 0.0; + r->n = 0; +} + +/** + * gts_range_add_value: + * @r: a #GtsRange. + * @val: a value to add to @r. + * + * Adds @val to @r. + */ +void gts_range_add_value (GtsRange * r, gdouble val) +{ + g_return_if_fail (r != NULL); + + if (val < r->min) r->min = val; + if (val > r->max) r->max = val; + r->sum += val; + r->sum2 += val*val; + r->n++; +} + +/** + * gts_range_update: + * @r: a #GtsRange. + * + * Updates the fields of @r. + */ +void gts_range_update (GtsRange * r) +{ + g_return_if_fail (r != NULL); + + if (r->n > 0) { + if (r->sum2 - r->sum*r->sum/(gdouble) r->n >= 0.) + r->stddev = sqrt ((r->sum2 - r->sum*r->sum/(gdouble) r->n) + /(gdouble) r->n); + else + r->stddev = 0.; + r->mean = r->sum/(gdouble) r->n; + } + else + r->min = r->max = r->mean = r->stddev = 0.; +} + +/** + * gts_range_print: + * @r: a #GtsRange. + * @fptr: a file pointer. + * + * Writes a text representation of @r in @fptr. + */ +void gts_range_print (GtsRange * r, FILE * fptr) +{ + g_return_if_fail (r != NULL); + g_return_if_fail (fptr != NULL); + fprintf (fptr, "min: %g mean: %g | %g max: %g", + r->min, r->mean, r->stddev, r->max); +} + +static void stats_foreach_vertex (GtsVertex * v, GtsSurfaceStats * stats) +{ + GSList * i = v->segments; + guint nedges = 0; + + while (i) { + if (GTS_IS_EDGE (i->data) && + gts_edge_has_parent_surface (i->data, stats->parent)) + nedges++; + i = i->next; + } + gts_range_add_value (&stats->edges_per_vertex, nedges); +} + +static void stats_foreach_edge (GtsEdge * e, GtsSurfaceStats * stats) +{ + guint nt = gts_edge_face_number (e, stats->parent); + + if (gts_segment_is_duplicate (GTS_SEGMENT (e))) + stats->n_duplicate_edges++; + if (nt == 1) + stats->n_boundary_edges++; + else if (nt > 2) + stats->n_non_manifold_edges++; + gts_range_add_value (&stats->faces_per_edge, nt); +} + +static void stats_foreach_face (GtsTriangle * t, GtsSurfaceStats * stats) +{ + if (!gts_face_is_compatible (GTS_FACE (t), stats->parent)) + stats->n_incompatible_faces++; + if (gts_triangle_is_duplicate (t)) + stats->n_duplicate_faces++; + stats->n_faces++; +} + +/** + * gts_surface_stats: + * @s: a #GtsSurface. + * @stats: a #GtsSurfaceStats. + * + * Fills @stats with the statistics relevant to surface @s. + */ +void gts_surface_stats (GtsSurface * s, GtsSurfaceStats * stats) +{ + g_return_if_fail (s != NULL); + g_return_if_fail (stats != NULL); + + stats->parent = s; + stats->n_faces = 0; + stats->n_incompatible_faces = 0; + stats->n_duplicate_faces = 0; + stats->n_duplicate_edges = 0; + stats->n_boundary_edges = 0; + stats->n_non_manifold_edges = 0; + gts_range_init (&stats->edges_per_vertex); + gts_range_init (&stats->faces_per_edge); + + gts_surface_foreach_vertex (s, (GtsFunc) stats_foreach_vertex, stats); + gts_surface_foreach_edge (s, (GtsFunc) stats_foreach_edge, stats); + gts_surface_foreach_face (s, (GtsFunc) stats_foreach_face, stats); + + gts_range_update (&stats->edges_per_vertex); + gts_range_update (&stats->faces_per_edge); +} + +static void quality_foreach_edge (GtsSegment * s, + GtsSurfaceQualityStats * stats) +{ + GSList * i = GTS_EDGE (s)->triangles; + + gts_range_add_value (&stats->edge_length, + gts_point_distance (GTS_POINT (s->v1), + GTS_POINT (s->v2))); + while (i) { + GSList * j = i->next; + while (j) { + gts_range_add_value (&stats->edge_angle, + fabs (gts_triangles_angle (i->data, j->data))); + j = j->next; + } + i = i->next; + } +} + +static void quality_foreach_face (GtsTriangle * t, + GtsSurfaceQualityStats * stats) +{ + gts_range_add_value (&stats->face_quality, gts_triangle_quality (t)); + gts_range_add_value (&stats->face_area, gts_triangle_area (t)); +} + +/** + * gts_surface_quality_stats: + * @s: a #GtsSurface. + * @stats: a #GtsSurfaceQualityStats. + * + * Fills @stats with quality statistics relevant to surface @s. + */ +void gts_surface_quality_stats (GtsSurface * s, GtsSurfaceQualityStats * stats) +{ + g_return_if_fail (s != NULL); + g_return_if_fail (stats != NULL); + + stats->parent = s; + gts_range_init (&stats->face_quality); + gts_range_init (&stats->face_area); + gts_range_init (&stats->edge_length); + gts_range_init (&stats->edge_angle); + + gts_surface_foreach_edge (s, (GtsFunc) quality_foreach_edge, stats); + gts_surface_foreach_face (s, (GtsFunc) quality_foreach_face, stats); + + gts_range_update (&stats->face_quality); + gts_range_update (&stats->face_area); + gts_range_update (&stats->edge_length); + gts_range_update (&stats->edge_angle); +} + +/** + * gts_surface_print_stats: + * @s: a #GtsSurface. + * @fptr: a file pointer. + * + * Writes in the file pointed to by @fptr the statistics for surface @s. + */ +void gts_surface_print_stats (GtsSurface * s, FILE * fptr) +{ + GtsSurfaceStats stats; + GtsSurfaceQualityStats qstats; + + g_return_if_fail (s != NULL); + g_return_if_fail (fptr != NULL); + + gts_surface_stats (s, &stats); + gts_surface_quality_stats (s, &qstats); + + fprintf (fptr, + "# vertices: %u edges: %u faces: %u\n" + "# Connectivity statistics\n" + "# incompatible faces: %u\n" + "# duplicate faces: %u\n" + "# boundary edges: %u\n" + "# duplicate edges: %u\n" + "# non-manifold edges: %u\n", + stats.edges_per_vertex.n, + stats.faces_per_edge.n, + stats.n_faces, + stats.n_incompatible_faces, + stats.n_duplicate_faces, + stats.n_boundary_edges, + stats.n_duplicate_edges, + stats.n_non_manifold_edges); + fputs ("# edges per vertex: ", fptr); + gts_range_print (&stats.edges_per_vertex, fptr); + fputs ("\n# faces per edge: ", fptr); + gts_range_print (&stats.faces_per_edge, fptr); + fputs ("\n# Geometric statistics\n# face quality: ", fptr); + gts_range_print (&qstats.face_quality, fptr); + fputs ("\n# face area : ", fptr); + gts_range_print (&qstats.face_area, fptr); + fputs ("\n# edge length : ", fptr); + gts_range_print (&qstats.edge_length, fptr); + fputc ('\n', fptr); +} + +static void write_vertex (GtsPoint * p, gpointer * data) +{ + (*GTS_OBJECT (p)->klass->write) (GTS_OBJECT (p), (FILE *) data[0]); + if (!GTS_POINT_CLASS (GTS_OBJECT (p)->klass)->binary) + fputc ('\n', (FILE *) data[0]); + g_hash_table_insert (data[2], p, + GUINT_TO_POINTER (++(*((guint *) data[1])))); +} + +static void write_edge (GtsSegment * s, gpointer * data) +{ + fprintf ((FILE *) data[0], "%u %u", + GPOINTER_TO_UINT (g_hash_table_lookup (data[2], s->v1)), + GPOINTER_TO_UINT (g_hash_table_lookup (data[2], s->v2))); + if (GTS_OBJECT (s)->klass->write) + (*GTS_OBJECT (s)->klass->write) (GTS_OBJECT (s), (FILE *) data[0]); + fputc ('\n', (FILE *) data[0]); + g_hash_table_insert (data[3], s, + GUINT_TO_POINTER (++(*((guint *) data[1])))); +} + +static void write_face (GtsTriangle * t, gpointer * data) +{ + fprintf (data[0], "%u %u %u", + GPOINTER_TO_UINT (g_hash_table_lookup (data[3], t->e1)), + GPOINTER_TO_UINT (g_hash_table_lookup (data[3], t->e2)), + GPOINTER_TO_UINT (g_hash_table_lookup (data[3], t->e3))); + if (GTS_OBJECT (t)->klass->write) + (*GTS_OBJECT (t)->klass->write) (GTS_OBJECT (t), data[0]); + fputc ('\n', data[0]); +} + +/** + * gts_surface_write: + * @s: a #GtsSurface. + * @fptr: a file pointer. + * + * Writes in the file @fptr an ASCII representation of @s. The file + * format is as follows. + * + * All the lines beginning with #GTS_COMMENTS are ignored. The first line + * contains three unsigned integers separated by spaces. The first + * integer is the number of vertices, nv, the second is the number of + * edges, ne and the third is the number of faces, nf. + * + * Follows nv lines containing the x, y and z coordinates of the + * vertices. Follows ne lines containing the two indices (starting + * from one) of the vertices of each edge. Follows nf lines containing + * the three ordered indices (also starting from one) of the edges of + * each face. + * + * The format described above is the least common denominator to all + * GTS files. Consistent with an object-oriented approach, the GTS + * file format is extensible. Each of the lines of the file can be + * extended with user-specific attributes accessible through the + * read() and write() virtual methods of each of the objects written + * (surface, vertices, edges or faces). When read with different + * object classes, these extra attributes are just ignored. + */ +void gts_surface_write (GtsSurface * s, FILE * fptr) +{ + guint n; + gpointer data[4]; + GHashTable * vindex, * eindex; + GtsSurfaceStats stats; + + g_return_if_fail (s != NULL); + g_return_if_fail (fptr != NULL); + + data[0] = fptr; + data[1] = &n; + data[2] = vindex = g_hash_table_new (NULL, NULL); + data[3] = eindex = g_hash_table_new (NULL, NULL); + + gts_surface_stats (s, &stats); + fprintf (fptr, "%u %u %u", + stats.edges_per_vertex.n, + stats.faces_per_edge.n, + stats.n_faces); + if (GTS_OBJECT (s)->klass->write) + (*GTS_OBJECT (s)->klass->write) (GTS_OBJECT (s), fptr); + fputc ('\n', fptr); + n = 0; + gts_surface_foreach_vertex (s, (GtsFunc) write_vertex, data); + n = 0; + if (GTS_POINT_CLASS (s->vertex_class)->binary) + fputc ('\n', fptr); + gts_surface_foreach_edge (s, (GtsFunc) write_edge, data); + gts_surface_foreach_face (s, (GtsFunc) write_face, data); + g_hash_table_destroy (vindex); + g_hash_table_destroy (eindex); +} + +static void write_vertex_oogl (GtsPoint * p, gpointer * data) +{ + FILE * fp = data[0]; + + fprintf (fp, "%g %g %g", p->x, p->y, p->z); + if (GTS_OBJECT (p)->klass->color) { + GtsColor c = (* GTS_OBJECT (p)->klass->color) (GTS_OBJECT (p)); + fprintf (fp, " %g %g %g 1.0\n", c.r, c.g, c.b); + } + else + fputc ('\n', fp); + GTS_OBJECT (p)->reserved = GUINT_TO_POINTER ((*((guint *) data[1]))++); +} + +static void write_face_oogl (GtsTriangle * t, FILE * fp) +{ + GtsVertex * v1, * v2, * v3; + gts_triangle_vertices (t, &v1, &v2, &v3); + fprintf (fp, "3 %u %u %u", + GPOINTER_TO_UINT (GTS_OBJECT (v1)->reserved), + GPOINTER_TO_UINT (GTS_OBJECT (v2)->reserved), + GPOINTER_TO_UINT (GTS_OBJECT (v3)->reserved)); + if (GTS_OBJECT (t)->klass->color) { + GtsColor c = (* GTS_OBJECT (t)->klass->color) (GTS_OBJECT (t)); + fprintf (fp, " %g %g %g\n", c.r, c.g, c.b); + } + else + fputc ('\n', fp); +} + +/** + * gts_surface_write_oogl: + * @s: a #GtsSurface. + * @fptr: a file pointer. + * + * Writes in the file @fptr an OOGL (Geomview) representation of @s. + */ +void gts_surface_write_oogl (GtsSurface * s, FILE * fptr) +{ + guint n = 0; + gpointer data[2]; + GtsSurfaceStats stats; + + g_return_if_fail (s != NULL); + g_return_if_fail (fptr != NULL); + + data[0] = fptr; + data[1] = &n; + + gts_surface_stats (s, &stats); + if (GTS_OBJECT_CLASS (s->vertex_class)->color) + fputs ("COFF ", fptr); + else + fputs ("OFF ", fptr); + fprintf (fptr, "%u %u %u\n", + stats.edges_per_vertex.n, + stats.n_faces, + stats.faces_per_edge.n); + gts_surface_foreach_vertex (s, (GtsFunc) write_vertex_oogl, data); + gts_surface_foreach_face (s, (GtsFunc) write_face_oogl, fptr); + gts_surface_foreach_vertex (s, (GtsFunc) gts_object_reset_reserved, NULL); +} + +static void write_vertex_vtk (GtsPoint * p, gpointer * data) +{ + FILE * fp = data[0]; + + fprintf (fp, "%g %g %g\n", p->x, p->y, p->z); + GTS_OBJECT (p)->reserved = GUINT_TO_POINTER ((*((guint *) data[1]))++); +} + +static void write_face_vtk (GtsTriangle * t, FILE * fp) +{ + GtsVertex * v1, * v2, * v3; + gts_triangle_vertices (t, &v1, &v2, &v3); + fprintf (fp, "3 %u %u %u\n", + GPOINTER_TO_UINT (GTS_OBJECT (v1)->reserved), + GPOINTER_TO_UINT (GTS_OBJECT (v2)->reserved), + GPOINTER_TO_UINT (GTS_OBJECT (v3)->reserved)); +} + +/** + * gts_surface_write_vtk: + * @s: a #GtsSurface. + * @fptr: a file pointer. + * + * Writes in the file @fptr a VTK representation of @s. + */ +void gts_surface_write_vtk (GtsSurface * s, FILE * fptr) +{ + guint n = 0; + gpointer data[2]; + GtsSurfaceStats stats; + + g_return_if_fail (s != NULL); + g_return_if_fail (fptr != NULL); + + data[0] = fptr; + data[1] = &n; + + gts_surface_stats (s, &stats); + fprintf (fptr, + "# vtk DataFile Version 2.0\n" + "Generated by GTS\n" + "ASCII\n" + "DATASET POLYDATA\n" + "POINTS %u float\n", + stats.edges_per_vertex.n); + gts_surface_foreach_vertex (s, (GtsFunc) write_vertex_vtk, data); + fprintf (fptr, + "POLYGONS %u %u\n", + stats.n_faces, stats.n_faces*4); + gts_surface_foreach_face (s, (GtsFunc) write_face_vtk, fptr); + gts_surface_foreach_vertex (s, (GtsFunc) gts_object_reset_reserved, NULL); +} + +static void write_edge_oogl_boundary (GtsSegment * s, gpointer * data) +{ + if (!gts_edge_is_boundary (GTS_EDGE (s), data[1])) + return; + + if (GTS_OBJECT (s)->klass->color) { + GtsColor c = (* GTS_OBJECT (s)->klass->color) (GTS_OBJECT (s)); + fprintf (data[0], "VECT 1 2 1 2 1 %g %g %g %g %g %g %g %g %g 1.\n", + GTS_POINT (s->v1)->x, GTS_POINT (s->v1)->y, GTS_POINT (s->v1)->z, + GTS_POINT (s->v2)->x, GTS_POINT (s->v2)->y, GTS_POINT (s->v2)->z, + c.r, c.g, c.b); + } + else + fprintf (data[0], "VECT 1 2 0 2 0 %g %g %g %g %g %g\n", + GTS_POINT (s->v1)->x, GTS_POINT (s->v1)->y, GTS_POINT (s->v1)->z, + GTS_POINT (s->v2)->x, GTS_POINT (s->v2)->y, GTS_POINT (s->v2)->z); +} + +/** + * gts_surface_write_oogl_boundary: + * @s: a #GtsSurface. + * @fptr: a file pointer. + * + * Writes in the file @fptr an OOGL (Geomview) representation of the + * boundary of @s. + */ +void gts_surface_write_oogl_boundary (GtsSurface * s, FILE * fptr) +{ + gpointer data[2]; + + g_return_if_fail (s != NULL); + g_return_if_fail (fptr != NULL); + + data[0] = fptr; + data[1] = s; + fputs ("LIST {\n", fptr); + gts_surface_foreach_edge (s, (GtsFunc) write_edge_oogl_boundary, data); + fputs ("}\n", fptr); +} + +#ifdef USE_SURFACE_BTREE +static gint vertex_foreach_face (GtsTriangle * t, + gpointer t_data, + gpointer * info) +#else /* not USE_SURFACE_BTREE */ +static void vertex_foreach_face (GtsTriangle * t, + gpointer t_data, + gpointer * info) +#endif /* not USE_SURFACE_BTREE */ +{ + GHashTable * hash = info[0]; + gpointer data = info[1]; + GtsFunc func = (GtsFunc) info[2]; + GtsSegment + * s1 = GTS_SEGMENT (t->e1); + + if (!g_hash_table_lookup (hash, s1->v1)) { + (*func) (s1->v1, data); + g_hash_table_insert (hash, s1->v1, GINT_TO_POINTER (-1)); + } + if (!g_hash_table_lookup (hash, s1->v2)) { + (*func) (s1->v2, data); + g_hash_table_insert (hash, s1->v2, GINT_TO_POINTER (-1)); + } + if (!g_hash_table_lookup (hash, gts_triangle_vertex (t))) { + (*func) (gts_triangle_vertex (t), data); + g_hash_table_insert (hash, gts_triangle_vertex (t), + GINT_TO_POINTER (-1)); + } +#ifdef USE_SURFACE_BTREE + return FALSE; +#endif /* USE_SURFACE_BTREE */ +} + +/** + * gts_surface_foreach_vertex: + * @s: a #GtsSurface. + * @func: a #GtsFunc. + * @data: user data to be passed to @func. + * + * Calls @func once for each vertex of @s. + */ +void gts_surface_foreach_vertex (GtsSurface * s, GtsFunc func, gpointer data) +{ + gpointer info[3]; + + g_return_if_fail (s != NULL); + g_return_if_fail (func != NULL); + + /* forbid removal of faces */ + s->keep_faces = TRUE; + info[0] = g_hash_table_new (NULL, NULL); + info[1] = data; + info[2] = func; +#ifdef USE_SURFACE_BTREE + g_tree_traverse (s->faces, (GTraverseFunc) vertex_foreach_face, G_IN_ORDER, + info); +#else /* not USE_SURFACE_BTREE */ + g_hash_table_foreach (s->faces, (GHFunc) vertex_foreach_face, info); +#endif /* not USE_SURFACE_BTREE */ + g_hash_table_destroy (info[0]); + /* allow removal of faces */ + s->keep_faces = FALSE; +} + +#ifdef USE_SURFACE_BTREE +static gint edge_foreach_face (GtsTriangle * t, + gpointer t_data, + gpointer * info) +#else /* not USE_SURFACE_BTREE */ +static void edge_foreach_face (GtsTriangle * t, + gpointer t_data, + gpointer * info) +#endif /* not USE_SURFACE_BTREE */ +{ + GHashTable * hash = info[0]; + gpointer data = info[1]; + GtsFunc func = (GtsFunc) info[2]; + + if (!g_hash_table_lookup (hash, t->e1)) { + (*func) (t->e1, data); + g_hash_table_insert (hash, t->e1, GINT_TO_POINTER (-1)); + } + if (!g_hash_table_lookup (hash, t->e2)) { + (*func) (t->e2, data); + g_hash_table_insert (hash, t->e2, GINT_TO_POINTER (-1)); + } + if (!g_hash_table_lookup (hash, t->e3)) { + (*func) (t->e3, data); + g_hash_table_insert (hash, t->e3, GINT_TO_POINTER (-1)); + } +#ifdef USE_SURFACE_BTREE + return FALSE; +#endif /* not USE_SURFACE_BTREE */ +} + +/** + * gts_surface_foreach_edge: + * @s: a #GtsSurface. + * @func: a #GtsFunc. + * @data: user data to be passed to @func. + * + * Calls @func once for each edge of @s. + */ +void gts_surface_foreach_edge (GtsSurface * s, GtsFunc func, gpointer data) +{ + gpointer info[3]; + + g_return_if_fail (s != NULL); + g_return_if_fail (func != NULL); + + /* forbid removal of faces */ + s->keep_faces = TRUE; + info[0] = g_hash_table_new (NULL, NULL); + info[1] = data; + info[2] = func; +#ifdef USE_SURFACE_BTREE + g_tree_traverse (s->faces, (GTraverseFunc) edge_foreach_face, G_IN_ORDER, + info); +#else /* not USE_SURFACE_BTREE */ + g_hash_table_foreach (s->faces, (GHFunc) edge_foreach_face, info); +#endif /* not USE_SURFACE_BTREE */ + g_hash_table_destroy (info[0]); + /* allow removal of faces */ + s->keep_faces = FALSE; +} + +#ifdef USE_SURFACE_BTREE +static gint foreach_face (GtsFace * f, + gpointer t_data, + gpointer * info) +#else /* not USE_SURFACE_BTREE */ +static void foreach_face (GtsFace * f, + gpointer t_data, + gpointer * info) +#endif /* not USE_SURFACE_BTREE */ +{ + (*((GtsFunc) info[0])) (f, info[1]); +#ifdef USE_SURFACE_BTREE + return FALSE; +#endif /* USE_SURFACE_BTREE */ +} + +/** + * gts_surface_foreach_face: + * @s: a #GtsSurface. + * @func: a #GtsFunc. + * @data: user data to be passed to @func. + * + * Calls @func once for each face of @s. + */ +void gts_surface_foreach_face (GtsSurface * s, + GtsFunc func, + gpointer data) +{ + gpointer info[2]; + + g_return_if_fail (s != NULL); + g_return_if_fail (func != NULL); + + /* forbid removal of faces */ + s->keep_faces = TRUE; + info[0] = func; + info[1] = data; +#ifdef USE_SURFACE_BTREE + g_tree_traverse (s->faces, (GTraverseFunc) foreach_face, G_IN_ORDER, + info); +#else /* not USE_SURFACE_BTREE */ + g_hash_table_foreach (s->faces, (GHFunc) foreach_face, info); +#endif /* not USE_SURFACE_BTREE */ + /* allow removal of faces */ + s->keep_faces = FALSE; +} + +#ifdef USE_SURFACE_BTREE +static gint foreach_face_remove (GtsFace * f, + gpointer t_data, + gpointer * info) +{ + if ((*((GtsFunc) info[0])) (f, info[1])) { + GtsSurface * s = info[2]; + guint * n = info[3]; + + f->surfaces = g_slist_remove (f->surfaces, s); + if (!GTS_OBJECT_DESTROYED (f) && + !gts_allow_floating_faces && + f->surfaces == NULL) + gts_object_destroy (GTS_OBJECT (f)); + + if (GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass)->remove_face) + (* GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass)->remove_face) (s, f); + + g_tree_remove (s->faces, f); + (*n)++; + } + return FALSE; +} +#else /* not USE_SURFACE_BTREE */ +static gboolean foreach_face_remove (GtsFace * f, + gpointer t_data, + gpointer * info) +{ + if ((*((GtsFunc) info[0])) (f, info[1])) { + GtsSurface * s = info[2]; + + f->surfaces = g_slist_remove (f->surfaces, s); + if (!GTS_OBJECT_DESTROYED (f) && + !gts_allow_floating_faces && + f->surfaces == NULL) + gts_object_destroy (GTS_OBJECT (f)); + + if (GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass)->remove_face) + (* GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass)->remove_face) (s, f); + + return TRUE; + } + return FALSE; +} +#endif /* not USE_SURFACE_BTREE */ + +/** + * gts_surface_foreach_face_remove: + * @s: a #GtsSurface. + * @func: a #GtsFunc. + * @data: user data to be passed to @func. + * + * Calls @func once for each face of @s. If @func returns %TRUE the + * corresponding face is removed from @s (and destroyed if it does not + * belong to any other surface and #gts_allow_floating_faces is set to + * %FALSE). + * + * Returns: the number of faces removed from @s. + */ +guint gts_surface_foreach_face_remove (GtsSurface * s, + GtsFunc func, + gpointer data) +{ + gpointer info[4]; + guint n = 0; + + g_return_val_if_fail (s != NULL, 0); + g_return_val_if_fail (func != NULL, 0); + + /* forbid removal of faces */ + s->keep_faces = TRUE; + info[0] = func; + info[1] = data; + info[2] = s; +#ifdef USE_SURFACE_BTREE + info[3] = &n; + g_tree_traverse (s->faces, (GTraverseFunc) foreach_face_remove, G_PRE_ORDER, + info); +#else /* not USE_SURFACE_BTREE */ + n = g_hash_table_foreach_remove (s->faces, + (GHRFunc) foreach_face_remove, + info); +#endif /* not USE_SURFACE_BTREE */ + /* allow removal of faces */ + s->keep_faces = FALSE; + + return n; +} + +static void midvertex_insertion (GtsEdge * e, + GtsSurface * surface, + GtsEHeap * heap, + GtsRefineFunc refine_func, + gpointer refine_data, + GtsVertexClass * vertex_class, + GtsEdgeClass * edge_class) +{ + GtsVertex * midvertex; + GtsEdge * e1, * e2; + GSList * i; + + midvertex = (*refine_func) (e, vertex_class, refine_data); + e1 = gts_edge_new (edge_class, GTS_SEGMENT (e)->v1, midvertex); + gts_eheap_insert (heap, e1); + e2 = gts_edge_new (edge_class, GTS_SEGMENT (e)->v2, midvertex); + gts_eheap_insert (heap, e2); + + /* creates new faces and modifies old ones */ + i = e->triangles; + while (i) { + GtsTriangle * t = i->data; + GtsVertex * v1, * v2, * v3; + GtsEdge * te2, * te3, * ne, * tmp; + + gts_triangle_vertices_edges (t, e, &v1, &v2, &v3, &e, &te2, &te3); + ne = gts_edge_new (edge_class, midvertex, v3); + gts_eheap_insert (heap, ne); + if (GTS_SEGMENT (e1)->v1 == v2) { + tmp = e1; e1 = e2; e2 = tmp; + } + e1->triangles = g_slist_prepend (e1->triangles, t); + ne->triangles = g_slist_prepend (ne->triangles, t); + te2->triangles = g_slist_remove (te2->triangles, t); + t->e1 = e1; t->e2 = ne; t->e3 = te3; + gts_surface_add_face (surface, + gts_face_new (surface->face_class, e2, te2, ne)); + i = i->next; + } + /* destroys edge */ + g_slist_free (e->triangles); + e->triangles = NULL; + gts_object_destroy (GTS_OBJECT (e)); +} + +static gdouble edge_length2_inverse (GtsSegment * s) +{ + return - gts_point_distance2 (GTS_POINT (s->v1), GTS_POINT (s->v2)); +} + +static void create_heap_refine (GtsEdge * e, GtsEHeap * heap) +{ + gts_eheap_insert (heap, e); +} + +/** + * gts_surface_refine: + * @surface: a #GtsSurface. + * @cost_func: a function returning the cost for a given edge. + * @cost_data: user data to be passed to @cost_func. + * @refine_func: a #GtsRefineFunc. + * @refine_data: user data to be passed to @refine_func. + * @stop_func: a #GtsStopFunc. + * @stop_data: user data to be passed to @stop_func. + * + * Refine @surface using a midvertex insertion technique. All the + * edges of @surface are ordered according to @cost_func. The edges + * are then processed in order until @stop_func returns %TRUE. Each + * edge is split in two and new edges and faces are created. + * + * If @cost_func is set to %NULL, the edges are sorted according + * to their length squared (the longest is on top). + * + * If @refine_func is set to %NULL gts_segment_midvertex() is used. + * + */ +void gts_surface_refine (GtsSurface * surface, + GtsKeyFunc cost_func, + gpointer cost_data, + GtsRefineFunc refine_func, + gpointer refine_data, + GtsStopFunc stop_func, + gpointer stop_data) +{ + GtsEHeap * heap; + GtsEdge * e; + gdouble top_cost; + + g_return_if_fail (surface != NULL); + g_return_if_fail (stop_func != NULL); + + if (cost_func == NULL) + cost_func = (GtsKeyFunc) edge_length2_inverse; + if (refine_func == NULL) + refine_func = (GtsRefineFunc) gts_segment_midvertex; + + heap = gts_eheap_new (cost_func, cost_data); + gts_eheap_freeze (heap); + gts_surface_foreach_edge (surface, (GtsFunc) create_heap_refine, heap); + gts_eheap_thaw (heap); + while ((e = gts_eheap_remove_top (heap, &top_cost)) && + !(*stop_func) (top_cost, + gts_eheap_size (heap) + + gts_edge_face_number (e, surface) + 2, + stop_data)) + midvertex_insertion (e, surface, heap, refine_func, refine_data, + surface->vertex_class, surface->edge_class); + gts_eheap_destroy (heap); +} + +static GSList * edge_triangles (GtsEdge * e1, GtsEdge * e) +{ + GSList * i = e1->triangles; + GSList * triangles = NULL; + + while (i) { + GtsTriangle * t = i->data; + if (t->e1 == e || t->e2 == e || t->e3 == e) { + GtsEdge * e2; + GSList * j; + if (t->e1 == e) { + if (t->e2 == e1) + e2 = t->e3; + else + e2 = t->e2; + } + else if (t->e2 == e) { + if (t->e3 == e1) + e2 = t->e1; + else + e2 = t->e3; + } + else { + if (t->e2 == e1) + e2 = t->e1; + else + e2 = t->e2; + } + j = e2->triangles; + while (j) { + GtsTriangle * t = j->data; + if (t->e1 != e && t->e2 != e && t->e3 != e) + triangles = g_slist_prepend (triangles, t); + j = j->next; + } + } + else + triangles = g_slist_prepend (triangles, t); + i = i->next; + } + return triangles; +} + +static void replace_vertex (GSList * i, GtsVertex * v1, GtsVertex * v) +{ + while (i) { + GtsSegment * s = i->data; + if (s->v1 == v1) + s->v1 = v; + else + s->v2 = v; + i = i->next; + } +} + +/** + * gts_edge_collapse_creates_fold: + * @e: a #GtsEdge. + * @v: a #GtsVertex. + * @max: the maximum value of the square of the cosine of the angle between + * two triangles. + * + * Returns: %TRUE if collapsing edge @e to vertex @v would create + * faces making an angle the cosine squared of which would be larger than max, + * %FALSE otherwise. + */ +gboolean gts_edge_collapse_creates_fold (GtsEdge * e, + GtsVertex * v, + gdouble max) +{ + GtsVertex * v1, * v2; + GtsSegment * s; + GSList * i; + gboolean folded = FALSE; + + g_return_val_if_fail (e != NULL, TRUE); + g_return_val_if_fail (v != NULL, TRUE); + + s = GTS_SEGMENT (e); + v1 = s->v1; + v2 = s->v2; + replace_vertex (v1->segments, v1, v); + replace_vertex (v2->segments, v2, v); + + i = v1->segments; + while (i && !folded) { + GtsSegment * s = i->data; + if (GTS_IS_EDGE (s)) { + GtsEdge * e1 = GTS_EDGE (s); + if (e1 != e) { + GSList * triangles = edge_triangles (e1, e); + folded = gts_triangles_are_folded (triangles, s->v1, s->v2, max); + g_slist_free (triangles); + } + } + i = i->next; + } + + i = v2->segments; + while (i && !folded) { + GtsSegment * s = i->data; + if (GTS_IS_EDGE (s)) { + GtsEdge * e1 = GTS_EDGE (s); + if (e1 != e) { + GSList * triangles = edge_triangles (e1, e); + folded = gts_triangles_are_folded (triangles, s->v1, s->v2, max); + g_slist_free (triangles); + } + } + i = i->next; + } +#if 1 + if (!folded) { + GSList * triangles = gts_vertex_triangles (v1, NULL); + i = triangles = gts_vertex_triangles (v2, triangles); + while (i && !folded) { + GtsTriangle * t = i->data; + if (t->e1 != e && t->e2 != e && t->e3 != e) { + GtsEdge * e1 = gts_triangle_edge_opposite (t, v); + g_assert (e1); + folded = gts_triangles_are_folded (e1->triangles, + GTS_SEGMENT (e1)->v1, + GTS_SEGMENT (e1)->v2, + max); + } + i = i->next; + } + g_slist_free (triangles); + } +#endif + replace_vertex (v1->segments, v, v1); + replace_vertex (v2->segments, v, v2); + return folded; +} + +/** + * gts_edge_collapse_is_valid: + * @e: a #GtsEdge. + * + * An implementation of the topological constraints described in the + * "Mesh Optimization" article of Hoppe et al (1993). + * + * Returns: %TRUE if @e can be collapsed without violation of the topological + * constraints, %FALSE otherwise. + */ +gboolean gts_edge_collapse_is_valid (GtsEdge * e) +{ + GSList * i; + + g_return_val_if_fail (e != NULL, FALSE); + + i = GTS_SEGMENT (e)->v1->segments; + while (i) { + GtsEdge * e1 = i->data; + if (e1 != e && GTS_IS_EDGE (e1)) { + GtsEdge * e2 = NULL; + GSList * j = GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e)->v1 ? + GTS_SEGMENT (e1)->v2->segments : GTS_SEGMENT (e1)->v1->segments; + while (j && !e2) { + GtsEdge * e1 = j->data; + if (GTS_IS_EDGE (e1) && + (GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e)->v2 || + GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e)->v2)) + e2 = e1; + j = j->next; + } + if (e2 && !gts_triangle_use_edges (e, e1, e2)) + return FALSE; + } + i = i->next; + } + + if (gts_edge_is_boundary (e, NULL)) { + GtsTriangle * t = e->triangles->data; + if (gts_edge_is_boundary (t->e1, NULL) && + gts_edge_is_boundary (t->e2, NULL) && + gts_edge_is_boundary (t->e3, NULL)) + return FALSE; + } + else { + if (gts_vertex_is_boundary (GTS_SEGMENT (e)->v1, NULL) && + gts_vertex_is_boundary (GTS_SEGMENT (e)->v2, NULL)) + return FALSE; + if (gts_edge_belongs_to_tetrahedron (e)) + return FALSE; + } + + return TRUE; +} + +#define HEAP_INSERT_EDGE(h, e) (GTS_OBJECT (e)->reserved = gts_eheap_insert (h, e)) +#define HEAP_REMOVE_EDGE(h, e) (gts_eheap_remove (h, GTS_OBJECT (e)->reserved),\ + GTS_OBJECT (e)->reserved = NULL) + +static GtsVertex * edge_collapse (GtsEdge * e, + GtsEHeap * heap, + GtsCoarsenFunc coarsen_func, + gpointer coarsen_data, + GtsVertexClass * klass, + gdouble maxcosine2) +{ + GSList * i; + GtsVertex * v1 = GTS_SEGMENT (e)->v1, * v2 = GTS_SEGMENT (e)->v2, * mid; + + /* if the edge is degenerate (i.e. v1 == v2), destroy and return */ + if (v1 == v2) { + gts_object_destroy (GTS_OBJECT (e)); + return NULL; + } + + if (!gts_edge_collapse_is_valid (e)) { + GTS_OBJECT (e)->reserved = + gts_eheap_insert_with_key (heap, e, G_MAXDOUBLE); + return NULL; + } + + mid = (*coarsen_func) (e, klass, coarsen_data); + + if (gts_edge_collapse_creates_fold (e, mid, maxcosine2)) { + GTS_OBJECT (e)->reserved = + gts_eheap_insert_with_key (heap, e, G_MAXDOUBLE); + gts_object_destroy (GTS_OBJECT (mid)); + return NULL; + } + + gts_object_destroy (GTS_OBJECT (e)); + + gts_vertex_replace (v1, mid); + gts_object_destroy (GTS_OBJECT (v1)); + gts_vertex_replace (v2, mid); + gts_object_destroy (GTS_OBJECT (v2)); + + /* destroy duplicate edges */ + i = mid->segments; + while (i) { + GtsEdge * e1 = i->data; + GtsEdge * duplicate; + while ((duplicate = gts_edge_is_duplicate (e1))) { + gts_edge_replace (duplicate, GTS_EDGE (e1)); + HEAP_REMOVE_EDGE (heap, duplicate); + gts_object_destroy (GTS_OBJECT (duplicate)); + } + i = i->next; + if (!e1->triangles) { + /* e1 is the result of the collapse of one edge of a pair of identical + faces (it should not happen unless duplicate triangles are present in + the initial surface) */ + g_warning ("file %s: line %d (%s): probably duplicate triangle.", + __FILE__, __LINE__, G_GNUC_PRETTY_FUNCTION); + HEAP_REMOVE_EDGE (heap, e1); + gts_object_destroy (GTS_OBJECT (e1)); + if (i == NULL) /* mid has been destroyed */ + mid = NULL; + } + } + + return mid; +} + +/* + * I don't see where this code is ever used, but keep it for a bit + * in case it is needed for debugging + */ +#ifdef GTS_NEED_UPDATE_CLOSEST_NEIGHBORS +static void update_closest_neighbors (GtsVertex * v, GtsEHeap * heap) +{ + GSList * i = v->segments; + + while (i) { + GtsSegment * s = i->data; + if (GTS_IS_EDGE (s)) { + HEAP_REMOVE_EDGE (heap, GTS_EDGE (s)); + HEAP_INSERT_EDGE (heap, GTS_EDGE (s)); + } + i = i->next; + } +} +#endif + +static void update_2nd_closest_neighbors (GtsVertex * v, GtsEHeap * heap) +{ + GSList * i = v->segments; + GSList * list = NULL; + + while (i) { + GtsSegment * s = i->data; + if (GTS_IS_EDGE (s)) { + GtsVertex * v1 = s->v1 == v ? s->v2 : s->v1; + GSList * j = v1->segments; + while (j) { + GtsSegment * s1 = j->data; + if (GTS_IS_EDGE (s1) && !g_slist_find (list, s1)) + list = g_slist_prepend (list, s1); + j = j->next; + } + } + i = i->next; + } + + i = list; + while (i) { + GtsEdge * e = i->data; + HEAP_REMOVE_EDGE (heap, e); + HEAP_INSERT_EDGE (heap, e); + i = i->next; + } + + g_slist_free (list); +} + +static gdouble edge_length2 (GtsEdge * e) +{ + return gts_point_distance2 (GTS_POINT (GTS_SEGMENT (e)->v1), + GTS_POINT (GTS_SEGMENT (e)->v2)); +} + +static void create_heap_coarsen (GtsEdge * e, GtsEHeap * heap) +{ + HEAP_INSERT_EDGE (heap, e); +} + +/** + * gts_surface_coarsen: + * @surface: a #GtsSurface. + * @cost_func: a function returning the cost for a given edge. + * @cost_data: user data to be passed to @cost_func. + * @coarsen_func: a #GtsCoarsenVertexFunc. + * @coarsen_data: user data to be passed to @coarsen_func. + * @stop_func: a #GtsStopFunc. + * @stop_data: user data to be passed to @stop_func. + * @minangle: minimum angle between two neighboring triangles. + * + * The edges of @surface are sorted according to @cost_func to + * create a priority heap (a #GtsEHeap). The edges are extracted in + * turn from the top of the heap and collapsed (i.e. the vertices are + * replaced by the vertex returned by the @coarsen_func function) + * until the @stop_func functions returns %TRUE. + * + * If @cost_func is set to %NULL, the edges are sorted according + * to their length squared (the shortest is on top). + * + * If @coarsen_func is set to %NULL gts_segment_midvertex() is used. + * + * The minimum angle is used to avoid introducing faces which would be folded. + */ +void gts_surface_coarsen (GtsSurface * surface, + GtsKeyFunc cost_func, + gpointer cost_data, + GtsCoarsenFunc coarsen_func, + gpointer coarsen_data, + GtsStopFunc stop_func, + gpointer stop_data, + gdouble minangle) +{ + GtsEHeap * heap; + GtsEdge * e; + gdouble top_cost; + gdouble maxcosine2; + + g_return_if_fail (surface != NULL); + g_return_if_fail (stop_func != NULL); + + if (cost_func == NULL) + cost_func = (GtsKeyFunc) edge_length2; + if (coarsen_func == NULL) + coarsen_func = (GtsCoarsenFunc) gts_segment_midvertex; + + heap = gts_eheap_new (cost_func, cost_data); + maxcosine2 = cos (minangle); maxcosine2 *= maxcosine2; + + gts_eheap_freeze (heap); + gts_surface_foreach_edge (surface, (GtsFunc) create_heap_coarsen, heap); + gts_eheap_thaw (heap); + /* we want to control edge destruction manually */ + gts_allow_floating_edges = TRUE; + while ((e = gts_eheap_remove_top (heap, &top_cost)) && + (top_cost < G_MAXDOUBLE) && + !(*stop_func) (top_cost, gts_eheap_size (heap) - + gts_edge_face_number (e, surface), stop_data)) + { + GtsVertex * v = edge_collapse (e, heap, coarsen_func, coarsen_data, + surface->vertex_class, maxcosine2); + if (v != NULL) + update_2nd_closest_neighbors (v, heap); + } + gts_allow_floating_edges = FALSE; + + /* set reserved field of remaining edges back to NULL */ + if (e) GTS_OBJECT (e)->reserved = NULL; + gts_eheap_foreach (heap, (GFunc) gts_object_reset_reserved, NULL); + + gts_eheap_destroy (heap); +} + +/** + * gts_coarsen_stop_number: + * @cost: the cost of the edge collapse considered. + * @nedge: the current number of edges of the surface being simplified. + * @min_number: a pointer to the minimum number of edges desired for the + * surface being simplified. + * + * This function is to be used as the @stop_func argument of + * gts_surface_coarsen() or gts_psurface_new(). + * + * Returns: %TRUE if the edge collapse would create a surface with a smaller + * number of edges than given by @min_number, %FALSE otherwise. + */ +gboolean gts_coarsen_stop_number (gdouble cost, + guint nedge, + guint * min_number) +{ + g_return_val_if_fail (min_number != NULL, TRUE); + + if (nedge < *min_number) + return TRUE; + return FALSE; +} + +/** + * gts_coarsen_stop_cost: + * @cost: the cost of the edge collapse considered. + * @nedge: the current number of edges of the surface being simplified. + * @max_cost: a pointer to the maximum cost allowed for an edge collapse. + * + * This function is to be used as the @stop_func argument of + * gts_surface_coarsen() or gts_psurface_new(). + * + * Returns: %TRUE if the cost of the edge collapse considered is larger than + * given by @max_cost, %FALSE otherwise. + */ +gboolean gts_coarsen_stop_cost (gdouble cost, + guint nedge, + gdouble * max_cost) +{ + g_return_val_if_fail (max_cost != NULL, TRUE); + + if (cost > *max_cost) + return TRUE; + return FALSE; +} + +#define GTS_M_ICOSAHEDRON_X /* sqrt(sqrt(5)+1)/sqrt(2*sqrt(5)) */ \ + 0.850650808352039932181540497063011072240401406 +#define GTS_M_ICOSAHEDRON_Y /* sqrt(2)/sqrt(5+sqrt(5)) */ \ + 0.525731112119133606025669084847876607285497935 +#define GTS_M_ICOSAHEDRON_Z 0.0 + +static guint generate_icosahedron (GtsSurface * s) +{ + GtsVertex * v01 = gts_vertex_new (s->vertex_class, + +GTS_M_ICOSAHEDRON_Z, +GTS_M_ICOSAHEDRON_X, -GTS_M_ICOSAHEDRON_Y); + GtsVertex * v02 = gts_vertex_new (s->vertex_class, + +GTS_M_ICOSAHEDRON_X, +GTS_M_ICOSAHEDRON_Y, +GTS_M_ICOSAHEDRON_Z); + GtsVertex * v03 = gts_vertex_new (s->vertex_class, + +GTS_M_ICOSAHEDRON_Y, +GTS_M_ICOSAHEDRON_Z, -GTS_M_ICOSAHEDRON_X); + GtsVertex * v04 = gts_vertex_new (s->vertex_class, + +GTS_M_ICOSAHEDRON_Y, +GTS_M_ICOSAHEDRON_Z, +GTS_M_ICOSAHEDRON_X); + GtsVertex * v05 = gts_vertex_new (s->vertex_class, + +GTS_M_ICOSAHEDRON_X, -GTS_M_ICOSAHEDRON_Y, +GTS_M_ICOSAHEDRON_Z); + GtsVertex * v06 = gts_vertex_new (s->vertex_class, + +GTS_M_ICOSAHEDRON_Z, +GTS_M_ICOSAHEDRON_X, +GTS_M_ICOSAHEDRON_Y); + GtsVertex * v07 = gts_vertex_new (s->vertex_class, + -GTS_M_ICOSAHEDRON_Y, +GTS_M_ICOSAHEDRON_Z, +GTS_M_ICOSAHEDRON_X); + GtsVertex * v08 = gts_vertex_new (s->vertex_class, + +GTS_M_ICOSAHEDRON_Z, -GTS_M_ICOSAHEDRON_X, -GTS_M_ICOSAHEDRON_Y); + GtsVertex * v09 = gts_vertex_new (s->vertex_class, + -GTS_M_ICOSAHEDRON_X, +GTS_M_ICOSAHEDRON_Y, +GTS_M_ICOSAHEDRON_Z); + GtsVertex * v10 = gts_vertex_new (s->vertex_class, + -GTS_M_ICOSAHEDRON_Y, +GTS_M_ICOSAHEDRON_Z, -GTS_M_ICOSAHEDRON_X); + GtsVertex * v11 = gts_vertex_new (s->vertex_class, + -GTS_M_ICOSAHEDRON_X, -GTS_M_ICOSAHEDRON_Y, +GTS_M_ICOSAHEDRON_Z); + GtsVertex * v12 = gts_vertex_new (s->vertex_class, + +GTS_M_ICOSAHEDRON_Z, -GTS_M_ICOSAHEDRON_X, +GTS_M_ICOSAHEDRON_Y); + + GtsEdge * e01 = gts_edge_new (s->edge_class, v01, v02); + GtsEdge * e02 = gts_edge_new (s->edge_class, v03, v02); + GtsEdge * e03 = gts_edge_new (s->edge_class, v01, v03); + GtsEdge * e04 = gts_edge_new (s->edge_class, v04, v05); + GtsEdge * e05 = gts_edge_new (s->edge_class, v02, v05); + GtsEdge * e06 = gts_edge_new (s->edge_class, v04, v02); + GtsEdge * e07 = gts_edge_new (s->edge_class, v06, v07); + GtsEdge * e08 = gts_edge_new (s->edge_class, v04, v07); + GtsEdge * e09 = gts_edge_new (s->edge_class, v06, v04); + GtsEdge * e10 = gts_edge_new (s->edge_class, v08, v03); + GtsEdge * e11 = gts_edge_new (s->edge_class, v03, v05); + GtsEdge * e12 = gts_edge_new (s->edge_class, v08, v05); + GtsEdge * e13 = gts_edge_new (s->edge_class, v06, v09); + GtsEdge * e14 = gts_edge_new (s->edge_class, v07, v09); + GtsEdge * e15 = gts_edge_new (s->edge_class, v08, v10); + GtsEdge * e16 = gts_edge_new (s->edge_class, v03, v10); + GtsEdge * e17 = gts_edge_new (s->edge_class, v06, v01); + GtsEdge * e18 = gts_edge_new (s->edge_class, v01, v09); + GtsEdge * e19 = gts_edge_new (s->edge_class, v08, v11); + GtsEdge * e20 = gts_edge_new (s->edge_class, v10, v11); + GtsEdge * e21 = gts_edge_new (s->edge_class, v06, v02); + GtsEdge * e22 = gts_edge_new (s->edge_class, v12, v11); + GtsEdge * e23 = gts_edge_new (s->edge_class, v12, v08); + GtsEdge * e24 = gts_edge_new (s->edge_class, v12, v07); + GtsEdge * e25 = gts_edge_new (s->edge_class, v07, v11); + GtsEdge * e26 = gts_edge_new (s->edge_class, v12, v04); + GtsEdge * e27 = gts_edge_new (s->edge_class, v09, v11); + GtsEdge * e28 = gts_edge_new (s->edge_class, v10, v09); + GtsEdge * e29 = gts_edge_new (s->edge_class, v12, v05); + GtsEdge * e30 = gts_edge_new (s->edge_class, v01, v10); + + gts_surface_add_face (s, gts_face_new (s->face_class, e01, e02, e03)); + gts_surface_add_face (s, gts_face_new (s->face_class, e04, e05, e06)); + gts_surface_add_face (s, gts_face_new (s->face_class, e07, e08, e09)); + gts_surface_add_face (s, gts_face_new (s->face_class, e10, e11, e12)); + gts_surface_add_face (s, gts_face_new (s->face_class, e13, e14, e07)); + gts_surface_add_face (s, gts_face_new (s->face_class, e15, e16, e10)); + gts_surface_add_face (s, gts_face_new (s->face_class, e17, e18, e13)); + gts_surface_add_face (s, gts_face_new (s->face_class, e19, e20, e15)); + gts_surface_add_face (s, gts_face_new (s->face_class, e21, e01, e17)); + gts_surface_add_face (s, gts_face_new (s->face_class, e22, e19, e23)); + gts_surface_add_face (s, gts_face_new (s->face_class, e09, e06, e21)); + gts_surface_add_face (s, gts_face_new (s->face_class, e24, e25, e22)); + gts_surface_add_face (s, gts_face_new (s->face_class, e26, e08, e24)); + gts_surface_add_face (s, gts_face_new (s->face_class, e20, e27, e28)); + gts_surface_add_face (s, gts_face_new (s->face_class, e29, e04, e26)); + gts_surface_add_face (s, gts_face_new (s->face_class, e14, e27, e25)); + gts_surface_add_face (s, gts_face_new (s->face_class, e23, e12, e29)); + gts_surface_add_face (s, gts_face_new (s->face_class, e02, e05, e11)); + gts_surface_add_face (s, gts_face_new (s->face_class, e30, e28, e18)); + gts_surface_add_face (s, gts_face_new (s->face_class, e03, e16, e30)); + + return 0; +} + +static GtsVertex * unit_sphere_arc_midvertex (GtsSegment * s, + GtsVertexClass * vertex_class) +{ + GtsPoint * p1, * p2; + gdouble x, y, z, norm; + + p1 = GTS_POINT (s->v1); p2 = GTS_POINT (s->v2); + + x = 0.5*(p1->x + p2->x); + y = 0.5*(p1->y + p2->y); + z = 0.5*(p1->z + p2->z); + + norm = x*x + y*y + z*z; + norm = sqrt (norm); + + x /= norm; y /= norm; z /= norm; + + return gts_vertex_new (vertex_class, x, y, z); +} + +static void tessellate_face (GtsFace * f, + GtsSurface * s, + GtsRefineFunc refine_func, + gpointer refine_data, + GtsVertexClass * vertex_class, + GtsEdgeClass * edge_class) +{ + GtsTriangle * t; + GtsEdge * e1, * e2, * e3; /* former edges */ + GtsVertex * v1, * v2, * v3; /* initial vertices */ + GtsVertex * v4, * v5, * v6; /* new vertices */ + GtsEdge * e56, * e64, * e45; /* new inside edges */ + GtsEdge * e24, * e34, * e35, * e15, * e16, * e26; /* new border edges */ + GSList * dum; + GtsEdge * edum; + + t = GTS_TRIANGLE (f); + e1 = t->e1; e2 = t->e2; e3 = t->e3; + + if (GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e2)->v1) { + v1 = GTS_SEGMENT (e2)->v2; + v2 = GTS_SEGMENT (e1)->v1; + v3 = GTS_SEGMENT (e1)->v2; + } + else if (GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e2)->v2) { + v1 = GTS_SEGMENT (e2)->v1; + v2 = GTS_SEGMENT (e1)->v1; + v3 = GTS_SEGMENT (e1)->v2; + } + else if (GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e2)->v1) { + v1 = GTS_SEGMENT (e2)->v2; + v2 = GTS_SEGMENT (e1)->v2; + v3 = GTS_SEGMENT (e1)->v1; + } + else if (GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e2)->v2) { + v1 = GTS_SEGMENT (e2)->v1; + v2 = GTS_SEGMENT (e1)->v2; + v3 = GTS_SEGMENT (e1)->v1; + } + else { + v1 = v2 = v3 = NULL; + g_assert_not_reached (); + } + + e1->triangles = g_slist_remove (e1->triangles, t); + e2->triangles = g_slist_remove (e2->triangles, t); + e3->triangles = g_slist_remove (e3->triangles, t); + + if (GTS_OBJECT (e1)->reserved) { + dum = (GTS_OBJECT (e1)->reserved); + e24 = dum->data; + e34 = dum->next->data; + v4 = GTS_SEGMENT (e24)->v2; + if (GTS_SEGMENT (e24)->v1 == v3) { + edum = e34; e34 = e24; e24 = edum; + } + } + else { + v4 = (*refine_func) (e1, vertex_class, refine_data); + e24 = gts_edge_new (edge_class, v2, v4); + e34 = gts_edge_new (edge_class, v3, v4); + dum = g_slist_append (NULL, e24); + dum = g_slist_append (dum, e34); + GTS_OBJECT (e1)->reserved = dum; + } + if (GTS_OBJECT (e2)->reserved) { + dum = (GTS_OBJECT (e2)->reserved); + e35 = dum->data; + e15 = dum->next->data; + v5 = GTS_SEGMENT (e35)->v2; + if (GTS_SEGMENT (e35)->v1 == v1) { + edum = e15; e15 = e35; e35 = edum; + } + } + else { + v5 = (*refine_func) (e2, vertex_class, refine_data); + e35 = gts_edge_new (edge_class, v3, v5); + e15 = gts_edge_new (edge_class, v1, v5); + dum = g_slist_append (NULL, e35); + dum = g_slist_append (dum, e15); + GTS_OBJECT (e2)->reserved = dum; + } + if (GTS_OBJECT (e3)->reserved) { + dum = (GTS_OBJECT (e3)->reserved); + e16 = dum->data; + e26 = dum->next->data; + v6 = GTS_SEGMENT (e16)->v2; + if (GTS_SEGMENT (e16)->v1 == v2) { + edum = e16; e16 = e26; e26 = edum; + } + } + else { + v6 = (*refine_func) (e3, vertex_class, refine_data); + e16 = gts_edge_new (edge_class, v1, v6); + e26 = gts_edge_new (edge_class, v2, v6); + dum = g_slist_append (NULL, e16); + dum = g_slist_append (dum, e26); + GTS_OBJECT (e3)->reserved = dum; + } + + if (e1->triangles == NULL) { + g_slist_free (GTS_OBJECT (e1)->reserved); + GTS_OBJECT (e1)->reserved = NULL; + gts_object_destroy (GTS_OBJECT (e1)); + e1 = NULL; + } + if (e2->triangles == NULL) { + g_slist_free (GTS_OBJECT (e2)->reserved); + GTS_OBJECT (e2)->reserved = NULL; + gts_object_destroy (GTS_OBJECT (e2)); + e2 = NULL; + } + if (e3->triangles == NULL) { + g_slist_free (GTS_OBJECT (e3)->reserved); + GTS_OBJECT (e3)->reserved = NULL; + gts_object_destroy (GTS_OBJECT (e3)); + e3 = NULL; + } + + e56 = gts_edge_new (edge_class, v5, v6); + e64 = gts_edge_new (edge_class, v6, v4); + e45 = gts_edge_new (edge_class, v4, v5); + t->e1 = e56; e56->triangles = g_slist_prepend (e56->triangles, t); + t->e2 = e64; e64->triangles = g_slist_prepend (e64->triangles, t); + t->e3 = e45; e45->triangles = g_slist_prepend (e45->triangles, t); + + gts_surface_add_face (s, gts_face_new (s->face_class, e16, e56, e15)); + gts_surface_add_face (s, gts_face_new (s->face_class, e26, e24, e64)); + gts_surface_add_face (s, gts_face_new (s->face_class, e45, e34, e35)); +} + +static void create_array_tessellate (GtsFace * f, GPtrArray * array) +{ + g_ptr_array_add (array, f); +} + +/** + * gts_surface_tessellate: + * @s: a #GtsSurface. + * @refine_func: a #GtsRefineFunc. + * @refine_data: user data to be passed to @refine_func. + * + * Tessellate each triangle of @s with 4 triangles: + * the number of triangles is increased by a factor of 4. + * http://mathworld.wolfram.com/GeodesicDome.html + * + * If @refine_func is set to %NULL a mid arc function is used: if + * the surface is a polyhedron with the unit sphere as circum sphere, + * then gts_surface_tessellate() corresponds to a geodesation step + * (see gts_surface_generate_sphere()). + * + */ +void gts_surface_tessellate (GtsSurface * s, + GtsRefineFunc refine_func, + gpointer refine_data) +{ + GPtrArray * array; + guint i; + + g_return_if_fail (s != NULL); + + if (refine_func == NULL) /* tessellate_surface == geodesate_surface */ + refine_func = (GtsRefineFunc) unit_sphere_arc_midvertex; + + array = g_ptr_array_new (); + gts_surface_foreach_face (s, (GtsFunc) create_array_tessellate, array); + for(i = 0; i < array->len; i++) + tessellate_face (g_ptr_array_index (array, i), + s, refine_func, refine_data, + s->vertex_class, s->edge_class); + g_ptr_array_free (array, TRUE); +} + +/** + * gts_surface_generate_sphere: + * @s: a #GtsSurface. + * @geodesation_order: a #guint. + * + * Add a triangulated unit sphere generated by recursive subdivision to @s. + * First approximation is an isocahedron; each level of refinement + * (@geodesation_order) increases the number of triangles by a factor of 4. + * http://mathworld.wolfram.com/GeodesicDome.html + * + * Returns: @s. + */ +GtsSurface * gts_surface_generate_sphere (GtsSurface * s, + guint geodesation_order) +{ + guint cgo; + + g_return_val_if_fail (s != NULL, NULL); + g_return_val_if_fail (geodesation_order != 0, NULL); + + generate_icosahedron (s); + + for (cgo = 1; cgo < geodesation_order; cgo++) + gts_surface_tessellate (s, NULL, NULL); + + return s; +} + +static void foreach_vertex_copy (GtsPoint * p, GtsVertexClass * klass) +{ + GTS_OBJECT (p)->reserved = gts_vertex_new (klass, p->x, p->y, p->z); +} + +static void foreach_edge_copy (GtsSegment * s, GtsEdgeClass * klass) +{ + GTS_OBJECT (s)->reserved = gts_edge_new (klass, + GTS_OBJECT (s->v1)->reserved, + GTS_OBJECT (s->v2)->reserved); +} + +static void foreach_face_copy (GtsTriangle * t, + GtsSurface * s) +{ + gts_surface_add_face (s, gts_face_new (s->face_class, + GTS_OBJECT (t->e1)->reserved, + GTS_OBJECT (t->e2)->reserved, + GTS_OBJECT (t->e3)->reserved)); +} + +/** + * gts_surface_copy: + * @s1: a #GtsSurface. + * @s2: a #GtsSurface. + * + * Add a copy of all the faces, edges and vertices of @s2 to @s1. + * + * Returns: @s1. + */ +GtsSurface * gts_surface_copy (GtsSurface * s1, GtsSurface * s2) +{ + g_return_val_if_fail (s1 != NULL, NULL); + g_return_val_if_fail (s2 != NULL, NULL); + + gts_surface_foreach_vertex (s2, (GtsFunc) foreach_vertex_copy, + s1->vertex_class); + gts_surface_foreach_edge (s2, (GtsFunc) foreach_edge_copy, s1->edge_class); + gts_surface_foreach_face (s2, (GtsFunc) foreach_face_copy, s1); + + gts_surface_foreach_vertex (s2, (GtsFunc) gts_object_reset_reserved, NULL); + gts_surface_foreach_edge (s2, (GtsFunc) gts_object_reset_reserved, NULL); + + return s1; +} + +static void merge_foreach_face (GtsFace * f, + GtsSurface * s) +{ + gts_surface_add_face (s, f); +} + +/** + * gts_surface_merge: + * @s: a #GtsSurface. + * @with: another #GtsSurface. + * + * Adds all the faces of @with which do not already belong to @s + * to @s. + */ +void gts_surface_merge (GtsSurface * s, GtsSurface * with) +{ + g_return_if_fail (s != NULL); + g_return_if_fail (with != NULL); + + gts_surface_foreach_face (with, (GtsFunc) merge_foreach_face, s); +} + +static void manifold_foreach_edge (GtsEdge * e, gpointer * data) +{ + gboolean * is_manifold = data[0]; + + if (*is_manifold) { + if (gts_edge_face_number (e, data[1]) > 2) + *is_manifold = FALSE; + } +} + +/** + * gts_surface_is_manifold: + * @s: a #GtsSurface. + * + * Returns: %TRUE if the surface is a manifold, %FALSE otherwise. + */ +gboolean gts_surface_is_manifold (GtsSurface * s) +{ + gboolean is_manifold = TRUE; + gpointer data[2]; + + g_return_val_if_fail (s != NULL, FALSE); + + data[0] = &is_manifold; + data[1] = s; + gts_surface_foreach_edge (s, (GtsFunc) manifold_foreach_edge, data); + return is_manifold; +} + +static void closed_foreach_edge (GtsEdge * e, gpointer * data) +{ + gboolean * is_closed = data[0]; + + if (*is_closed) { + if (gts_edge_face_number (e, data[1]) != 2) + *is_closed = FALSE; + } +} + +/** + * gts_surface_is_closed: + * @s: a #GtsSurface. + * + * Returns: %TRUE if @s is a closed surface, %FALSE otherwise. Note that a + * closed surface is also a manifold. + */ +gboolean gts_surface_is_closed (GtsSurface * s) +{ + gboolean is_closed = TRUE; + gpointer data[2]; + + g_return_val_if_fail (s != NULL, FALSE); + + data[0] = &is_closed; + data[1] = s; + gts_surface_foreach_edge (s, (GtsFunc) closed_foreach_edge, data); + return is_closed; +} + +static void orientable_foreach_edge (GtsEdge * e, gpointer * data) +{ + gboolean * is_orientable = data[0]; + + if (*is_orientable) { + GtsSurface * surface = data[1]; + GtsFace * f1 = NULL, * f2 = NULL; + GSList * i = e->triangles; + while (i && *is_orientable) { + GtsFace * f = i->data; + if (GTS_IS_FACE (f) && gts_face_has_parent_surface (f, surface)) { + if (!f1) f1 = f; + else if (!f2) f2 = f; + else *is_orientable = FALSE; + } + i = i->next; + } + if (f1 && f2 && !gts_triangles_are_compatible (GTS_TRIANGLE (f1), + GTS_TRIANGLE (f2), e)) + *is_orientable = FALSE; + } +} + +/** + * gts_surface_is_orientable: + * @s: a #GtsSurface. + * + * Returns: %TRUE if all the faces of @s have compatible orientation + * as checked by gts_faces_are_compatible(), %FALSE otherwise. Note that + * an orientable surface is also a manifold. + */ +gboolean gts_surface_is_orientable (GtsSurface * s) +{ + gboolean is_orientable = TRUE; + gpointer data[2]; + + g_return_val_if_fail (s != NULL, FALSE); + + data[0] = &is_orientable; + data[1] = s; + gts_surface_foreach_edge (s, (GtsFunc) orientable_foreach_edge, data); + return is_orientable; +} + +static void volume_foreach_face (GtsTriangle * t, + gdouble * volume) +{ + GtsVertex * va, * vb, * vc; + GtsPoint * pa, * pb, * pc; + + gts_triangle_vertices (t, &va, &vb, &vc); + pa = GTS_POINT (va); + pb = GTS_POINT (vb); + pc = GTS_POINT (vc); + + *volume += (pa->x * (pb->y * pc->z - pb->z * pc->y) + + pb->x * (pc->y * pa->z - pc->z * pa->y) + + pc->x * (pa->y * pb->z - pa->z * pb->y)); +} + +/** + * gts_surface_volume: + * @s: a #GtsSurface. + * + * Returns: the signed volume of the domain bounded by the surface @s. It + * makes sense only if @s is a closed and orientable manifold. + */ +gdouble gts_surface_volume (GtsSurface * s) +{ + gdouble volume = 0.0; + + g_return_val_if_fail (s != NULL, 0.0); + + gts_surface_foreach_face (s, (GtsFunc) volume_foreach_face, &volume); + + return volume/6.; +} + +static void center_of_mass_foreach_face (GtsTriangle * t, + gpointer * data) +{ + GtsVertex * v1, * v2, * v3; + GtsPoint * p1, * p2, * p3; + gdouble x1, y1, z1, x2, y2, z2, nx, ny, nz; + gdouble * volume = data[0]; + gdouble * cm = data[1]; + + gts_triangle_vertices (t, &v1, &v2, &v3); + p1 = GTS_POINT (v1); + p2 = GTS_POINT (v2); + p3 = GTS_POINT (v3); + + x1 = p2->x - p1->x; + y1 = p2->y - p1->y; + z1 = p2->z - p1->z; + + x2 = p3->x - p1->x; + y2 = p3->y - p1->y; + z2 = p3->z - p1->z; + + nx = y1*z2 - z1*y2; + ny = z1*x2 - x1*z2; + nz = x1*y2 - y1*x2; + + cm[0] += nx*(p1->x*p1->x + p2->x*p2->x + p3->x*p3->x + + p1->x*p2->x + p1->x*p3->x + p2->x*p3->x); + cm[1] += ny*(p1->y*p1->y + p2->y*p2->y + p3->y*p3->y + + p1->y*p2->y + p1->y*p3->y + p2->y*p3->y); + cm[2] += nz*(p1->z*p1->z + p2->z*p2->z + p3->z*p3->z + + p1->z*p2->z + p1->z*p3->z + p2->z*p3->z); + + *volume += nx*(p1->x + p2->x + p3->x); +} + + +/** + * gts_surface_center_of_mass: + * @s: a #GtsSurface. + * @cm: a #GtsVector. + * + * Fills @cm with the coordinates of the center of mass of @s. + * + * Returns: the signed volume of the domain bounded by the surface @s. + */ +gdouble gts_surface_center_of_mass (GtsSurface * s, + GtsVector cm) +{ + gdouble volume = 0.; + gpointer data[2]; + + g_return_val_if_fail (s != NULL, 0.0); + + data[0] = &volume; + data[1] = &(cm[0]); + cm[0] = cm[1] = cm[2] = 0.; + gts_surface_foreach_face (s, (GtsFunc) center_of_mass_foreach_face, data); + + if (volume != 0.) { + cm[0] /= 4.*volume; + cm[1] /= 4.*volume; + cm[2] /= 4.*volume; + } + + return volume/6.; +} + +static void center_of_area_foreach_face (GtsTriangle * t, + gpointer * data) +{ + GtsVertex * v1, * v2, * v3; + GtsPoint * p1, * p2, * p3; + gdouble a; + gdouble * area = data[0]; + gdouble * cm = data[1]; + + gts_triangle_vertices (t, &v1, &v2, &v3); + p1 = GTS_POINT (v1); + p2 = GTS_POINT (v2); + p3 = GTS_POINT (v3); + + a = gts_triangle_area (t); + cm[0] += a*(p1->x + p2->x + p3->x); + cm[1] += a*(p1->y + p2->y + p3->y); + cm[2] += a*(p1->z + p2->z + p3->z); + *area += a; +} + + +/** + * gts_surface_center_of_area: + * @s: a #GtsSurface. + * @cm: a #GtsVector. + * + * Fills @cm with the coordinates of the center of area of @s. + * + * Returns: the area of surface @s. + */ +gdouble gts_surface_center_of_area (GtsSurface * s, + GtsVector cm) +{ + gdouble area = 0.; + gpointer data[2]; + + g_return_val_if_fail (s != NULL, 0.0); + + data[0] = &area; + data[1] = &(cm[0]); + cm[0] = cm[1] = cm[2] = 0.; + gts_surface_foreach_face (s, (GtsFunc) center_of_area_foreach_face, data); + + if (area != 0.) { + cm[0] /= 3.*area; + cm[1] /= 3.*area; + cm[2] /= 3.*area; + } + + return area; +} + +static void number_foreach (gpointer data, guint * n) +{ + (*n)++; +} + +/** + * gts_surface_vertex_number: + * @s: a #GtsSurface. + * + * Returns: the number of vertices of @s. + */ +guint gts_surface_vertex_number (GtsSurface * s) +{ + guint n = 0; + + g_return_val_if_fail (s != NULL, 0); + + gts_surface_foreach_vertex (s, (GtsFunc) number_foreach, &n); + + return n; +} + +/** + * gts_surface_edge_number: + * @s: a #GtsSurface. + * + * Returns: the number of edges of @s. + */ +guint gts_surface_edge_number (GtsSurface * s) +{ + guint n = 0; + + g_return_val_if_fail (s != NULL, 0); + + gts_surface_foreach_edge (s, (GtsFunc) number_foreach, &n); + + return n; +} + +/** + * gts_surface_face_number: + * @s: a #GtsSurface. + * + * Returns: the number of faces of @s + */ +guint gts_surface_face_number (GtsSurface * s) +{ + g_return_val_if_fail (s != NULL, 0); + +#ifdef USE_SURFACE_BTREE + return g_tree_nnodes (s->faces); +#else /* not USE_SURFACE_BTREE */ + return g_hash_table_size (s->faces); +#endif /* not USE_SURFACE_BTREE */ +} + +static void build_list_face (GtsTriangle * t, GSList ** list) +{ + *list = g_slist_prepend (*list, gts_bbox_triangle (gts_bbox_class (), t)); +} + +static void build_list_boundary (GtsEdge * e, GSList ** list) +{ + if (gts_edge_is_boundary (e, NULL)) + *list = g_slist_prepend (*list, gts_bbox_segment (gts_bbox_class (), + GTS_SEGMENT (e))); +} + +/** + * gts_surface_distance: + * @s1: a #GtsSurface. + * @s2: a #GtsSurface. + * @delta: a spatial increment defined as the percentage of the diagonal + * of the bounding box of @s2. + * @face_range: a #GtsRange. + * @boundary_range: a #GtsRange. + * + * Using the gts_bb_tree_surface_distance() and + * gts_bb_tree_surface_boundary_distance() functions fills @face_range + * and @boundary_range with the min, max and average Euclidean + * (minimum) distances between the faces of @s1 and the faces of @s2 + * and between the boundary edges of @s1 and @s2. + */ +void gts_surface_distance (GtsSurface * s1, GtsSurface * s2, gdouble delta, + GtsRange * face_range, GtsRange * boundary_range) +{ + GNode * face_tree, * boundary_tree; + GSList * bboxes; + + g_return_if_fail (s1 != NULL); + g_return_if_fail (s2 != NULL); + g_return_if_fail (delta > 0. && delta < 1.); + g_return_if_fail (face_range != NULL); + g_return_if_fail (boundary_range != NULL); + + bboxes = NULL; + gts_surface_foreach_face (s2, (GtsFunc) build_list_face, &bboxes); + if (bboxes != NULL) { + face_tree = gts_bb_tree_new (bboxes); + g_slist_free (bboxes); + + gts_bb_tree_surface_distance (face_tree, s1, + (GtsBBoxDistFunc) gts_point_triangle_distance, + delta, face_range); + gts_bb_tree_destroy (face_tree, TRUE); + + bboxes = NULL; + gts_surface_foreach_edge (s2, (GtsFunc) build_list_boundary, &bboxes); + if (bboxes != NULL) { + boundary_tree = gts_bb_tree_new (bboxes); + g_slist_free (bboxes); + + gts_bb_tree_surface_boundary_distance (boundary_tree, + s1, + (GtsBBoxDistFunc) gts_point_segment_distance, + delta, boundary_range); + gts_bb_tree_destroy (boundary_tree, TRUE); + } + else + gts_range_reset (boundary_range); + } + else { + gts_range_reset (face_range); + gts_range_reset (boundary_range); + } +} + +static void surface_boundary (GtsEdge * e, gpointer * data) +{ + GSList ** list = data[0]; + + if (gts_edge_is_boundary (e, data[1])) + *list = g_slist_prepend (*list, e); +} + +/** + * gts_surface_boundary: + * @surface: a #GtsSurface. + * + * Returns: a list of #GtsEdge boundary of @surface. + */ +GSList * gts_surface_boundary (GtsSurface * surface) +{ + GSList * list = NULL; + gpointer data[2]; + + g_return_val_if_fail (surface != NULL, NULL); + + data[0] = &list; + data[1] = surface; + gts_surface_foreach_edge (surface, (GtsFunc) surface_boundary, data); + + return list; +} + +struct _GtsSurfaceTraverse { + GtsFifo * q; + GtsSurface * s; +}; + +/** + * gts_surface_traverse_new: + * @s: a #GtsSurface. + * @f: a #GtsFace belonging to @s. + * + * Returns: a new #GtsSurfaceTraverse, initialized to start traversing + * from face @f of surface @s. + */ +GtsSurfaceTraverse * gts_surface_traverse_new (GtsSurface * s, + GtsFace * f) +{ + GtsSurfaceTraverse * t; + + g_return_val_if_fail (s != NULL, NULL); + g_return_val_if_fail (f != NULL, NULL); + g_return_val_if_fail (gts_face_has_parent_surface (f, s), NULL); + + t = g_malloc (sizeof (GtsSurfaceTraverse)); + t->q = gts_fifo_new (); + t->s = s; + GTS_OBJECT (f)->reserved = GUINT_TO_POINTER (1); + gts_fifo_push (t->q, f); + return t; +} + +static void push_neighbor (GtsFace * v, gpointer * data) +{ + if (!GTS_OBJECT (v)->reserved) { + GTS_OBJECT (v)->reserved = + GUINT_TO_POINTER (GPOINTER_TO_UINT (GTS_OBJECT (data[1])->reserved) + 1); + gts_fifo_push (data[0], v); + } +} + +/** + * gts_surface_traverse_next: + * @t: a #GtsSurfaceTraverse. + * @level: a pointer to a guint or %NULL. + * + * Returns: the next face of the traversal in breadth-first order or + * %NULL if no faces are left. If @level if not %NULL, it is filled + * with the level of the returned face (0 for the initial face, 1 for + * its neighbors and so on). + */ +GtsFace * gts_surface_traverse_next (GtsSurfaceTraverse * t, + guint * level) +{ + GtsFace * u; + + g_return_val_if_fail (t != NULL, NULL); + + u = gts_fifo_pop (t->q); + if (u) { + gpointer data[2]; + + if (level) + *level = GPOINTER_TO_UINT (GTS_OBJECT (u)->reserved); + data[0] = t->q; + data[1] = u; + gts_face_foreach_neighbor (u, t->s, (GtsFunc) push_neighbor, data); + } + return u; +} + +/** + * gts_surface_traverse_destroy: + * @t: a #GtsSurfaceTraverse. + * + * Frees all the memory allocated for @t. + */ +void gts_surface_traverse_destroy (GtsSurfaceTraverse * t) +{ + g_return_if_fail (t != NULL); + + gts_surface_foreach_face (t->s, (GtsFunc) gts_object_reset_reserved, NULL); + gts_fifo_destroy (t->q); + g_free (t); +} + +static void traverse_manifold (GtsTriangle * t, GtsSurface * s) +{ + if (g_slist_length (GTS_FACE (t)->surfaces) > 1) + return; + + gts_surface_add_face (s, GTS_FACE (t)); + if (g_slist_length (t->e1->triangles) == 2) { + if (t->e1->triangles->data != t) + traverse_manifold (t->e1->triangles->data, s); + else + traverse_manifold (t->e1->triangles->next->data, s); + } + if (g_slist_length (t->e2->triangles) == 2) { + if (t->e2->triangles->data != t) + traverse_manifold (t->e2->triangles->data, s); + else + traverse_manifold (t->e2->triangles->next->data, s); + } + if (g_slist_length (t->e3->triangles) == 2) { + if (t->e3->triangles->data != t) + traverse_manifold (t->e3->triangles->data, s); + else + traverse_manifold (t->e3->triangles->next->data, s); + } +} + +static void non_manifold_edges (GtsEdge * e, gpointer * data) +{ + GtsSurface * s = data[0]; + GSList ** non_manifold = data[1]; + + if (gts_edge_face_number (e, s) > 2) { + GSList * i = e->triangles; + + while (i) { + if (gts_face_has_parent_surface (i->data, s) && + !g_slist_find (*non_manifold, i->data)) + *non_manifold = g_slist_prepend (*non_manifold, i->data); + i = i->next; + } + } +} + +static void traverse_boundary (GtsEdge * e, gpointer * data) +{ + GtsSurface * orig = data[0]; + GSList ** components = data[1]; + GtsFace * f = gts_edge_is_boundary (e, orig); + + if (f != NULL && g_slist_length (f->surfaces) == 1) { + GtsSurface * s = + gts_surface_new (GTS_SURFACE_CLASS (GTS_OBJECT (orig)->klass), + orig->face_class, + orig->edge_class, + orig->vertex_class); + GSList * non_manifold = NULL, * i; + gpointer data[2]; + + *components = g_slist_prepend (*components, s); + data[0] = s; + data[1] = &non_manifold; + traverse_manifold (GTS_TRIANGLE (f), s); + + gts_surface_foreach_edge (s, (GtsFunc) non_manifold_edges, data); + i = non_manifold; + while (i) { + gts_surface_remove_face (s, i->data); + i = i->next; + } + g_slist_free (non_manifold); + } +} + +static void traverse_remaining (GtsFace * f, gpointer * data) +{ + GtsSurface * orig = data[0]; + GSList ** components = data[1]; + + if (g_slist_length (f->surfaces) == 1) { + GtsSurface * s = + gts_surface_new (GTS_SURFACE_CLASS (GTS_OBJECT (orig)->klass), + orig->face_class, + orig->edge_class, + orig->vertex_class); + GSList * non_manifold = NULL, * i; + gpointer data[2]; + + *components = g_slist_prepend (*components, s); + data[0] = s; + data[1] = &non_manifold; + traverse_manifold (GTS_TRIANGLE (f), s); + + gts_surface_foreach_edge (s, (GtsFunc) non_manifold_edges, data); + i = non_manifold; + while (i) { + gts_surface_remove_face (s, i->data); + i = i->next; + } + g_slist_free (non_manifold); + } +} + +/** + * gts_surface_split: + * @s: a #GtsSurface. + * + * Splits a surface into connected and manifold components. + * + * Returns: a list of new #GtsSurface. + */ +GSList * gts_surface_split (GtsSurface * s) +{ + gpointer data[2]; + GSList * components = NULL; + + g_return_val_if_fail (s != NULL, NULL); + + data[0] = s; + data[1] = &components; + + /* boundary components */ + gts_surface_foreach_edge (s, (GtsFunc) traverse_boundary, data); + + /* remaining components */ + gts_surface_foreach_face (s, (GtsFunc) traverse_remaining, data); + + return components; +} Index: trunk/src_3rd/gts/triangle.c =================================================================== --- trunk/src_3rd/gts/triangle.c (nonexistent) +++ trunk/src_3rd/gts/triangle.c (revision 1065) @@ -0,0 +1,1094 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "gts.h" + +static void triangle_destroy (GtsObject * object) +{ + GtsTriangle * triangle = GTS_TRIANGLE (object); + GtsEdge * e1 = triangle->e1; + GtsEdge * e2 = triangle->e2; + GtsEdge * e3 = triangle->e3; + + e1->triangles = g_slist_remove (e1->triangles, triangle); + if (!GTS_OBJECT_DESTROYED (e1) && + !gts_allow_floating_edges && e1->triangles == NULL) + gts_object_destroy (GTS_OBJECT (e1)); + + e2->triangles = g_slist_remove (e2->triangles, triangle); + if (!GTS_OBJECT_DESTROYED (e2) && + !gts_allow_floating_edges && e2->triangles == NULL) + gts_object_destroy (GTS_OBJECT (e2)); + + e3->triangles = g_slist_remove (e3->triangles, triangle); + if (!GTS_OBJECT_DESTROYED (e3) && + !gts_allow_floating_edges && e3->triangles == NULL) + gts_object_destroy (GTS_OBJECT (e3)); + + (* GTS_OBJECT_CLASS (gts_triangle_class ())->parent_class->destroy) (object); +} + +static void triangle_class_init (GtsObjectClass * klass) +{ + klass->destroy = triangle_destroy; +} + +static void triangle_init (GtsTriangle * triangle) +{ + triangle->e1 = triangle->e2 = triangle->e3 = NULL; +} + +/** + * gts_triangle_class: + * + * Returns: the #GtsTriangleClass. + */ +GtsTriangleClass * gts_triangle_class (void) +{ + static GtsTriangleClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo triangle_info = { + "GtsTriangle", + sizeof (GtsTriangle), + sizeof (GtsTriangleClass), + (GtsObjectClassInitFunc) triangle_class_init, + (GtsObjectInitFunc) triangle_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (gts_object_class (), + &triangle_info); + } + + return klass; +} + +/** + * gts_triangle_set: + * @triangle: a #GtsTriangle. + * @e1: a #GtsEdge. + * @e2: another #GtsEdge touching @e1. + * @e3: another #GtsEdge touching both @e1 and @e2. + * + * Sets the edge of @triangle to @e1, @e2 and @e3 while checking that they + * define a valid triangle. + */ +void gts_triangle_set (GtsTriangle * triangle, + GtsEdge * e1, + GtsEdge * e2, + GtsEdge * e3) +{ + g_return_if_fail (e1 != NULL); + g_return_if_fail (e2 != NULL); + g_return_if_fail (e3 != NULL); + g_return_if_fail (e1 != e2 && e1 != e3 && e2 != e3); + + triangle->e1 = e1; + triangle->e2 = e2; + triangle->e3 = e3; + + if (GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e2)->v1) + g_return_if_fail (gts_segment_connect (GTS_SEGMENT (e3), + GTS_SEGMENT (e1)->v2, + GTS_SEGMENT (e2)->v2)); + else if (GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e2)->v1) + g_return_if_fail (gts_segment_connect (GTS_SEGMENT (e3), + GTS_SEGMENT (e1)->v1, + GTS_SEGMENT (e2)->v2)); + else if (GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e2)->v2) + g_return_if_fail (gts_segment_connect (GTS_SEGMENT (e3), + GTS_SEGMENT (e1)->v1, + GTS_SEGMENT (e2)->v1)); + else if (GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e2)->v2) + g_return_if_fail (gts_segment_connect (GTS_SEGMENT (e3), + GTS_SEGMENT (e1)->v2, + GTS_SEGMENT (e2)->v1)); + else + g_assert_not_reached (); + + e1->triangles = g_slist_prepend (e1->triangles, triangle); + e2->triangles = g_slist_prepend (e2->triangles, triangle); + e3->triangles = g_slist_prepend (e3->triangles, triangle); +} + +/** + * gts_triangle_new: + * @klass: a #GtsTriangleClass. + * @e1: a #GtsEdge. + * @e2: another #GtsEdge touching @e1. + * @e3: another #GtsEdge touching both @e1 and @e2. + * + * Returns: a new #GtsTriangle having @e1, @e2 and @e3 as edges. + */ +GtsTriangle * gts_triangle_new (GtsTriangleClass * klass, + GtsEdge * e1, + GtsEdge * e2, + GtsEdge * e3) +{ + GtsTriangle * t; + + t = GTS_TRIANGLE (gts_object_new (GTS_OBJECT_CLASS (klass))); + gts_triangle_set (t, e1, e2, e3); + + return t; +} + +/** + * gts_triangle_vertex_opposite: + * @t: a #GtsTriangle. + * @e: a #GtsEdge used by @t. + * + * This function fails if @e is not an edge of @t. + * + * Returns: a #GtsVertex, vertex of @t which does not belong to @e. + */ +GtsVertex * gts_triangle_vertex_opposite (GtsTriangle * t, GtsEdge * e) +{ + g_return_val_if_fail (t != NULL, NULL); + g_return_val_if_fail (e != NULL, NULL); + + if (t->e1 == e) { + GtsVertex * v = GTS_SEGMENT (t->e2)->v1; + if (v != GTS_SEGMENT (e)->v1 && v != GTS_SEGMENT (e)->v2) + return v; + return GTS_SEGMENT (t->e2)->v2; + } + if (t->e2 == e) { + GtsVertex * v = GTS_SEGMENT (t->e1)->v1; + if (v != GTS_SEGMENT (e)->v1 && v != GTS_SEGMENT (e)->v2) + return v; + return GTS_SEGMENT (t->e1)->v2; + } + if (t->e3 == e) { + GtsVertex * v = GTS_SEGMENT (t->e2)->v1; + if (v != GTS_SEGMENT (e)->v1 && v != GTS_SEGMENT (e)->v2) + return v; + return GTS_SEGMENT (t->e2)->v2; + } + g_assert_not_reached (); + return NULL; +} + +/** + * gts_triangle_edge_opposite: + * @t: a #GtsTriangle. + * @v: a #GtsVertex of @t. + * + * Returns: the edge of @t opposite @v or %NULL if @v is not a vertice of @t. + */ +GtsEdge * gts_triangle_edge_opposite (GtsTriangle * t, GtsVertex * v) +{ + GtsSegment * s1, * s2, * s3; + + g_return_val_if_fail (t != NULL, NULL); + g_return_val_if_fail (v != NULL, NULL); + + s1 = GTS_SEGMENT (t->e1); + s2 = GTS_SEGMENT (t->e2); + + if (s1->v1 != v && s1->v2 != v) { + if (s2->v1 != v && s2->v2 != v) + return NULL; + return t->e1; + } + if (s2->v1 != v && s2->v2 != v) + return t->e2; + s3 = GTS_SEGMENT (t->e3); + g_assert (s3->v1 != v && s3->v2 != v); + return t->e3; +} + +/** + * gts_triangles_angle: + * @t1: a #GtsTriangle. + * @t2: a #GtsTriangle. + * + * Returns: the value (in radians) of the angle between @t1 and @t2. + */ +gdouble gts_triangles_angle (GtsTriangle * t1, + GtsTriangle * t2) +{ + gdouble nx1, ny1, nz1, nx2, ny2, nz2; + gdouble pvx, pvy, pvz; + gdouble theta; + + g_return_val_if_fail (t1 != NULL && t2 != NULL, 0.0); + + gts_triangle_normal (t1, &nx1, &ny1, &nz1); + gts_triangle_normal (t2, &nx2, &ny2, &nz2); + + pvx = ny1*nz2 - nz1*ny2; + pvy = nz1*nx2 - nx1*nz2; + pvz = nx1*ny2 - ny1*nx2; + + theta = atan2 (sqrt (pvx*pvx + pvy*pvy + pvz*pvz), + nx1*nx2 + ny1*ny2 + nz1*nz2) - M_PI; + return theta < - M_PI ? theta + 2.*M_PI : theta; +} + +/** + * gts_triangles_are_compatible: + * @t1: a #GtsTriangle. + * @t2: a #GtsTriangle. + * @e: a #GtsEdge used by both @t1 and @t2. + * + * Checks if @t1 and @t2 have compatible orientations i.e. if @t1 and + * @t2 can be part of the same surface without conflict in the surface + * normal orientation. + * + * Returns: %TRUE if @t1 and @t2 are compatible, %FALSE otherwise. + */ +gboolean gts_triangles_are_compatible (GtsTriangle * t1, + GtsTriangle * t2, + GtsEdge * e) +{ + GtsEdge * e1 = NULL, * e2 = NULL; + + g_return_val_if_fail (t1 != NULL, FALSE); + g_return_val_if_fail (t2 != NULL, FALSE); + g_return_val_if_fail (e != NULL, FALSE); + + if (t1->e1 == e) e1 = t1->e2; + else if (t1->e2 == e) e1 = t1->e3; + else if (t1->e3 == e) e1 = t1->e1; + else + g_assert_not_reached (); + if (t2->e1 == e) e2 = t2->e2; + else if (t2->e2 == e) e2 = t2->e3; + else if (t2->e3 == e) e2 = t2->e1; + else + g_assert_not_reached (); + if (GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e2)->v1 || + GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e2)->v2 || + GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e2)->v1 || + GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e2)->v2) + return FALSE; + return TRUE; +} + +/** + * gts_triangle_area: + * @t: a #GtsTriangle. + * + * Returns: the area of the triangle @t. + */ +gdouble gts_triangle_area (GtsTriangle * t) +{ + gdouble x, y, z; + + g_return_val_if_fail (t != NULL, 0.0); + + gts_triangle_normal (t, &x, &y, &z); + + return sqrt (x*x + y*y + z*z)/2.; +} + +/** + * gts_triangle_perimeter: + * @t: a #GtsTriangle. + * + * Returns: the perimeter of the triangle @t. + */ +gdouble gts_triangle_perimeter (GtsTriangle * t) +{ + GtsVertex * v; + + g_return_val_if_fail (t != NULL, 0.0); + + v = gts_triangle_vertex (t); + return + gts_point_distance (GTS_POINT (GTS_SEGMENT (t->e1)->v1), + GTS_POINT (GTS_SEGMENT (t->e1)->v2)) + + gts_point_distance (GTS_POINT (GTS_SEGMENT (t->e1)->v1), + GTS_POINT (v)) + + gts_point_distance (GTS_POINT (GTS_SEGMENT (t->e1)->v2), + GTS_POINT (v)); +} + +/* perimeter of the equilateral triangle of area unity */ +#define GOLDEN_PERIMETER 4.5590141139 + +/** + * gts_triangle_quality: + * @t: a #GtsTriangle. + * + * The quality of a triangle is defined as the ratio of the square + * root of its surface area to its perimeter relative to this same + * ratio for an equilateral triangle with the same area. The quality + * is then one for an equilateral triangle and tends to zero for a + * very stretched triangle. + * + * Returns: the quality of the triangle @t. + */ +gdouble gts_triangle_quality (GtsTriangle * t) +{ + gdouble perimeter; + + g_return_val_if_fail (t != NULL, 0.0); + + perimeter = gts_triangle_perimeter (t); + return perimeter > 0.0 ? + GOLDEN_PERIMETER*sqrt (gts_triangle_area (t))/perimeter : + 0.0; +} + +/** + * gts_triangle_normal: + * @t: a #GtsTriangle. + * @x: the x coordinate of the normal. + * @y: the y coordinate of the normal. + * @z: the z coordinate of the normal. + * + * Computes the coordinates of the oriented normal of @t as the + * cross-product of two edges, using the left-hand rule. The normal is + * not normalized. If this triangle is part of a closed and oriented + * surface, the normal points to the outside of the surface. + */ +void gts_triangle_normal (GtsTriangle * t, + gdouble * x, + gdouble * y, + gdouble * z) +{ + GtsVertex * v1, * v2 = NULL, * v3 = NULL; + GtsPoint * p1, * p2, * p3; + gdouble x1, y1, z1, x2, y2, z2; + + g_return_if_fail (t != NULL); + + v1 = GTS_SEGMENT (t->e1)->v1; + if (GTS_SEGMENT (t->e1)->v1 == GTS_SEGMENT (t->e2)->v1) { + v2 = GTS_SEGMENT (t->e2)->v2; + v3 = GTS_SEGMENT (t->e1)->v2; + } + else if (GTS_SEGMENT (t->e1)->v2 == GTS_SEGMENT (t->e2)->v2) { + v2 = GTS_SEGMENT (t->e1)->v2; + v3 = GTS_SEGMENT (t->e2)->v1; + } + else if (GTS_SEGMENT (t->e1)->v1 == GTS_SEGMENT (t->e2)->v2) { + v2 = GTS_SEGMENT (t->e2)->v1; + v3 = GTS_SEGMENT (t->e1)->v2; + } + else if (GTS_SEGMENT (t->e1)->v2 == GTS_SEGMENT (t->e2)->v1) { + v2 = GTS_SEGMENT (t->e1)->v2; + v3 = GTS_SEGMENT (t->e2)->v2; + } + else { + fprintf (stderr, "t: %p t->e1: %p t->e2: %p t->e3: %p t->e1->v1: %p t->e1->v2: %p t->e2->v1: %p t->e2->v2: %p t->e3->v1: %p t->e3->v2: %p\n", + t, t->e1, t->e2, + t->e3, GTS_SEGMENT (t->e1)->v1, GTS_SEGMENT (t->e1)->v2, + GTS_SEGMENT (t->e2)->v1, GTS_SEGMENT (t->e2)->v2, + GTS_SEGMENT (t->e3)->v1, GTS_SEGMENT (t->e3)->v2); + g_assert_not_reached (); + } + + p1 = GTS_POINT (v1); + p2 = GTS_POINT (v2); + p3 = GTS_POINT (v3); + + x1 = p2->x - p1->x; + y1 = p2->y - p1->y; + z1 = p2->z - p1->z; + + x2 = p3->x - p1->x; + y2 = p3->y - p1->y; + z2 = p3->z - p1->z; + + *x = y1*z2 - z1*y2; + *y = z1*x2 - x1*z2; + *z = x1*y2 - y1*x2; +} + +/** + * gts_triangle_orientation: + * @t: a #GtsTriangle. + * + * Checks for the orientation of the plane (x,y) projection of a + * triangle. See gts_point_orientation() for details. This function + * is geometrically robust. + * + * Returns: a number depending on the orientation of the vertices of @t. + */ +gdouble gts_triangle_orientation (GtsTriangle * t) +{ + GtsVertex * v1, * v2 = NULL, * v3 = NULL; + + g_return_val_if_fail (t != NULL, 0.0); + + v1 = GTS_SEGMENT (t->e1)->v1; + if (GTS_SEGMENT (t->e1)->v1 == GTS_SEGMENT (t->e2)->v1) { + v2 = GTS_SEGMENT (t->e2)->v2; + v3 = GTS_SEGMENT (t->e1)->v2; + } + else if (GTS_SEGMENT (t->e1)->v2 == GTS_SEGMENT (t->e2)->v2) { + v2 = GTS_SEGMENT (t->e1)->v2; + v3 = GTS_SEGMENT (t->e2)->v1; + } + else if (GTS_SEGMENT (t->e1)->v1 == GTS_SEGMENT (t->e2)->v2) { + v2 = GTS_SEGMENT (t->e2)->v1; + v3 = GTS_SEGMENT (t->e1)->v2; + } + else if (GTS_SEGMENT (t->e1)->v2 == GTS_SEGMENT (t->e2)->v1) { + v2 = GTS_SEGMENT (t->e1)->v2; + v3 = GTS_SEGMENT (t->e2)->v2; + } + else + g_assert_not_reached (); + return gts_point_orientation (GTS_POINT (v1), + GTS_POINT (v2), + GTS_POINT (v3)); +} + +/** + * gts_triangle_revert: + * @t: a #GtsTriangle. + * + * Changes the orientation of triangle @t, turning it inside out. + */ +void gts_triangle_revert (GtsTriangle * t) +{ + GtsEdge * e; + + g_return_if_fail (t != NULL); + + e = t->e1; + t->e1 = t->e2; + t->e2 = e; +} + +/** + * gts_triangles_from_edges: + * @edges: a list of #GtsEdge. + * + * Builds a list of unique triangles which have one of their edges in @edges. + * + * Returns: the list of triangles. + */ +GSList * gts_triangles_from_edges (GSList * edges) +{ + GHashTable * hash; + GSList * triangles = NULL, * i; + + hash = g_hash_table_new (NULL, NULL); + i = edges; + while (i) { + GSList * j = GTS_EDGE (i->data)->triangles; + while (j) { + GtsTriangle * t = j->data; + if (g_hash_table_lookup (hash, t) == NULL) { + triangles = g_slist_prepend (triangles, t); + g_hash_table_insert (hash, t, i); + } + j = j->next; + } + i = i->next; + } + g_hash_table_destroy (hash); + + return triangles; +} + +/** + * gts_triangle_vertices_edges: + * @t: a #GtsTriangle. + * @e: a #GtsEdge belonging to the edges of @t or %NULL. + * @v1: a #GtsVertex used by @t. + * @v2: a #GtsVertex used by @t. + * @v3: a #GtsVertex used by @t. + * @e1: a #GtsEdge used by @t. + * @e2: a #GtsEdge used by @t. + * @e3: a #GtsEdge used by @t. + * + * Given @t and @e, returns @v1, @v2, @v3, @e1, @e2 and @e3. @e1 + * has @v1 and @v2 as vertices, @e2 has @v2 and @v3 as vertices + * and @e3 has @v3 and @v1 as vertices. @v1, @v2 and @v3 respects + * the orientation of @t. If @e is not NULL, @e1 and @e are + * identical. + */ +void gts_triangle_vertices_edges (GtsTriangle * t, + GtsEdge * e, + GtsVertex ** v1, + GtsVertex ** v2, + GtsVertex ** v3, + GtsEdge ** e1, + GtsEdge ** e2, + GtsEdge ** e3) +{ + GtsEdge * ee1, * ee2; + + g_return_if_fail (t != NULL); + + if (e == t->e1 || e == NULL) { + *e1 = ee1 = t->e1; *e2 = ee2 = t->e2; *e3 = t->e3; + } + else if (e == t->e2) { + *e1 = ee1 = e; *e2 = ee2 = t->e3; *e3 = t->e1; + } + else if (e == t->e3) { + *e1 = ee1 = e; *e2 = ee2 = t->e1; *e3 = t->e2; + } + else { + g_assert_not_reached (); + ee1 = ee2 = NULL; /* to avoid complaints from the compiler */ + } + if (GTS_SEGMENT (ee1)->v2 == GTS_SEGMENT (ee2)->v1) { + *v1 = GTS_SEGMENT (ee1)->v1; + *v2 = GTS_SEGMENT (ee1)->v2; + *v3 = GTS_SEGMENT (ee2)->v2; + } + else if (GTS_SEGMENT (ee1)->v2 == GTS_SEGMENT (ee2)->v2) { + *v1 = GTS_SEGMENT (ee1)->v1; + *v2 = GTS_SEGMENT (ee1)->v2; + *v3 = GTS_SEGMENT (ee2)->v1; + } + else if (GTS_SEGMENT (ee1)->v1 == GTS_SEGMENT (ee2)->v1) { + *v1 = GTS_SEGMENT (ee1)->v2; + *v2 = GTS_SEGMENT (ee1)->v1; + *v3 = GTS_SEGMENT (ee2)->v2; + } + else if (GTS_SEGMENT (ee1)->v1 == GTS_SEGMENT (ee2)->v2) { + *v1 = GTS_SEGMENT (ee1)->v2; + *v2 = GTS_SEGMENT (ee1)->v1; + *v3 = GTS_SEGMENT (ee2)->v1; + } + else + g_assert_not_reached (); +} + +/* sqrt(3) */ +#define SQRT3 1.73205080757 + +/** + * gts_triangle_enclosing: + * @klass: the class of the new triangle. + * @points: a list of #GtsPoint. + * @scale: a scaling factor (must be larger than one). + * + * Builds a new triangle (including new vertices and edges) enclosing + * the plane projection of all the points in @points. This triangle is + * equilateral and encloses a rectangle defined by the maximum and + * minimum x and y coordinates of the points. @scale is an homothetic + * scaling factor. If equal to one, the triangle encloses exactly the + * enclosing rectangle. + * + * Returns: a new #GtsTriangle. + */ +GtsTriangle * gts_triangle_enclosing (GtsTriangleClass * klass, + GSList * points, gdouble scale) +{ + gdouble xmax, xmin, ymax, ymin; + gdouble xo, yo, r; + GtsVertex * v1, * v2, * v3; + GtsEdge * e1, * e2, * e3; + + if (points == NULL) + return NULL; + + xmax = xmin = GTS_POINT (points->data)->x; + ymax = ymin = GTS_POINT (points->data)->y; + points = points->next; + while (points) { + GtsPoint * p = points->data; + if (p->x > xmax) xmax = p->x; + else if (p->x < xmin) xmin = p->x; + if (p->y > ymax) ymax = p->y; + else if (p->y < ymin) ymin = p->y; + points = points->next; + } + xo = (xmax + xmin)/2.; + yo = (ymax + ymin)/2.; + r = scale*sqrt((xmax - xo)*(xmax - xo) + (ymax - yo)*(ymax - yo)); + if (r == 0.0) r = scale; + v1 = gts_vertex_new (gts_vertex_class (), + xo + r*SQRT3, yo - r, 0.0); + v2 = gts_vertex_new (gts_vertex_class (), + xo, yo + 2.*r, 0.0); + v3 = gts_vertex_new (gts_vertex_class (), + xo - r*SQRT3, yo - r, 0.0); + e1 = gts_edge_new (gts_edge_class (), v1, v2); + e2 = gts_edge_new (gts_edge_class (), v2, v3); + e3 = gts_edge_new (gts_edge_class (), v3, v1); + return gts_triangle_new (gts_triangle_class (), e1, e2, e3); +} + +/** + * gts_triangle_neighbor_number: + * @t: a #GtsTriangle. + * + * Returns: the number of triangles neighbors of @t. + */ +guint gts_triangle_neighbor_number (GtsTriangle * t) +{ + GSList * i; + guint nn = 0; + GtsEdge * ee[4], ** e = ee; + + g_return_val_if_fail (t != NULL, 0); + + ee[0] = t->e1; ee[1] = t->e2; ee[2] = t->e3; ee[3] = NULL; + while (*e) { + i = (*e++)->triangles; + while (i) { + GtsTriangle * t1 = i->data; + if (t1 != t) + nn++; + i = i->next; + } + } + return nn; +} + +/** + * gts_triangle_neighbors: + * @t: a #GtsTriangle. + * + * Returns: a list of #GtsTriangle neighbors of @t. + */ +GSList * gts_triangle_neighbors (GtsTriangle * t) +{ + GSList * i, * list = NULL; + GtsEdge * ee[4], ** e = ee; + + g_return_val_if_fail (t != NULL, NULL); + + ee[0] = t->e1; ee[1] = t->e2; ee[2] = t->e3; ee[3] = NULL; + while (*e) { + i = (*e++)->triangles; + while (i) { + GtsTriangle * t1 = i->data; + if (t1 != t) + list = g_slist_prepend (list, t1); + i = i->next; + } + } + return list; +} + +/** + * gts_triangles_common_edge: + * @t1: a #GtsTriangle. + * @t2: a #GtsTriangle. + * + * Returns: a #GtsEdge common to both @t1 and @t2 or %NULL if @t1 and @t2 + * do not share any edge. + */ +GtsEdge * gts_triangles_common_edge (GtsTriangle * t1, + GtsTriangle * t2) +{ + g_return_val_if_fail (t1 != NULL, NULL); + g_return_val_if_fail (t2 != NULL, NULL); + + if (t1->e1 == t2->e1 || t1->e1 == t2->e2 || t1->e1 == t2->e3) + return t1->e1; + if (t1->e2 == t2->e1 || t1->e2 == t2->e2 || t1->e2 == t2->e3) + return t1->e2; + if (t1->e3 == t2->e1 || t1->e3 == t2->e2 || t1->e3 == t2->e3) + return t1->e3; + return NULL; +} + +/** + * gts_triangle_is_duplicate: + * @t: a #GtsTriangle. + * + * Returns: a #GtsTriangle different from @t but sharing all its edges + * with @t or %NULL if there is none. + */ +GtsTriangle * gts_triangle_is_duplicate (GtsTriangle * t) +{ + GSList * i; + GtsEdge * e2, * e3; + + g_return_val_if_fail (t != NULL, NULL); + + e2 = t->e2; + e3 = t->e3; + i = t->e1->triangles; + while (i) { + GtsTriangle * t1 = i->data; + if (t1 != t && + (t1->e1 == e2 || t1->e2 == e2 || t1->e3 == e2) && + (t1->e1 == e3 || t1->e2 == e3 || t1->e3 == e3)) + return t1; + i = i->next; + } + + return NULL; +} + +/** + * gts_triangle_use_edges: + * @e1: a #GtsEdge. + * @e2: a #GtsEdge. + * @e3: a #GtsEdge. + * + * Returns: a #GtsTriangle having @e1, @e2 and @e3 as edges or %NULL if @e1, + * @e2 and @e3 are not part of any triangle. + */ +GtsTriangle * gts_triangle_use_edges (GtsEdge * e1, + GtsEdge * e2, + GtsEdge * e3) +{ + GSList * i; + + g_return_val_if_fail (e1 != NULL, NULL); + g_return_val_if_fail (e2 != NULL, NULL); + g_return_val_if_fail (e3 != NULL, NULL); + + i = e1->triangles; + while (i) { + GtsTriangle * t = i->data; + if ((t->e1 == e2 && (t->e2 == e3 || t->e3 == e3)) || + (t->e2 == e2 && (t->e1 == e3 || t->e3 == e3)) || + (t->e3 == e2 && (t->e1 == e3 || t->e2 == e3))) + return t; + i = i->next; + } + + return NULL; +} + +/** + * gts_triangle_is_ok: + * @t: a #GtsTriangle. + * + * Returns: %TRUE if @t is a non-degenerate, non-duplicate triangle, + * %FALSE otherwise. + */ +gboolean gts_triangle_is_ok (GtsTriangle * t) +{ + g_return_val_if_fail (t != NULL, FALSE); + g_return_val_if_fail (t->e1 != NULL, FALSE); + g_return_val_if_fail (t->e2 != NULL, FALSE); + g_return_val_if_fail (t->e3 != NULL, FALSE); + g_return_val_if_fail (t->e1 != t->e2 && t->e1 != t->e3 && t->e2 != t->e3, + FALSE); + g_return_val_if_fail (gts_segments_touch (GTS_SEGMENT (t->e1), + GTS_SEGMENT (t->e2)), + FALSE); + g_return_val_if_fail (gts_segments_touch (GTS_SEGMENT (t->e1), + GTS_SEGMENT (t->e3)), + FALSE); + g_return_val_if_fail (gts_segments_touch (GTS_SEGMENT (t->e2), + GTS_SEGMENT (t->e3)), + FALSE); + g_return_val_if_fail (GTS_SEGMENT (t->e1)->v1 != GTS_SEGMENT (t->e1)->v2, + FALSE); + g_return_val_if_fail (GTS_SEGMENT (t->e2)->v1 != GTS_SEGMENT (t->e2)->v2, + FALSE); + g_return_val_if_fail (GTS_SEGMENT (t->e3)->v1 != GTS_SEGMENT (t->e3)->v2, + FALSE); + g_return_val_if_fail (GTS_OBJECT (t)->reserved == NULL, FALSE); + g_return_val_if_fail (!gts_triangle_is_duplicate (t), FALSE); + return TRUE; +} + +/** + * gts_triangle_vertices: + * @t: a #GtsTriangle. + * @v1: a pointer on a #GtsVertex. + * @v2: a pointer on a #GtsVertex. + * @v3: a pointer on a #GtsVertex. + * + * Fills @v1, @v2 and @v3 with the oriented set of vertices, summits of @t. + */ +void gts_triangle_vertices (GtsTriangle * t, + GtsVertex ** v1, GtsVertex ** v2, GtsVertex ** v3) +{ + GtsSegment * e1, * e2; + + g_return_if_fail (t != NULL); + g_return_if_fail (v1 != NULL && v2 != NULL && v3 != NULL); + + e1 = GTS_SEGMENT (t->e1); + e2 = GTS_SEGMENT (t->e2); + + if (GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e2)->v1) { + *v1 = GTS_SEGMENT (e1)->v1; + *v2 = GTS_SEGMENT (e1)->v2; + *v3 = GTS_SEGMENT (e2)->v2; + } + else if (GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e2)->v2) { + *v1 = GTS_SEGMENT (e1)->v1; + *v2 = GTS_SEGMENT (e1)->v2; + *v3 = GTS_SEGMENT (e2)->v1; + } + else if (GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e2)->v1) { + *v1 = GTS_SEGMENT (e1)->v2; + *v2 = GTS_SEGMENT (e1)->v1; + *v3 = GTS_SEGMENT (e2)->v2; + } + else { + *v1 = GTS_SEGMENT (e1)->v2; + *v2 = GTS_SEGMENT (e1)->v1; + *v3 = GTS_SEGMENT (e2)->v1; + } +} + +/** + * gts_triangle_circumcircle_center: + * @t: a #GtsTriangle. + * @point_class: a #GtsPointClass. + * + * Returns: a new #GtsPoint, center of the circumscribing circle of @t or + * %NULL if the circumscribing circle is not defined. + */ +GtsPoint * gts_triangle_circumcircle_center (GtsTriangle * t, + GtsPointClass * point_class) +{ + GtsVertex * v1, * v2, * v3; + gdouble xa, ya, xb, yb, xc, yc; + gdouble xd, yd, xe, ye; + gdouble xad, yad, xae, yae; + gdouble det; + + g_return_val_if_fail (t != NULL, NULL); + g_return_val_if_fail (point_class != NULL, NULL); + + gts_triangle_vertices (t, &v1, &v2, &v3); + + xa = GTS_POINT (v1)->x; ya = GTS_POINT (v1)->y; + xb = GTS_POINT (v2)->x; yb = GTS_POINT (v2)->y; + xc = GTS_POINT (v3)->x; yc = GTS_POINT (v3)->y; + xd = (xa + xb)/2.; yd = (ya + yb)/2.; + xe = (xa + xc)/2.; ye = (ya + yc)/2.; + xad = xd - xa; yad = yd - ya; + xae = xe - xa; yae = ye - ya; + det = xad*yae - xae*yad; + if (det == 0.) + return NULL; + return gts_point_new (point_class, + (yae*yad*(yd - ye) + xad*yae*xd - xae*yad*xe)/det, + -(xae*xad*(xd - xe) + yad*xae*yd - yae*xad*ye)/det, + 0.); +} + +/* square of the maximum area ratio admissible */ +#define AREA_RATIO_MAX2 1e8 + +static gboolean points_are_folded (GtsPoint * A, + GtsPoint * B, + GtsPoint * C, + GtsPoint * D, + gdouble max) +{ + GtsVector AB, AC, AD; + GtsVector n1, n2; + gdouble nn1, nn2, n1n2; + + gts_vector_init (AB, A, B); + gts_vector_init (AC, A, C); + gts_vector_init (AD, A, D); + gts_vector_cross (n1, AB, AC); + gts_vector_cross (n2, AD, AB); + + nn1 = gts_vector_scalar (n1, n1); + nn2 = gts_vector_scalar (n2, n2); + if (nn1 >= AREA_RATIO_MAX2*nn2 || nn2 >= AREA_RATIO_MAX2*nn1) + return TRUE; + n1n2 = gts_vector_scalar (n1, n2); + if (n1n2 > 0.) + return FALSE; + if (n1n2*n1n2/(nn1*nn2) > max) + return TRUE; + return FALSE; +} + +static GtsVertex * triangle_use_vertices (GtsTriangle * t, + GtsVertex * A, + GtsVertex * B) +{ + GtsVertex + * v1 = GTS_SEGMENT (t->e1)->v1, + * v2 = GTS_SEGMENT (t->e1)->v2, + * v3 = gts_triangle_vertex (t); + + if (v1 == A) { + if (v2 == B) + return v3; + g_assert (v3 == B); + return v2; + } + if (v2 == A) { + if (v1 == B) + return v3; + g_assert (v3 == B); + return v1; + } + if (v3 == A) { + if (v1 == B) + return v2; + g_assert (v2 == B); + return v1; + } + g_assert_not_reached (); + return NULL; +} + +/** + * gts_triangles_are_folded: + * @triangles: a list of #GtsTriangle. + * @A: a #GtsVertex. + * @B: another #GtsVertex. + * @max: the maximum value of the square of the cosine of the angle between + * two triangles. + * + * Given a list of triangles sharing @A and @B as vertices, checks if any + * two triangles in the list make an angle larger than a given value defined + * by @max. + * + * Returns: %TRUE if any pair of triangles in @triangles makes an angle larger + * than the maximum value, %FALSE otherwise. + */ +gboolean gts_triangles_are_folded (GSList * triangles, + GtsVertex * A, GtsVertex * B, + gdouble max) +{ + GSList * i; + + g_return_val_if_fail (A != NULL, TRUE); + g_return_val_if_fail (B != NULL, TRUE); + + i = triangles; + while (i) { + GtsVertex * C = triangle_use_vertices (i->data, A, B); + GSList * j = i->next; + while (j) { + GtsVertex * D = triangle_use_vertices (j->data, A, B); + if (points_are_folded (GTS_POINT (A), + GTS_POINT (B), + GTS_POINT (C), + GTS_POINT (D), + max)) + return TRUE; + j = j->next; + } + i = i->next; + } + return FALSE; +} + +/** + * gts_triangle_is_stabbed: + * @t: a #GtsTriangle. + * @p: a #GtsPoint. + * @orientation: a pointer or %NULL. + * + * Returns: one of the vertices of @t, one of the edges of @t or @t if + * any of these are stabbed by the ray starting at @p (included) and + * ending at (@p->x, @p->y, +infty), %NULL otherwise. If the ray is + * contained in the plane of the triangle %NULL is also returned. If + * @orientation is not %NULL, it is set to the value of the + * orientation of @p relative to @t (as given by + * gts_point_orientation_3d()). + */ +GtsObject * gts_triangle_is_stabbed (GtsTriangle * t, + GtsPoint * p, + gdouble * orientation) +{ + GtsVertex * v1, * v2, * v3, * inverted = NULL; + GtsEdge * e1, * e2, * e3, * tmp; + gdouble o, o1, o2, o3; + + g_return_val_if_fail (t != NULL, NULL); + g_return_val_if_fail (p != NULL, NULL); + + gts_triangle_vertices_edges (t, NULL, &v1, &v2, &v3, &e1, &e2, &e3); + o = gts_point_orientation (GTS_POINT (v1), GTS_POINT (v2), GTS_POINT (v3)); + if (o == 0.) + return NULL; + if (o < 0.) { + inverted = v1; + v1 = v2; + v2 = inverted; + tmp = e2; + e2 = e3; + e3 = tmp; + } + o = gts_point_orientation_3d (GTS_POINT (v1), + GTS_POINT (v2), + GTS_POINT (v3), + p); + if (o < 0.) + return NULL; + o1 = gts_point_orientation (GTS_POINT (v1), GTS_POINT (v2), p); + if (o1 < 0.) + return NULL; + o2 = gts_point_orientation (GTS_POINT (v2), GTS_POINT (v3), p); + if (o2 < 0.) + return NULL; + o3 = gts_point_orientation (GTS_POINT (v3), GTS_POINT (v1), p); + if (o3 < 0.) + return NULL; + if (orientation) *orientation = inverted ? -o : o; + if (o1 == 0.) { + if (o2 == 0.) + return GTS_OBJECT (v2); + if (o3 == 0.) + return GTS_OBJECT (v1); + return GTS_OBJECT (e1); + } + if (o2 == 0.) { + if (o3 == 0.) + return GTS_OBJECT (v3); + return GTS_OBJECT (e2); + } + if (o3 == 0.) + return GTS_OBJECT (e3); + return GTS_OBJECT (t); +} + +/** + * gts_triangle_interpolate_height: + * @t: a #GtsTriangle. + * @p: a #GtsPoint. + * + * Fills the z-coordinate of point @p belonging to the plane + * projection of triangle @t with the linearly interpolated value of + * the z-coordinates of the vertices of @t. + */ +void gts_triangle_interpolate_height (GtsTriangle * t, GtsPoint * p) +{ + GtsPoint * p1, * p2, * p3; + gdouble x1, x2, y1, y2, det; + + g_return_if_fail (t != NULL); + g_return_if_fail (p != NULL); + + p1 = GTS_POINT (GTS_SEGMENT (t->e1)->v1); + p2 = GTS_POINT (GTS_SEGMENT (t->e1)->v2); + p3 = GTS_POINT (gts_triangle_vertex (t)); + + x1 = p2->x - p1->x; + y1 = p2->y - p1->y; + x2 = p3->x - p1->x; + y2 = p3->y - p1->y; + det = x1*y2 - x2*y1; + if (det == 0.) + p->z = (p1->z + p2->z + p3->z)/3.; + else { + gdouble x = p->x - p1->x; + gdouble y = p->y - p1->y; + gdouble a = (x*y2 - y*x2)/det; + gdouble b = (y*x1 - x*y1)/det; + + p->z = (1. - a - b)*p1->z + a*p2->z + b*p3->z; + } +} Index: trunk/src_3rd/gts/tribox3.c =================================================================== --- trunk/src_3rd/gts/tribox3.c (nonexistent) +++ trunk/src_3rd/gts/tribox3.c (revision 1065) @@ -0,0 +1,192 @@ +/** + * History: + * 2004-10-27 Stephane Popinet: changed float to double + */ + +/********************************************************/ +/* AABB-triangle overlap test code */ +/* by Tomas Akenine-Möller */ +/* Function: int triBoxOverlap(float boxcenter[3], */ +/* float boxhalfsize[3],float triverts[3][3]); */ +/* History: */ +/* 2001-03-05: released the code in its first version */ +/* 2001-06-18: changed the order of the tests, faster */ +/* */ +/* Acknowledgement: Many thanks to Pierre Terdiman for */ +/* suggestions and discussions on how to optimize code. */ +/* Thanks to David Hunt for finding a ">="-bug! */ +/********************************************************/ +#include +#include + +#define X 0 +#define Y 1 +#define Z 2 + +#define CROSS(dest,v1,v2) \ + dest[0]=v1[1]*v2[2]-v1[2]*v2[1]; \ + dest[1]=v1[2]*v2[0]-v1[0]*v2[2]; \ + dest[2]=v1[0]*v2[1]-v1[1]*v2[0]; + +#define DOT(v1,v2) (v1[0]*v2[0]+v1[1]*v2[1]+v1[2]*v2[2]) + +#define SUB(dest,v1,v2) \ + dest[0]=v1[0]-v2[0]; \ + dest[1]=v1[1]-v2[1]; \ + dest[2]=v1[2]-v2[2]; + +#define FINDMINMAX(x0,x1,x2,min,max) \ + min = max = x0; \ + if(x1max) max=x1;\ + if(x2max) max=x2; + +int planeBoxOverlap(double normal[3], double vert[3], double maxbox[3]) // -NJMP- +{ + int q; + double vmin[3],vmax[3],v; + for(q=X;q<=Z;q++) + { + v=vert[q]; // -NJMP- + if(normal[q]>0.0f) + { + vmin[q]=-maxbox[q] - v; // -NJMP- + vmax[q]= maxbox[q] - v; // -NJMP- + } + else + { + vmin[q]= maxbox[q] - v; // -NJMP- + vmax[q]=-maxbox[q] - v; // -NJMP- + } + } + if(DOT(normal,vmin)>0.0f) return 0; // -NJMP- + if(DOT(normal,vmax)>=0.0f) return 1; // -NJMP- + + return 0; +} + + +/*======================== X-tests ========================*/ +#define AXISTEST_X01(a, b, fa, fb) \ + p0 = a*v0[Y] - b*v0[Z]; \ + p2 = a*v2[Y] - b*v2[Z]; \ + if(p0rad || max<-rad) return 0; + +#define AXISTEST_X2(a, b, fa, fb) \ + p0 = a*v0[Y] - b*v0[Z]; \ + p1 = a*v1[Y] - b*v1[Z]; \ + if(p0rad || max<-rad) return 0; + +/*======================== Y-tests ========================*/ +#define AXISTEST_Y02(a, b, fa, fb) \ + p0 = -a*v0[X] + b*v0[Z]; \ + p2 = -a*v2[X] + b*v2[Z]; \ + if(p0rad || max<-rad) return 0; + +#define AXISTEST_Y1(a, b, fa, fb) \ + p0 = -a*v0[X] + b*v0[Z]; \ + p1 = -a*v1[X] + b*v1[Z]; \ + if(p0rad || max<-rad) return 0; + +/*======================== Z-tests ========================*/ + +#define AXISTEST_Z12(a, b, fa, fb) \ + p1 = a*v1[X] - b*v1[Y]; \ + p2 = a*v2[X] - b*v2[Y]; \ + if(p2rad || max<-rad) return 0; + +#define AXISTEST_Z0(a, b, fa, fb) \ + p0 = a*v0[X] - b*v0[Y]; \ + p1 = a*v1[X] - b*v1[Y]; \ + if(p0rad || max<-rad) return 0; + +int triBoxOverlap(double boxcenter[3],double boxhalfsize[3],double triverts[3][3]) +{ + + /* use separating axis theorem to test overlap between triangle and box */ + /* need to test for overlap in these directions: */ + /* 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle */ + /* we do not even need to test these) */ + /* 2) normal of the triangle */ + /* 3) crossproduct(edge from tri, {x,y,z}-directin) */ + /* this gives 3x3=9 more tests */ + double v0[3],v1[3],v2[3]; +// double axis[3]; + double min,max,p0,p1,p2,rad,fex,fey,fez; // -NJMP- "d" local variable removed + double normal[3],e0[3],e1[3],e2[3]; + + /* This is the fastest branch on Sun */ + /* move everything so that the boxcenter is in (0,0,0) */ + SUB(v0,triverts[0],boxcenter); + SUB(v1,triverts[1],boxcenter); + SUB(v2,triverts[2],boxcenter); + + /* compute triangle edges */ + SUB(e0,v1,v0); /* tri edge 0 */ + SUB(e1,v2,v1); /* tri edge 1 */ + SUB(e2,v0,v2); /* tri edge 2 */ + + /* Bullet 3: */ + /* test the 9 tests first (this was faster) */ + fex = fabsf(e0[X]); + fey = fabsf(e0[Y]); + fez = fabsf(e0[Z]); + AXISTEST_X01(e0[Z], e0[Y], fez, fey); + AXISTEST_Y02(e0[Z], e0[X], fez, fex); + AXISTEST_Z12(e0[Y], e0[X], fey, fex); + + fex = fabsf(e1[X]); + fey = fabsf(e1[Y]); + fez = fabsf(e1[Z]); + AXISTEST_X01(e1[Z], e1[Y], fez, fey); + AXISTEST_Y02(e1[Z], e1[X], fez, fex); + AXISTEST_Z0(e1[Y], e1[X], fey, fex); + + fex = fabsf(e2[X]); + fey = fabsf(e2[Y]); + fez = fabsf(e2[Z]); + AXISTEST_X2(e2[Z], e2[Y], fez, fey); + AXISTEST_Y1(e2[Z], e2[X], fez, fex); + AXISTEST_Z12(e2[Y], e2[X], fey, fex); + + /* Bullet 1: */ + /* first test overlap in the {x,y,z}-directions */ + /* find min, max of the triangle each direction, and test for overlap in */ + /* that direction -- this is equivalent to testing a minimal AABB around */ + /* the triangle against the AABB */ + + /* test in X-direction */ + FINDMINMAX(v0[X],v1[X],v2[X],min,max); + if(min>boxhalfsize[X] || max<-boxhalfsize[X]) return 0; + + /* test in Y-direction */ + FINDMINMAX(v0[Y],v1[Y],v2[Y],min,max); + if(min>boxhalfsize[Y] || max<-boxhalfsize[Y]) return 0; + + /* test in Z-direction */ + FINDMINMAX(v0[Z],v1[Z],v2[Z],min,max); + if(min>boxhalfsize[Z] || max<-boxhalfsize[Z]) return 0; + + /* Bullet 2: */ + /* test if the box intersects the plane of the triangle */ + /* compute plane equation of triangle: normal*x+d=0 */ + CROSS(normal,e0,e1); + // -NJMP- (line removed here) + if(!planeBoxOverlap(normal,v0,boxhalfsize)) return 0; // -NJMP- + + return 1; /* box and triangle overlaps */ +} + Index: trunk/src_3rd/gts/vertex.c =================================================================== --- trunk/src_3rd/gts/vertex.c (nonexistent) +++ trunk/src_3rd/gts/vertex.c (revision 1065) @@ -0,0 +1,780 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "gts.h" + +gboolean gts_allow_floating_vertices = FALSE; + +static void vertex_destroy (GtsObject * object) +{ + GtsVertex * vertex = GTS_VERTEX (object); + GSList * i; + + i = vertex->segments; + while (i) { + GTS_OBJECT_SET_FLAGS (i->data, GTS_DESTROYED); + i = i->next; + } + i = vertex->segments; + while (i) { + GSList * next = i->next; + gts_object_destroy (i->data); + i = next; + } + g_assert (vertex->segments == NULL); + + (* GTS_OBJECT_CLASS (gts_vertex_class ())->parent_class->destroy) (object); +} + +static void vertex_clone (GtsObject * clone, GtsObject * object) +{ + (* GTS_OBJECT_CLASS (gts_vertex_class ())->parent_class->clone) (clone, + object); + GTS_VERTEX (clone)->segments = NULL; +} + +static void vertex_class_init (GtsVertexClass * klass) +{ + klass->intersection_attributes = NULL; + GTS_OBJECT_CLASS (klass)->clone = vertex_clone; + GTS_OBJECT_CLASS (klass)->destroy = vertex_destroy; +} + +static void vertex_init (GtsVertex * vertex) +{ + vertex->segments = NULL; +} + +/** + * gts_vertex_class: + * + * Returns: the #GtsVertexClass. + */ +GtsVertexClass * gts_vertex_class (void) +{ + static GtsVertexClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo vertex_info = { + "GtsVertex", + sizeof (GtsVertex), + sizeof (GtsVertexClass), + (GtsObjectClassInitFunc) vertex_class_init, + (GtsObjectInitFunc) vertex_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_point_class ()), + &vertex_info); + } + + return klass; +} + +/** + * gts_vertex_new: + * @klass: a #GtsVertexClass. + * @x: the x-coordinate of the vertex to create. + * @y: the y-coordinate of the vertex to create. + * @z: the y-coordinate of the vertex to create. + * + * Returns: a new #GtsVertex with @x, @y and @z as coordinates. + */ +GtsVertex * gts_vertex_new (GtsVertexClass * klass, + gdouble x, gdouble y, gdouble z) +{ + GtsVertex * v; + + v = GTS_VERTEX (gts_object_new (GTS_OBJECT_CLASS (klass))); + gts_point_set (GTS_POINT (v), x, y, z); + + return v; +} + +/** + * gts_vertex_replace: + * @v: a #GtsVertex. + * @with: another #GtsVertex. + * + * Replaces vertex @v with vertex @with. @v and @with must be + * different. All the #GtsSegment which have @v has one of their + * vertices are updated. The segments list of vertex @v is freed and + * @v->segments is set to %NULL. + */ +void gts_vertex_replace (GtsVertex * v, GtsVertex * with) +{ + GSList * i; + + g_return_if_fail (v != NULL); + g_return_if_fail (with != NULL); + g_return_if_fail (v != with); + + i = v->segments; + while (i) { + GtsSegment * s = i->data; + if (s->v1 != with && s->v2 != with) + with->segments = g_slist_prepend (with->segments, s); + if (s->v1 == v) s->v1 = with; + if (s->v2 == v) s->v2 = with; + i = i->next; + } + g_slist_free (v->segments); + v->segments = NULL; +} + +/** + * gts_vertex_is_unattached: + * @v: a #GtsVertex. + * + * Returns: %TRUE if @v is not the endpoint of any #GtsSegment, + * %FALSE otherwise. + */ +gboolean gts_vertex_is_unattached (GtsVertex * v) +{ + g_return_val_if_fail (v != NULL, FALSE); + if (v->segments == NULL) + return TRUE; + return FALSE; +} + +/** + * gts_vertices_are_connected: + * @v1: a #GtsVertex. + * @v2: another #GtsVertex. + * + * Returns: if @v1 and @v2 are the vertices of the same #GtsSegment + * this segment else %NULL. + */ +GtsSegment * gts_vertices_are_connected (GtsVertex * v1, GtsVertex * v2) +{ + GSList * i; + + g_return_val_if_fail (v1 != NULL, FALSE); + g_return_val_if_fail (v2 != NULL, FALSE); + + i = v1->segments; + while (i) { + GtsSegment * s = i->data; + + if (s->v1 == v2 || s->v2 == v2) + return s; + i = i->next; + } + return NULL; +} + +/** + * gts_vertices_from_segments: + * @segments: a list of #GtsSegment. + * + * Returns: a list of #GtsVertex, vertices of a #GtsSegment in @segments. + * Each element in the list is unique (no duplicates). + */ +GSList * gts_vertices_from_segments (GSList * segments) +{ + GHashTable * hash; + GSList * vertices = NULL, * i; + + hash = g_hash_table_new (NULL, NULL); + i = segments; + while (i) { + GtsSegment * s = i->data; + if (g_hash_table_lookup (hash, s->v1) == NULL) { + vertices = g_slist_prepend (vertices, s->v1); + g_hash_table_insert (hash, s->v1, s); + } + if (g_hash_table_lookup (hash, s->v2) == NULL) { + vertices = g_slist_prepend (vertices, s->v2); + g_hash_table_insert (hash, s->v2, s); + } + i = i->next; + } + g_hash_table_destroy (hash); + return vertices; +} + +/** + * gts_vertex_triangles: + * @v: a #GtsVertex. + * @list: a list of #GtsTriangle. + * + * Adds all the #GtsTriangle which share @v as a vertex and do not + * already belong to @list. + * + * Returns: the new list of unique #GtsTriangle which share @v as a + * vertex. + */ +GSList * gts_vertex_triangles (GtsVertex * v, + GSList * list) +{ + GSList * i; + + g_return_val_if_fail (v != NULL, NULL); + + i = v->segments; + while (i) { + GtsSegment * s = i->data; + if (GTS_IS_EDGE (s)) { + GSList * j = GTS_EDGE (s)->triangles; + while (j) { + if (!g_slist_find (list, j->data)) + list = g_slist_prepend (list, j->data); + j = j->next; + } + } + i = i->next; + } + return list; +} + +/** + * gts_vertex_faces: + * @v: a #GtsVertex. + * @surface: a #GtsSurface or %NULL. + * @list: a list of #GtsFace. + * + * Adds all the #GtsFace belonging to @surface (if not %NULL) which share + * @v as a vertex and do not already belong to @list. + * + * Returns: the new list of unique #GtsFace belonging to @surface + * which share @v as a vertex. + */ +GSList * gts_vertex_faces (GtsVertex * v, + GtsSurface * surface, + GSList * list) +{ + GSList * i; + + g_return_val_if_fail (v != NULL, NULL); + + i = v->segments; + while (i) { + GtsSegment * s = i->data; + if (GTS_IS_EDGE (s)) { + GSList * j = GTS_EDGE (s)->triangles; + while (j) { + GtsTriangle * t = j->data; + if (GTS_IS_FACE (t) + && + (!surface || gts_face_has_parent_surface (GTS_FACE (t), surface)) + && + !g_slist_find (list, t)) + list = g_slist_prepend (list, t); + j = j->next; + } + } + i = i->next; + } + return list; +} + +/** + * gts_vertex_neighbors: + * @v: a #GtsVertex. + * @list: a list of #GtsVertex. + * @surface: a #GtsSurface or %NULL. + * + * Adds to @list all the #GtsVertex connected to @v by a #GtsSegment and not + * already in @list. If @surface is not %NULL only the vertices connected to + * @v by an edge belonging to @surface are considered. + * + * Returns: the new list of unique #GtsVertex. + */ +GSList * gts_vertex_neighbors (GtsVertex * v, + GSList * list, + GtsSurface * surface) +{ + GSList * i; + + g_return_val_if_fail (v != NULL, NULL); + + i = v->segments; + while (i) { + GtsSegment * s = i->data; + GtsVertex * v1 = s->v1 == v ? s->v2 : s->v1; + if (v1 != v && + (!surface || + (GTS_IS_EDGE (s) && + gts_edge_has_parent_surface (GTS_EDGE (s), surface))) && + !g_slist_find (list, v1)) + list = g_slist_prepend (list, v1); + i = i->next; + } + return list; +} + +/** + * gts_vertex_is_boundary: + * @v: a #GtsVertex. + * @surface: a #GtsSurface or %NULL. + * + * Returns: %TRUE if @v is used by a #GtsEdge boundary of @surface as + * determined by gts_edge_is_boundary(), %FALSE otherwise. + */ +gboolean gts_vertex_is_boundary (GtsVertex * v, GtsSurface * surface) +{ + GSList * i; + + g_return_val_if_fail (v != NULL, FALSE); + + i = v->segments; + while (i) { + if (GTS_IS_EDGE (i->data) && + gts_edge_is_boundary (i->data, surface)) + return TRUE; + i = i->next; + } + + return FALSE; +} + +/** + * gts_vertices_merge: + * @vertices: a list of #GtsVertex. + * @epsilon: half the size of the bounding box to consider for each vertex. + * @check: function called for each pair of vertices about to be merged + * or %NULL. + * + * For each vertex v in @vertices look if there are any vertex of + * @vertices contained in a box centered on v of size 2*@epsilon. If + * there are and if @check is not %NULL and returns %TRUE, replace + * them with v (using gts_vertex_replace()), destroy them and remove + * them from list. This is done efficiently using Kd-Trees. + * + * Returns: the updated list of vertices. + */ +GList * gts_vertices_merge (GList * vertices, + gdouble epsilon, + gboolean (* check) (GtsVertex *, GtsVertex *)) +{ + GPtrArray * array; + GList * i; + GNode * kdtree; + + g_return_val_if_fail (vertices != NULL, 0); + + array = g_ptr_array_new (); + i = vertices; + while (i) { + g_ptr_array_add (array, i->data); + i = i->next; + } + kdtree = gts_kdtree_new (array, NULL); + g_ptr_array_free (array, TRUE); + + i = vertices; + while (i) { + GtsVertex * v = i->data; + if (!GTS_OBJECT (v)->reserved) { /* Do something only if v is active */ + GtsBBox * bbox; + GSList * selected, * j; + + /* build bounding box */ + bbox = gts_bbox_new (gts_bbox_class (), + v, + GTS_POINT (v)->x - epsilon, + GTS_POINT (v)->y - epsilon, + GTS_POINT (v)->z - epsilon, + GTS_POINT (v)->x + epsilon, + GTS_POINT (v)->y + epsilon, + GTS_POINT (v)->z + epsilon); + + /* select vertices which are inside bbox using kdtree */ + j = selected = gts_kdtree_range (kdtree, bbox, NULL); + while (j) { + GtsVertex * sv = j->data; + if (sv != v && !GTS_OBJECT (sv)->reserved && (!check || (*check) (sv, v))) { + /* sv is not v and is active */ + gts_vertex_replace (sv, v); + GTS_OBJECT (sv)->reserved = sv; /* mark sv as inactive */ + } + j = j->next; + } + g_slist_free (selected); + gts_object_destroy (GTS_OBJECT (bbox)); + } + i = i->next; + } + + gts_kdtree_destroy (kdtree); + + /* destroy inactive vertices and removes them from list */ + + /* we want to control vertex destruction */ + gts_allow_floating_vertices = TRUE; + + i = vertices; + while (i) { + GtsVertex * v = i->data; + GList * next = i->next; + if (GTS_OBJECT (v)->reserved) { /* v is inactive */ + gts_object_destroy (GTS_OBJECT (v)); + vertices = g_list_remove_link (vertices, i); + g_list_free_1 (i); + } + i = next; + } + gts_allow_floating_vertices = FALSE; + + return vertices; +} + +/* returns the list of edges belonging to @surface turning around @v */ +static GSList * edge_fan_list (GtsVertex * v, + GtsSurface * surface, + GtsFace * f, + GtsEdge * e, + GtsFace * first) +{ + GSList * i = e->triangles; + GtsFace * neighbor = NULL; + GtsEdge * next = NULL, * enext = NULL; + + while (i) { + GtsFace * f1 = i->data; + if (GTS_IS_FACE (f1) && + f1 != f && + gts_face_has_parent_surface (f1, surface)) { + g_return_val_if_fail (neighbor == NULL, NULL); /* non-manifold edge */ + neighbor = f1; + } + i = i->next; + } + if (neighbor == NULL || neighbor == first) /* end of fan */ + return NULL; + + if (GTS_TRIANGLE (neighbor)->e1 == e) { + next = GTS_TRIANGLE (neighbor)->e2; + enext = GTS_TRIANGLE (neighbor)->e3; + } + else if (GTS_TRIANGLE (neighbor)->e2 == e) { + next = GTS_TRIANGLE (neighbor)->e3; + enext = GTS_TRIANGLE (neighbor)->e1; + } + else if (GTS_TRIANGLE (neighbor)->e3 == e) { + next = GTS_TRIANGLE (neighbor)->e1; + enext = GTS_TRIANGLE (neighbor)->e2; + } + else + g_assert_not_reached (); + + /* checking for correct orientation */ + g_return_val_if_fail (GTS_SEGMENT (enext)->v1 == v || + GTS_SEGMENT (enext)->v2 == v, NULL); + + return g_slist_prepend (edge_fan_list (v, surface, neighbor, enext, first), + next); +} + +/** + * gts_vertex_fan_oriented: + * @v: a #GtsVertex. + * @surface: a #GtsSurface. + * + * Returns: a list of #GtsEdge describing in counterclockwise order the + * boundary of the fan of summit @v, the faces of the fan belonging to + * @surface. + */ +GSList * gts_vertex_fan_oriented (GtsVertex * v, GtsSurface * surface) +{ + GtsFace * f = NULL; + guint d = 2; + GSList * i; + GtsVertex * v1, * v2, * v3; + GtsEdge * e1, * e2, * e3; + + g_return_val_if_fail (v != NULL, NULL); + g_return_val_if_fail (surface != NULL, NULL); + + i = v->segments; + while (i) { + GtsEdge * e = i->data; + if (GTS_IS_EDGE (e)) { + GSList * j = e->triangles; + GtsFace * f1 = NULL; + guint degree = 0; + while (j) { + if (GTS_IS_FACE (j->data) && + gts_face_has_parent_surface (j->data, surface)) { + f1 = j->data; + degree++; + } + j = j->next; + } + if (f1 != NULL) { + g_return_val_if_fail (degree <= 2, NULL); /* non-manifold edge */ + if (degree == 1) { + gts_triangle_vertices_edges (GTS_TRIANGLE (f1), NULL, + &v1, &v2, &v3, &e1, &e2, &e3); + if (v == v2) { + e2 = e3; + e3 = e1; + } + else if (v == v3) { + e3 = e2; + e2 = e1; + } + if (e3 != e) { + d = 1; + f = f1; + } + } + else if (degree <= d) + f = f1; + } + } + i = i->next; + } + + if (f == NULL) + return NULL; + + gts_triangle_vertices_edges (GTS_TRIANGLE (f), NULL, + &v1, &v2, &v3, &e1, &e2, &e3); + if (v == v2) { + e2 = e3; + e3 = e1; + } + else if (v == v3) { + e3 = e2; + e2 = e1; + } + + return g_slist_prepend (edge_fan_list (v, surface, f, e3, f), e2); +} + +#define edge_use_vertex(e, v) (GTS_SEGMENT(e)->v1 == v ||\ + GTS_SEGMENT(e)->v2 == v) + +static GtsEdge * replace_vertex (GtsTriangle * t, + GtsEdge * e1, + GtsVertex * v, + GtsVertex * with) +{ + GtsEdge * e = NULL; + + if (t->e1 != e1 && edge_use_vertex (t->e1, v)) + e = t->e1; + else if (t->e2 != e1 && edge_use_vertex (t->e2, v)) + e = t->e2; + else if (t->e3 != e1 && edge_use_vertex (t->e3, v)) + e = t->e3; + else + return NULL; + + if (with != v) { + GtsSegment * s = GTS_SEGMENT (e); + if (s->v1 == v) s->v1 = with; + if (s->v2 == v) s->v2 = with; + with->segments = g_slist_prepend (with->segments, s); + v->segments = g_slist_remove (v->segments, s); + } + + return e; +} + +static void triangle_next (GtsEdge * e, GtsVertex * v, GtsVertex * with) +{ + GSList * i; + + if (e == NULL) + return; + + i = e->triangles; + while (i) { + GtsTriangle * t = i->data; + if (GTS_OBJECT (t)->reserved) { + GTS_OBJECT (t)->reserved = NULL; + triangle_next (replace_vertex (t, e, v, with), v, with); + } + i = i->next; + } +} + +/** + * gts_vertex_is_contact: + * @v: a #GtsVertex. + * @sever: if %TRUE and if @v is a contact vertex between two or more + * sets of connected triangles replaces it with as many vertices, + * clones of @v. + * + * Returns: the number of sets of connected triangles sharing @v as a + * contact vertex. + */ +guint gts_vertex_is_contact (GtsVertex * v, gboolean sever) +{ + GSList * triangles, * i; + GtsVertex * with = v; + guint ncomponent = 0; + + g_return_val_if_fail (v != NULL, 0); + + triangles = gts_vertex_triangles (v, NULL); + i = triangles; + while (i) { + GTS_OBJECT (i->data)->reserved = i; + i = i->next; + } + + i = triangles; + while (i) { + GtsTriangle * t = i->data; + if (GTS_OBJECT (t)->reserved) { + GtsEdge * e; + if (ncomponent && sever) + with = GTS_VERTEX (gts_object_clone (GTS_OBJECT (v))); + GTS_OBJECT (t)->reserved = NULL; + e = replace_vertex (t, NULL, v, with); + triangle_next (e, v, with); + triangle_next (replace_vertex (t, e, v, with), v, with); + ncomponent++; + } + i = i->next; + } + g_slist_free (triangles); + + return ncomponent; +} + +/* GtsVertexNormal: Object */ + +static void vertex_normal_attributes (GtsVertex * v, + GtsObject * e, + GtsObject * t) +{ + g_return_if_fail (GTS_IS_EDGE (e)); + g_return_if_fail (GTS_IS_TRIANGLE (t)); + + if (GTS_IS_VERTEX_NORMAL (GTS_SEGMENT (e)->v1) && + GTS_IS_VERTEX_NORMAL (GTS_SEGMENT (e)->v2)) { + GtsPoint * p1 = GTS_POINT (GTS_SEGMENT (e)->v1); + GtsPoint * p2 = GTS_POINT (GTS_SEGMENT (e)->v2); + GtsPoint * p = GTS_POINT (v); + gdouble a, b, lambda; + guint i; + + a = p2->x - p1->x; b = p->x - p1->x; + if (fabs (p2->y - p1->y) > fabs (a)) { + a = p2->y - p1->y; b = p->y - p1->y; + } + if (fabs (p2->z - p1->z) > fabs (a)) { + a = p2->z - p1->z; b = p->z - p1->z; + } + lambda = a != 0. ? b/a : 0.; + for (i = 0; i < 3; i++) + GTS_VERTEX_NORMAL (v)->n[i] = + (1. - lambda)*GTS_VERTEX_NORMAL (GTS_SEGMENT (e)->v1)->n[i] + + lambda*GTS_VERTEX_NORMAL (GTS_SEGMENT (e)->v2)->n[i]; + } + else { + GtsVertex * v1, * v2, * v3; + + gts_triangle_vertices (GTS_TRIANGLE (t), &v1, &v2, &v3); + if (GTS_IS_VERTEX_NORMAL (v1) && + GTS_IS_VERTEX_NORMAL (v2) && + GTS_IS_VERTEX_NORMAL (v3)) { + GtsVector a1, a2, a3, det; + guint i, j = 0; + gdouble l1, l2; + + gts_vector_init (a1, GTS_POINT (v1), GTS_POINT (v)); + gts_vector_init (a2, GTS_POINT (v1), GTS_POINT (v2)); + gts_vector_init (a3, GTS_POINT (v1), GTS_POINT (v3)); + gts_vector_cross (det, a2, a3); + if (fabs (det[1]) > fabs (det[0])) j = 1; + if (fabs (det[2]) > fabs (det[j])) j = 2; + if (det[j] == 0.) { + g_warning ("vertex_normal_attributes: det[%d] == 0.", j); + return; + } + switch (j) { + case 0: + l1 = (a1[1]*a3[2] - a1[2]*a3[1])/det[0]; + l2 = (a1[2]*a2[1] - a1[1]*a2[2])/det[0]; + break; + case 1: + l1 = (a1[2]*a3[0] - a1[0]*a3[2])/det[1]; + l2 = (a1[0]*a2[2] - a1[2]*a2[0])/det[1]; + break; + case 2: + l1 = (a1[0]*a3[1] - a1[1]*a3[0])/det[2]; + l2 = (a1[1]*a2[0] - a1[0]*a2[1])/det[2]; + break; + default: + l1 = l2 = 0.; + } + for (i = 0; i < 3; i++) + GTS_VERTEX_NORMAL (v)->n[i] = + GTS_VERTEX_NORMAL (v1)->n[i]*(1. - l1 - l2) + + GTS_VERTEX_NORMAL (v2)->n[i]*l1 + + GTS_VERTEX_NORMAL (v3)->n[i]*l2; + } + } +} + +static void gts_vertex_normal_class_init (GtsVertexClass * klass) +{ + klass->intersection_attributes = vertex_normal_attributes; +} + +GtsVertexClass * gts_vertex_normal_class (void) +{ + static GtsVertexClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo gts_vertex_normal_info = { + "GtsVertexNormal", + sizeof (GtsVertexNormal), + sizeof (GtsVertexClass), + (GtsObjectClassInitFunc) gts_vertex_normal_class_init, + (GtsObjectInitFunc) NULL, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_vertex_class ()), + >s_vertex_normal_info); + } + + return klass; +} + +/* GtsColorVertex: Object */ + +GtsVertexClass * gts_color_vertex_class (void) +{ + static GtsVertexClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo gts_color_vertex_info = { + "GtsColorVertex", + sizeof (GtsColorVertex), + sizeof (GtsVertexClass), + (GtsObjectClassInitFunc) NULL, + (GtsObjectInitFunc) NULL, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_vertex_class ()), + >s_color_vertex_info); + } + + return klass; +} + Index: trunk/src_3rd/gts/vopt.c =================================================================== --- trunk/src_3rd/gts/vopt.c (nonexistent) +++ trunk/src_3rd/gts/vopt.c (revision 1065) @@ -0,0 +1,521 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "gts.h" + +/* #define DEBUG_VOPT */ + +/* compute the normal (nx, ny, nz) as the cross-product of the first two + oriented edges and the norm nt = |t| as (v1xv2).v3 */ +static void triangle_normal (GtsTriangle * t, + gdouble * nx, + gdouble * ny, + gdouble * nz, + gdouble * nt) +{ + GtsPoint * p1, * p2 = NULL, * p3 = NULL; + gdouble x1, y1, z1, x2, y2, z2; + + g_return_if_fail (t != NULL); + + p1 = GTS_POINT (GTS_SEGMENT (t->e1)->v1); + if (GTS_SEGMENT (t->e1)->v1 == GTS_SEGMENT (t->e2)->v1) { + p2 = GTS_POINT (GTS_SEGMENT (t->e2)->v2); + p3 = GTS_POINT (GTS_SEGMENT (t->e1)->v2); + } + else if (GTS_SEGMENT (t->e1)->v2 == GTS_SEGMENT (t->e2)->v2) { + p2 = GTS_POINT (GTS_SEGMENT (t->e1)->v2); + p3 = GTS_POINT (GTS_SEGMENT (t->e2)->v1); + } + else if (GTS_SEGMENT (t->e1)->v1 == GTS_SEGMENT (t->e2)->v2) { + p2 = GTS_POINT (GTS_SEGMENT (t->e2)->v1); + p3 = GTS_POINT (GTS_SEGMENT (t->e1)->v2); + } + else if (GTS_SEGMENT (t->e1)->v2 == GTS_SEGMENT (t->e2)->v1) { + p2 = GTS_POINT (GTS_SEGMENT (t->e1)->v2); + p3 = GTS_POINT (GTS_SEGMENT (t->e2)->v2); + } + else + g_assert_not_reached (); + + x1 = p2->x - p1->x; + y1 = p2->y - p1->y; + z1 = p2->z - p1->z; + + x2 = p3->x - p1->x; + y2 = p3->y - p1->y; + z2 = p3->z - p1->z; + + *nt = ((p1->y*p2->z - p1->z*p2->y)*p3->x + + (p1->z*p2->x - p1->x*p2->z)*p3->y + + (p1->x*p2->y - p1->y*p2->x)*p3->z); + *nx = y1*z2 - z1*y2; + *ny = z1*x2 - x1*z2; + *nz = x1*y2 - y1*x2; +} + +static void boundary_preservation (GtsEdge * edge, + GtsFace * f, + GtsVector e1, GtsVector e2, + GtsMatrix * H, GtsVector c) +{ + GtsTriangle * t = GTS_TRIANGLE (f); + GtsEdge * edge2; + GtsVertex * v1 = GTS_SEGMENT (edge)->v1, * v2 = GTS_SEGMENT (edge)->v2; + GtsPoint * p1, * p2; + GtsVector e, e3; + + /* find orientation of segment */ + edge2 = edge == t->e1 ? t->e2 : edge == t->e2 ? t->e3 : t->e1; + if (v2 != GTS_SEGMENT (edge2)->v1 && v2 != GTS_SEGMENT (edge2)->v2) { + v2 = v1; v1 = GTS_SEGMENT (edge)->v2; + } + p1 = GTS_POINT (v1); + p2 = GTS_POINT (v2); + + e[0] = p2->x - p1->x; + e[1] = p2->y - p1->y; + e[2] = p2->z - p1->z; + + e1[0] += e[0]; + e1[1] += e[1]; + e1[2] += e[2]; + + e3[0] = p2->y*p1->z - p2->z*p1->y; + e3[1] = p2->z*p1->x - p2->x*p1->z; + e3[2] = p2->x*p1->y - p2->y*p1->x; + + e2[0] += e3[0]; + e2[1] += e3[1]; + e2[2] += e3[2]; + + H[0][0] += e[1]*e[1] + e[2]*e[2]; + H[0][1] -= e[0]*e[1]; + H[0][2] -= e[0]*e[2]; + H[1][0] = H[0][1]; + H[1][1] += e[0]*e[0] + e[2]*e[2]; + H[1][2] -= e[1]*e[2]; + H[2][0] = H[0][2]; + H[2][1] = H[1][2]; + H[2][2] += e[0]*e[0] + e[1]*e[1]; + + c[0] += e[1]*e3[2] - e[2]*e3[1]; + c[1] += e[2]*e3[0] - e[0]*e3[2]; + c[2] += e[0]*e3[1] - e[1]*e3[0]; +} + +static gdouble boundary_cost (GtsEdge * edge, + GtsFace * f, + GtsVertex * v) +{ + GtsTriangle * t = GTS_TRIANGLE (f); + GtsEdge * edge2; + GtsVertex * v1 = GTS_SEGMENT (edge)->v1, * v2 = GTS_SEGMENT (edge)->v2; + GtsPoint * p1, * p2; + GtsVector e; + GtsPoint * p = GTS_POINT (v); + + /* find orientation of segment */ + edge2 = edge == t->e1 ? t->e2 : edge == t->e2 ? t->e3 : t->e1; + if (v2 != GTS_SEGMENT (edge2)->v1 && v2 != GTS_SEGMENT (edge2)->v2) { + v2 = v1; v1 = GTS_SEGMENT (edge)->v2; + } + p1 = GTS_POINT (v1); + p2 = GTS_POINT (v2); + + e[0] = (p2->y - p1->y)*(p->z - p2->z) - (p2->z - p1->z)*(p->y - p2->y); + e[1] = (p2->z - p1->z)*(p->x - p2->x) - (p2->x - p1->x)*(p->z - p2->z); + e[2] = (p2->x - p1->x)*(p->y - p2->y) - (p2->y - p1->y)*(p->x - p2->x); + + return e[0]*e[0] + e[1]*e[1] + e[2]*e[2]; +} + +static gdouble edge_boundary_cost (GtsEdge * e, GtsVertex * v) +{ + gdouble cost = 0.; + GSList * i; + + i = GTS_SEGMENT (e)->v1->segments; + while (i) { + GtsFace * f; + if (GTS_IS_EDGE (i->data) && + (f = gts_edge_is_boundary (i->data, NULL))) + cost += boundary_cost (i->data, f, v); + i = i->next; + } + i = GTS_SEGMENT (e)->v2->segments; + while (i) { + GtsFace * f; + if (i->data != e && + GTS_IS_EDGE (i->data) && + (f = gts_edge_is_boundary (i->data, NULL))) + cost += boundary_cost (i->data, f, v); + i = i->next; + } + + return cost/4.; +} + +static gdouble edge_volume_cost (GtsEdge * e, GtsVertex * v) +{ + GSList * i, * triangles; + gdouble n1, n2, n3, nt; + gdouble cost = 0.0, a; + + triangles = gts_vertex_triangles (GTS_SEGMENT (e)->v1, NULL); + triangles = gts_vertex_triangles (GTS_SEGMENT (e)->v2, triangles); + + i = triangles; + while (i) { + if (GTS_IS_FACE (i->data)) { + triangle_normal (i->data, &n1, &n2, &n3, &nt); + a = GTS_POINT (v)->x*n1 + + GTS_POINT (v)->y*n2 + + GTS_POINT (v)->z*n3 - nt; + cost += a*a; + } + i = i->next; + } + g_slist_free (triangles); + + return cost/36.; +} + +static gdouble edge_shape_cost (GtsEdge * e, GtsVertex * v) +{ + GSList * list, * i; + GtsVertex + * v1 = GTS_SEGMENT (e)->v1, + * v2 = GTS_SEGMENT (e)->v2; + gdouble cost = 0.; + + list = gts_vertex_neighbors (v1, NULL, NULL); + list = gts_vertex_neighbors (v2, list, NULL); + i = list; + while (i) { + GtsPoint * p = i->data; + if (p != GTS_POINT (v1) && p != GTS_POINT (v2)) + cost += gts_point_distance2 (p, GTS_POINT (v)); + i = i->next; + } + g_slist_free (list); + + return cost; +} + +/** + * gts_volume_optimized_vertex: + * @edge: a #GtsEdge. + * @klass: a #GtsVertexClass to be used for the new vertex. + * @params: a #GtsVolumeOptimizedParms. + * + * Returns: a #GtsVertex which can be used to replace @edge for an + * edge collapse operation. The position of the vertex is optimized in + * order to minimize the changes in area and volume for the surface + * using @edge. The volume enclosed by the surface is locally + * preserved. For more details see "Fast and memory efficient + * polygonal simplification" (1998) and "Evaluation of memoryless + * simplification" (1999) by Lindstrom and Turk. + */ +GtsVertex * gts_volume_optimized_vertex (GtsEdge * edge, + GtsVertexClass * klass, + GtsVolumeOptimizedParams * params) +{ + GSList * triangles, * i; + gdouble sn1 = 0., sn2 = 0., sn3 = 0.; + gdouble sn11 = 0., sn22 = 0., sn33 = 0.; + gdouble sn12 = 0., sn13 = 0., sn23 = 0.; + gdouble st = 0., stn1 = 0., stn2 = 0., stn3 = 0.; + gdouble n1, n2, n3, nt; + GtsMatrix * A, * Ai; + GtsVector A1, b; + GtsVector e1 = {0., 0., 0.}, e2 = {0., 0., 0.}; + GtsMatrix * Hb; + GtsVector cb = {0., 0., 0.}; + GtsVertex * v; + GtsVertex * v1, * v2; + guint n = 0, nb = 0; +#ifdef DEBUG_VOPT + guint nold = 0; +#endif + + g_return_val_if_fail (edge != NULL, NULL); + g_return_val_if_fail (klass != NULL, NULL); + g_return_val_if_fail (params != NULL, NULL); + + A = gts_matrix_zero (NULL); + Hb = gts_matrix_zero (NULL); + v1 = GTS_SEGMENT (edge)->v1; + v2 = GTS_SEGMENT (edge)->v2; + + /* boundary preservation */ + i = v1->segments; + while (i) { + GtsEdge * edge1 = i->data; + GtsFace * f; + if (GTS_IS_EDGE (edge1) && + (f = gts_edge_is_boundary (edge1, NULL))) { + boundary_preservation (edge1, f, e1, e2, Hb, cb); + nb++; + } + i = i->next; + } + i = v2->segments; + while (i) { + GtsEdge * edge1 = i->data; + GtsFace * f; + if (edge1 != edge && + GTS_IS_EDGE (edge1) && + (f = gts_edge_is_boundary (edge1, NULL))) { + boundary_preservation (edge1, f, e1, e2, Hb, cb); + nb++; + } + i = i->next; + } + if (nb > 0) { + GtsMatrix * H = gts_matrix_new ( + e1[2]*e1[2] + e1[1]*e1[1], - e1[0]*e1[1], - e1[0]*e1[2], 0., + - e1[0]*e1[1], e1[2]*e1[2] + e1[0]*e1[0], - e1[1]*e1[2], 0., + - e1[0]*e1[2], - e1[1]*e1[2], e1[1]*e1[1] + e1[0]*e1[0], 0., + 0., 0., 0., 0.); + GtsVector c; + + c[0] = e1[1]*e2[2] - e1[2]*e2[1]; + c[1] = e1[2]*e2[0] - e1[0]*e2[2]; + c[2] = e1[0]*e2[1] - e1[1]*e2[0]; + n = gts_matrix_quadratic_optimization (A, b, n, H, c); + gts_matrix_destroy (H); + } + + g_assert (n <= 2); + +#ifdef DEBUG_VOPT + if (n != nold) { + fprintf (stderr, "--- boundary preservation ---\n"); + gts_matrix_print (A, stderr); + gts_vector_print (b, stderr); + nold = n; + } +#endif + + /* volume preservation */ + triangles = gts_vertex_triangles (v1, NULL); + triangles = gts_vertex_triangles (v2, triangles); + + i = triangles; + while (i) { + if (GTS_IS_FACE (i->data)) { + triangle_normal (i->data, &n1, &n2, &n3, &nt); + sn1 += n1; sn2 += n2; sn3 += n3; + sn11 += n1*n1; sn22 += n2*n2; sn33 += n3*n3; + sn12 += n1*n2; sn13 += n1*n3; sn23 += n2*n3; + st += nt; stn1 += nt*n1; stn2 += nt*n2; stn3 += nt*n3; + } + i = i->next; + } + g_slist_free (triangles); + + A1[0] = sn1; A1[1] = sn2; A1[2] = sn3; + n = gts_matrix_compatible_row (A, b, n, A1, st); + +#ifdef DEBUG_VOPT + if (n != nold) { + fprintf (stderr, "--- volume preservation ---\n"); + gts_matrix_print (A, stderr); + gts_vector_print (b, stderr); + nold = n; + } +#endif + +#if 1 /* Weighted average of volume and boundary optimization */ + if (n < 3) { + /* volume optimization and boundary optimization */ + GtsMatrix * H = gts_matrix_new (sn11, sn12, sn13, 0., + sn12, sn22, sn23, 0., + sn13, sn23, sn33, 0., + 0., 0., 0., 0.); + GtsVector c; + gdouble le = 9.*params->boundary_weight* + gts_point_distance2 (GTS_POINT (v1), + GTS_POINT (v2)); + guint i, j; + + c[0] = - stn1; c[1] = - stn2; c[2] = - stn3; + if (nb > 0) + for (i = 0; i < 3; i++) { + for (j = 0; j < 3; j++) + H[i][j] = params->volume_weight*H[i][j] + le*Hb[i][j]; + c[i] = params->volume_weight*c[i] + le*cb[i]; + } + n = gts_matrix_quadratic_optimization (A, b, n, H, c); + gts_matrix_destroy (H); + } + +#ifdef DEBUG_VOPT + if (n != nold) { + fprintf (stderr, "--- volume and boundary optimization ---\n"); + gts_matrix_print (A, stderr); + gts_vector_print (b, stderr); + nold = n; + } +#endif + + if (n < 3) { + /* triangle shape optimization */ + gdouble nv = 0.0; + GtsMatrix * H; + GtsVector c = {0., 0., 0.}; + GSList * list, * i; + + list = gts_vertex_neighbors (v1, NULL, NULL); + list = gts_vertex_neighbors (v2, list, NULL); + + i = list; + while (i) { + GtsPoint * p1 = i->data; + if (p1 != GTS_POINT (v1) && p1 != GTS_POINT (v2)) { + nv += 1.0; + c[0] -= p1->x; + c[1] -= p1->y; + c[2] -= p1->z; + } + i = i->next; + } + g_slist_free (list); + + H = gts_matrix_new (nv, 0., 0., 0., + 0., nv, 0., 0., + 0., 0., nv, 0., + 0., 0., 0., 0.); + n = gts_matrix_quadratic_optimization (A, b, n, H, c); + gts_matrix_destroy (H); + } + +#ifdef DEBUG_VOPT + if (n != nold) { + fprintf (stderr, "--- triangle shape optimization ---\n"); + gts_matrix_print (A, stderr); + gts_vector_print (b, stderr); + nold = n; + } +#endif +#else /* Weighted average of volume, boundary and shape optimization */ + if (n < 3) { + /* volume optimization, boundary and shape optimization */ + GtsMatrix * H; + GtsVector c; + gdouble l2 = gts_point_distance2 (GTS_POINT (v1), + GTS_POINT (v2)); + gdouble wv = params->volume_weight/32.; + gdouble wb = params->boundary_weight/4.*l2; + gdouble ws = params->shape_weight*l2*l2; + + gdouble nv = 0.0; + GtsVector cs = {0., 0., 0.}; + GSList * list, * i; + + list = gts_vertex_neighbors (v1, NULL, NULL); + list = gts_vertex_neighbors (v2, list, NULL); + + i = list; + while (i) { + GtsPoint * p1 = i->data; + if (p1 != GTS_POINT (v1) && p1 != GTS_POINT (v2)) { + nv += 1.0; + cs[0] -= p1->x; + cs[1] -= p1->y; + cs[2] -= p1->z; + } + i = i->next; + } + g_slist_free (list); + + H = gts_matrix_new (wv*sn11 + wb*Hb[0][0] + ws*nv, + wv*sn12 + wb*Hb[0][1], + wv*sn13 + wb*Hb[0][2], + wv*sn12 + wb*Hb[1][0], + wv*sn22 + wb*Hb[1][1] + ws*nv, + wv*sn23 + wb*Hb[1][2], + wv*sn13 + wb*Hb[2][0], + wv*sn23 + wb*Hb[2][1], + wv*sn33 + wb*Hb[2][2] + ws*nv); + + c[0] = - wv*stn1 + wb*cb[0] + ws*cs[0]; + c[1] = - wv*stn2 + wb*cb[1] + ws*cs[1]; + c[2] = - wv*stn3 + wb*cb[2] + ws*cs[2]; + + n = gts_matrix_quadratic_optimization (A, b, n, H, c); + gts_matrix_destroy (H); + } + +#ifdef DEBUG_VOPT + if (n != nold) { + fprintf (stderr, "--- volume, boundary and shape optimization ---\n"); + gts_matrix_print (A, stderr); + gts_vector_print (b, stderr); + nold = n; + } +#endif +#endif /* Weighted average of volume, boundary and shape optimization */ + + g_assert (n == 3); + g_assert ((Ai = gts_matrix3_inverse (A))); + + v = gts_vertex_new (klass, + Ai[0][0]*b[0] + Ai[0][1]*b[1] + Ai[0][2]*b[2], + Ai[1][0]*b[0] + Ai[1][1]*b[1] + Ai[1][2]*b[2], + Ai[2][0]*b[0] + Ai[2][1]*b[1] + Ai[2][2]*b[2]); + + gts_matrix_destroy (A); + gts_matrix_destroy (Ai); + gts_matrix_destroy (Hb); + + return v; +} + +/** + * gts_volume_optimized_cost: + * @e: a #GtsEdge. + * @params: a #GtsVolumeOptimizedParams. + * + * Returns: the cost for the collapse of @e as minimized by the function + * gts_volume_optimized_vertex(). + */ +gdouble gts_volume_optimized_cost (GtsEdge * e, + GtsVolumeOptimizedParams * params) +{ + GtsVertex * v; + gdouble cost; + gdouble length2; + + g_return_val_if_fail (e != NULL, G_MAXDOUBLE); + g_return_val_if_fail (params != NULL, G_MAXDOUBLE); + + v = gts_volume_optimized_vertex (e, gts_vertex_class (), params); + + length2 = gts_point_distance2 (GTS_POINT (GTS_SEGMENT (e)->v1), + GTS_POINT (GTS_SEGMENT (e)->v2)); + cost = + params->volume_weight*edge_volume_cost (e, v) + + params->boundary_weight*length2*edge_boundary_cost (e, v) + + params->shape_weight*length2*length2*edge_shape_cost (e, v); + gts_object_destroy (GTS_OBJECT (v)); + + return cost; +} Index: trunk/src_3rd =================================================================== --- trunk/src_3rd (nonexistent) +++ trunk/src_3rd (revision 1065) Property changes on: trunk/src_3rd ___________________________________________________________________ Added: svn:externals ## -0,0 +1 ## +genht svn://repo.hu/genht/trunk/src Index: trunk/src_plugins/mincut/pcb-mincut/Makefile =================================================================== --- trunk/src_plugins/mincut/pcb-mincut/Makefile (revision 1064) +++ trunk/src_plugins/mincut/pcb-mincut/Makefile (revision 1065) @@ -1,6 +1,6 @@ CFLAGS = -g -Wall LDFLAGS = -lm -OBJS = main.o solve.o load.o graph.o ../3rd/genht/htsi.o +OBJS = main.o solve.o load.o graph.o ../../../src_3rd/genht/htsi.o all: main Index: trunk/util/Makefile.in =================================================================== --- trunk/util/Makefile.in (revision 1064) +++ trunk/util/Makefile.in (revision 1065) @@ -5,7 +5,7 @@ EXE=@sys/ext_exe@ CC=@cc/cc@ -HASHOBJ=../src/3rd/genht/hash.o ../src/3rd/genht/htsp.o +HASHOBJ=../src_3rd/genht/hash.o ../src_3rd/genht/htsp.o gsch2pcb-rnd$(EXE): gsch2pcb.o ../src/libpcb_fp.o ../src/portability.o ../src/ds.o ../src/paths.o $(HASHOBJ) $(CC) gsch2pcb.o ../src/libpcb_fp.o ../src/portability.o ../src/ds.o ../src/paths.o $(HASHOBJ) -o gsch2pcb-rnd$(EXE) $(LDFLAGS)