[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
gEDA-cvs: pcb.git: branch: master updated (90127e104c5b0b77b547541042e5b37530f41e54)
The branch, master has been updated
via 90127e104c5b0b77b547541042e5b37530f41e54 (commit)
from 26539af7e3199193c94eefadd6d902a4f803fd4f (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
=========
Summary
=========
gts/Makefile.am | 91 ++
gts/NOTES | 3 +
gts/bbtree.c | 1289 +++++++++++++++
gts/boolean.c | 2046 +++++++++++++++++++++++
gts/cdt.c | 1194 ++++++++++++++
gts/config.h.win32 | 29 +
gts/container.c | 493 ++++++
gts/curvature.c | 621 +++++++
gts/edge.c | 585 +++++++
gts/eheap.c | 461 ++++++
gts/face.c | 297 ++++
gts/fifo.c | 192 +++
gts/graph.c | 1776 ++++++++++++++++++++
gts/gts-config.in | 134 ++
gts/gts-private.h | 37 +
gts/gts.def | 425 +++++
gts/gts.h | 2573 +++++++++++++++++++++++++++++
gts/gts.m4 | 194 +++
gts/gtsconfig.h | 14 +
gts/heap.c | 258 +++
gts/hsurface.c | 405 +++++
gts/iso.c | 455 ++++++
gts/isotetra.c | 840 ++++++++++
gts/kdtree.c | 152 ++
gts/makefile.msc | 96 ++
gts/matrix.c | 725 +++++++++
gts/misc.c | 692 ++++++++
gts/named.c | 188 +++
gts/object.c | 345 ++++
gts/oocs.c | 387 +++++
gts/partition.c | 1219 ++++++++++++++
gts/pgraph.c | 584 +++++++
gts/point.c | 986 +++++++++++
gts/predicates.c | 2696 ++++++++++++++++++++++++++++++
gts/predicates.h | 41 +
gts/predicates_init | Bin 0 -> 12295 bytes
gts/predicates_init.c | 108 ++
gts/psurface.c | 471 ++++++
gts/refine.c | 418 +++++
gts/rounding.h | 85 +
gts/segment.c | 233 +++
gts/split.c | 1831 +++++++++++++++++++++
gts/stripe.c | 766 +++++++++
gts/surface.c | 2737 +++++++++++++++++++++++++++++++
gts/triangle.c | 1094 +++++++++++++
gts/tribox3.c | 192 +++
gts/vertex.c | 780 +++++++++
gts/vopt.c | 521 ++++++
src/Makefile.am | 2 +
src/toporouter.c | 4315 +++++++++++++++++++++++++++++++++++++++++++++++++
src/toporouter.h | 387 +++++
51 files changed, 36463 insertions(+), 0 deletions(-)
create mode 100644 gts/Makefile.am
create mode 100644 gts/NOTES
create mode 100644 gts/bbtree.c
create mode 100644 gts/boolean.c
create mode 100644 gts/cdt.c
create mode 100644 gts/config.h.win32
create mode 100644 gts/container.c
create mode 100644 gts/curvature.c
create mode 100644 gts/edge.c
create mode 100644 gts/eheap.c
create mode 100644 gts/face.c
create mode 100644 gts/fifo.c
create mode 100644 gts/graph.c
create mode 100644 gts/gts-config.in
create mode 100644 gts/gts-private.h
create mode 100644 gts/gts.def
create mode 100644 gts/gts.h
create mode 100644 gts/gts.m4
create mode 100644 gts/gtsconfig.h
create mode 100644 gts/heap.c
create mode 100644 gts/hsurface.c
create mode 100644 gts/iso.c
create mode 100644 gts/isotetra.c
create mode 100644 gts/kdtree.c
create mode 100644 gts/makefile.msc
create mode 100644 gts/matrix.c
create mode 100644 gts/misc.c
create mode 100644 gts/named.c
create mode 100644 gts/object.c
create mode 100644 gts/oocs.c
create mode 100644 gts/partition.c
create mode 100644 gts/pgraph.c
create mode 100644 gts/point.c
create mode 100644 gts/predicates.c
create mode 100644 gts/predicates.h
create mode 100755 gts/predicates_init
create mode 100644 gts/predicates_init.c
create mode 100644 gts/psurface.c
create mode 100644 gts/refine.c
create mode 100644 gts/rounding.h
create mode 100644 gts/segment.c
create mode 100644 gts/split.c
create mode 100644 gts/stripe.c
create mode 100644 gts/surface.c
create mode 100644 gts/triangle.c
create mode 100644 gts/tribox3.c
create mode 100644 gts/vertex.c
create mode 100644 gts/vopt.c
create mode 100644 src/toporouter.c
create mode 100644 src/toporouter.h
=================
Commit Messages
=================
commit 90127e104c5b0b77b547541042e5b37530f41e54
Author: Anthony Blake <tonyb33@xxxxxxxxx>
Commit: Anthony Blake <tonyb33@xxxxxxxxx>
Added topological autorouter
Topological autorouter (not yet exporting geometry)
GTS with small but crucial bug fixes
:000000 100644 0000000... 82935ab... A gts/Makefile.am
:000000 100644 0000000... c697949... A gts/NOTES
:000000 100644 0000000... cec93e4... A gts/bbtree.c
:000000 100644 0000000... faa051e... A gts/boolean.c
:000000 100644 0000000... 6c17249... A gts/cdt.c
:000000 100644 0000000... 085ddb5... A gts/config.h.win32
:000000 100644 0000000... e1dc0fa... A gts/container.c
:000000 100644 0000000... 70f6af2... A gts/curvature.c
:000000 100644 0000000... 47754de... A gts/edge.c
:000000 100644 0000000... 29f462d... A gts/eheap.c
:000000 100644 0000000... f6009f1... A gts/face.c
:000000 100644 0000000... 8b3d2b6... A gts/fifo.c
:000000 100644 0000000... 1566c95... A gts/graph.c
:000000 100644 0000000... 0eed417... A gts/gts-config.in
:000000 100644 0000000... 59246d1... A gts/gts-private.h
:000000 100644 0000000... 4ae8ed9... A gts/gts.def
:000000 100644 0000000... 70d0f07... A gts/gts.h
:000000 100644 0000000... a04da49... A gts/gts.m4
:000000 100644 0000000... c2d32e6... A gts/gtsconfig.h
:000000 100644 0000000... 4a37e58... A gts/heap.c
:000000 100644 0000000... 80ac66a... A gts/hsurface.c
:000000 100644 0000000... 5995a19... A gts/iso.c
:000000 100644 0000000... 35fe2ba... A gts/isotetra.c
:000000 100644 0000000... ec5d422... A gts/kdtree.c
:000000 100644 0000000... 8a151ed... A gts/makefile.msc
:000000 100644 0000000... 7ada15d... A gts/matrix.c
:000000 100644 0000000... 31142a8... A gts/misc.c
:000000 100644 0000000... 379f9f6... A gts/named.c
:000000 100644 0000000... 5970e50... A gts/object.c
:000000 100644 0000000... f0d76bf... A gts/oocs.c
:000000 100644 0000000... 16dc0e1... A gts/partition.c
:000000 100644 0000000... 2c13c1e... A gts/pgraph.c
:000000 100644 0000000... 42fce69... A gts/point.c
:000000 100644 0000000... 3c850a8... A gts/predicates.c
:000000 100644 0000000... 8b026ed... A gts/predicates.h
:000000 100755 0000000... 4aeeeb2... A gts/predicates_init
:000000 100644 0000000... 5d8cfb7... A gts/predicates_init.c
:000000 100644 0000000... 6db3ae2... A gts/psurface.c
:000000 100644 0000000... 293eb11... A gts/refine.c
:000000 100644 0000000... 053b32f... A gts/rounding.h
:000000 100644 0000000... 58a0540... A gts/segment.c
:000000 100644 0000000... c63516c... A gts/split.c
:000000 100644 0000000... 7e98a9c... A gts/stripe.c
:000000 100644 0000000... f5adfaa... A gts/surface.c
:000000 100644 0000000... 5213a51... A gts/triangle.c
:000000 100644 0000000... c0ea778... A gts/tribox3.c
:000000 100644 0000000... d312869... A gts/vertex.c
:000000 100644 0000000... d772af9... A gts/vopt.c
:100644 100644 270613c... 27ad499... M src/Makefile.am
:000000 100644 0000000... 7ce571c... A src/toporouter.c
:000000 100644 0000000... d65e737... A src/toporouter.h
=========
Changes
=========
commit 90127e104c5b0b77b547541042e5b37530f41e54
Author: Anthony Blake <tonyb33@xxxxxxxxx>
Commit: Anthony Blake <tonyb33@xxxxxxxxx>
Added topological autorouter
Topological autorouter (not yet exporting geometry)
GTS with small but crucial bug fixes
diff --git a/gts/Makefile.am b/gts/Makefile.am
new file mode 100644
index 0000000..82935ab
--- /dev/null
+++ b/gts/Makefile.am
@@ -0,0 +1,91 @@
+## Process this file with automake to produce Makefile.in
+
+INCLUDES = -I$(top_srcdir) -I$(includedir) -DG_LOG_DOMAIN=\"Gts\"
+
+bin_SCRIPTS=gts-config
+
+BUILT_SOURCES= \
+ gts-config \
+ predicates_init.h
+
+gts-config: gts-config.in
+
+lib_LTLIBRARIES = libgts.la
+
+libgts_la_LDFLAGS = -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE)\
+ -release $(LT_RELEASE) -export-dynamic
+
+libgts_la_DEPENDENCIES = \
+ predicates_init.h
+
+libgts_la_SOURCES = \
+ predicates_init.h \
+ object.c \
+ point.c \
+ vertex.c \
+ segment.c \
+ edge.c \
+ triangle.c \
+ face.c \
+ kdtree.c \
+ bbtree.c \
+ misc.c \
+ gts.h \
+ gts-private.h \
+ predicates.c \
+ predicates.h \
+ rounding.h \
+ heap.c \
+ eheap.c \
+ fifo.c \
+ matrix.c \
+ surface.c \
+ stripe.c \
+ vopt.c \
+ refine.c \
+ iso.c \
+ isotetra.c \
+ split.c \
+ psurface.c \
+ hsurface.c \
+ cdt.c \
+ boolean.c \
+ named.c \
+ oocs.c \
+ container.c \
+ graph.c \
+ pgraph.c \
+ partition.c \
+ curvature.c \
+ tribox3.c
+
+include_HEADERS = \
+ gts.h gtsconfig.h
+
+predicates.o: predicates.c predicates_init.h predicates.h
+ $(COMPILE) -c $(srcdir)/predicates.c
+
+predicates_init: predicates_init.c rounding.h
+ $(COMPILE) $(srcdir)/predicates_init.c -o $(srcdir)/predicates_init
+
+predicates_init.h: predicates_init
+ ./predicates_init > $(srcdir)/predicates_init.h
+
+CLEANFILES = $(BUILT_SOURCES)
+
+gts.def: libgts.la .libs/libgts.a
+ nm -g .libs/libgts.a | awk 'BEGIN{print "EXPORTS"}{if ($$2 == "T" || $$2 == "D" || $$2 == "B") print " " $$3}' > gts.def
+
+EXTRA_DIST = \
+ predicates_init.c \
+ gts.m4 \
+ makefile.msc \
+ config.h.win32 \
+ gts.def \
+ NOTES
+
+dist-hook: gts.def
+ cd $(distdir); rm -f $(BUILT_SOURCES)
+
+m4datadir = $(datadir)/aclocal
+m4data_DATA = gts.m4
diff --git a/gts/NOTES b/gts/NOTES
new file mode 100644
index 0000000..c697949
--- /dev/null
+++ b/gts/NOTES
@@ -0,0 +1,3 @@
+- hsurface may create surfaces with duplicates edges and triangles (because of
+ collapses of "empty triangles").
+- psurface however will not.
diff --git a/gts/bbtree.c b/gts/bbtree.c
new file mode 100644
index 0000000..cec93e4
--- /dev/null
+++ b/gts/bbtree.c
@@ -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 <math.h>
+#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.;
+}
diff --git a/gts/boolean.c b/gts/boolean.c
new file mode 100644
index 0000000..faa051e
--- /dev/null
+++ b/gts/boolean.c
@@ -0,0 +1,2046 @@
+/* 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 <math.h>
+#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;
+}
+
+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);
+}
+
+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;
+}
diff --git a/gts/cdt.c b/gts/cdt.c
new file mode 100644
index 0000000..6c17249
--- /dev/null
+++ b/gts/cdt.c
@@ -0,0 +1,1194 @@
+/* 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+
+#include <math.h>
+#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 */
+
+# 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 */
+
+typedef struct _SFindClosest SFindClosest;
+
+struct _SFindClosest {
+ gdouble dmin;
+ GtsFace *closest;
+ GtsPoint * p;
+ gint stop;
+};
+
+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 */
+
+/* Due to an unkown reason g_hash_table_foreach does not allow to stop
+ * the loop, hence the redefinition. I hope they don't change
+ * the GHashTable, GHashNode structures ... */
+typedef struct _GHashNode GHashNode;
+
+struct _GHashNode
+{
+ gpointer key;
+ gpointer value;
+ GHashNode *next;
+};
+
+struct _GHashTable
+{
+ gint size;
+ gint nnodes;
+ guint frozen;
+ GHashNode **nodes;
+ GHashFunc hash_func;
+ GCompareFunc key_compare_func;
+};
+
+/* select the face closest to @p among n^1/3 randomly picked faces
+ * of @surface */
+static GtsFace * closest_face (GtsSurface * s, GtsPoint * p)
+{
+ guint i, n, nt, ns;
+ gdouble dmin = G_MAXDOUBLE;
+ GtsFace * closest = NULL;
+ GHashNode * node;
+ GHashTable * hash_table = s->faces;
+
+ nt = g_hash_table_size (hash_table);
+ if (!nt)
+ return NULL;
+
+ ns = exp(log((gdouble) nt)/3.);
+ for (i = 0, n = 0; i < hash_table->size && n < ns; i++)
+ for (node = hash_table->nodes[i]; node && n < ns; node = node->next) {
+ GtsFace * f = node->key;
+
+ if (gts_triangle_orientation (GTS_TRIANGLE (f)) > 0.) {
+ GtsPoint * p1 = GTS_POINT (GTS_SEGMENT (GTS_TRIANGLE (f)->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 = f;
+ }
+ n++;
+ }
+ }
+ return 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;
+}
diff --git a/gts/config.h.win32 b/gts/config.h.win32
new file mode 100644
index 0000000..085ddb5
--- /dev/null
+++ b/gts/config.h.win32
@@ -0,0 +1,29 @@
+/* config.h.win32: Handcrafted for Microsoft C */
+
+/* Define if you need to in order for stat and other things to work. */
+/* #undef _POSIX_SOURCE */
+
+/* Define if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+#define GTS_MAJOR_VERSION 0
+#define GTS_MINOR_VERSION 6
+#define GTS_MICRO_VERSION 0
+#define GTS_INTERFACE_AGE 0
+#define GTS_BINARY_AGE 0
+
+/* Define if you have the <floatingpoint.h> header file. */
+/* #undef HAVE_FLOATINGPOINT_H */
+
+/* Define if you have the <fpu_control.h> header file. */
+/* #undef HAVE_FPU_CONTROL_H */
+
+/* Define if this is Win32, possibly using the Cygwin emulation layer. */
+#define WIN32 1
+
+/* Define if this is Win32 without Cygwin. */
+#define NATIVE_WIN32 1
+
+#ifndef M_PI
+# define M_PI 3.14159265359
+#endif /* not M_PI */
diff --git a/gts/container.c b/gts/container.c
new file mode 100644
index 0000000..e1dc0fa
--- /dev/null
+++ b/gts/container.c
@@ -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;
+}
diff --git a/gts/curvature.c b/gts/curvature.c
new file mode 100644
index 0000000..70f6af2
--- /dev/null
+++ b/gts/curvature.c
@@ -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 <math.h>
+#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);
+}
diff --git a/gts/edge.c b/gts/edge.c
new file mode 100644
index 0000000..47754de
--- /dev/null
+++ b/gts/edge.c
@@ -0,0 +1,585 @@
+/* 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(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;
+ GtsVertex * v1, * v2;
+
+ g_return_val_if_fail (e != NULL, FALSE);
+
+ v1 = GTS_SEGMENT (e)->v1;
+ v2 = GTS_SEGMENT (e)->v2;
+ 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);
+}
diff --git a/gts/eheap.c b/gts/eheap.c
new file mode 100644
index 0000000..29f462d
--- /dev/null
+++ b/gts/eheap.c
@@ -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 <stdlib.h>
+#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;
+}
diff --git a/gts/face.c b/gts/face.c
new file mode 100644
index 0000000..f6009f1
--- /dev/null
+++ b/gts/face.c
@@ -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));
+}
diff --git a/gts/fifo.c b/gts/fifo.c
new file mode 100644
index 0000000..8b3d2b6
--- /dev/null
+++ b/gts/fifo.c
@@ -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);
+}
diff --git a/gts/graph.c b/gts/graph.c
new file mode 100644
index 0000000..1566c95
--- /dev/null
+++ b/gts/graph.c
@@ -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 <math.h>
+#include <stdlib.h>
+#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;
+}
+
diff --git a/gts/gts-config.in b/gts/gts-config.in
new file mode 100644
index 0000000..0eed417
--- /dev/null
+++ b/gts/gts-config.in
@@ -0,0 +1,134 @@
+#!/bin/sh
+
+glib_libs="@glib_libs@"
+glib_cflags="@glib_cflags@"
+glib_thread_libs="@glib_thread_libs@"
+glib_thread_cflags="@glib_thread_cflags@"
+glib_module_libs="@glib_module_libs@"
+glib_module_cflags="@glib_module_cflags@"
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+exec_prefix_set=no
+
+usage()
+{
+ cat <<EOF
+Usage: gts-config [OPTIONS] [LIBRARIES]
+Options:
+ [--prefix[=DIR]]
+ [--exec-prefix[=DIR]]
+ [--version]
+ [--libs]
+ [--cflags]
+ [--check]
+Libraries:
+ gts
+ gmodule
+ gthread
+EOF
+ exit $1
+}
+
+uniquify()
+{
+ echo $1 | awk '{
+ for (i = 1; i <= NF; i++) {
+ unique = 1;
+ for (j = i + 1; j <= NF && unique; j++)
+ if ($i == $j)
+ unique = 0;
+ if (unique)
+ printf ("%s ", $i);
+ }
+ }'
+}
+
+if test $# -eq 0; then
+ usage 1 1>&2
+fi
+
+while test $# -gt 0; do
+ case "$1" in
+ -*=*) optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+ *) optarg= ;;
+ esac
+
+ case $1 in
+ --prefix=*)
+ prefix=$optarg
+ if test $exec_prefix_set = no ; then
+ exec_prefix=$optarg
+ fi
+ ;;
+ --prefix)
+ echo_prefix=yes
+ ;;
+ --exec-prefix=*)
+ exec_prefix=$optarg
+ exec_prefix_set=yes
+ ;;
+ --exec-prefix)
+ echo_exec_prefix=yes
+ ;;
+ --version)
+ echo @GTS_VERSION@
+ exit 0
+ ;;
+ --cflags)
+ echo_cflags=yes
+ ;;
+ --libs)
+ echo_libs=yes
+ ;;
+ --check)
+ echo_check=yes
+ ;;
+ gts)
+ lib_gts=yes
+ ;;
+ gmodule)
+ lib_gmodule=yes
+ ;;
+ gthread)
+ lib_gthread=yes
+ ;;
+ *)
+ usage 1 1>&2
+ ;;
+ esac
+ shift
+done
+
+if test "$echo_prefix" = "yes"; then
+ echo $prefix
+fi
+if test "$echo_exec_prefix" = "yes"; then
+ echo $exec_prefix
+fi
+if test "$echo_cflags" = "yes"; then
+ if test "$lib_gmodule" = "yes"; then
+ glib_cflags="$glib_cflags $glib_module_cflags"
+ fi
+ if test "$lib_gthread" = "yes"; then
+ glib_cflags="$glib_cflags $glib_thread_cflags"
+ fi
+ glib_cflags="$glib_cflags -I${prefix}/include"
+ glib_cflags=`uniquify "$glib_cflags"`
+ if test "$echo_check" = "yes"; then
+ echo -DGTS_CHECK_CASTS $glib_cflags
+ else
+ echo $glib_cflags
+ fi
+fi
+if test "$echo_libs" = "yes"; then
+ if test "$lib_gmodule" = "yes"; then
+ glib_libs="$glib_libs $glib_module_libs"
+ fi
+ if test "$lib_gthread" = "yes"; then
+ glib_libs="$glib_libs $glib_thread_libs"
+ fi
+ glib_libs="-L${exec_prefix}/lib -lgts $glib_libs -lm"
+ glib_libs=`uniquify "$glib_libs"`
+ echo $glib_libs
+fi
diff --git a/gts/gts-private.h b/gts/gts-private.h
new file mode 100644
index 0000000..59246d1
--- /dev/null
+++ b/gts/gts-private.h
@@ -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__ */
diff --git a/gts/gts.def b/gts/gts.def
new file mode 100644
index 0000000..4ae8ed9
--- /dev/null
+++ b/gts/gts.def
@@ -0,0 +1,425 @@
+EXPORTS
+ gts_finalize
+ gts_object_attributes
+ gts_object_check_cast
+ gts_object_class
+ gts_object_class_check_cast
+ gts_object_class_from_name
+ gts_object_class_new
+ gts_object_clone
+ gts_object_destroy
+ gts_object_init
+ gts_object_new
+ gts_object_reset_reserved
+ gts_point_class
+ gts_point_distance
+ gts_point_distance2
+ gts_point_in_circle
+ gts_point_in_sphere
+ gts_point_in_triangle_circle
+ gts_point_is_in_triangle
+ gts_point_is_inside_surface
+ gts_point_new
+ gts_point_orientation
+ gts_point_orientation_3d
+ gts_point_orientation_3d_sos
+ gts_point_orientation_sos
+ gts_point_segment_closest
+ gts_point_segment_distance
+ gts_point_segment_distance2
+ gts_point_set
+ gts_point_transform
+ gts_point_triangle_closest
+ gts_point_triangle_distance
+ gts_point_triangle_distance2
+ gts_segment_triangle_intersection
+ gts_allow_floating_vertices
+ gts_color_vertex_class
+ gts_vertex_class
+ gts_vertex_faces
+ gts_vertex_fan_oriented
+ gts_vertex_is_boundary
+ gts_vertex_is_contact
+ gts_vertex_is_unattached
+ gts_vertex_neighbors
+ gts_vertex_new
+ gts_vertex_normal_class
+ gts_vertex_replace
+ gts_vertex_triangles
+ gts_vertices_are_connected
+ gts_vertices_from_segments
+ gts_vertices_merge
+ gts_segment_class
+ gts_segment_is_duplicate
+ gts_segment_is_ok
+ gts_segment_midvertex
+ gts_segment_new
+ gts_segments_are_intersecting
+ gts_segments_from_vertices
+ gts_allow_floating_edges
+ gts_edge_belongs_to_tetrahedron
+ gts_edge_class
+ gts_edge_face_number
+ gts_edge_has_any_parent_surface
+ gts_edge_has_parent_surface
+ gts_edge_is_boundary
+ gts_edge_is_contact
+ gts_edge_is_duplicate
+ gts_edge_manifold_faces
+ gts_edge_new
+ gts_edge_replace
+ gts_edge_swap
+ gts_edges_from_vertices
+ gts_edges_merge
+ gts_triangle_area
+ gts_triangle_circumcircle_center
+ gts_triangle_class
+ gts_triangle_edge_opposite
+ gts_triangle_enclosing
+ gts_triangle_interpolate_height
+ gts_triangle_is_duplicate
+ gts_triangle_is_ok
+ gts_triangle_is_stabbed
+ gts_triangle_neighbor_number
+ gts_triangle_neighbors
+ gts_triangle_new
+ gts_triangle_normal
+ gts_triangle_orientation
+ gts_triangle_perimeter
+ gts_triangle_quality
+ gts_triangle_revert
+ gts_triangle_set
+ gts_triangle_use_edges
+ gts_triangle_vertex_opposite
+ gts_triangle_vertices
+ gts_triangle_vertices_edges
+ gts_triangles_angle
+ gts_triangles_are_compatible
+ gts_triangles_are_folded
+ gts_triangles_common_edge
+ gts_triangles_from_edges
+ gts_allow_floating_faces
+ gts_face_class
+ gts_face_foreach_neighbor
+ gts_face_has_parent_surface
+ gts_face_is_compatible
+ gts_face_neighbor_number
+ gts_face_neighbors
+ gts_face_new
+ gts_faces_from_edges
+ gts_kdtree_new
+ gts_kdtree_range
+ gts_bb_tree_destroy
+ gts_bb_tree_draw
+ gts_bb_tree_is_overlapping
+ gts_bb_tree_new
+ gts_bb_tree_overlap
+ gts_bb_tree_point_closest
+ gts_bb_tree_point_closest_bboxes
+ gts_bb_tree_point_distance
+ gts_bb_tree_segment_distance
+ gts_bb_tree_stabbed
+ gts_bb_tree_surface
+ gts_bb_tree_surface_boundary_distance
+ gts_bb_tree_surface_distance
+ gts_bb_tree_traverse_overlapping
+ gts_bb_tree_triangle_distance
+ gts_bbox_bboxes
+ gts_bbox_class
+ gts_bbox_diagonal2
+ gts_bbox_draw
+ gts_bbox_is_stabbed
+ gts_bbox_new
+ gts_bbox_overlaps_segment
+ gts_bbox_overlaps_triangle
+ gts_bbox_point_distance2
+ gts_bbox_points
+ gts_bbox_segment
+ gts_bbox_set
+ gts_bbox_surface
+ gts_bbox_triangle
+ gts_bboxes_are_overlapping
+ gts_file_assign_next
+ gts_file_assign_start
+ gts_file_assign_variables
+ gts_file_destroy
+ gts_file_error
+ gts_file_first_token_after
+ gts_file_getc
+ gts_file_getc_scope
+ gts_file_new
+ gts_file_new_from_string
+ gts_file_next_token
+ gts_file_read
+ gts_file_variable_error
+ gts_file_verror
+ incircle
+ insphere
+ orient2d
+ orient3d
+ gts_heap_destroy
+ gts_heap_foreach
+ gts_heap_freeze
+ gts_heap_insert
+ gts_heap_new
+ gts_heap_remove_top
+ gts_heap_size
+ gts_heap_thaw
+ gts_heap_top
+ gts_eheap_decrease_key
+ gts_eheap_destroy
+ gts_eheap_foreach
+ gts_eheap_freeze
+ gts_eheap_insert
+ gts_eheap_insert_with_key
+ gts_eheap_key
+ gts_eheap_new
+ gts_eheap_randomized
+ gts_eheap_remove
+ gts_eheap_remove_top
+ gts_eheap_size
+ gts_eheap_thaw
+ gts_eheap_top
+ gts_eheap_update
+ gts_fifo_destroy
+ gts_fifo_foreach
+ gts_fifo_is_empty
+ gts_fifo_new
+ gts_fifo_pop
+ gts_fifo_push
+ gts_fifo_reverse
+ gts_fifo_size
+ gts_fifo_top
+ gts_fifo_write
+ gts_matrix3_inverse
+ gts_matrix_assign
+ gts_matrix_compatible_row
+ gts_matrix_destroy
+ gts_matrix_determinant
+ gts_matrix_identity
+ gts_matrix_inverse
+ gts_matrix_new
+ gts_matrix_print
+ gts_matrix_product
+ gts_matrix_projection
+ gts_matrix_quadratic_optimization
+ gts_matrix_rotate
+ gts_matrix_scale
+ gts_matrix_translate
+ gts_matrix_transpose
+ gts_matrix_zero
+ gts_vector4_print
+ gts_vector_print
+ gts_coarsen_stop_cost
+ gts_coarsen_stop_number
+ gts_edge_collapse_creates_fold
+ gts_edge_collapse_is_valid
+ gts_range_add_value
+ gts_range_init
+ gts_range_print
+ gts_range_reset
+ gts_range_update
+ gts_surface_add_face
+ gts_surface_area
+ gts_surface_boundary
+ gts_surface_center_of_area
+ gts_surface_center_of_mass
+ gts_surface_class
+ gts_surface_coarsen
+ gts_surface_copy
+ gts_surface_distance
+ gts_surface_edge_number
+ gts_surface_face_number
+ gts_surface_foreach_edge
+ gts_surface_foreach_face
+ gts_surface_foreach_face_remove
+ gts_surface_foreach_vertex
+ gts_surface_generate_sphere
+ gts_surface_is_closed
+ gts_surface_is_manifold
+ gts_surface_is_orientable
+ gts_surface_merge
+ gts_surface_new
+ gts_surface_print_stats
+ gts_surface_quality_stats
+ gts_surface_read
+ gts_surface_refine
+ gts_surface_remove_face
+ gts_surface_split
+ gts_surface_stats
+ gts_surface_tessellate
+ gts_surface_traverse_destroy
+ gts_surface_traverse_new
+ gts_surface_traverse_next
+ gts_surface_vertex_number
+ gts_surface_volume
+ gts_surface_write
+ gts_surface_write_oogl
+ gts_surface_write_oogl_boundary
+ gts_surface_write_vtk
+ gts_surface_strip
+ gts_volume_optimized_cost
+ gts_volume_optimized_vertex
+ gts_delaunay_conform
+ gts_delaunay_refine
+ gts_edge_is_encroached
+ gts_vertex_encroaches_edge
+ gts_grid_plane_destroy
+ gts_grid_plane_new
+ gts_iso_slice_destroy
+ gts_iso_slice_fill
+ gts_iso_slice_fill_cartesian
+ gts_iso_slice_new
+ gts_isosurface_cartesian
+ gts_isosurface_slice
+ gts_isosurface_tetra
+ gts_isosurface_tetra_bcl
+ gts_isosurface_tetra_bounded
+ gts_hsplit_force_expand
+ gts_psurface_close
+ gts_psurface_open
+ gts_psurface_read_vertex
+ gts_psurface_write
+ gts_split_class
+ gts_split_collapse
+ gts_split_expand
+ gts_split_height
+ gts_split_new
+ gts_split_traverse
+ gts_psurface_add_vertex
+ gts_psurface_class
+ gts_psurface_foreach_vertex
+ gts_psurface_get_vertex_number
+ gts_psurface_max_vertex_number
+ gts_psurface_min_vertex_number
+ gts_psurface_new
+ gts_psurface_remove_vertex
+ gts_psurface_set_vertex_number
+ gts_hsplit_class
+ gts_hsplit_collapse
+ gts_hsplit_expand
+ gts_hsplit_new
+ gts_hsurface_class
+ gts_hsurface_foreach
+ gts_hsurface_height
+ gts_hsurface_new
+ gts_hsurface_traverse
+ gts_constraint_class
+ gts_delaunay_add_constraint
+ gts_delaunay_add_vertex
+ gts_delaunay_add_vertex_to_face
+ gts_delaunay_check
+ gts_delaunay_remove_hull
+ gts_delaunay_remove_vertex
+ gts_list_face_class
+ gts_point_locate
+ gts_surface_foreach_intersecting_face
+ gts_surface_inter_boolean
+ gts_surface_inter_check
+ gts_surface_inter_class
+ gts_surface_inter_new
+ gts_surface_intersection
+ gts_surface_is_self_intersecting
+ gts_nedge_class
+ gts_nface_class
+ gts_nvertex_class
+ gts_cluster_add
+ gts_cluster_class
+ gts_cluster_grid_add_triangle
+ gts_cluster_grid_class
+ gts_cluster_grid_new
+ gts_cluster_grid_update
+ gts_cluster_new
+ gts_cluster_update
+ gts_containee_class
+ gts_containee_is_contained
+ gts_containee_new
+ gts_containee_replace
+ gts_container_add
+ gts_container_class
+ gts_container_foreach
+ gts_container_new
+ gts_container_remove
+ gts_container_size
+ gts_hash_container_class
+ gts_slist_containee_class
+ gts_slist_container_class
+ gts_allow_floating_gnodes
+ gts_fnode_class
+ gts_fnode_new
+ gts_gedge_class
+ gts_gedge_new
+ gts_gedge_weight
+ gts_gnode_class
+ gts_gnode_degree
+ gts_gnode_foreach_edge
+ gts_gnode_foreach_neighbor
+ gts_gnode_move_cost
+ gts_gnode_new
+ gts_gnode_weight
+ gts_graph_class
+ gts_graph_distance_sum
+ gts_graph_edges_cut
+ gts_graph_edges_cut_weight
+ gts_graph_farthest
+ gts_graph_foreach_edge
+ gts_graph_new
+ gts_graph_print_stats
+ gts_graph_read
+ gts_graph_read_jostle
+ gts_graph_traverse_destroy
+ gts_graph_traverse_new
+ gts_graph_traverse_next
+ gts_graph_traverse_what_next
+ gts_graph_weight
+ gts_graph_write
+ gts_graph_write_dot
+ gts_ngnode_class
+ gts_ngnode_new
+ gts_pgedge_class
+ gts_pgedge_new
+ gts_pnode_class
+ gts_pnode_new
+ gts_segments_graph_new
+ gts_surface_graph_new
+ gts_surface_graph_surface
+ gts_wgedge_class
+ gts_wgedge_new
+ gts_wgnode_class
+ gts_wgnode_new
+ gts_wgraph_class
+ gts_wgraph_weight_max
+ gts_gnode_split_class
+ gts_gnode_split_collapse
+ gts_gnode_split_expand
+ gts_gnode_split_new
+ gts_pgraph_add_node
+ gts_pgraph_class
+ gts_pgraph_down
+ gts_pgraph_get_node_number
+ gts_pgraph_max_node_number
+ gts_pgraph_min_node_number
+ gts_pgraph_new
+ gts_pgraph_remove_node
+ gts_pgraph_set_node_number
+ gts_graph_bfgg_bisection
+ gts_graph_bisection_bkl_refine
+ gts_graph_bisection_check
+ gts_graph_bisection_destroy
+ gts_graph_bisection_kl_refine
+ gts_graph_bisection_new
+ gts_graph_bubble_partition
+ gts_graph_ggg_bisection
+ gts_graph_partition_balance
+ gts_graph_partition_clone
+ gts_graph_partition_destroy
+ gts_graph_partition_edges_cut
+ gts_graph_partition_edges_cut_weight
+ gts_graph_partition_print_stats
+ gts_graph_recursive_bisection
+ gts_vertex_gaussian_curvature
+ gts_vertex_mean_curvature_normal
+ gts_vertex_principal_curvatures
+ gts_vertex_principal_directions
+ planeBoxOverlap
+ triBoxOverlap
diff --git a/gts/gts.h b/gts/gts.h
new file mode 100644
index 0000000..70d0f07
--- /dev/null
+++ b/gts/gts.h
@@ -0,0 +1,2573 @@
+/* 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 <stdio.h>
+#include <glib.h>
+#ifndef NATIVE_WIN32
+# include <gtsconfig.h>
+#endif
+#ifdef GTS_COMPILATION
+# include "config.h"
+#endif /* not GTS_COMPILATION */
+
+#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@xxxxxxxxxxxxxxxxxxxxx"
+
+/* 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);
+
+/* Boolean 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__ */
diff --git a/gts/gts.m4 b/gts/gts.m4
new file mode 100644
index 0000000..a04da49
--- /dev/null
+++ b/gts/gts.m4
@@ -0,0 +1,194 @@
+# Configure paths for GTS
+# Stéphane Popinet 2001-10-4
+# adapted from
+# Configure paths for GLIB
+# Owen Taylor 97-11-3
+
+dnl AM_PATH_GTS([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ]]])
+dnl Test for GTS, and define GTS_CFLAGS and GTS_LIBS
+dnl
+AC_DEFUN([AM_PATH_GTS],
+[dnl
+dnl Get the cflags and libraries from the gts-config script
+dnl
+AC_ARG_WITH(gts-prefix,[ --with-gts-prefix=PFX Prefix where GTS is installed (optional)],
+ gts_config_prefix="$withval", gts_config_prefix="")
+AC_ARG_WITH(gts-exec-prefix,[ --with-gts-exec-prefix=PFX Exec prefix where GTS is installed (optional)],
+ gts_config_exec_prefix="$withval", gts_config_exec_prefix="")
+AC_ARG_ENABLE(gtstest, [ --disable-gtstest Do not try to compile and run a test GTS program],
+ , enable_gtstest=yes)
+
+ if test x$gts_config_exec_prefix != x ; then
+ gts_config_args="$gts_config_args --exec-prefix=$gts_config_exec_prefix"
+ if test x${GTS_CONFIG+set} != xset ; then
+ GTS_CONFIG=$gts_config_exec_prefix/bin/gts-config
+ fi
+ fi
+ if test x$gts_config_prefix != x ; then
+ gts_config_args="$gts_config_args --prefix=$gts_config_prefix"
+ if test x${GTS_CONFIG+set} != xset ; then
+ GTS_CONFIG=$gts_config_prefix/bin/gts-config
+ fi
+ fi
+
+ for module in . $4
+ do
+ case "$module" in
+ gmodule)
+ gts_config_args="$gts_config_args gmodule"
+ ;;
+ gthread)
+ gts_config_args="$gts_config_args gthread"
+ ;;
+ esac
+ done
+
+ AC_PATH_PROG(GTS_CONFIG, gts-config, no)
+ min_gts_version=ifelse([$1], ,0.99.7,$1)
+ AC_MSG_CHECKING(for GTS - version >= $min_gts_version)
+ no_gts=""
+ if test "$GTS_CONFIG" = "no" ; then
+ no_gts=yes
+ else
+ GTS_CFLAGS=`$GTS_CONFIG $gts_config_args --cflags`
+ GTS_LIBS=`$GTS_CONFIG $gts_config_args --libs`
+ gts_config_major_version=`$GTS_CONFIG $gts_config_args --version | \
+ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
+ gts_config_minor_version=`$GTS_CONFIG $gts_config_args --version | \
+ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
+ gts_config_micro_version=`$GTS_CONFIG $gts_config_args --version | \
+ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
+ if test "x$enable_gtstest" = "xyes" ; then
+ ac_save_CFLAGS="$CFLAGS"
+ ac_save_LIBS="$LIBS"
+ CFLAGS="$CFLAGS $GTS_CFLAGS"
+ LIBS="$GTS_LIBS $LIBS"
+dnl
+dnl Now check if the installed GTS is sufficiently new. (Also sanity
+dnl checks the results of gts-config to some extent
+dnl
+ rm -f conf.gtstest
+ AC_TRY_RUN([
+#include <gts.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main ()
+{
+ int major, minor, micro;
+ char *tmp_version;
+
+ system ("touch conf.gtstest");
+
+ /* HP/UX 9 (%@#!) writes to sscanf strings */
+ tmp_version = g_strdup("$min_gts_version");
+ if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) {
+ printf("%s, bad version string\n", "$min_gts_version");
+ exit(1);
+ }
+
+ if ((gts_major_version != $gts_config_major_version) ||
+ (gts_minor_version != $gts_config_minor_version) ||
+ (gts_micro_version != $gts_config_micro_version))
+ {
+ printf("\n*** 'gts-config --version' returned %d.%d.%d, but GTS (%d.%d.%d)\n",
+ $gts_config_major_version, $gts_config_minor_version, $gts_config_micro_version,
+ gts_major_version, gts_minor_version, gts_micro_version);
+ printf ("*** was found! If gts-config was correct, then it is best\n");
+ printf ("*** to remove the old version of GTS. You may also be able to fix the error\n");
+ printf("*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing\n");
+ printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n");
+ printf("*** required on your system.\n");
+ printf("*** If gts-config was wrong, set the environment variable GTS_CONFIG\n");
+ printf("*** to point to the correct copy of gts-config, and remove the file config.cache\n");
+ printf("*** before re-running configure\n");
+ }
+ else if ((gts_major_version != GTS_MAJOR_VERSION) ||
+ (gts_minor_version != GTS_MINOR_VERSION) ||
+ (gts_micro_version != GTS_MICRO_VERSION))
+ {
+ printf("*** GTS header files (version %d.%d.%d) do not match\n",
+ GTS_MAJOR_VERSION, GTS_MINOR_VERSION, GTS_MICRO_VERSION);
+ printf("*** library (version %d.%d.%d)\n",
+ gts_major_version, gts_minor_version, gts_micro_version);
+ }
+ else
+ {
+ if ((gts_major_version > major) ||
+ ((gts_major_version == major) && (gts_minor_version > minor)) ||
+ ((gts_major_version == major) && (gts_minor_version == minor) && (gts_micro_version >= micro)))
+ {
+ return 0;
+ }
+ else
+ {
+ printf("\n*** An old version of GTS (%d.%d.%d) was found.\n",
+ gts_major_version, gts_minor_version, gts_micro_version);
+ printf("*** You need a version of GTS newer than %d.%d.%d. The latest version of\n",
+ major, minor, micro);
+ printf("*** GTS is always available from http://gts.sourceforge.net.\n");
+ printf("***\n");
+ printf("*** If you have already installed a sufficiently new version, this error\n");
+ printf("*** probably means that the wrong copy of the gts-config shell script is\n");
+ printf("*** being found. The easiest way to fix this is to remove the old version\n");
+ printf("*** of GTS, but you can also set the GTS_CONFIG environment to point to the\n");
+ printf("*** correct copy of gts-config. (In this case, you will have to\n");
+ printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n");
+ printf("*** so that the correct libraries are found at run-time))\n");
+ }
+ }
+ return 1;
+}
+],, no_gts=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
+ CFLAGS="$ac_save_CFLAGS"
+ LIBS="$ac_save_LIBS"
+ fi
+ fi
+ if test "x$no_gts" = x ; then
+ AC_MSG_RESULT(yes)
+ ifelse([$2], , :, [$2])
+ else
+ AC_MSG_RESULT(no)
+ if test "$GTS_CONFIG" = "no" ; then
+ echo "*** The gts-config script installed by GTS could not be found"
+ echo "*** If GTS was installed in PREFIX, make sure PREFIX/bin is in"
+ echo "*** your path, or set the GTS_CONFIG environment variable to the"
+ echo "*** full path to gts-config."
+ else
+ if test -f conf.gtstest ; then
+ :
+ else
+ echo "*** Could not run GTS test program, checking why..."
+ CFLAGS="$CFLAGS $GTS_CFLAGS"
+ LIBS="$LIBS $GTS_LIBS"
+ AC_TRY_LINK([
+#include <gts.h>
+#include <stdio.h>
+], [ return ((gts_major_version) || (gts_minor_version) || (gts_micro_version)); ],
+ [ echo "*** The test program compiled, but did not run. This usually means"
+ echo "*** that the run-time linker is not finding GTS or finding the wrong"
+ echo "*** version of GTS. If it is not finding GTS, you'll need to set your"
+ echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
+ echo "*** to the installed location Also, make sure you have run ldconfig if that"
+ echo "*** is required on your system"
+ echo "***"
+ echo "*** If you have an old version installed, it is best to remove it, although"
+ echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"
+ echo "***"],
+ [ echo "*** The test program failed to compile or link. See the file config.log for the"
+ echo "*** exact error that occured. This usually means GTS was incorrectly installed"
+ echo "*** or that you have moved GTS since it was installed. In the latter case, you"
+ echo "*** may want to edit the gts-config script: $GTS_CONFIG" ])
+ CFLAGS="$ac_save_CFLAGS"
+ LIBS="$ac_save_LIBS"
+ fi
+ fi
+ GTS_CFLAGS=""
+ GTS_LIBS=""
+ ifelse([$3], , :, [$3])
+ fi
+ AC_SUBST(GTS_CFLAGS)
+ AC_SUBST(GTS_LIBS)
+ rm -f conf.gtstest
+])
diff --git a/gts/gtsconfig.h b/gts/gtsconfig.h
new file mode 100644
index 0000000..c2d32e6
--- /dev/null
+++ b/gts/gtsconfig.h
@@ -0,0 +1,14 @@
+/* gtsconfig.h
+ *
+ * This is a generated file. Please modify `configure.in'
+ */
+
+#ifndef GTSCONFIG_H
+#define GTSCONFIG_H
+
+
+#define GTS_MAJOR_VERSION 0
+#define GTS_MINOR_VERSION 7
+#define GTS_MICRO_VERSION 6
+
+#endif /* GTSCONFIG_H */
diff --git a/gts/heap.c b/gts/heap.c
new file mode 100644
index 0000000..4a37e58
--- /dev/null
+++ b/gts/heap.c
@@ -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 <stdlib.h>
+#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;
+}
diff --git a/gts/hsurface.c b/gts/hsurface.c
new file mode 100644
index 0000000..80ac66a
--- /dev/null
+++ b/gts/hsurface.c
@@ -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 <stdlib.h>
+#include <string.h>
+#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;
+}
diff --git a/gts/iso.c b/gts/iso.c
new file mode 100644
index 0000000..5995a19
--- /dev/null
+++ b/gts/iso.c
@@ -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);
+}
diff --git a/gts/isotetra.c b/gts/isotetra.c
new file mode 100644
index 0000000..35fe2ba
--- /dev/null
+++ b/gts/isotetra.c
@@ -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 <math.h>
+#include <string.h>
+#include <gts.h>
+#ifdef NATIVE_WIN32
+# include <memory.h>
+# 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);
+}
diff --git a/gts/kdtree.c b/gts/kdtree.c
new file mode 100644
index 0000000..ec5d422
--- /dev/null
+++ b/gts/kdtree.c
@@ -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 <stdlib.h>
+#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;
+}
+
diff --git a/gts/makefile.msc b/gts/makefile.msc
new file mode 100644
index 0000000..8a151ed
--- /dev/null
+++ b/gts/makefile.msc
@@ -0,0 +1,96 @@
+## Makefile for building the gts dlls with Microsoft C
+## Modified by M J Loehr from GLIB makefile.msc
+## Use: nmake -f makefile.msc install
+
+# Change this to wherever you want to install the DLLs. This directory
+# should be in your PATH.
+BIN = C:\bin
+
+################################################################
+
+# Nothing much configurable below
+
+# No general LDFLAGS needed
+LDFLAGS = /link
+INSTALL = copy
+CP = copy
+RM = del
+CC = cl
+
+GTS_VER = 0.7
+
+CFLAGS = -I. -DHAVE_CONFIG_H -G5 -GF -Ox -W3 -MD -nologo
+
+all : \
+ config.h \
+ gts-$(GTS_VER).dll
+
+install : all
+ $(INSTALL) gts-$(GTS_VER).dll $(BIN)
+
+gts_OBJECTS = \
+ object.obj \
+ point.obj \
+ vertex.obj \
+ segment.obj \
+ edge.obj \
+ triangle.obj \
+ face.obj \
+ kdtree.obj \
+ bbtree.obj \
+ misc.obj \
+ predicates.obj \
+ heap.obj \
+ eheap.obj \
+ fifo.obj \
+ matrix.obj \
+ surface.obj \
+ stripe.obj \
+ vopt.obj \
+ refine.obj \
+ iso.obj \
+ split.obj \
+ psurface.obj \
+ hsurface.obj \
+ cdt.obj \
+ boolean.obj \
+ named.obj \
+ oocs.obj \
+ container.obj \
+ graph.obj \
+ pgraph.obj \
+ partition.obj \
+ isotetra.obj \
+ curvature.obj
+
+gts-$(GTS_VER).dll : $(gts_OBJECTS) gts.def
+ $(CC) $(CFLAGS) -LD -Fegts-$(GTS_VER).dll $(gts_OBJECTS) glib-1.3.lib user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:gts.def
+
+config.h: config.h.win32
+ $(CP) config.h.win32 config.h
+
+predicates.obj: predicates.c predicates_init.h predicates.h
+ $(CC) $(CFLAGS) -GD -c -DGTS_COMPILATION -DG_LOG_DOMAIN=\"Gts\" predicates.c
+
+predicates_init.h: predicates_init
+ predicates_init > predicates_init.h
+
+predicates_init: predicates_init.c
+ $(CC) $(CFLAGS) predicates_init.c -o predicates_init
+
+.c.obj:
+ $(CC) $(CFLAGS) -GD -c -DGTS_COMPILATION -DG_LOG_DOMAIN=\"Gts\" $<
+
+clean:
+ $(RM) config.h
+ $(RM) *.obj
+ $(RM) *.dll
+ $(RM) *.lib
+ $(RM) *.err
+ $(RM) *.map
+ $(RM) *.sym
+ $(RM) *.exp
+ $(RM) *.lk1
+ $(RM) *.mk1
+ $(RM) *.pdb
+ $(RM) *.ilk
diff --git a/gts/matrix.c b/gts/matrix.c
new file mode 100644
index 0000000..7ada15d
--- /dev/null
+++ b/gts/matrix.c
@@ -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 <math.h>
+#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.@xxx + @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;
+}
diff --git a/gts/misc.c b/gts/misc.c
new file mode 100644
index 0000000..31142a8
--- /dev/null
+++ b/gts/misc.c
@@ -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 <stdlib.h>
+#include <string.h>
+
+#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 = GTS_INTERFACE_AGE;
+const guint gts_binary_age = GTS_BINARY_AGE;
+
+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 */
diff --git a/gts/named.c b/gts/named.c
new file mode 100644
index 0000000..379f9f6
--- /dev/null
+++ b/gts/named.c
@@ -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 <string.h>
+#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;
+}
diff --git a/gts/object.c b/gts/object.c
new file mode 100644
index 0000000..5970e50
--- /dev/null
+++ b/gts/object.c
@@ -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 <string.h>
+#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;
+ }
+}
diff --git a/gts/oocs.c b/gts/oocs.c
new file mode 100644
index 0000000..f0d76bf
--- /dev/null
+++ b/gts/oocs.c
@@ -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 <math.h>
+#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;
+}
diff --git a/gts/partition.c b/gts/partition.c
new file mode 100644
index 0000000..16dc0e1
--- /dev/null
+++ b/gts/partition.c
@@ -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 <math.h>
+
+#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;
+}
diff --git a/gts/pgraph.c b/gts/pgraph.c
new file mode 100644
index 0000000..2c13c1e
--- /dev/null
+++ b/gts/pgraph.c
@@ -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;
+}
+
diff --git a/gts/point.c b/gts/point.c
new file mode 100644
index 0000000..42fce69
--- /dev/null
+++ b/gts/point.c
@@ -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 <math.h>
+#include <stdlib.h>
+#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;
+ }
+}
diff --git a/gts/predicates.c b/gts/predicates.c
new file mode 100644
index 0000000..3c850a8
--- /dev/null
+++ b/gts/predicates.c
@@ -0,0 +1,2696 @@
+/*****************************************************************************/
+/* */
+/* 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@xxxxxxxxxx */
+/* */
+/* 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@xxxxxxxxxx if you */
+/* have questions. */
+/* */
+/*****************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#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;
+
+#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;
+}
diff --git a/gts/predicates.h b/gts/predicates.h
new file mode 100644
index 0000000..8b026ed
--- /dev/null
+++ b/gts/predicates.h
@@ -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__ */
diff --git a/gts/predicates_init b/gts/predicates_init
new file mode 100755
index 0000000..4aeeeb2
Binary files /dev/null and b/gts/predicates_init differ
diff --git a/gts/predicates_init.c b/gts/predicates_init.c
new file mode 100644
index 0000000..5d8cfb7
--- /dev/null
+++ b/gts/predicates_init.c
@@ -0,0 +1,108 @@
+/* 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.
+ */
+
+/* This program creates a header file defining the various constants needed by
+ * predicates.c. These constant are machine dependent.
+ * adapted from predicates.c by Jonathan Richard Shewchuk
+ */
+
+#include <stdio.h>
+
+/* FPU control. We MUST have only double precision (not extended precision) */
+#include "rounding.h"
+
+int main (int argc, char * argv[])
+{
+ 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;
+ /* splitter = 2^ceiling(p / 2) + 1. Used to split floats in half. */
+ double splitter = 1.0;
+ /* A set of coefficients used to calculate maximum roundoff errors. */
+ double resulterrbound;
+ double ccwerrboundA, ccwerrboundB, ccwerrboundC;
+ double o3derrboundA, o3derrboundB, o3derrboundC;
+ double iccerrboundA, iccerrboundB, iccerrboundC;
+ double isperrboundA, isperrboundB, isperrboundC;
+
+ FPU_ROUND_DOUBLE;
+
+ epsilon = 1.0;
+ splitter = 1.0;
+ /* 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;
+
+ puts ("/* This file was generated automatically by predicates_init\n"
+ " *\n"
+ " * This file is free software; you can redistribute it and/or\n"
+ " * modify it under the terms of the GNU Library General Public\n"
+ " * License as published by the Free Software Foundation; either\n"
+ " * version 2 of the License, or (at your option) any later version.\n"
+ " *\n"
+ " * This file is distributed in the hope that it will be useful,\n"
+ " * but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+ " * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
+ " */\n");
+ printf ("static double splitter = %f;\n", splitter);
+ printf ("static double resulterrbound = %.16g;\n", resulterrbound);
+ printf ("static double ccwerrboundA = %.16g;\n", ccwerrboundA);
+ printf ("static double ccwerrboundB = %.16g;\n", ccwerrboundB);
+ printf ("static double ccwerrboundC = %.16g;\n", ccwerrboundC);
+ printf ("static double o3derrboundA = %.16g;\n", o3derrboundA);
+ printf ("static double o3derrboundB = %.16g;\n", o3derrboundB);
+ printf ("static double o3derrboundC = %.16g;\n", o3derrboundC);
+ printf ("static double iccerrboundA = %.16g;\n", iccerrboundA);
+ printf ("static double iccerrboundB = %.16g;\n", iccerrboundB);
+ printf ("static double iccerrboundC = %.16g;\n", iccerrboundC);
+ printf ("static double isperrboundA = %.16g;\n", isperrboundA);
+ printf ("static double isperrboundB = %.16g;\n", isperrboundB);
+ printf ("static double isperrboundC = %.16g;\n", isperrboundC);
+
+ FPU_RESTORE;
+
+ return 0;
+}
diff --git a/gts/psurface.c b/gts/psurface.c
new file mode 100644
index 0000000..6db3ae2
--- /dev/null
+++ b/gts/psurface.c
@@ -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 <stdlib.h>
+#include <math.h>
+#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);
+ }
+}
diff --git a/gts/refine.c b/gts/refine.c
new file mode 100644
index 0000000..293eb11
--- /dev/null
+++ b/gts/refine.c
@@ -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 <math.h>
+#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;
+}
diff --git a/gts/rounding.h b/gts/rounding.h
new file mode 100644
index 0000000..053b32f
--- /dev/null
+++ b/gts/rounding.h
@@ -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 <fpu_control.h>
+# 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 <floatingpoint.h>
+# define FPU_ROUND_DOUBLE (fpsetprec(FP_PD))
+# define FPU_RESTORE (fpsetprec(FP_PE))
+# else /* not __FreeBSD__ */
+# ifdef WIN32
+# ifdef _MSC_VER
+# include <float.h>
+ 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 <float.h>
+ 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 */
diff --git a/gts/segment.c b/gts/segment.c
new file mode 100644
index 0000000..58a0540
--- /dev/null
+++ b/gts/segment.c
@@ -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;
+}
diff --git a/gts/split.c b/gts/split.c
new file mode 100644
index 0000000..c63516c
--- /dev/null
+++ b/gts/split.c
@@ -0,0 +1,1831 @@
+/* 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 <stdlib.h>
+#include <string.h>
+#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 */
+
+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++;
+ }
+}
+
+/**
+ * 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;
+ }
+ }
+}
+
+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;
+}
+
+/**
+ * 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;
+ GtsVertex * v1, * v2;
+ GtsEdge * e;
+ GSList * i;
+ GtsSplitCFace * cf;
+
+ 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;
+ v1 = GTS_SPLIT_V1 (vs);
+ v2 = GTS_SPLIT_V2 (vs);
+#ifdef DYNAMIC_SPLIT
+ vs->ncf = 0;
+ vs->cfaces = NULL;
+#else
+ 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;
+}
+
+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;
+}
+
+#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;
+}
diff --git a/gts/stripe.c b/gts/stripe.c
new file mode 100644
index 0000000..7e98a9c
--- /dev/null
+++ b/gts/stripe.c
@@ -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;
+}
diff --git a/gts/surface.c b/gts/surface.c
new file mode 100644
index 0000000..f5adfaa
--- /dev/null
+++ b/gts/surface.c
@@ -0,0 +1,2737 @@
+/* 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 <stdlib.h>
+#include <math.h>
+#include <string.h>
+#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;
+}
+
+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;
+ }
+}
+
+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;
+}
diff --git a/gts/triangle.c b/gts/triangle.c
new file mode 100644
index 0000000..5213a51
--- /dev/null
+++ b/gts/triangle.c
@@ -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 <math.h>
+#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;
+ }
+}
diff --git a/gts/tribox3.c b/gts/tribox3.c
new file mode 100644
index 0000000..c0ea778
--- /dev/null
+++ b/gts/tribox3.c
@@ -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 <math.h>
+#include <stdio.h>
+
+#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(x1<min) min=x1;\
+ if(x1>max) max=x1;\
+ if(x2<min) min=x2;\
+ if(x2>max) 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(p0<p2) {min=p0; max=p2;} else {min=p2; max=p0;} \
+ rad = fa * boxhalfsize[Y] + fb * boxhalfsize[Z]; \
+ if(min>rad || 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(p0<p1) {min=p0; max=p1;} else {min=p1; max=p0;} \
+ rad = fa * boxhalfsize[Y] + fb * boxhalfsize[Z]; \
+ if(min>rad || 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(p0<p2) {min=p0; max=p2;} else {min=p2; max=p0;} \
+ rad = fa * boxhalfsize[X] + fb * boxhalfsize[Z]; \
+ if(min>rad || 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(p0<p1) {min=p0; max=p1;} else {min=p1; max=p0;} \
+ rad = fa * boxhalfsize[X] + fb * boxhalfsize[Z]; \
+ if(min>rad || 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(p2<p1) {min=p2; max=p1;} else {min=p1; max=p2;} \
+ rad = fa * boxhalfsize[X] + fb * boxhalfsize[Y]; \
+ if(min>rad || 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(p0<p1) {min=p0; max=p1;} else {min=p1; max=p0;} \
+ rad = fa * boxhalfsize[X] + fb * boxhalfsize[Y]; \
+ if(min>rad || 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 */
+}
+
diff --git a/gts/vertex.c b/gts/vertex.c
new file mode 100644
index 0000000..d312869
--- /dev/null
+++ b/gts/vertex.c
@@ -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 <math.h>
+#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;
+}
+
diff --git a/gts/vopt.c b/gts/vopt.c
new file mode 100644
index 0000000..d772af9
--- /dev/null
+++ b/gts/vopt.c
@@ -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;
+}
diff --git a/src/Makefile.am b/src/Makefile.am
index 270613c..27ad499 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -132,6 +132,8 @@ PCB_SRCS = \
strflags.h \
thermal.c \
thermal.h \
+ toporouter.c \
+ toporouter.h \
undo.c \
undo.h \
vector.c \
diff --git a/src/toporouter.c b/src/toporouter.c
new file mode 100644
index 0000000..7ce571c
--- /dev/null
+++ b/src/toporouter.c
@@ -0,0 +1,4315 @@
+/*
+ * COPYRIGHT
+ *
+ * Topological Autorouter for
+ * PCB, interactive printed circuit board design
+ * Copyright (C) 2009 Anthony Blake
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Contact addresses for email:
+ * Anthony Blake, tonyb33@xxxxxxxxx
+ *
+ */
+
+
+#include "toporouter.h"
+
+static void toporouter_edge_init (ToporouterEdge *edge)
+{
+ edge->netlist = NULL;
+}
+
+/**
+ * toporouter_edge_class:
+ *
+ * Returns: the #ToporouterEdgeClass.
+ */
+ToporouterEdgeClass *
+toporouter_edge_class(void)
+{
+ static ToporouterEdgeClass *class = NULL;
+
+ if (class == NULL) {
+ GtsObjectClassInfo constraint_info = {
+ "ToporouterEdge",
+ sizeof (ToporouterEdge),
+ sizeof (ToporouterEdgeClass),
+ (GtsObjectClassInitFunc) NULL,
+ (GtsObjectInitFunc) toporouter_edge_init,
+ (GtsArgSetFunc) NULL,
+ (GtsArgGetFunc) NULL
+ };
+ class = gts_object_class_new (GTS_OBJECT_CLASS (gts_edge_class ()),
+ &constraint_info);
+ }
+
+ return class;
+}
+
+static void toporouter_bbox_init (ToporouterBBox *box)
+{
+ box->data = NULL;
+ box->type = OTHER;
+ box->constraints = NULL;
+ box->netlist = NULL;
+ box->style = NULL;
+}
+
+/**
+ * toporouter_bbox_class:
+ *
+ * Returns: the #ToporouterBBoxClass.
+ */
+ToporouterBBoxClass *
+toporouter_bbox_class(void)
+{
+ static ToporouterBBoxClass *class = NULL;
+
+ if (class == NULL) {
+ GtsObjectClassInfo constraint_info = {
+ "ToporouterBBox",
+ sizeof (ToporouterBBox),
+ sizeof (ToporouterBBoxClass),
+ (GtsObjectClassInitFunc) NULL,
+ (GtsObjectInitFunc) toporouter_bbox_init,
+ (GtsArgSetFunc) NULL,
+ (GtsArgGetFunc) NULL
+ };
+ class = gts_object_class_new (GTS_OBJECT_CLASS (gts_bbox_class ()),
+ &constraint_info);
+ }
+
+ return class;
+}
+
+static void toporouter_vertex_class_init (ToporouterVertexClass *klass)
+{
+
+}
+
+static void toporouter_vertex_init (ToporouterVertex *vertex)
+{
+// vertex->data = NULL;
+// vertex->type = TOPOROUTER_VERTEX_TYPE_NULL;
+ vertex->boxes = NULL;
+ vertex->parent = NULL;
+ //vertex->visible = NULL;
+ vertex->flags = 0;
+ vertex->regions = NULL;
+ vertex->zlink = NULL;
+
+ vertex->corridors = NULL;
+// vertex->incident = NULL;
+ vertex->attached = NULL;
+}
+
+/**
+ * toporouter_vertex_class:
+ *
+ * Returns: the #ToporouterVertexClass.
+ */
+ToporouterVertexClass *
+toporouter_vertex_class(void)
+{
+ static ToporouterVertexClass *klass = NULL;
+
+ if (klass == NULL) {
+ GtsObjectClassInfo constraint_info = {
+ "ToporouterVertex",
+ sizeof (ToporouterVertex),
+ sizeof (ToporouterVertexClass),
+ (GtsObjectClassInitFunc) toporouter_vertex_class_init,
+ (GtsObjectInitFunc) toporouter_vertex_init,
+ (GtsArgSetFunc) NULL,
+ (GtsArgGetFunc) NULL
+ };
+ klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_vertex_class ()),
+ &constraint_info);
+ }
+
+ return klass;
+}
+
+static void toporouter_constraint_class_init (ToporouterConstraintClass *klass)
+{
+
+}
+
+static void toporouter_constraint_init (ToporouterConstraint *constraint)
+{
+// constraint->data = NULL;
+// constraint->type = TOPOROUTER_CONSTRAINT_TYPE_NULL;
+ constraint->box = NULL;
+ //constraint->objectconstraints = NULL;
+}
+
+/**
+ * toporouter_constraint_class:
+ *
+ * Returns: the #ToporouterConstraintClass.
+ */
+ToporouterConstraintClass *
+toporouter_constraint_class(void)
+{
+ static ToporouterConstraintClass *klass = NULL;
+
+ if (klass == NULL) {
+ GtsObjectClassInfo constraint_info = {
+ "ToporouterConstraint",
+ sizeof (ToporouterConstraint),
+ sizeof (ToporouterConstraintClass),
+ (GtsObjectClassInitFunc) toporouter_constraint_class_init,
+ (GtsObjectInitFunc) toporouter_constraint_init,
+ (GtsArgSetFunc) NULL,
+ (GtsArgGetFunc) NULL
+ };
+ klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_constraint_class ()),
+ &constraint_info);
+ }
+
+ return klass;
+}
+
+#define MARGIN 10.0f
+
+drawing_context_t *
+toporouter_output_init(int w, int h, char *filename)
+{
+ drawing_context_t *dc;
+
+ dc = malloc(sizeof(drawing_context_t));
+
+ dc->iw = w;
+ dc->ih = h;
+ dc->filename = filename;
+
+ /* Calculate scaling to maintain aspect ratio */
+ if(PCB->MaxWidth > PCB->MaxHeight) {
+ /* Scale board width to match image width minus 2xMARGIN */
+ dc->s = ((double)dc->iw - (2 * MARGIN)) / (double)PCB->MaxWidth;
+ dc->ih = (double)PCB->MaxHeight * dc->s + (2 * MARGIN);
+ }else{
+ /* Scale board height to match image height minus 2xMARGIN */
+ dc->s = ((double)dc->ih - (2 * MARGIN)) / (double)PCB->MaxHeight;
+ dc->iw = (double)PCB->MaxWidth * dc->s + (2 * MARGIN);
+ }
+
+#ifdef CAIRO_H
+ dc->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, dc->iw, dc->ih);
+ dc->cr = cairo_create (dc->surface);
+
+ cairo_rectangle (dc->cr, 0, 0, dc->iw, dc->ih);
+ cairo_set_source_rgb (dc->cr, 0, 0, 0);
+ cairo_fill (dc->cr);
+
+#endif
+
+ return dc;
+}
+
+void
+toporouter_output_close(drawing_context_t *dc)
+{
+#ifdef CAIRO_H
+ cairo_surface_write_to_png (dc->surface, dc->filename);
+ cairo_destroy (dc->cr);
+ cairo_surface_destroy (dc->surface);
+#endif
+}
+
+gdouble
+lookup_keepaway(char *name)
+{
+ if(name)
+ STYLE_LOOP(PCB);
+ {
+ if(!strcmp(style->Name, name)) return style->Keepaway;
+ }
+ END_LOOP;
+ return Settings.Keepaway;
+}
+
+gdouble
+lookup_thickness(char *name)
+{
+ if(name)
+ STYLE_LOOP(PCB);
+ {
+ if(!strcmp(style->Name, name)) return style->Thick;
+ }
+ END_LOOP;
+ return Settings.LineThickness;
+}
+
+gint
+toporouter_draw_vertex(gpointer item, gpointer data)
+{
+#ifdef CAIRO_H
+ drawing_context_t *dc = (drawing_context_t *) data;
+ ToporouterVertex *tv;
+ PinType *pin;
+ PadType *pad;
+ gdouble blue;
+
+ if(TOPOROUTER_IS_VERTEX((GtsObject*)item)) {
+ tv = TOPOROUTER_VERTEX((GtsObject*)item);
+ //printf("tv->type = %d\n", tv->type);
+ if(!dc->mode) {
+ if(tv->boxes) {
+ pin = (PinType*) TOPOROUTER_BBOX(tv->boxes->data)->data;
+ pad = (PadType*) TOPOROUTER_BBOX(tv->boxes->data)->data;
+
+ blue = 0.0f;
+ switch(TOPOROUTER_BBOX(tv->boxes->data)->type) {
+ case PIN:
+ cairo_set_source_rgba(dc->cr, 1.0f, 0., 0.0f, 0.2f);
+ cairo_arc(dc->cr,
+ tv->v.p.x * dc->s + MARGIN,
+ tv->v.p.y * dc->s + MARGIN,
+ (((gdouble)pin->Thickness / 2.0f) + (gdouble)lookup_keepaway(pin->Name) ) * dc->s, 0, 2 * M_PI);
+ cairo_fill(dc->cr);
+
+ cairo_set_source_rgba(dc->cr, 1.0f, 0., 0., 0.4f);
+ cairo_arc(dc->cr,
+ tv->v.p.x * dc->s + MARGIN,
+ tv->v.p.y * dc->s + MARGIN,
+ (gdouble)(pin->Thickness) / 2.0f * dc->s,
+ 0, 2 * M_PI);
+ cairo_fill(dc->cr);
+
+ break;
+ case VIA:
+ cairo_set_source_rgba(dc->cr, 0.0f, 0., 1., 0.2f);
+ cairo_arc(dc->cr,
+ tv->v.p.x * dc->s + MARGIN,
+ tv->v.p.y * dc->s + MARGIN,
+ (((gdouble)pin->Thickness / 2.0f) + (gdouble)lookup_keepaway(pin->Name) ) * dc->s, 0, 2 * M_PI);
+ cairo_fill(dc->cr);
+
+ cairo_set_source_rgba(dc->cr, 0.0f, 0., 1., 0.4f);
+ cairo_arc(dc->cr,
+ tv->v.p.x * dc->s + MARGIN,
+ tv->v.p.y * dc->s + MARGIN,
+ (gdouble)(pin->Thickness) / 2.0f * dc->s,
+ 0, 2 * M_PI);
+ cairo_fill(dc->cr);
+
+ break;
+ case PAD:
+ cairo_set_source_rgba(dc->cr, 0.0f, 1., 0., 0.5f);
+ cairo_arc(dc->cr,
+ tv->v.p.x * dc->s + MARGIN,
+ tv->v.p.y * dc->s + MARGIN,
+ 400. * dc->s, 0, 2 * M_PI);
+ cairo_fill(dc->cr);
+
+ break;
+ default:
+ break;
+ }
+ }
+ }else{
+ if(tv->flags & VERTEX_FLAG_BLUE) {
+ cairo_set_source_rgba(dc->cr, 0., 0., 1., 0.8f);
+ cairo_arc(dc->cr,
+ tv->v.p.x * dc->s + MARGIN,
+ tv->v.p.y * dc->s + MARGIN,
+ 500. * dc->s, 0, 2 * M_PI);
+ cairo_fill(dc->cr);
+ }else if(tv->flags & VERTEX_FLAG_RED) {
+ cairo_set_source_rgba(dc->cr, 1., 0., 0., 0.8f);
+ cairo_arc(dc->cr,
+ tv->v.p.x * dc->s + MARGIN,
+ tv->v.p.y * dc->s + MARGIN,
+ 500. * dc->s, 0, 2 * M_PI);
+ cairo_fill(dc->cr);
+
+ }else if(tv->flags & VERTEX_FLAG_GREEN) {
+ cairo_set_source_rgba(dc->cr, 0., 1., 0., 0.8f);
+ cairo_arc(dc->cr,
+ tv->v.p.x * dc->s + MARGIN,
+ tv->v.p.y * dc->s + MARGIN,
+ 500. * dc->s, 0, 2 * M_PI);
+ cairo_fill(dc->cr);
+ }
+
+ }
+ }else{
+ fprintf(stderr, "Unknown data passed to toporouter_draw_vertex, aborting foreach\n");
+ return -1;
+ }
+ return 0;
+#else
+ return -1;
+#endif
+}
+
+gint
+toporouter_draw_edge(gpointer item, gpointer data)
+{
+#ifdef CAIRO_H
+ drawing_context_t *dc = (drawing_context_t *) data;
+ ToporouterEdge *te;
+ ToporouterConstraint *tc;
+
+ if(TOPOROUTER_IS_EDGE((GtsObject*)item)) {
+ te = TOPOROUTER_EDGE((GtsObject*)item);
+ cairo_set_source_rgba(dc->cr, 1.0f, 1.0f, 1.0f, 0.5f);
+ cairo_move_to(dc->cr,
+ te->e.segment.v1->p.x * dc->s + MARGIN,
+ te->e.segment.v1->p.y * dc->s + MARGIN);
+ cairo_line_to(dc->cr,
+ te->e.segment.v2->p.x * dc->s + MARGIN,
+ te->e.segment.v2->p.y * dc->s + MARGIN);
+ cairo_stroke(dc->cr);
+ }else if(TOPOROUTER_IS_CONSTRAINT((GtsObject*)item)) {
+ tc = TOPOROUTER_CONSTRAINT((GtsObject*)item);
+ if(tc->box) {
+ switch(tc->box->type) {
+ case BOARD:
+ cairo_set_source_rgba(dc->cr, 1.0f, 0.0f, 1.0f, 0.9f);
+ cairo_move_to(dc->cr,
+ tc->c.edge.segment.v1->p.x * dc->s + MARGIN,
+ tc->c.edge.segment.v1->p.y * dc->s + MARGIN);
+ cairo_line_to(dc->cr,
+ tc->c.edge.segment.v2->p.x * dc->s + MARGIN,
+ tc->c.edge.segment.v2->p.y * dc->s + MARGIN);
+ cairo_stroke(dc->cr);
+ break;
+ case PAD:
+ cairo_set_source_rgba(dc->cr, 1.0f, 0.0f, 0.0f, 0.9f);
+ cairo_move_to(dc->cr,
+ tc->c.edge.segment.v1->p.x * dc->s + MARGIN,
+ tc->c.edge.segment.v1->p.y * dc->s + MARGIN);
+ cairo_line_to(dc->cr,
+ tc->c.edge.segment.v2->p.x * dc->s + MARGIN,
+ tc->c.edge.segment.v2->p.y * dc->s + MARGIN);
+ cairo_stroke(dc->cr);
+ break;
+ case LINE:
+ cairo_set_source_rgba(dc->cr, 0.0f, 1.0f, 0.0f, 0.9f);
+ cairo_move_to(dc->cr,
+ tc->c.edge.segment.v1->p.x * dc->s + MARGIN,
+ tc->c.edge.segment.v1->p.y * dc->s + MARGIN);
+ cairo_line_to(dc->cr,
+ tc->c.edge.segment.v2->p.x * dc->s + MARGIN,
+ tc->c.edge.segment.v2->p.y * dc->s + MARGIN);
+ cairo_stroke(dc->cr);
+ break;
+
+ default:
+ break;
+ }
+ }
+ }else{
+ fprintf(stderr, "Unknown data passed to toporouter_draw_edge, aborting foreach\n");
+ return -1;
+ }
+
+ return 0;
+#else
+ return -1;
+#endif
+}
+
+void
+toporouter_draw_surface(GtsSurface *s, char *filename, int w, int h, int mode, void *data)
+{
+ drawing_context_t *dc;
+
+ dc = toporouter_output_init(w, h, filename);
+ dc->mode = mode;
+ dc->data = data;
+
+ gts_surface_foreach_edge(s, toporouter_draw_edge, dc);
+ gts_surface_foreach_vertex(s, toporouter_draw_vertex, dc);
+
+ if(mode == 1) {
+ toporouter_route_t *routedata = (toporouter_route_t *)data;
+// GSList *i = routedata->src_points;
+ ToporouterVertex *tv;
+ ToporouterVertex *tv2;
+/*
+ while(i) {
+ toporouter_draw_vertex(i->data, dc);
+ i = i->next;
+ }
+ i = routedata->dest_points;
+ while(i) {
+ toporouter_draw_vertex(i->data, dc);
+ i = i->next;
+ }
+ */
+ cairo_set_source_rgba(dc->cr, 0.0f, 1.0f, 0.0f, 0.8f);
+ tv = routedata->curpoint;
+ tv2 = tv->parent;
+ while(tv2) {
+ cairo_move_to(dc->cr,
+ GTS_POINT(tv)->x * dc->s + MARGIN,
+ GTS_POINT(tv)->y * dc->s + MARGIN);
+ cairo_line_to(dc->cr,
+ GTS_POINT(tv2)->x * dc->s + MARGIN,
+ GTS_POINT(tv2)->y * dc->s + MARGIN);
+ cairo_stroke(dc->cr);
+
+ tv = tv->parent;
+ tv2 = tv2->parent;
+
+ }
+
+ }else if(mode == 2) {
+ toporouter_route_t *routedata = (toporouter_route_t *)data;
+ ToporouterVertex *tv = NULL, *tv2 = NULL;
+ GSList *i = routedata->path;
+
+
+ while(i) {
+ tv = TOPOROUTER_VERTEX(i->data);
+ if(tv && tv2) {
+ GSList *j = g_hash_table_lookup(tv->corridors, tv2);
+
+ cairo_set_source_rgba(dc->cr, 0.0f, 1.0f, 0.0f, 0.8f);
+ cairo_move_to(dc->cr,
+ GTS_POINT(tv)->x * dc->s + MARGIN,
+ GTS_POINT(tv)->y * dc->s + MARGIN);
+ cairo_line_to(dc->cr,
+ GTS_POINT(tv2)->x * dc->s + MARGIN,
+ GTS_POINT(tv2)->y * dc->s + MARGIN);
+ cairo_stroke(dc->cr);
+
+ while(j) {
+ GtsSegment *tempseg = GTS_SEGMENT(j->data);
+
+ cairo_set_source_rgba(dc->cr, 0.0f, 0.0f, 1.0f, 0.4f);
+ cairo_move_to(dc->cr,
+ GTS_POINT(tempseg->v1)->x * dc->s + MARGIN,
+ GTS_POINT(tempseg->v1)->y * dc->s + MARGIN);
+ cairo_line_to(dc->cr,
+ GTS_POINT(tempseg->v2)->x * dc->s + MARGIN,
+ GTS_POINT(tempseg->v2)->y * dc->s + MARGIN);
+ cairo_stroke(dc->cr);
+
+ j = j->next;
+ }
+
+
+ }
+
+ tv2 = tv;
+ i = i->next;
+ }
+
+ }
+
+ toporouter_output_close(dc);
+}
+
+toporouter_t *toporouter_new()
+{
+ toporouter_t *r = calloc(1, sizeof(toporouter_t));
+
+ r->layers = NULL;
+ r->flags = 0;
+ r->viamax = 3;
+ r->viacost = 1000.;
+ r->stublength = 300.;
+
+ r->bboxes = NULL;
+ r->bboxtree = NULL;
+
+ return r;
+}
+
+void
+toporouter_layer_free(toporouter_layer_t *l)
+{
+ g_slist_free(l->vertices);
+ g_slist_free(l->constraints);
+
+}
+
+void
+toporouter_free(toporouter_t *r)
+{
+ int i;
+ for(i=0;i<PCB->Data->LayerN;i++) {
+ toporouter_layer_free(&r->layers[i]);
+ }
+
+ free(r->layers);
+ free(r);
+}
+/*
+Boolean
+IsPointOnPin (float X, float Y, float Radius, PinTypePtr pin)
+{
+ if (TEST_FLAG (SQUAREFLAG, pin))
+ {
+ BoxType b;
+ BDimension t = pin->Thickness / 2;
+
+ b.X1 = pin->X - t;
+ b.X2 = pin->X + t;
+ b.Y1 = pin->Y - t;
+ b.Y2 = pin->Y + t;
+ if (IsPointInBox (X, Y, &b, Radius))
+ return (True);
+ }
+ else if (SQUARE (pin->X - X) + SQUARE (pin->Y - Y) <=
+ SQUARE (pin->Thickness / 2 + Radius))
+ return (True);
+ return (False);
+}
+
+struct ans_info
+{
+ void **ptr1, **ptr2, **ptr3;
+ Boolean BackToo;
+ float area;
+ jmp_buf env;
+ int locked;
+};
+
+static int
+pinorvia_callback (const BoxType * box, void *cl)
+{
+ struct ans_info *i = (struct ans_info *) cl;
+ PinTypePtr pin = (PinTypePtr) box;
+
+ if (TEST_FLAG (i->locked, pin))
+ return 0;
+
+ if (!IsPointOnPin (PosX, PosY, SearchRadius, pin))
+ return 0;
+ *i->ptr1 = pin->Element ? pin->Element : pin;
+ *i->ptr2 = *i->ptr3 = pin;
+ longjmp (i->env, 1);
+ return 1;
+}
+*/
+
+/* wind:
+ * returns 1,0,-1 for counterclockwise, collinear or clockwise, respectively.
+ */
+int
+wind(toporouter_spoint_t *p1, toporouter_spoint_t *p2, toporouter_spoint_t *p3)
+{
+ double rval, dx1, dx2, dy1, dy2;
+ dx1 = p2->x - p1->x; dy1 = p2->y - p1->y;
+ dx2 = p3->x - p2->x; dy2 = p3->y - p2->y;
+ rval = (dx1*dy2)-(dy1*dx2);
+ return (rval > 0.0001) ? 1 : ((rval < -0.0001) ? -1 : 0);
+}
+
+/* wind_double:
+ * returns 1,0,-1 for counterclockwise, collinear or clockwise, respectively.
+ */
+int
+wind_double(gdouble p1_x, gdouble p1_y, gdouble p2_x, gdouble p2_y, gdouble p3_x, gdouble p3_y)
+{
+ double rval, dx1, dx2, dy1, dy2;
+ dx1 = p2_x - p1_x; dy1 = p2_y - p1_y;
+ dx2 = p3_x - p2_x; dy2 = p3_y - p2_y;
+ rval = (dx1*dy2)-(dy1*dx2);
+ return (rval > 0.0001) ? 1 : ((rval < -0.0001) ? -1 : 0);
+}
+
+inline void
+print_toporouter_constraint(ToporouterConstraint *tc)
+{
+ printf("%f,%f -> %f,%f ",
+ tc->c.edge.segment.v1->p.x,
+ tc->c.edge.segment.v1->p.y,
+ tc->c.edge.segment.v2->p.x,
+ tc->c.edge.segment.v2->p.y);
+}
+
+inline void
+print_toporouter_vertex(ToporouterVertex *tv)
+{
+ printf("%f,%f ", tv->v.p.x, tv->v.p.y);
+}
+
+
+/**
+ * vertices_on_line:
+ * Given vertex a, gradient m, and radius r:
+ *
+ * Return vertices on line of a & m at r from a
+ */
+void
+vertices_on_line(toporouter_spoint_t *a, gdouble m, gdouble r, toporouter_spoint_t *b0, toporouter_spoint_t *b1)
+{
+
+ gdouble c, temp;
+
+ if(m == INFINITY || m == -INFINITY) {
+ b0->y = a->y + r;
+ b1->y = a->y - r;
+
+ b0->x = a->x;
+ b1->x = a->x;
+
+ return;
+ }
+
+ c = a->y - (m * a->x);
+
+ temp = sqrt( pow(r, 2) / ( 1 + pow(m, 2) ) );
+
+ b0->x = a->x + temp;
+ b1->x = a->x - temp;
+
+ b0->y = b0->x * m + c;
+ b1->y = b1->x * m + c;
+
+}
+
+/**
+ * vertices_on_line:
+ * Given vertex a, gradient m, and radius r:
+ *
+ * Return vertices on line of a & m at r from a
+ */
+void
+points_on_line(GtsPoint *a, gdouble m, gdouble r, GtsPoint *b0, GtsPoint *b1)
+{
+
+ gdouble c, temp;
+
+ if(m == INFINITY || m == -INFINITY) {
+ b0->y = a->y + r;
+ b1->y = a->y - r;
+
+ b0->x = a->x;
+ b1->x = a->x;
+
+ return;
+ }
+
+ c = a->y - (m * a->x);
+
+ temp = sqrt( pow(r, 2) / ( 1 + pow(m, 2) ) );
+
+ b0->x = a->x + temp;
+ b1->x = a->x - temp;
+
+ b0->y = b0->x * m + c;
+ b1->y = b1->x * m + c;
+
+}
+
+/*
+ * Returns gradient of segment given by a & b
+ */
+gdouble
+vertex_gradient(toporouter_spoint_t *a, toporouter_spoint_t *b)
+{
+ if(a->x == b->x) return INFINITY;
+
+ return ((b->y - a->y) / (b->x - a->x));
+}
+
+/*
+ * Returns gradient of segment given by (x0,y0) & (x1,y1)
+ */
+inline gdouble
+cartesian_gradient(gdouble x0, gdouble y0, gdouble x1, gdouble y1)
+{
+ if(x0 == x1) return INFINITY;
+
+ return ((y1 - y0) / (x1 - x0));
+}
+
+/*
+ * Returns gradient of segment given by (x0,y0) & (x1,y1)
+ */
+inline gdouble
+point_gradient(GtsPoint *a, GtsPoint *b)
+{
+ return cartesian_gradient(a->x, a->y, b->x, b->y);
+}
+
+gdouble
+segment_gradient(GtsSegment *s)
+{
+ return cartesian_gradient(
+ GTS_POINT(s->v1)->x,
+ GTS_POINT(s->v1)->y,
+ GTS_POINT(s->v2)->x,
+ GTS_POINT(s->v2)->y);
+}
+
+/*
+ * Returns gradient perpendicular to m
+ */
+gdouble
+perpendicular_gradient(gdouble m)
+{
+ if(m == INFINITY) return 0.0f;
+ return -1.0f/m;
+}
+
+/*
+ * Returns the distance between two vertices in the x-y plane
+ */
+gdouble
+vertices_plane_distance(toporouter_spoint_t *a, toporouter_spoint_t *b) {
+ return sqrt( pow(a->x - b->x, 2) + pow(a->y - b->y, 2) );
+}
+
+/*
+ * Finds the point p distance r away from a on the line segment of a & b
+ */
+inline void
+vertex_outside_segment(toporouter_spoint_t *a, toporouter_spoint_t *b, gdouble r, toporouter_spoint_t *p)
+{
+ gdouble m;
+ toporouter_spoint_t temp[2];
+
+ m = vertex_gradient(a,b);
+
+ vertices_on_line(a, vertex_gradient(a,b), r, &temp[0], &temp[1]);
+
+ if(vertices_plane_distance(&temp[0], b) > vertices_plane_distance(&temp[1], b)) {
+ p->x = temp[0].x;
+ p->y = temp[0].y;
+ }else{
+ p->x = temp[1].x;
+ p->y = temp[1].y;
+ }
+
+}
+
+
+/* wind_v:
+ * returns 1,0,-1 for counterclockwise, collinear or clockwise, respectively.
+ */
+int
+point_wind(GtsPoint *a, GtsPoint *b, GtsPoint *c)
+{
+ gdouble rval, dx1, dx2, dy1, dy2;
+ dx1 = b->x - a->x; dy1 = b->y - a->y;
+ dx2 = c->x - b->x; dy2 = c->y - b->y;
+ rval = (dx1*dy2)-(dy1*dx2);
+ return (rval > 0.0001) ? 1 : ((rval < -0.0001) ? -1 : 0);
+}
+
+inline int
+vertex_wind(GtsVertex *a, GtsVertex *b, GtsVertex *c)
+{
+ return point_wind(GTS_POINT(a), GTS_POINT(b), GTS_POINT(c));
+}
+
+/* proper intersection:
+ * AB and CD must share a point interior to both segments.
+ * returns TRUE if AB properly intersects CD.
+ */
+int
+point_intersect_prop(GtsPoint *a, GtsPoint *b, GtsPoint *c, GtsPoint *d)
+{
+
+ if( point_wind(a, b, c) == 0 ||
+ point_wind(a, b, d) == 0 ||
+ point_wind(c, d, a) == 0 ||
+ point_wind(c, d, b) == 0 ) return 0;
+
+ return ( point_wind(a, b, c) ^ point_wind(a, b, d) ) &&
+ ( point_wind(c, d, a) ^ point_wind(c, d, b) );
+}
+
+inline int
+vertex_intersect_prop(GtsVertex *a, GtsVertex *b, GtsVertex *c, GtsVertex *d)
+{
+ return point_intersect_prop(GTS_POINT(a), GTS_POINT(b), GTS_POINT(c), GTS_POINT(d));
+}
+
+/* intersection vertex:
+ * AB and CD must share a point interior to both segments.
+ * returns vertex at intersection of AB and CD.
+ */
+GtsVertex *
+vertex_intersect(GtsVertex *a, GtsVertex *b, GtsVertex *c, GtsVertex *d)
+{
+ GtsVertexClass *vertex_class = GTS_VERTEX_CLASS (toporouter_vertex_class ());
+ GtsVertex *rval;
+
+ gdouble ua_top = ((d->p.x - c->p.x) * (a->p.y - c->p.y)) -
+ ((d->p.y - c->p.y) * (a->p.x - c->p.x));
+ gdouble ua_bot = ((d->p.y - c->p.y) * (b->p.x - a->p.x)) -
+ ((d->p.x - c->p.x) * (b->p.y - a->p.y));
+ gdouble ua = ua_top / ua_bot;
+ gdouble rx = a->p.x + (ua * (b->p.x - a->p.x));
+ gdouble ry = a->p.y + (ua * (b->p.y - a->p.y));
+
+ rval = gts_vertex_new (vertex_class, rx, ry, 0.0f);
+
+ return rval;
+}
+
+/*
+ * returns true if c is between a and b
+ */
+int
+point_between(GtsPoint *a, GtsPoint *b, GtsPoint *c)
+{
+ if( point_wind(a, b, c) != 0 ) return 0;
+
+ if( a->x != b->x ) {
+ return ((a->x <= c->x) &&
+ (c->x <= b->x)) ||
+ ((a->x >= c->x) &&
+ (c->x >= b->x));
+ }
+ return ((a->y <= c->y) &&
+ (c->y <= b->y)) ||
+ ((a->y >= c->y) &&
+ (c->y >= b->y));
+}
+
+inline int
+vertex_between(GtsVertex *a, GtsVertex *b, GtsVertex *c)
+{
+ return point_between(GTS_POINT(a), GTS_POINT(b), GTS_POINT(c));
+}
+
+void
+delaunay_create_from_vertices(GSList *vertices, GtsSurface **surface, GtsTriangle **t)
+{
+// GtsSurface *surface;
+ GSList *i;
+// GtsTriangle *t;
+ GtsVertex *v1, *v2, *v3;
+
+ *t = gts_triangle_enclosing (gts_triangle_class (), vertices, 1000.0f);
+ gts_triangle_vertices (*t, &v1, &v2, &v3);
+
+ *surface = gts_surface_new (gts_surface_class (), gts_face_class (),
+ GTS_EDGE_CLASS(toporouter_edge_class ()), GTS_VERTEX_CLASS(toporouter_vertex_class ()) );
+
+ gts_surface_add_face (*surface, gts_face_new (gts_face_class (), (*t)->e1, (*t)->e2, (*t)->e3));
+
+ i = vertices;
+ while (i) {
+ g_assert (gts_delaunay_add_vertex (*surface, i->data, NULL) == NULL);
+ i = i->next;
+ }
+ /*
+ gts_allow_floating_vertices = TRUE;
+ gts_object_destroy (GTS_OBJECT (v1));
+ gts_object_destroy (GTS_OBJECT (v2));
+ gts_object_destroy (GTS_OBJECT (v3));
+ gts_allow_floating_vertices = FALSE;
+*/
+// return surface;
+}
+
+ToporouterBBox *
+toporouter_bbox_create(int layer, GSList *vertices, toporouter_term_t type, gpointer data)
+{
+ ToporouterBBox *bbox;
+ GtsSurface *s;
+ GtsTriangle *t;
+
+ delaunay_create_from_vertices(vertices, &s, &t);
+ bbox = TOPOROUTER_BBOX( gts_bbox_surface(GTS_BBOX_CLASS(toporouter_bbox_class()), s) );
+ bbox->type = type;
+ bbox->data = data;
+
+ bbox->surface = s;
+ bbox->enclosing = t;
+
+ bbox->layer = layer;
+
+ return bbox;
+}
+
+GSList *
+g_slist_insert_unique(GSList *list, void *data)
+{
+ GSList *i;
+
+ i = list;
+ while(i) {
+ if(i->data == data) return list;
+ i = i->next;
+ }
+
+ return g_slist_prepend(list, data);
+}
+
+struct delaunay_remove_similar_segment_data {
+ GtsSegment *seg;
+ GtsSurface *s;
+};
+
+gint
+delaunay_remove_similar_segment_func(gpointer item, gpointer data)
+{
+ struct delaunay_remove_similar_segment_data *d = data;
+ GtsPoint *p = GTS_POINT(item);
+
+ if(p->x == GTS_POINT(d->seg->v1)->x && p->y == GTS_POINT(d->seg->v1)->y) {
+ gts_delaunay_remove_vertex(d->s, GTS_VERTEX(item));
+ }else if(p->x == GTS_POINT(d->seg->v2)->x && p->y == GTS_POINT(d->seg->v2)->y) {
+ gts_delaunay_remove_vertex(d->s, GTS_VERTEX(item));
+ }
+
+ return 0;
+}
+
+void
+delaunay_remove_similar_segment(GtsSurface *s, GtsSegment *seg)
+{
+ struct delaunay_remove_similar_segment_data d;
+ d.seg = seg;
+ d.s = s;
+
+ gts_surface_foreach_vertex(s, delaunay_remove_similar_segment_func, &d);
+
+}
+
+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);
+ //TODO: edge_destroy(edge);
+}
+
+//#define TRACE_INS_CONST 1
+
+GSList *
+insert_constraint_edge_rec(toporouter_t *r, toporouter_layer_t *l, GtsVertex **v, ToporouterBBox *box)
+{
+ GtsEdgeClass *edge_class = GTS_EDGE_CLASS (toporouter_constraint_class ());
+ GSList *i, *ii, *rlist = NULL, *tempconstraints;
+ ToporouterConstraint *tc;
+ GtsEdge *e[2];
+ GtsVertex *newv, *tempv;
+ GtsVertex *p[2];
+ gdouble wind[4];
+ int j;
+
+ g_assert(v[0] != v[1]);
+
+#ifdef TRACE_INS_CONST
+ fprintf(stderr, "entering insert_constraint_edge_rec v0 = %f,%f v1 = %f,%f\n",
+ v[0]->p.x, v[0]->p.y,
+ v[1]->p.x, v[1]->p.y);
+#endif
+
+ /* detect edge intersections */
+ i = l->constraints;
+ while(i) {
+ tc = i->data;
+
+ for(j=0;j<2;j++)
+ wind[j] = vertex_wind(v[j], GTS_SEGMENT(tc)->v1, GTS_SEGMENT(tc)->v2);
+
+ wind[2] = vertex_wind(v[0], v[1], GTS_SEGMENT(tc)->v1);
+ wind[3] = vertex_wind(v[0], v[1], GTS_SEGMENT(tc)->v2);
+
+
+ if(wind[0] == 0 && wind[1] == 0) {
+ /* both points colinear */
+
+#ifdef TRACE_INS_CONST
+ fprintf(stderr, "\tboth points colinear with %f,%f - %f,%f\n",
+ tc->c.edge.segment.v1->p.x, tc->c.edge.segment.v1->p.y,
+ tc->c.edge.segment.v2->p.x, tc->c.edge.segment.v2->p.y);
+#endif
+
+ if(v[0] == GTS_SEGMENT(tc)->v1 && v[1] != GTS_SEGMENT(tc)->v2 &&
+ vertex_between(GTS_SEGMENT(tc)->v1, GTS_SEGMENT(tc)->v2, v[1]) ) {
+#ifdef TRACE_INS_CONST
+ fprintf(stderr, "\t\tv[0] == tc->v1 and v[1] in tc\n");
+#endif
+ l->constraints = g_slist_remove(l->constraints, tc);
+
+ e[0] = gts_edge_new (edge_class, GTS_SEGMENT(tc)->v2, v[1]);
+ TOPOROUTER_CONSTRAINT(e[0])->box = tc->box;
+ l->constraints = g_slist_prepend (l->constraints, e[0]);
+
+ gts_delaunay_add_vertex(tc->box->surface, v[1], NULL);
+
+ tc->box->constraints = g_slist_remove(tc->box->constraints, tc);
+ tc->box->constraints = g_slist_prepend(tc->box->constraints, e[0]);
+
+ gts_edge_remove(GTS_EDGE(tc));
+
+ tempconstraints = insert_constraint_edge_rec(r, l, v, box);
+ ii = tempconstraints;
+ while(ii) {
+ if(GTS_VERTEX(GTS_SEGMENT(ii->data)->v1) == v[0] && GTS_VERTEX(GTS_SEGMENT(ii->data)->v2) == v[1]) {
+ tc->box->constraints = g_slist_prepend(tc->box->constraints, TOPOROUTER_CONSTRAINT(ii->data));
+ break;
+ }else if(GTS_VERTEX(GTS_SEGMENT(ii->data)->v1) == v[1] && GTS_VERTEX(GTS_SEGMENT(ii->data)->v2) == v[0]) {
+ tc->box->constraints = g_slist_prepend(tc->box->constraints, TOPOROUTER_CONSTRAINT(ii->data));
+ break;
+ }
+
+ ii = ii->next;
+ }
+
+ return tempconstraints;
+
+ }else if(v[0] == GTS_SEGMENT(tc)->v2 && v[1] != GTS_SEGMENT(tc)->v1 &&
+ vertex_between(GTS_SEGMENT(tc)->v1, GTS_SEGMENT(tc)->v2, v[1]) ) {
+#ifdef TRACE_INS_CONST
+ fprintf(stderr, "\t\tv[0] == tc->v2 and v[1] in tc\n");
+#endif
+
+ l->constraints = g_slist_remove(l->constraints, tc);
+
+ e[0] = gts_edge_new (edge_class, GTS_SEGMENT(tc)->v1, v[1]);
+ TOPOROUTER_CONSTRAINT(e[0])->box = tc->box;
+ l->constraints = g_slist_prepend (l->constraints, e[0]);
+
+ gts_delaunay_add_vertex(tc->box->surface, v[1], NULL);
+
+ tc->box->constraints = g_slist_remove(tc->box->constraints, tc);
+ tc->box->constraints = g_slist_prepend(tc->box->constraints, e[0]);
+
+ gts_edge_remove(GTS_EDGE(tc));
+
+ tempconstraints = insert_constraint_edge_rec(r, l, v, box);
+ ii = tempconstraints;
+ while(ii) {
+ if(GTS_VERTEX(GTS_SEGMENT(ii->data)->v1) == v[0] && GTS_VERTEX(GTS_SEGMENT(ii->data)->v2) == v[1]) {
+ tc->box->constraints = g_slist_prepend(tc->box->constraints, TOPOROUTER_CONSTRAINT(ii->data));
+ break;
+ }else if(GTS_VERTEX(GTS_SEGMENT(ii->data)->v1) == v[1] && GTS_VERTEX(GTS_SEGMENT(ii->data)->v2) == v[0]) {
+ tc->box->constraints = g_slist_prepend(tc->box->constraints, TOPOROUTER_CONSTRAINT(ii->data));
+ break;
+ }
+
+ ii = ii->next;
+ }
+
+ return tempconstraints;
+
+ }else if(v[1] == GTS_SEGMENT(tc)->v1 && v[0] != GTS_SEGMENT(tc)->v2 &&
+ vertex_between(GTS_SEGMENT(tc)->v1, GTS_SEGMENT(tc)->v2, v[0] )) {
+#ifdef TRACE_INS_CONST
+ fprintf(stderr, "\t\tv[1] == tc->v1 and v[0] in tc\n");
+#endif
+
+ l->constraints = g_slist_remove(l->constraints, tc);
+
+ e[0] = gts_edge_new (edge_class, GTS_SEGMENT(tc)->v2, v[0]);
+ TOPOROUTER_CONSTRAINT(e[0])->box = tc->box;
+ l->constraints = g_slist_prepend (l->constraints, e[0]);
+
+ gts_delaunay_add_vertex(tc->box->surface, v[0], NULL);
+
+ tc->box->constraints = g_slist_remove(tc->box->constraints, tc);
+ tc->box->constraints = g_slist_prepend(tc->box->constraints, e[0]);
+
+ gts_edge_remove(GTS_EDGE(tc));
+
+ tempconstraints = insert_constraint_edge_rec(r, l, v, box);
+ ii = tempconstraints;
+ while(ii) {
+ if(GTS_VERTEX(GTS_SEGMENT(ii->data)->v1) == v[0] && GTS_VERTEX(GTS_SEGMENT(ii->data)->v2) == v[1]) {
+ tc->box->constraints = g_slist_prepend(tc->box->constraints, TOPOROUTER_CONSTRAINT(ii->data));
+ break;
+ }else if(GTS_VERTEX(GTS_SEGMENT(ii->data)->v1) == v[1] && GTS_VERTEX(GTS_SEGMENT(ii->data)->v2) == v[0]) {
+ tc->box->constraints = g_slist_prepend(tc->box->constraints, TOPOROUTER_CONSTRAINT(ii->data));
+ break;
+ }
+
+ ii = ii->next;
+ }
+
+ return tempconstraints;
+
+ }else if(v[1] == GTS_SEGMENT(tc)->v2 && v[0] != GTS_SEGMENT(tc)->v1 &&
+ vertex_between(GTS_SEGMENT(tc)->v1, GTS_SEGMENT(tc)->v2, v[0])) {
+#ifdef TRACE_INS_CONST
+ fprintf(stderr, "\t\tv[1] == tc->v2 and v[0] in tc\n");
+#endif
+
+ l->constraints = g_slist_remove(l->constraints, tc);
+
+ e[0] = gts_edge_new (edge_class, GTS_SEGMENT(tc)->v1, v[0]);
+ TOPOROUTER_CONSTRAINT(e[0])->box = tc->box;
+ l->constraints = g_slist_prepend (l->constraints, e[0]);
+
+ gts_delaunay_add_vertex(tc->box->surface, v[0], NULL);
+
+ tc->box->constraints = g_slist_remove(tc->box->constraints, tc);
+ tc->box->constraints = g_slist_prepend(tc->box->constraints, e[0]);
+
+ gts_edge_remove(GTS_EDGE(tc));
+
+ tempconstraints = insert_constraint_edge_rec(r, l, v, box);
+ ii = tempconstraints;
+ while(ii) {
+ if(GTS_VERTEX(GTS_SEGMENT(ii->data)->v1) == v[0] && GTS_VERTEX(GTS_SEGMENT(ii->data)->v2) == v[1]) {
+ tc->box->constraints = g_slist_prepend(tc->box->constraints, TOPOROUTER_CONSTRAINT(ii->data));
+ break;
+ }else if(GTS_VERTEX(GTS_SEGMENT(ii->data)->v1) == v[1] && GTS_VERTEX(GTS_SEGMENT(ii->data)->v2) == v[0]) {
+ tc->box->constraints = g_slist_prepend(tc->box->constraints, TOPOROUTER_CONSTRAINT(ii->data));
+ break;
+ }
+
+ ii = ii->next;
+ }
+
+ return tempconstraints;
+
+ }else if(v[0] != GTS_SEGMENT(tc)->v1 && v[1] != GTS_SEGMENT(tc)->v1 &&
+ v[0] != GTS_SEGMENT(tc)->v2 && v[1] != GTS_SEGMENT(tc)->v2 &&
+ vertex_between(GTS_SEGMENT(tc)->v1, GTS_SEGMENT(tc)->v2, v[0]) &&
+ vertex_between(GTS_SEGMENT(tc)->v1, GTS_SEGMENT(tc)->v2, v[1])
+ ){
+#ifdef TRACE_INS_CONST
+ fprintf(stderr, "\t\twithin segment\n");
+#endif
+
+ l->constraints = g_slist_remove(l->constraints, tc);
+
+ if(gts_point_distance(>S_SEGMENT(tc)->v1->p, &(v[0]->p)) <
+ gts_point_distance(>S_SEGMENT(tc)->v1->p, &(v[1]->p))) {
+ p[0] = v[0];
+ p[1] = v[1];
+ }else{
+ p[0] = v[1];
+ p[1] = v[0];
+ }
+
+ e[0] = gts_edge_new (edge_class, GTS_SEGMENT(tc)->v1, p[0]);
+ e[1] = gts_edge_new (edge_class, GTS_SEGMENT(tc)->v2, p[1]);
+ TOPOROUTER_CONSTRAINT(e[0])->box = TOPOROUTER_CONSTRAINT(e[1])->box = tc->box;
+ l->constraints = g_slist_prepend (l->constraints, e[0]);
+ l->constraints = g_slist_prepend (l->constraints, e[1]);
+
+ gts_delaunay_add_vertex(tc->box->surface, p[0], NULL);
+ gts_delaunay_add_vertex(tc->box->surface, p[1], NULL);
+
+ tc->box->constraints = g_slist_remove(tc->box->constraints, tc);
+ tc->box->constraints = g_slist_prepend(tc->box->constraints, e[0]);
+ tc->box->constraints = g_slist_prepend(tc->box->constraints, e[1]);
+
+ gts_edge_remove(GTS_EDGE(tc));
+
+ tempconstraints = insert_constraint_edge_rec(r, l, v, box);
+ ii = tempconstraints;
+ while(ii) {
+ if(GTS_VERTEX(GTS_SEGMENT(ii->data)->v1) == p[0] && GTS_VERTEX(GTS_SEGMENT(ii->data)->v2) == p[1]) {
+ tc->box->constraints = g_slist_prepend(tc->box->constraints, TOPOROUTER_CONSTRAINT(ii->data));
+ break;
+ }else if(GTS_VERTEX(GTS_SEGMENT(ii->data)->v1) == p[1] && GTS_VERTEX(GTS_SEGMENT(ii->data)->v2) == p[0]) {
+ tc->box->constraints = g_slist_prepend(tc->box->constraints, TOPOROUTER_CONSTRAINT(ii->data));
+ break;
+ }
+
+ ii = ii->next;
+ }
+
+ return tempconstraints;
+
+ }
+ }
+ if( v[1] != GTS_SEGMENT(tc)->v2 && v[0] != GTS_SEGMENT(tc)->v2 &&
+ v[1] != GTS_SEGMENT(tc)->v1 && v[0] != GTS_SEGMENT(tc)->v1 ) {
+
+ if(!wind[0] && wind[1] &&
+ vertex_between(GTS_SEGMENT(tc)->v1, GTS_SEGMENT(tc)->v2, v[0])) {
+
+ /* v[0] lies on tc segment and v[1] does not */
+#ifdef TRACE_INS_CONST
+ fprintf(stderr, "\tv[0] lies on tc segment\n");
+#endif
+ l->constraints = g_slist_remove(l->constraints, tc);
+
+ e[0] = gts_edge_new (edge_class, GTS_SEGMENT(tc)->v1, v[0]);
+ e[1] = gts_edge_new (edge_class, GTS_SEGMENT(tc)->v2, v[0]);
+ TOPOROUTER_CONSTRAINT(e[0])->box = TOPOROUTER_CONSTRAINT(e[1])->box = tc->box;
+ l->constraints = g_slist_prepend (l->constraints, e[0]);
+ l->constraints = g_slist_prepend (l->constraints, e[1]);
+
+ gts_delaunay_add_vertex(tc->box->surface, v[0], NULL);
+
+ tc->box->constraints = g_slist_remove(tc->box->constraints, tc);
+ tc->box->constraints = g_slist_prepend(tc->box->constraints, e[0]);
+ tc->box->constraints = g_slist_prepend(tc->box->constraints, e[1]);
+
+ gts_edge_remove(GTS_EDGE(tc));
+
+ return insert_constraint_edge_rec(r, l, v, box);
+
+ }else if(!wind[1] && wind[0] &&
+ vertex_between(GTS_SEGMENT(tc)->v1, GTS_SEGMENT(tc)->v2, v[1])) {
+
+ /* v[1] lies on tc segment and v[0] does not */
+#ifdef TRACE_INS_CONST
+ fprintf(stderr, "\tv[1] lies on tc segment\n");
+#endif
+ l->constraints = g_slist_remove(l->constraints, tc);
+
+ e[0] = gts_edge_new (edge_class, GTS_SEGMENT(tc)->v1, v[1]);
+ e[1] = gts_edge_new (edge_class, GTS_SEGMENT(tc)->v2, v[1]);
+ TOPOROUTER_CONSTRAINT(e[0])->box = TOPOROUTER_CONSTRAINT(e[1])->box = tc->box;
+ l->constraints = g_slist_prepend (l->constraints, e[0]);
+ l->constraints = g_slist_prepend (l->constraints, e[1]);
+
+ gts_delaunay_add_vertex(tc->box->surface, v[1], NULL);
+
+ tc->box->constraints = g_slist_remove(tc->box->constraints, tc);
+ tc->box->constraints = g_slist_prepend(tc->box->constraints, e[0]);
+ tc->box->constraints = g_slist_prepend(tc->box->constraints, e[1]);
+
+ gts_edge_remove(GTS_EDGE(tc));
+
+ return insert_constraint_edge_rec(r, l, v, box);
+
+ }else if(!wind[2] && wind[3] &&
+ vertex_between(v[0], v[1], GTS_SEGMENT(tc)->v1)) {
+
+ /* tc v1 lies on segment, tc v2 does not */
+#ifdef TRACE_INS_CONST
+ fprintf(stderr, "\ttc v1 lies on segment, tc v2 does not\n");
+ fprintf(stderr, "\ttc = %f,%f - %f,%f\n",
+ GTS_SEGMENT(tc)->v1->p.x, GTS_SEGMENT(tc)->v1->p.y,
+ GTS_SEGMENT(tc)->v2->p.x, GTS_SEGMENT(tc)->v2->p.y);
+#endif
+
+ p[0] = GTS_SEGMENT(tc)->v1;
+ p[1] = v[0];
+ rlist = insert_constraint_edge_rec(r, l, p, box);
+ p[1] = v[1];
+ return g_slist_concat(rlist, insert_constraint_edge_rec(r, l, p, box));
+
+ }else if(!wind[3] && wind[2] &&
+ vertex_between(v[0], v[1], GTS_SEGMENT(tc)->v2)) {
+
+ /* tc v2 lies on segment, tc v1 does not */
+#ifdef TRACE_INS_CONST
+ fprintf(stderr, "\ttc v2 lies on segment, tc v1 does not\n");
+ fprintf(stderr, "\ttc = %f,%f - %f,%f\n",
+ GTS_SEGMENT(tc)->v1->p.x, GTS_SEGMENT(tc)->v1->p.y,
+ GTS_SEGMENT(tc)->v2->p.x, GTS_SEGMENT(tc)->v2->p.y);
+#endif
+
+ p[0] = GTS_SEGMENT(tc)->v2;
+ p[1] = v[0];
+ rlist = insert_constraint_edge_rec(r, l, p, box);
+ p[1] = v[1];
+ return g_slist_concat(rlist, insert_constraint_edge_rec(r, l, p, box));
+
+ }else if(vertex_intersect_prop(v[0], v[1], GTS_SEGMENT(tc)->v1, GTS_SEGMENT(tc)->v2)) {
+ /* proper intersection */
+#ifdef TRACE_INS_CONST
+ fprintf(stderr, "\tproper intersection with %f,%f - %f,%f\n",
+ GTS_SEGMENT(tc)->v1->p.x, GTS_SEGMENT(tc)->v1->p.y,
+ GTS_SEGMENT(tc)->v2->p.x, GTS_SEGMENT(tc)->v2->p.y);
+#endif
+
+ l->constraints = g_slist_remove(l->constraints, tc);
+
+ newv = vertex_intersect(v[0], v[1], GTS_SEGMENT(tc)->v1, GTS_SEGMENT(tc)->v2);
+
+ ii = l->vertices;
+ while (ii) {
+ tempv = ii->data;
+ if(tempv->p.x == newv->p.x && tempv->p.y == newv->p.y) {
+ free(newv);
+ newv = tempv;
+ goto insert_constraint_proper_intersection_cont;
+ }
+ ii = ii->next;
+ }
+#ifdef TRACE_INS_CONST
+ fprintf(stderr, "\tproceeding with new vertex\n");
+#endif
+ TOPOROUTER_VERTEX(newv)->boxes = g_slist_insert_unique(NULL, box);
+ TOPOROUTER_VERTEX(newv)->boxes = g_slist_insert_unique(TOPOROUTER_VERTEX(newv)->boxes, tc->box);
+ l->vertices = g_slist_prepend(l->vertices, newv);
+
+insert_constraint_proper_intersection_cont:
+
+ e[0] = gts_edge_new (edge_class, GTS_SEGMENT(tc)->v1, newv);
+ e[1] = gts_edge_new (edge_class, GTS_SEGMENT(tc)->v2, newv);
+ TOPOROUTER_CONSTRAINT(e[0])->box = TOPOROUTER_CONSTRAINT(e[1])->box = tc->box;
+ l->constraints = g_slist_prepend (l->constraints, e[0]);
+ l->constraints = g_slist_prepend (l->constraints, e[1]);
+
+ gts_delaunay_add_vertex(tc->box->surface, newv, NULL);
+
+ tc->box->constraints = g_slist_remove(tc->box->constraints, tc);
+ tc->box->constraints = g_slist_prepend(tc->box->constraints, e[0]);
+ tc->box->constraints = g_slist_prepend(tc->box->constraints, e[1]);
+
+ gts_edge_remove(GTS_EDGE(tc));
+
+ p[0] = newv;
+ p[1] = v[0];
+ rlist = insert_constraint_edge_rec(r, l, p, box);
+ p[1] = v[1];
+ return g_slist_concat(rlist, insert_constraint_edge_rec(r, l, p, box));
+
+ }
+ }
+
+ i = i->next;
+ }
+
+ /* if we get to here, there were no problems, continue creating edge */
+
+ /* check no points lie in edge */
+ ii = l->vertices;
+ while (ii) {
+ tempv = GTS_VERTEX(ii->data);
+ if(tempv != v[0] && tempv != v[1] && vertex_between(v[0], v[1], tempv)) {
+#ifdef TRACE_INS_CONST
+ fprintf(stderr, "\tpoint in edge\n");
+#endif
+ p[0] = tempv;
+ p[1] = v[0];
+ rlist = insert_constraint_edge_rec(r, l, p, box);
+ p[1] = v[1];
+ return g_slist_concat(rlist, insert_constraint_edge_rec(r, l, p, box));
+ }
+ ii = ii->next;
+ }
+
+#ifdef TRACE_INS_CONST
+ fprintf(stderr, "\tno probs, creating edge\n");
+#endif
+
+ e[0] = gts_edge_new (edge_class, v[0], v[1]);
+ TOPOROUTER_CONSTRAINT(e[0])->box = box;
+ l->constraints = g_slist_prepend (l->constraints, e[0]);
+
+ return g_slist_prepend(NULL, e[0]);
+}
+
+GtsVertex *
+insert_vertex(toporouter_t *r, toporouter_layer_t *l, gdouble x, gdouble y, ToporouterBBox *box)
+{
+ GtsVertexClass *vertex_class = GTS_VERTEX_CLASS (toporouter_vertex_class ());
+ GtsVertex *v;
+ GSList *i;
+
+ i = l->vertices;
+ while (i) {
+ v = i->data;
+ if(v->p.x == x && v->p.y == y)
+ return v;
+ i = i->next;
+ }
+
+ v = gts_vertex_new (vertex_class , x, y, 0.0f);
+ TOPOROUTER_VERTEX(v)->boxes = g_slist_prepend(NULL, box);
+ l->vertices = g_slist_prepend(l->vertices, v);
+
+ return v;
+}
+
+GSList *
+insert_constraint_edge(toporouter_t *r, toporouter_layer_t *l, gdouble x1, gdouble y1, guint flags1, gdouble x2, gdouble y2, guint flags2, ToporouterBBox *box)
+{
+ GtsVertexClass *vertex_class = GTS_VERTEX_CLASS (toporouter_vertex_class ());
+ GtsVertex *p[2];
+ GtsVertex *v;
+ GSList *i;
+
+ p[0] = p[1] = NULL;
+
+ /* insert or find points */
+
+ i = l->vertices;
+ while (i) {
+ v = i->data;
+ if(v->p.x == x1 && v->p.y == y1)
+ p[0] = v;
+ if(v->p.x == x2 && v->p.y == y2)
+ p[1] = v;
+ i = i->next;
+ }
+
+ if(p[0] == NULL) {
+ p[0] = gts_vertex_new (vertex_class, x1, y1, 0.0f);
+ TOPOROUTER_VERTEX(p[0])->boxes = g_slist_prepend(NULL, box);
+ l->vertices = g_slist_prepend(l->vertices, p[0]);
+ }
+ if(p[1] == NULL) {
+ p[1] = gts_vertex_new (vertex_class, x2, y2, 0.0f);
+ TOPOROUTER_VERTEX(p[1])->boxes = g_slist_prepend(NULL, box);
+ l->vertices = g_slist_prepend(l->vertices, p[1]);
+ }
+
+ TOPOROUTER_VERTEX(p[0])->flags = flags1;
+ TOPOROUTER_VERTEX(p[1])->flags = flags2;
+
+ return insert_constraint_edge_rec(r, l, p, box);
+}
+
+void
+insert_constraints_from_list(toporouter_t *r, toporouter_layer_t *l, GSList *vlist, ToporouterBBox *box)
+{
+ GSList *i = vlist;
+ GtsPoint *curpoint = GTS_POINT(i->data);
+
+ i = i->next;
+ while(i) {
+ box->constraints = g_slist_concat(box->constraints,
+ insert_constraint_edge(r, l,
+ curpoint->x, curpoint->y, TOPOROUTER_VERTEX(curpoint)->flags,
+ GTS_POINT(i->data)->x, GTS_POINT(i->data)->y, TOPOROUTER_VERTEX(i->data)->flags,
+ box));
+ curpoint = GTS_POINT(i->data);
+ i = i->next;
+ }
+
+ box->constraints = g_slist_concat(box->constraints,
+ insert_constraint_edge(r, l,
+ curpoint->x, curpoint->y, TOPOROUTER_VERTEX(curpoint)->flags,
+ GTS_POINT(vlist->data)->x, GTS_POINT(vlist->data)->y, TOPOROUTER_VERTEX(vlist->data)->flags,
+ box));
+
+}
+
+void
+insert_centre_point(toporouter_t *r, toporouter_layer_t *l, gdouble x, gdouble y)
+{
+ GSList *i;
+ GtsVertexClass *vertex_class = GTS_VERTEX_CLASS (toporouter_vertex_class ());
+
+ i = l->vertices;
+ while (i) {
+ GtsPoint *p = GTS_POINT(i->data);
+ if(p->x == x && p->y == y)
+ return;
+ i = i->next;
+ }
+
+ l->vertices = g_slist_prepend(l->vertices, gts_vertex_new(vertex_class, x, y, 0.0f));
+}
+
+GtsPoint *
+midpoint(GtsPoint *a, GtsPoint *b)
+{
+ return gts_point_new(gts_point_class(), (a->x + b->x) / 2., (a->y + b->y) / 2., 0.);
+}
+/*
+GSList *
+vertex_polygon_attachment_list(ToporouterBBox *b, toporouter_layer_t *l)
+{
+ GtsVertexClass *vertex_class = GTS_VERTEX_CLASS (toporouter_vertex_class ());
+ ToporouterVertex *v = TOPOROUTER_VERTEX(b->point);
+ GSList *attach = NULL;
+ GSList *i = b->constraints;
+ gdouble tempwind;
+ gdouble r1;
+ gdouble r = (lookup_thickness(((PinType*)b->data)->Name) / 2.)
+ + lookup_keepaway(((PinType*)b->data)->Name);
+
+ while(i) {
+ GtsVertex* temp = gts_segment_midvertex(GTS_SEGMENT(i->data), vertex_class);
+ gdouble m = segment_gradient(GTS_SEGMENT(i->data));
+ ToporouterVertex *b0, *b1;
+ gdouble seglength = gts_point_distance(GTS_POINT(GTS_SEGMENT(i->data)->v1),
+ GTS_POINT(GTS_SEGMENT(i->data)->v2));
+
+ if(r >= seglength / 2.) {
+ TOPOROUTER_VERTEX(GTS_SEGMENT(i->data)->v1)->flags |= VERTEX_FLAG_ATTACH;
+ TOPOROUTER_VERTEX(GTS_SEGMENT(i->data)->v2)->flags |= VERTEX_FLAG_ATTACH;
+ gts_object_destroy (GTS_OBJECT (temp));
+ i = i->next;
+ continue;
+ }
+
+ b0 = TOPOROUTER_VERTEX( gts_vertex_new (vertex_class, 0., 0., 0.) );
+ b1 = TOPOROUTER_VERTEX( gts_vertex_new (vertex_class, 0., 0., 0.) );
+
+ points_on_line(GTS_POINT(temp), m, r, GTS_POINT(b0), GTS_POINT(b1));
+
+ TOPOROUTER_VERTEX(b0)->flags = VERTEX_FLAG_ATTACH;
+ TOPOROUTER_VERTEX(b1)->flags = VERTEX_FLAG_ATTACH;
+
+ attach = g_slist_prepend(attach, b0);
+ attach = g_slist_prepend(attach, b1);
+
+ gts_object_destroy (GTS_OBJECT (temp));
+
+ i = i->next;
+ }
+
+ return attach;
+}
+*/
+
+inline gdouble
+pad_rad(PadType *pad)
+{
+ return (lookup_thickness(pad->Name) / 2.) + lookup_keepaway(pad->Name);
+}
+
+inline gdouble
+pin_rad(PinType *pin)
+{
+ return (lookup_thickness(pin->Name) / 2.) + lookup_keepaway(pin->Name);
+}
+
+GSList *
+rect_with_attachments(gdouble rad,
+ gdouble x0, gdouble y0,
+ gdouble x1, gdouble y1,
+ gdouble x2, gdouble y2,
+ gdouble x3, gdouble y3)
+{
+ GtsVertexClass *vertex_class = GTS_VERTEX_CLASS (toporouter_vertex_class ());
+ GSList *r = NULL, *rr = NULL, *i;
+ ToporouterVertex *curpoint, *temppoint;
+
+
+ curpoint = TOPOROUTER_VERTEX(gts_vertex_new (vertex_class, x0, y0, 0.));
+
+ r = g_slist_prepend(NULL, curpoint);
+ r = g_slist_prepend(r, gts_vertex_new (vertex_class, x1, y1, 0.));
+ r = g_slist_prepend(r, gts_vertex_new (vertex_class, x2, y2, 0.));
+ r = g_slist_prepend(r, gts_vertex_new (vertex_class, x3, y3, 0.));
+
+ i = r;
+ while(i) {
+ GtsPoint *midp = midpoint(GTS_POINT(i->data), GTS_POINT(curpoint));
+ ToporouterVertex *b0;//, *b1;
+// gdouble seglength = gts_point_distance(GTS_POINT(i->data), GTS_POINT(curpoint));
+// gdouble m = cartesian_gradient( GTS_POINT(i->data)->x, GTS_POINT(i->data)->y,
+// GTS_POINT(curpoint)->x, GTS_POINT(curpoint)->y);
+
+ temppoint = TOPOROUTER_VERTEX(i->data);
+ rr = g_slist_prepend(rr, curpoint);
+/*
+ if(rad >= seglength / 3.) {
+ curpoint->flags |= VERTEX_FLAG_ATTACH;
+ temppoint->flags |= VERTEX_FLAG_ATTACH;
+ }else{
+
+ b0 = TOPOROUTER_VERTEX( gts_vertex_new (vertex_class, 0., 0., 0.) );
+ b1 = TOPOROUTER_VERTEX( gts_vertex_new (vertex_class, 0., 0., 0.) );
+
+ points_on_line(midp, m, rad, GTS_POINT(b0), GTS_POINT(b1));
+
+ TOPOROUTER_VERTEX(b0)->flags |= VERTEX_FLAG_ATTACH;
+ TOPOROUTER_VERTEX(b1)->flags |= VERTEX_FLAG_ATTACH;
+
+ if(gts_point_distance(GTS_POINT(curpoint), GTS_POINT(b0)) < gts_point_distance(GTS_POINT(curpoint), GTS_POINT(b1))) {
+ rr = g_slist_prepend(rr, b0);
+ rr = g_slist_prepend(rr, b1);
+ }else{
+ rr = g_slist_prepend(rr, b1);
+ rr = g_slist_prepend(rr, b0);
+ }
+ }
+*/
+ b0 = TOPOROUTER_VERTEX( gts_vertex_new (vertex_class, midp->x, midp->y, 0.) );
+ TOPOROUTER_VERTEX(b0)->flags |= VERTEX_FLAG_ATTACH;
+ //TOPOROUTER_VERTEX(b0)->attachcentre = centre;
+
+ rr = g_slist_prepend(rr, b0);
+
+ gts_object_destroy (GTS_OBJECT (midp));
+
+ curpoint = temppoint;
+ i = i->next;
+ }
+
+ g_slist_free(r);
+
+ return rr;
+}
+
+#define VERTEX_CENTRE(x) TOPOROUTER_VERTEX( vertex_bbox(x)->point )
+
+/*
+ * Read pad data from layer into toporouter_layer_t struct
+ *
+ * Inserts points and constraints into GSLists
+ */
+int
+read_pads(toporouter_t *r, toporouter_layer_t *l, int layer)
+{
+ toporouter_spoint_t p[2], rv[5];
+ gdouble x[2], y[2], t, m;
+ GSList *objectconstraints;
+
+ GSList *vlist = NULL;
+ ToporouterBBox *bbox = NULL;
+
+ int front = GetLayerGroupNumberByNumber (max_layer + COMPONENT_LAYER);
+ int back = GetLayerGroupNumberByNumber (max_layer + SOLDER_LAYER);
+
+ /* If its not the top or bottom layer, there are no pads to read */
+ if(layer != front && layer != back) return 0;
+
+ ELEMENT_LOOP(PCB->Data);
+ {
+ PAD_LOOP(element);
+ {
+ if( (layer == back && TEST_FLAG(ONSOLDERFLAG, pad)) ||
+ (layer == front && !TEST_FLAG(ONSOLDERFLAG, pad)) ) {
+
+
+ objectconstraints = NULL;
+ t = (gdouble)pad->Thickness / 2.0f;
+ x[0] = pad->Point1.X;
+ x[1] = pad->Point2.X;
+ y[0] = pad->Point1.Y;
+ y[1] = pad->Point2.Y;
+
+ if(r->flags & TOPOROUTER_FLAG_DEBUG_CDTS)
+ printf("Pad P1=%f,%f P2=%f,%f\n\t ", x[0], y[0], x[1], y[1] );
+
+ if(TEST_FLAG(SQUAREFLAG, pad)) {
+ /* Square or oblong pad. Four points and four constraint edges are
+ * used */
+
+ if(x[0] == x[1] && y[0] == y[1]) {
+ /* Pad is square */
+
+// vlist = g_slist_prepend(NULL, gts_vertex_new (vertex_class, x[0]-t, y[0]-t, 0.));
+// vlist = g_slist_prepend(vlist, gts_vertex_new (vertex_class, x[0]-t, y[0]+t, 0.));
+// vlist = g_slist_prepend(vlist, gts_vertex_new (vertex_class, x[0]+t, y[0]+t, 0.));
+// vlist = g_slist_prepend(vlist, gts_vertex_new (vertex_class, x[0]+t, y[0]-t, 0.));
+ vlist = rect_with_attachments(pad_rad(pad),
+ x[0]-t, y[0]-t,
+ x[0]-t, y[0]+t,
+ x[0]+t, y[0]+t,
+ x[0]+t, y[0]-t);
+ bbox = toporouter_bbox_create(layer, vlist, PAD, pad);
+ r->bboxes = g_slist_prepend(r->bboxes, bbox);
+ insert_constraints_from_list(r, l, vlist, bbox);
+ g_slist_free(vlist);
+
+ //bbox->point = GTS_POINT( gts_vertex_new(vertex_class, x[0], y[0], 0.) );
+ bbox->point = GTS_POINT( insert_vertex(r, l, x[0], y[0], bbox) );
+
+ }else {
+ /* Pad is diagonal oblong or othogonal oblong */
+
+ m = cartesian_gradient(x[0], y[0], x[1], y[1]);
+
+ p[0].x = x[0]; p[0].y = y[0];
+ p[1].x = x[1]; p[1].y = y[1];
+
+ vertex_outside_segment(&p[0], &p[1], t, &rv[0]);
+ vertices_on_line(&rv[0], perpendicular_gradient(m), t, &rv[1], &rv[2]);
+
+ vertex_outside_segment(&p[1], &p[0], t, &rv[0]);
+ vertices_on_line(&rv[0], perpendicular_gradient(m), t, &rv[3], &rv[4]);
+
+ if(wind(&rv[1], &rv[2], &rv[3]) != wind(&rv[2], &rv[3], &rv[4])) {
+ rv[0].x = rv[3].x; rv[0].y = rv[3].y;
+ rv[3].x = rv[4].x; rv[3].y = rv[4].y;
+ rv[4].x = rv[0].x; rv[4].y = rv[0].y;
+ }
+
+// vlist = g_slist_prepend(NULL, gts_vertex_new (vertex_class, rv[1].x, rv[1].y, 0.));
+// vlist = g_slist_prepend(vlist, gts_vertex_new (vertex_class, rv[2].x, rv[2].y, 0.));
+// vlist = g_slist_prepend(vlist, gts_vertex_new (vertex_class, rv[3].x, rv[3].y, 0.));
+// vlist = g_slist_prepend(vlist, gts_vertex_new (vertex_class, rv[4].x, rv[4].y, 0.));
+ vlist = rect_with_attachments(pad_rad(pad),
+ rv[1].x, rv[1].y,
+ rv[2].x, rv[2].y,
+ rv[3].x, rv[3].y,
+ rv[4].x, rv[4].y);
+ bbox = toporouter_bbox_create(layer, vlist, PAD, pad);
+ r->bboxes = g_slist_prepend(r->bboxes, bbox);
+ insert_constraints_from_list(r, l, vlist, bbox);
+ g_slist_free(vlist);
+
+ //bbox->point = GTS_POINT( gts_vertex_new(vertex_class, (x[0] + x[1]) / 2., (y[0] + y[1]) / 2., 0.) );
+ bbox->point = GTS_POINT( insert_vertex(r, l, (x[0] + x[1]) / 2., (y[0] + y[1]) / 2., bbox) );
+
+ }
+
+ }else {
+ /* Either round pad or pad with curved edges */
+
+ if(x[0] == x[1] && y[0] == y[1]) {
+ /* One point */
+
+ /* bounding box same as square pad */
+ vlist = rect_with_attachments(pad_rad(pad),
+ x[0]-t, y[0]-t,
+ x[0]-t, y[0]+t,
+ x[0]+t, y[0]+t,
+ x[0]+t, y[0]-t);
+ bbox = toporouter_bbox_create(layer, vlist, PAD, pad);
+ r->bboxes = g_slist_prepend(r->bboxes, bbox);
+ g_slist_free(vlist);
+
+ //bbox->point = GTS_POINT( insert_vertex(r, l, x[0], y[0], bbox) );
+ bbox->point = GTS_POINT( insert_vertex(r, l, x[0], y[0], bbox) );
+
+ }else{
+ /* Two points and one constraint edge */
+
+ /* the rest is just for bounding box */
+ m = cartesian_gradient(x[0], y[0], x[1], y[1]);
+
+ p[0].x = x[0]; p[0].y = y[0];
+ p[1].x = x[1]; p[1].y = y[1];
+
+ vertex_outside_segment(&p[0], &p[1], t, &rv[0]);
+ vertices_on_line(&rv[0], perpendicular_gradient(m), t, &rv[1], &rv[2]);
+
+ vertex_outside_segment(&p[1], &p[0], t, &rv[0]);
+ vertices_on_line(&rv[0], perpendicular_gradient(m), t, &rv[3], &rv[4]);
+
+ if(wind(&rv[1], &rv[2], &rv[3]) != wind(&rv[2], &rv[3], &rv[4])) {
+ rv[0].x = rv[3].x; rv[0].y = rv[3].y;
+ rv[3].x = rv[4].x; rv[3].y = rv[4].y;
+ rv[4].x = rv[0].x; rv[4].y = rv[0].y;
+ }
+
+ vlist = rect_with_attachments(pad_rad(pad),
+ rv[1].x, rv[1].y,
+ rv[2].x, rv[2].y,
+ rv[3].x, rv[3].y,
+ rv[4].x, rv[4].y);
+ bbox = toporouter_bbox_create(layer, vlist, PAD, pad);
+ r->bboxes = g_slist_prepend(r->bboxes, bbox);
+ insert_constraints_from_list(r, l, vlist, bbox);
+ g_slist_free(vlist);
+
+ //bbox->point = GTS_POINT( gts_vertex_new(vertex_class, (x[0] + x[1]) / 2., (y[0] + y[1]) / 2., 0.) );
+ bbox->point = GTS_POINT( insert_vertex(r, l, (x[0] + x[1]) / 2., (y[0] + y[1]) / 2., bbox) );
+
+ //bbox->constraints = g_slist_concat(bbox->constraints, insert_constraint_edge(r, l, x[0], y[0], x[1], y[1], bbox));
+
+ }
+
+
+ }
+
+ }
+ }
+ END_LOOP;
+ }
+ END_LOOP;
+
+ return 0;
+}
+
+/*
+ * Read points data (all layers) into GSList
+ *
+ * Inserts pin and via points
+ */
+int
+read_points(toporouter_t *r, toporouter_layer_t *l, int layer)
+{
+ gdouble x, y, t;
+
+ GSList *vlist = NULL;
+ ToporouterBBox *bbox = NULL;
+
+ ELEMENT_LOOP(PCB->Data);
+ {
+ PIN_LOOP(element);
+ {
+
+ t = (gdouble)pin->Thickness / 2.0f;
+ x = pin->X;
+ y = pin->Y;
+
+ if(TEST_FLAG (SQUAREFLAG, pin)) {
+
+ vlist = rect_with_attachments(pin_rad(pin),
+ x-t, y-t,
+ x-t, y+t,
+ x+t, y+t,
+ x+t, y-t);
+ bbox = toporouter_bbox_create(layer, vlist, PIN, pin);
+ r->bboxes = g_slist_prepend(r->bboxes, bbox);
+ insert_constraints_from_list(r, l, vlist, bbox);
+ g_slist_free(vlist);
+ bbox->point = GTS_POINT( insert_vertex(r, l, x, y, bbox) );
+
+ }else if(TEST_FLAG(OCTAGONFLAG, pin)){
+ /* TODO: Handle octagon pins */
+ fprintf(stderr, "No support for octagon pins yet\n");
+ }else{
+ vlist = rect_with_attachments(pin_rad(pin),
+ x-t, y-t,
+ x-t, y+t,
+ x+t, y+t,
+ x+t, y-t);
+ bbox = toporouter_bbox_create(layer, vlist, PIN, pin);
+ r->bboxes = g_slist_prepend(r->bboxes, bbox);
+ g_slist_free(vlist);
+ bbox->point = GTS_POINT( insert_vertex(r, l, x, y, bbox) );
+ }
+ }
+ END_LOOP;
+ }
+ END_LOOP;
+
+ VIA_LOOP(PCB->Data);
+ {
+
+ t = (gdouble)via->Thickness / 2.0f;
+ x = via->X;
+ y = via->Y;
+
+ if(TEST_FLAG (SQUAREFLAG, via)) {
+
+ vlist = rect_with_attachments(pin_rad((PinType*)via),
+ x-t, y-t,
+ x-t, y+t,
+ x+t, y+t,
+ x+t, y-t);
+ bbox = toporouter_bbox_create(layer, vlist, VIA, via);
+ r->bboxes = g_slist_prepend(r->bboxes, bbox);
+ insert_constraints_from_list(r, l, vlist, bbox);
+ g_slist_free(vlist);
+ bbox->point = GTS_POINT( insert_vertex(r, l, x, y, bbox) );
+
+ }else if(TEST_FLAG(OCTAGONFLAG, via)){
+ /* TODO: Handle octagon vias */
+ fprintf(stderr, "No support for octagon vias yet\n");
+ }else{
+
+ vlist = rect_with_attachments(pin_rad((PinType*)via),
+ x-t, y-t,
+ x-t, y+t,
+ x+t, y+t,
+ x+t, y-t);
+ bbox = toporouter_bbox_create(layer, vlist, VIA, via);
+ r->bboxes = g_slist_prepend(r->bboxes, bbox);
+ g_slist_free(vlist);
+
+ bbox->point = GTS_POINT( insert_vertex(r, l, x, y, bbox) );
+
+ }
+ }
+ END_LOOP;
+ return 0;
+}
+
+/*
+ * Read line data from layer into toporouter_layer_t struct
+ *
+ * Inserts points and constraints into GSLists
+ */
+int
+read_lines(toporouter_t *r, toporouter_layer_t *l, LayerType *layer, int ln)
+{
+ gdouble xs[2], ys[2];
+
+ GSList *vlist = NULL;
+ ToporouterBBox *bbox = NULL;
+
+ GtsVertexClass *vertex_class = GTS_VERTEX_CLASS (toporouter_vertex_class ());
+
+ LINE_LOOP(layer);
+ {
+ xs[0] = line->Point1.X;
+ xs[1] = line->Point2.X;
+ ys[0] = line->Point1.Y;
+ ys[1] = line->Point2.Y;
+ if(!(xs[0] == xs[1] && ys[0] == ys[1])) {
+ vlist = g_slist_prepend(NULL, gts_vertex_new (vertex_class, xs[0], ys[0], 0.));
+ vlist = g_slist_prepend(vlist, gts_vertex_new (vertex_class, xs[1], ys[1], 0.));
+ bbox = toporouter_bbox_create(ln, vlist, LINE, line);
+ r->bboxes = g_slist_prepend(r->bboxes, bbox);
+ g_slist_free(vlist);
+
+ bbox->constraints = g_slist_concat(bbox->constraints, insert_constraint_edge(r, l, xs[0], ys[0], 0, xs[1], ys[1], 0, bbox));
+ printf("length of line constraints = %d\n", g_slist_length(bbox->constraints));
+ }
+ }
+ END_LOOP;
+
+ return 0;
+}
+
+int
+read_board_constraints(toporouter_t *r, toporouter_layer_t *l, int layer)
+{
+ GtsVertexClass *vertex_class = GTS_VERTEX_CLASS (toporouter_vertex_class ());
+ GSList *vlist = NULL;
+ ToporouterBBox *bbox = NULL;
+
+ /* Add points for corners of board, and constrain those edges */
+ vlist = g_slist_prepend(NULL, gts_vertex_new (vertex_class, 0., 0., 0.));
+ vlist = g_slist_prepend(vlist, gts_vertex_new (vertex_class, PCB->MaxWidth, 0., 0.));
+ vlist = g_slist_prepend(vlist, gts_vertex_new (vertex_class, PCB->MaxWidth, PCB->MaxHeight, 0.));
+ vlist = g_slist_prepend(vlist, gts_vertex_new (vertex_class, 0., PCB->MaxHeight, 0.));
+ bbox = toporouter_bbox_create(layer, vlist, BOARD, NULL);
+ r->bboxes = g_slist_prepend(r->bboxes, bbox);
+ insert_constraints_from_list(r, l, vlist, bbox);
+ g_slist_free(vlist);
+
+ return 0;
+}
+
+gdouble
+triangle_cost(GtsTriangle *t, gpointer *data){
+
+ gdouble *min_quality = data[0];
+ gdouble *max_area = data[1];
+ gdouble quality = gts_triangle_quality(t);
+ gdouble area = gts_triangle_area(t);
+ gdouble min_edge = (PCB->Bloat + PCB->minWid) * 0.9;
+
+// fprintf(stderr, "quality = %f, area = %f \n",
+// quality, area);
+ if( gts_point_distance( &t->e1->segment.v1->p, &t->e1->segment.v2->p ) < min_edge )
+ return 0.0;
+ if( gts_point_distance( &t->e2->segment.v1->p, &t->e2->segment.v2->p ) < min_edge )
+ return 0.0;
+ if( gts_point_distance( &t->e3->segment.v1->p, &t->e3->segment.v2->p ) < min_edge )
+ return 0.0;
+
+ if (quality < *min_quality || area > *max_area)
+ return quality;
+ return 0.0;
+}
+
+
+
+
+void
+build_cdt(toporouter_t *r, toporouter_layer_t *l)
+{
+ /* TODO: generalize into surface *cdt_create(vertices, constraints) */
+ GSList *i;
+ GtsEdge *temp;
+ GtsVertex *v;
+ GtsTriangle *t;
+ GtsVertex *v1, *v2, *v3;
+
+ t = gts_triangle_enclosing (gts_triangle_class (), l->vertices, 1000.0f);
+ gts_triangle_vertices (t, &v1, &v2, &v3);
+
+ l->surface = gts_surface_new (gts_surface_class (), gts_face_class (),
+ GTS_EDGE_CLASS(toporouter_edge_class ()), GTS_VERTEX_CLASS(toporouter_vertex_class ()) );
+
+ gts_surface_add_face (l->surface, gts_face_new (gts_face_class (), t->e1, t->e2, t->e3));
+
+ i = l->vertices;
+ while (i) {
+ v = i->data;
+ //if(r->flags & TOPOROUTER_FLAG_DEBUG_CDTS)
+ // fprintf(stderr, "\tadding vertex %f,%f\n", v->p.x, v->p.y);
+ g_assert (gts_delaunay_add_vertex (l->surface, i->data, NULL) == NULL);
+ i = i->next;
+ }
+
+ fprintf(stderr, "ADDED VERTICES\n");
+/*
+ if((debugface = gts_delaunay_check(l->surface))) {
+ fprintf(stderr, "WARNING: Delaunay check failed\n");
+ fprintf(stderr, "\tViolating triangle:\n");
+ fprintf(stderr, "\t%f,%f %f,%f\n",
+ debugface->triangle.e1->segment.v1->p.x,
+ debugface->triangle.e1->segment.v1->p.y,
+ debugface->triangle.e1->segment.v2->p.x,
+ debugface->triangle.e1->segment.v2->p.y
+ );
+ fprintf(stderr, "\t%f,%f %f,%f\n",
+ debugface->triangle.e2->segment.v1->p.x,
+ debugface->triangle.e2->segment.v1->p.y,
+ debugface->triangle.e2->segment.v2->p.x,
+ debugface->triangle.e2->segment.v2->p.y
+ );
+ fprintf(stderr, "\t%f,%f %f,%f\n",
+ debugface->triangle.e3->segment.v1->p.x,
+ debugface->triangle.e3->segment.v1->p.y,
+ debugface->triangle.e3->segment.v2->p.x,
+ debugface->triangle.e3->segment.v2->p.y
+ );
+ if((f=fopen("fail.oogl", "w")) == NULL) {
+ fprintf(stderr, "Error opening file fail.oogl for output\n");
+ }else{
+ gts_surface_write_oogl(l->surface, f);
+ fclose(f);
+ }
+ toporouter_draw_surface(l->surface, "debug.png", 4096, 4096);
+
+ }
+*/
+ i = l->constraints;
+ while (i) {
+ temp = i->data;
+ //if(r->flags & TOPOROUTER_FLAG_DEBUG_CDTS)
+ fprintf(r->debug, "edge p1=%f,%f p2=%f,%f\n",
+ temp->segment.v1->p.x,
+ temp->segment.v1->p.y,
+ temp->segment.v2->p.x,
+ temp->segment.v2->p.y);
+
+ g_assert (gts_delaunay_add_constraint (l->surface, i->data) == NULL);
+ i = i->next;
+ }
+ fprintf(stderr, "ADDED CONSTRAINTS\n");
+ gts_allow_floating_vertices = TRUE;
+ gts_object_destroy (GTS_OBJECT (v1));
+ gts_object_destroy (GTS_OBJECT (v2));
+ gts_object_destroy (GTS_OBJECT (v3));
+ gts_allow_floating_vertices = FALSE;
+ /*
+ {
+ gpointer data[2];
+ gdouble quality = 0.65, area = G_MAXDOUBLE;
+ guint num = gts_delaunay_conform(l->surface, -1, (GtsEncroachFunc) gts_vertex_encroaches_edge, NULL);
+
+ if (num == 0){
+ data[0] = &quality;
+ data[1] = &area;
+ num = gts_delaunay_refine(l->surface, -1, (GtsEncroachFunc) gts_vertex_encroaches_edge, NULL, (GtsKeyFunc) triangle_cost, data);
+ }
+ }
+ */
+ gts_surface_print_stats(l->surface, stderr);
+}
+/*
+void
+insert_pad_stub_points(toporouter_t *r)
+{
+ GSList *i = r->bboxes, *j;
+ ToporouterBBox *box;
+ GtsSegment *seg;
+
+ while(i) {
+ box = TOPOROUTER_BBOX(i->data);
+ if(box->constraints) {
+ j = constraints;
+ while(j) {
+ seg = GTS_SEGMENT(j->data);
+
+ j = j->next;
+ }
+
+ }
+ i = i->next;
+ }
+
+
+
+}
+*/
+
+GtsTriangle*
+edge_triangle_oppisate(GtsEdge *e, GtsTriangle *t)
+{
+// g_assert(gts_triangle_neighbor_number(t) <= 1);
+
+ int length = g_slist_length(e->triangles);
+ if(length > 2) printf("length = %d\n", length);
+
+ if(e->triangles->data == t) {
+ if(e->triangles->next) return GTS_TRIANGLE(e->triangles->next->data);
+ return NULL;
+ }
+ return GTS_TRIANGLE(e->triangles->data);
+}
+
+struct visibility_data {
+ GSList *visited;
+ GSList *visible;
+
+// GSList *constraints;
+ toporouter_layer_t *l;
+
+ GtsVertex *origin;
+};
+
+/*
+ * returns non-zero if point A can see B with constriant in way
+ */
+inline int
+constraint_point_visible(ToporouterConstraint *c, GtsVertex *a, GtsVertex *b)
+{
+ int temp;
+ int w1, w2;
+
+ if(a == GTS_SEGMENT(c)->v1 || a == GTS_SEGMENT(c)->v2) return 1;
+
+ w1 = vertex_wind(GTS_SEGMENT(c)->v1, GTS_SEGMENT(c)->v2, a);
+ w2 = vertex_wind(GTS_SEGMENT(c)->v1, GTS_SEGMENT(c)->v2, b);
+
+ if(w1 != 0 && w2 != 0 && w1 != w2) {
+
+ if((temp = vertex_wind(a, GTS_SEGMENT(c)->v1, b)) == 0) return 0;
+
+ if(temp == vertex_wind(a, GTS_SEGMENT(c)->v1, GTS_SEGMENT(c)->v2)) {
+
+ if((temp = vertex_wind(a, GTS_SEGMENT(c)->v2, b)) == 0) return 0;
+
+ if(temp == vertex_wind(a, GTS_SEGMENT(c)->v2, GTS_SEGMENT(c)->v1)) return 0;
+
+
+ }
+ }
+ return 1;
+}
+
+guint
+point_visible(toporouter_layer_t *l, GtsVertex *a, GtsVertex *b)
+{
+ GSList *i = l->vertices;
+ double d[3];
+
+ while(i) {
+ if(GTS_VERTEX(i->data) != a && GTS_VERTEX(i->data) != b)
+ if(vertex_wind(a,b,GTS_VERTEX(i->data)) == 0) {
+ d[0] = gts_point_distance(GTS_POINT(a), GTS_POINT(b));
+ d[1] = gts_point_distance(GTS_POINT(b), GTS_POINT(i->data));
+ d[2] = gts_point_distance(GTS_POINT(a), GTS_POINT(i->data));
+ if(d[0] > d[1] && d[2] < d[0]) return 0;
+ }
+ i = i->next;
+ }
+
+ i = l->constraints;
+ while(i) {
+ if(!constraint_point_visible(TOPOROUTER_CONSTRAINT(i->data), a, b)){
+ //printf("pointviz return 0\n");
+ return 0;
+ }
+ i = i->next;
+ }
+ //printf("pointviz return 1\n");
+ return 1;
+}
+
+void
+compute_visibility_graph_rec(struct visibility_data *data, GtsEdge *e, GtsTriangle *t)
+{
+ GtsTriangle *cur = edge_triangle_oppisate(e, t);
+ GtsVertex *ov;
+ int viz[3];
+ //printf("computer vizibility\n");
+ if(cur == NULL) return;
+
+ g_assert(t != cur);
+
+ //g_tree_insert(data->visited, t, t);
+
+ //if(g_tree_lookup(data->visited, cur)) return;
+
+ if(g_slist_find(data->visited, cur)) return;
+
+ data->visited = g_slist_prepend(data->visited, cur);
+
+ ov = gts_triangle_vertex_opposite(cur, e);
+
+ if((viz[0] = point_visible(data->l, GTS_SEGMENT(e)->v1, data->origin))) {
+ data->visible = g_slist_insert_unique(data->visible, GTS_SEGMENT(e)->v1);
+ }
+ if((viz[1] = point_visible(data->l, GTS_SEGMENT(e)->v2, data->origin))) {
+ data->visible = g_slist_insert_unique(data->visible, GTS_SEGMENT(e)->v2);
+ }
+ if((viz[2] = point_visible(data->l, ov, data->origin))) {
+ // if(ov != data->origin)
+ data->visible = g_slist_insert_unique(data->visible, ov);
+ }
+
+ if(!viz[0] && !viz[1] && !viz[2]) return;
+
+ if(cur->e1 != e) {
+ if(!TOPOROUTER_IS_CONSTRAINT(cur->e1))
+ compute_visibility_graph_rec(data, cur->e1, cur);
+ }
+ if(cur->e2 != e) {
+ if(!TOPOROUTER_IS_CONSTRAINT(cur->e2))
+ compute_visibility_graph_rec(data, cur->e2, cur);
+ }
+ if(cur->e3 != e) {
+ if(!TOPOROUTER_IS_CONSTRAINT(cur->e3))
+ compute_visibility_graph_rec(data, cur->e3, cur);
+ }
+
+}
+gint
+visited_cmp(gconstpointer a, gconstpointer b)
+{
+ if(a<b) return -1;
+ if(a>b) return 1;
+ return 0;
+}
+
+gdouble
+point_xangle(GtsPoint *a, GtsPoint *b)
+{
+ gdouble dx, dy, theta;
+
+ dx = fabs(a->x - b->x);
+ dy = fabs(a->y - b->y);
+
+ if(dx < EPSILON) {
+ theta = M_PI / 2.;
+ } else theta = atan(dy/dx);
+
+ if(b->y >= a->y) {
+ if(b->x < a->x) theta = M_PI - theta;
+ }else{
+ if(b->x < a->x) theta += M_PI;
+ else theta = (2 * M_PI) - theta;
+ }
+
+ return theta;
+}
+
+gint
+point_angle_cmp(gconstpointer a, gconstpointer b, gpointer user_data)
+{
+ gdouble ta = point_xangle(GTS_POINT(user_data), GTS_POINT(a));
+ gdouble tb = point_xangle(GTS_POINT(user_data), GTS_POINT(b));
+ if(ta < tb) return -1;
+ if(ta > tb) return 1;
+ printf("\nWARNING: point_angle_cmp: colinear\n");
+ return 0;
+}
+
+toporouter_vertex_region_t *
+vertex_region_new(GSList *points, ToporouterVertex *v1, ToporouterVertex *v2, ToporouterVertex *origin)
+{
+ toporouter_vertex_region_t *r = malloc(sizeof(toporouter_vertex_region_t));
+ r->points = points;
+ r->v1 = v1;
+ r->v2 = v2;
+ r->origin = origin;
+ return r;
+}
+
+void
+vertex_visibility_graph_old(ToporouterVertex *v, toporouter_layer_t *l)
+{
+ GSList *j, *i, *cp = NULL, *r = NULL;
+ struct visibility_data data;
+
+ //if(v->visible) g_slist_free(v->visible);
+
+ data.visible = NULL;
+ data.l = l;
+ data.origin = GTS_VERTEX(v);
+
+ data.visited = gts_vertex_triangles(GTS_VERTEX(v), NULL);
+// printf("%d starting triangles, ", g_slist_length(data.visited));
+ j = data.visited;
+ while(j) {
+ compute_visibility_graph_rec(&data,
+ gts_triangle_edge_opposite(GTS_TRIANGLE(j->data), GTS_VERTEX(v)),
+ GTS_TRIANGLE(j->data));
+
+ j = j->next;
+ }
+
+// v->removed = NULL;
+ data.visible = g_slist_sort_with_data(data.visible, point_angle_cmp, v);
+
+ //printf("%d triangles visited, %d visible, ", g_slist_length(data.visited),
+ // g_slist_length(data.visible));
+ g_slist_free(data.visited);
+
+ j = GTS_VERTEX(v)->segments;
+ while(j) {
+ if(TOPOROUTER_IS_CONSTRAINT(j->data)) {
+ ToporouterVertex *p;
+ if(GTS_SEGMENT(j->data)->v1 == GTS_VERTEX(v))
+ p = TOPOROUTER_VERTEX( GTS_SEGMENT(j->data)->v2 );
+ else
+ p = TOPOROUTER_VERTEX( GTS_SEGMENT(j->data)->v1 );
+ cp = g_slist_prepend(cp, p);
+ }
+ j = j->next;
+ }
+ cp = g_slist_sort_with_data(cp, point_angle_cmp, v);
+
+ if(g_slist_length(cp) == 0) {
+ r = g_slist_prepend(NULL, vertex_region_new(data.visible, NULL, NULL, v));
+ }else{
+ ToporouterVertex *startc = TOPOROUTER_VERTEX( cp->data );
+ toporouter_vertex_region_t *curregion;
+ cp = g_slist_remove(cp, startc);
+
+ curregion = vertex_region_new(g_slist_prepend(NULL, startc), startc, NULL, v);
+
+ i = g_slist_find(data.visible, startc)->next;
+ if(i == NULL) i = data.visible;
+ while(TOPOROUTER_VERTEX(i->data) != startc) {
+ ToporouterVertex *curc = TOPOROUTER_VERTEX(i->data);
+ j = cp;
+ while(j) {
+ if(i->data == j->data) {
+ curregion->v2 = TOPOROUTER_VERTEX(i->data);
+ curregion->points = g_slist_insert_unique(curregion->points, curc);
+ r = g_slist_prepend(r, curregion);
+ curregion = vertex_region_new(g_slist_prepend(NULL, curc), curc, NULL, v);
+ cp = g_slist_remove(cp, curc);
+ goto visibility_cont_loop;
+ }
+ j = j->next;
+ }
+ curregion->points = g_slist_insert_unique(curregion->points, curc);
+
+visibility_cont_loop:
+ i = i->next;
+ if(i == NULL) i = data.visible;
+ }
+ curregion->v2 = startc;
+ curregion->points = g_slist_insert_unique(curregion->points, startc);
+ r = g_slist_prepend(r, curregion);
+
+ g_slist_free(data.visible);
+ }
+// r = g_slist_sort_with_data(r, point_angle_cmp, v);
+ v->regions = r;
+
+ //printf(" [REGIONS %d]\n", g_slist_length(r));
+
+// return data.visible;
+}
+
+GSList *
+vertex_corridor_edges(ToporouterVertex *v, ToporouterVertex *dest)
+{
+ GSList *r = NULL, *i, *triangles;
+ GtsSegment *s = gts_segment_new(gts_segment_class(),
+ gts_vertex_new(gts_vertex_class(), GTS_POINT(v)->x, GTS_POINT(v)->y, 0.),
+ gts_vertex_new(gts_vertex_class(), GTS_POINT(dest)->x, GTS_POINT(dest)->y, 0.));
+
+ triangles = gts_vertex_triangles(GTS_VERTEX(v), NULL);
+ i = triangles;
+ while(i) {
+ GtsTriangle *curt = GTS_TRIANGLE(i->data);
+ GtsEdge* e = gts_triangle_edge_opposite(curt, GTS_VERTEX(v));
+ GtsIntersect intersect = gts_segments_are_intersecting(s,GTS_SEGMENT(e));
+
+ if(intersect == GTS_IN) {
+intersect_gts_in:
+ r = g_slist_prepend(r, e);
+ curt = edge_triangle_oppisate(e, curt);
+
+ if(curt->e1 != e) {
+ intersect = gts_segments_are_intersecting(s,GTS_SEGMENT(curt->e1));
+ if(intersect == GTS_IN) {
+ e = curt->e1;
+ goto intersect_gts_in;
+ }else if(intersect == GTS_ON) {
+ e = curt->e1;
+ goto intersect_gts_on;
+ }
+ }
+ if(curt->e2 != e) {
+ intersect = gts_segments_are_intersecting(s,GTS_SEGMENT(curt->e2));
+ if(intersect == GTS_IN) {
+ e = curt->e2;
+ goto intersect_gts_in;
+ }else if(intersect == GTS_ON) {
+ e = curt->e2;
+ goto intersect_gts_on;
+ }
+ }
+ if(curt->e3 != e) {
+ intersect = gts_segments_are_intersecting(s,GTS_SEGMENT(curt->e3));
+ if(intersect == GTS_IN) {
+ e = curt->e3;
+ goto intersect_gts_in;
+ }else if(intersect == GTS_ON) {
+ e = curt->e3;
+ goto intersect_gts_on;
+ }
+ }
+
+ }else if(intersect == GTS_ON) {
+intersect_gts_on:
+ if(GTS_SEGMENT(e)->v1 == GTS_VERTEX(dest) || GTS_SEGMENT(e)->v2 == GTS_VERTEX(dest)) {
+ return r;
+ }
+ if(vertex_wind(GTS_VERTEX(v), GTS_SEGMENT(e)->v1, GTS_VERTEX(dest)) == 0) {
+ return g_slist_concat(r, vertex_corridor_edges(TOPOROUTER_VERTEX(GTS_SEGMENT(e)->v1), dest));
+ }else if(vertex_wind(GTS_VERTEX(v), GTS_SEGMENT(e)->v2, GTS_VERTEX(dest)) == 0) {
+ return g_slist_concat(r, vertex_corridor_edges(TOPOROUTER_VERTEX(GTS_SEGMENT(e)->v2), dest));
+ }
+ printf("ERROR: vertex_corridor_edges: GTS_ON but no colinear points\n");
+ }
+
+ i = i->next;
+ }
+
+ printf("ERROR: vertex_corridor_edges: no triangles had edges intersecting.. \n");
+ return r;
+}
+
+void
+vertex_visibility_graph(ToporouterVertex *v, toporouter_layer_t *l)
+{
+ GSList *i, *visible = NULL;
+ GSList *j, *cp = NULL, *r = NULL;
+
+ v->corridors = g_hash_table_new(g_direct_hash, g_direct_equal);
+
+ i = l->vertices;
+ while(i) {
+ if(TOPOROUTER_VERTEX(i->data) != v) {
+ if(point_visible(l, GTS_VERTEX(i->data), GTS_VERTEX(v))) {
+ visible = g_slist_prepend(visible, TOPOROUTER_VERTEX(i->data));
+ g_hash_table_insert(v->corridors, i->data, vertex_corridor_edges(v, TOPOROUTER_VERTEX(i->data)));
+ }
+ }
+ i = i->next;
+ }
+
+ visible = g_slist_sort_with_data(visible, point_angle_cmp, v);
+// printf("length of visible = %d\n", g_slist_length(visible));
+
+ j = GTS_VERTEX(v)->segments;
+ while(j) {
+ if(TOPOROUTER_IS_CONSTRAINT(j->data)) {
+ ToporouterVertex *p;
+ if(GTS_SEGMENT(j->data)->v1 == GTS_VERTEX(v))
+ p = TOPOROUTER_VERTEX( GTS_SEGMENT(j->data)->v2 );
+ else
+ p = TOPOROUTER_VERTEX( GTS_SEGMENT(j->data)->v1 );
+ cp = g_slist_prepend(cp, p);
+ }
+ j = j->next;
+ }
+
+ cp = g_slist_sort_with_data(cp, point_angle_cmp, v);
+
+ if(g_slist_length(cp) == 0) {
+ r = g_slist_prepend(NULL, vertex_region_new(visible, NULL, NULL, v));
+ }else{
+ ToporouterVertex *startc = TOPOROUTER_VERTEX( cp->data );
+ toporouter_vertex_region_t *curregion;
+ cp = g_slist_remove(cp, startc);
+
+ curregion = vertex_region_new(g_slist_prepend(NULL, startc), startc, NULL, v);
+
+ i = g_slist_find(visible, startc)->next;
+ if(i == NULL) i = visible;
+ while(TOPOROUTER_VERTEX(i->data) != startc) {
+ ToporouterVertex *curc = TOPOROUTER_VERTEX(i->data);
+ j = cp;
+ while(j) {
+ if(i->data == j->data) {
+ curregion->v2 = TOPOROUTER_VERTEX(i->data);
+ curregion->points = g_slist_insert_unique(curregion->points, curc);
+ r = g_slist_prepend(r, curregion);
+ curregion = vertex_region_new(g_slist_prepend(NULL, curc), curc, NULL, v);
+ cp = g_slist_remove(cp, curc);
+ goto temp_visibility_cont_loop;
+ }
+ j = j->next;
+ }
+ curregion->points = g_slist_insert_unique(curregion->points, curc);
+
+temp_visibility_cont_loop:
+ i = i->next;
+ if(i == NULL) i = visible;
+ }
+ curregion->v2 = startc;
+ curregion->points = g_slist_insert_unique(curregion->points, startc);
+ r = g_slist_prepend(r, curregion);
+
+ g_slist_free(visible);
+ }
+
+// r = g_slist_sort_with_data(r, point_angle_cmp, v);
+ v->regions = r;
+// v->visible = visible;
+
+// return visible;
+
+}
+
+/*
+ * returns > 0 if a is on b's box constraints
+ */
+int
+vertex_in_box_constraints(GtsVertex *v, ToporouterBBox *box)
+{
+ GSList *j;
+
+ j = box->constraints;
+ while(j) {
+ GtsSegment *seg = GTS_SEGMENT(j->data);
+ if(seg->v1 == v || seg->v2 == v)
+ return 1;
+ j = j->next;
+ }
+
+ return 0;
+}
+
+/*
+ * in != 0 for going in, otherwise 0 for going out of polygon
+ */
+
+
+/*
+ * returns a list of points which are candidates for attachment
+ */
+GSList *
+vertex_attachment_list(ToporouterVertex *curpoint, ToporouterVertex *v, toporouter_route_t *data, toporouter_layer_t *l)
+{
+ GSList *attach = NULL;
+
+ /* is the vertex on either src or dest bboxes? */
+ if(vertex_in_box_constraints(GTS_VERTEX(v), data->src)) {
+
+ printf("vertex in src box, thickness = %f keepaway = %f name = %s\n",
+ lookup_thickness(((PinType*)data->src->data)->Name),
+ lookup_keepaway(((PinType*)data->src->data)->Name),
+ ((PinType*)data->src->data)->Name);
+ // return vertex_polygon_attachment_list(v, data->src, l);
+ }else if(vertex_in_box_constraints(GTS_VERTEX(v), data->dest)) {
+
+ printf("vertex in dest box, thickness = %f keepaway = %f name = %s\n",
+ lookup_thickness(((PinType*)data->dest->data)->Name),
+ lookup_keepaway(((PinType*)data->dest->data)->Name),
+ ((PinType*)data->dest->data)->Name);
+
+// return vertex_polygon_attachment_list(v, data->dest, l);
+ }else{
+
+
+
+
+ }
+
+
+ return attach;
+}
+
+void
+compute_visibility_graph(toporouter_t *r)
+{
+ ToporouterVertex *v;
+ GSList *i;
+ toporouter_layer_t *cur_layer = r->layers;
+ guint n;
+
+ printf("COMPUTING VISIBILITY GRAPHS\n");
+
+ for(n = 0; n < max_layer; n++) {
+ i = cur_layer->vertices;
+ while(i) {
+
+ v = TOPOROUTER_VERTEX(i->data);
+
+ vertex_visibility_graph(v, cur_layer);
+
+ i = i->next;
+ }
+
+ cur_layer++;
+ }
+
+ cur_layer = r->layers;
+ i = cur_layer->vertices;
+ while(i) {
+ v = TOPOROUTER_VERTEX(i->data);
+
+ if(v->boxes) {
+ if(TOPOROUTER_BBOX(v->boxes->data)->type == PIN || TOPOROUTER_BBOX(v->boxes->data)->type == VIA) {
+ GSList *pins = g_slist_prepend(NULL, v);
+ GSList *b = gts_bb_tree_stabbed(r->bboxtree, GTS_POINT(v));
+ GSList *j = b;
+ while(j) {
+ ToporouterBBox *box = TOPOROUTER_BBOX(j->data);
+ if(box->type == PIN || box->type == VIA) {
+ TOPOROUTER_VERTEX(box->point)->zlink = pins;
+ if(TOPOROUTER_VERTEX(box->point) != v) {
+ pins = g_slist_append(pins, box->point);
+ }
+ }
+ j = j->next;
+ }
+ g_slist_free(b);
+ }
+ }
+ i = i->next;
+ }
+
+ printf("finished COMPUTING VISIBILITY GRAPHS\n");
+}
+
+
+void
+import_geometry(toporouter_t *r)
+{
+ GSList *i;
+ GtsVertex *v1, *v2, *v3;
+ int j;
+ char buffer[64];
+ toporouter_layer_t *cur_layer;
+
+ /*
+ printf("import board width = %d, board height = %d, layers = %d\n",
+ PCB->MaxWidth, PCB->MaxHeight, PCB->Data->LayerN);
+ layercount = 0;
+ LAYER_LOOP(PCB->Data, PCB->Data->LayerN);
+ {
+ printf("Layer %d:\n", layercount);
+ linecount = 0;
+ LINE_LOOP(layer);
+ {
+ linecount++;
+ }
+ END_LOOP;
+ printf("\t%d lines\n", layer->LineN);
+ printf("\t%d polygons\n", layer->PolygonN);
+
+ layercount++;
+ }
+ END_LOOP;
+*/
+
+ /* Allocate space for per layer struct */
+ cur_layer = r->layers = malloc(PCB->Data->LayerN * sizeof(toporouter_layer_t));
+
+
+
+// int front = GetLayerGroupNumberByNumber (max_layer + COMPONENT_LAYER);
+// int back = GetLayerGroupNumberByNumber (max_layer + SOLDER_LAYER);
+
+// fprintf(stderr, "front = %d, back = %d\n", front, back);
+
+ /* Foreach layer, read in pad vertices and constraints, and build CDT */
+// for(j=0;j<PCB->Data->LayerN;j++) {
+ LAYER_LOOP(PCB->Data, max_layer)
+ {
+ j = GetLayerNumber(PCB->Data, layer);
+// if(r->flags & TOPOROUTER_FLAG_DEBUG_CDTS)
+ printf("*** LAYER %d ***\n", j);
+
+ // Copy slists containing vertices and constraints for pins, vias and board extents
+ cur_layer->vertices = NULL;//g_slist_copy(vertices);
+ cur_layer->constraints = NULL;//g_slist_copy(constraints);
+
+ printf("reading board constraints\n");
+ read_board_constraints(r, cur_layer, j);
+ printf("reading points\n");
+ read_points(r, cur_layer, j);
+ printf("reading pads\n");
+ read_pads(r, cur_layer, j);
+ printf("reading lines\n");
+ read_lines(r, cur_layer, layer, j);
+ printf("building CDT\n");
+ build_cdt(r, cur_layer);
+
+ if(r->flags & TOPOROUTER_FLAG_DUMP_CDTS) {
+ sprintf(buffer, "cdt-layer%d.png", j);
+ toporouter_draw_surface(r->layers[j].surface, buffer, 1280, 1280, 0, NULL);
+/*
+ if((f=fopen(buffer, "w")) == NULL) {
+ fprintf(stderr, "Error opening file %s for output\n", buffer);
+ }else{
+ gts_surface_write_oogl(r->layers[j].surface, f);
+ fclose(f);
+ }
+*/
+ }
+ cur_layer++;
+ }
+ END_LOOP;
+
+
+ i = r->bboxes;
+ while(i) {
+ gts_triangle_vertices(TOPOROUTER_BBOX(i->data)->enclosing, &v1, &v2, &v3);
+
+ gts_allow_floating_vertices = TRUE;
+ gts_object_destroy (GTS_OBJECT (v1));
+ gts_object_destroy (GTS_OBJECT (v2));
+ gts_object_destroy (GTS_OBJECT (v3));
+ gts_allow_floating_vertices = FALSE;
+
+ i = i->next;
+ }
+
+ r->bboxtree = gts_bb_tree_new(r->bboxes);
+
+ {
+ NetListListType nets = CollectSubnets(False);
+ NETLIST_LOOP(&nets);
+ {
+ if(netlist->NetN > 0) {
+
+ NET_LOOP(netlist);
+ {
+ GtsPoint *p = gts_point_new(gts_point_class(), net->Connection->X, net->Connection->Y, 0.);
+ GSList *boxes = gts_bb_tree_stabbed(r->bboxtree, p);
+ GSList *i = boxes;
+
+ gts_object_destroy(GTS_OBJECT(p));
+
+ while(i) {
+ ToporouterBBox *box = TOPOROUTER_BBOX(i->data);
+ if(box->type == PIN || box->type == PAD || box->type == VIA) {
+ box->netlist = netlist->Net->Connection->menu->Name;
+ box->style = netlist->Net->Connection->menu->Style;
+ }
+ i = i->next;
+ }
+ if(!boxes)
+ printf("WARNING: no boxes found for netlist \"%s\"\n", netlist->Net->Connection->menu->Name);
+
+ g_slist_free(boxes);
+
+ }
+ END_LOOP;
+
+ }
+ }
+ END_LOOP;
+ FreeNetListListMemory(&nets);
+ }
+
+ compute_visibility_graph(r);
+
+ printf("finished import!\n");
+}
+
+
+gint
+compare_points(gconstpointer a, gconstpointer b)
+{
+ GtsPoint *i = GTS_POINT(a);
+ GtsPoint *j = GTS_POINT(b);
+
+ if(i->x == j->x) {
+ if(i->y == j->y) return 0;
+ if(i->y < j->y) return -1;
+ return 1;
+ }
+ if(i->x < j->x) return -1;
+ return 1;
+}
+
+gint
+compare_segments(gconstpointer a, gconstpointer b)
+{
+ if(a == b) return 0;
+ if(a < b) return -1;
+ return 1;
+}
+
+ToporouterBBox *
+find_routebox(toporouter_t *r, GtsPoint *p, int layer)
+{
+ GSList *hits;
+ ToporouterBBox *box;
+
+ hits = gts_bb_tree_stabbed(r->bboxtree, p);
+
+ while(hits) {
+ box = TOPOROUTER_BBOX(hits->data);
+ if(box->type != BOARD && box->layer == layer)
+ if(gts_point_locate(p, box->surface, NULL)) {
+ return box;
+ }
+ hits = hits->next;
+ }
+ return NULL;
+}
+
+static void
+dump_routebox(ToporouterBBox *b)
+{
+ printf ("RB: (%f,%f)-(%f,%f) l%d; ",
+ GTS_BBOX(b)->x1, GTS_BBOX(b)->y1,
+ GTS_BBOX(b)->x2, GTS_BBOX(b)->y2,
+ (int) b->layer);
+ switch (b->type)
+ {
+ case PAD:
+ printf ("PAD[%s %s]; ", ((PadType*)b->data)->Name, ((PadType*)b->data)->Number);
+ break;
+ case PIN:
+ printf ("PIN[%s %s]; ", ((PinType*)b->data)->Name, ((PinType*)b->data)->Number);
+ break;
+ case VIA:
+ printf ("VIA[%s %s]; ", ((PinType*)b->data)->Name, ((PinType*)b->data)->Number);
+ break;
+ case BOARD:
+ printf ("BOARD; ");
+ break;
+ default:
+ printf ("UNKNOWN; ");
+ break;
+ }
+
+ if(b->constraints)
+ printf("CONSTRAINTS[%d]; ", g_slist_length(b->constraints));
+
+ if(b->point)
+ printf("(has point); ");
+
+ printf ("\n");
+}
+
+
+void
+insert_joined_segments(
+ GtsPoint *curpoint,
+ GtsPoint *destpoint,
+ GtsSegment *seg,
+ GSList **ccw_points,
+ GSList **cw_points,
+ GTree *seen_segs
+ )
+{
+ GSList *i;
+ GtsSegment *tempseg;
+ int tempint;
+
+ tempint = point_wind(curpoint, destpoint, GTS_POINT(seg->v1));
+
+ if(tempint > 0) {
+ *ccw_points = g_slist_insert_unique(*ccw_points, seg->v1);
+ *cw_points = g_slist_insert_unique(*cw_points, seg->v2);
+ }else if(tempint < 0) {
+ *ccw_points = g_slist_insert_unique(*ccw_points, seg->v2);
+ *cw_points = g_slist_insert_unique(*cw_points, seg->v1);
+ }else{
+
+ tempint = point_wind(curpoint, destpoint, GTS_POINT(seg->v2));
+
+ if(tempint > 0) {
+ *ccw_points = g_slist_insert_unique(*ccw_points, seg->v2);
+ *cw_points = g_slist_insert_unique(*cw_points, seg->v1);
+ }else if(tempint < 0) {
+ *ccw_points = g_slist_insert_unique(*ccw_points, seg->v1);
+ *cw_points = g_slist_insert_unique(*cw_points, seg->v2);
+ }
+ }
+
+ g_tree_insert(seen_segs, seg, seg);
+
+ i = seg->v1->segments;
+ while(i) {
+ tempseg = GTS_SEGMENT(i->data);
+
+ if(tempseg != seg && !g_tree_lookup(seen_segs, tempseg)) {
+
+ if(TOPOROUTER_IS_CONSTRAINT(i->data)) {
+ insert_joined_segments(curpoint, destpoint, tempseg, ccw_points, cw_points, seen_segs);
+ return;
+ }
+ if(TOPOROUTER_IS_EDGE(i->data))
+ if(TOPOROUTER_EDGE(i->data)->netlist) {
+ insert_joined_segments(curpoint, destpoint, tempseg, ccw_points, cw_points, seen_segs);
+ return;
+ }
+
+ g_tree_insert(seen_segs, tempseg, tempseg);
+ }
+ i = i->next;
+ }
+
+ i = seg->v2->segments;
+ while(i) {
+ tempseg = GTS_SEGMENT(i->data);
+
+ if(tempseg != seg && !g_tree_lookup(seen_segs, tempseg)) {
+
+ if(TOPOROUTER_IS_CONSTRAINT(i->data)) {
+ insert_joined_segments(curpoint, destpoint, tempseg, ccw_points, cw_points, seen_segs);
+ return;
+ }
+ if(TOPOROUTER_IS_EDGE(i->data))
+ if(TOPOROUTER_EDGE(i->data)->netlist) {
+ insert_joined_segments(curpoint, destpoint, tempseg, ccw_points, cw_points, seen_segs);
+ return;
+ }
+
+ g_tree_insert(seen_segs, tempseg, tempseg);
+ }
+ i = i->next;
+ }
+
+ return;
+}
+
+// points should be a copy
+GSList *
+make_chain(GtsPoint *curpoint, GtsPoint *destpoint, GSList *points, int dir)
+{
+ GSList *chain = NULL;
+ GSList *i, *j;
+ GtsPoint *prevpoint;
+ int tempdir;
+
+ chain = g_slist_prepend(chain, destpoint);
+ prevpoint = destpoint;
+
+make_chain_next_point:
+ i = points;
+ while(i) {
+
+ j = points;
+ while(j) {
+ if(j != i) {
+ tempdir = point_wind(prevpoint, GTS_POINT(i->data), GTS_POINT(j->data));
+ if(tempdir != dir && tempdir != 0)
+ goto make_chain_next_i;
+
+ }
+ j = j->next;
+ }
+
+ chain = g_slist_prepend(chain, i->data);
+ points = g_slist_remove(points, i->data);
+ prevpoint = GTS_POINT(i->data);
+ goto make_chain_next_point;
+
+make_chain_next_i:
+ i = i->next;
+ }
+
+ chain = g_slist_prepend(chain, curpoint);
+ g_slist_free(points);
+
+ return chain;
+}
+
+gdouble
+chain_length(GSList *chain)
+{
+ GSList *i;
+ GtsPoint *curpoint, *prevpoint;
+ gdouble r = 0.;
+
+ i = chain;
+ prevpoint = GTS_POINT(i->data);
+ i = i->next;
+ while(i) {
+ curpoint = GTS_POINT(i->data);
+ r += gts_point_distance(curpoint, prevpoint);
+ prevpoint = curpoint;
+ i = i->next;
+ }
+
+ return r;
+}
+
+/*
+ * This calculates the "h"-cost, or heuristic cost, for use in the
+ * A* path finding algorithm.
+ */
+gdouble
+h_cost(toporouter_layer_t *l, GtsPoint *curpoint, GtsPoint *destpoint)
+{
+ GSList *i;
+ GTree *seen_segs;
+ GSList *ccw_points, *cw_points;
+ GSList *ccw_chain, *cw_chain;
+ gdouble r;
+
+ if(curpoint == destpoint) return 0.;
+
+ seen_segs = g_tree_new(compare_segments);
+ ccw_points = cw_points = NULL;
+
+ /* TODO: could also check for not enough edge capacity at this point */
+ i = l->constraints;
+ while(i) {
+ if(vertex_intersect_prop(
+ GTS_SEGMENT(i->data)->v1,
+ GTS_SEGMENT(i->data)->v2,
+ GTS_VERTEX(curpoint),
+ GTS_VERTEX(destpoint))
+ )
+ insert_joined_segments(curpoint, destpoint, GTS_SEGMENT(i->data), &ccw_points, &cw_points, seen_segs);
+ i = i->next;
+ }
+
+ i = l->edges;
+ while(i) {
+ if(vertex_intersect_prop(
+ GTS_SEGMENT(i->data)->v1,
+ GTS_SEGMENT(i->data)->v2,
+ GTS_VERTEX(curpoint),
+ GTS_VERTEX(destpoint))
+ )
+ insert_joined_segments(curpoint, destpoint, GTS_SEGMENT(i->data), &ccw_points, &cw_points, seen_segs);
+ i = i->next;
+ }
+
+ ccw_chain = make_chain(curpoint, destpoint, g_slist_copy(ccw_points), 1);
+ cw_chain = make_chain(curpoint, destpoint, g_slist_copy(cw_points), -1);
+
+ r = MIN(chain_length(ccw_chain), chain_length(cw_chain));
+
+ /* TODO: this heuristic could be improved by recursively checking bits which
+ * would break convexity with curpoint and destpoint
+ */
+
+ g_slist_free(ccw_chain);
+ g_slist_free(cw_chain);
+ g_slist_free(ccw_points);
+ g_slist_free(cw_points);
+ g_tree_destroy(seen_segs);
+
+ return r;
+}
+
+/*
+ * This calculates the "g"-cost, or generated cost (the cost of the path
+ * so far), for use in the A* path finding algorithm.
+ */
+/*
+gdouble
+g_cost(GSList *closelist, GtsPoint *curpoint)
+{
+ GSList *i;
+ GtsPoint *temppoint;
+
+ gdouble r = 0;
+
+ i = closelist;
+ while(i) {
+ temppoint = GTS_POINT(i->data);
+
+ r += gts_point_distance(temppoint, curpoint);
+
+ curpoint = temppoint;
+
+ i = i->next;
+ }
+
+ return r;
+}
+*/
+gdouble
+g_cost(ToporouterVertex *curpoint)
+{
+ gdouble r = 0.;
+ while(curpoint) {
+ r += gts_point_distance(GTS_POINT(curpoint), GTS_POINT(curpoint->parent));
+ curpoint = curpoint->parent;
+ }
+ return r;
+}
+
+gdouble
+simple_h_cost(ToporouterVertex *curpoint, ToporouterVertex *destpoint)
+{
+ return gts_point_distance(GTS_POINT(curpoint), GTS_POINT(destpoint));
+}
+
+
+/*
+ * This calculates the "f"-cost, which = g + h
+ * so far), for use in the A* path finding algorithm.
+ */
+/*
+gdouble
+f_cost(toporouter_layer_t *l, GSList *closelist, GtsPoint *curpoint, GtsPoint *destpoint) {
+ gdouble g = g_cost(closelist, curpoint);
+ gdouble h = h_cost(l, curpoint, destpoint);
+ gdouble f = g + h;
+
+ fprintf(stderr, "fcost: point %f,%f\t\tf=%f\tg=%f\th=%f\n", curpoint->x, curpoint->y, f, g, h);
+
+ return f;
+}
+*/
+/*
+GtsPoint *
+lowest_fcost(toporouter_layer_t *l, GSList *list, GtsPoint *destpoint)
+{
+ GtsPoint *p = GTS_POINT(list->data);
+ list = list->next;
+ while(list) {
+
+ list = list->next;
+ }
+
+
+
+}
+*/
+
+#define FCOST(x) (x->gcost + x->hcost)
+gdouble
+route_heap_cmp(gpointer item, gpointer data)
+{
+ return FCOST(TOPOROUTER_VERTEX(item));
+}
+/*
+gint
+route_heap_cmp(gconstpointer a, gconstpointer b)
+{
+ ToporouterVertex *va = TOPOROUTER_VERTEX(a);
+ ToporouterVertex *vb = TOPOROUTER_VERTEX(b);
+
+ if(FCOST(va) > FCOST(vb)) return 1;
+ else if(FCOST(va) < FCOST(vb)) return -1;
+ return 0;
+}
+*/
+/*
+GSList *
+neighbor_nodes(GtsPoint *p)
+{
+
+}
+*/
+
+#define openlist_insert(p) p->cost = f_cost(l, closelist, p, b->point);\
+ gts_heap_insert(openlist, p)
+
+#define closelist_insert(p) closelist = g_slist_prepend(closelist, p)
+/*
+void
+dump_attachments(GSList *i, ToporouterVertex *curpoint, toporouter_layer_t *cur_layer)
+{
+
+ while(i) {
+ toporouter_attachment_t *attachment = (toporouter_attachment_t *) i->data;
+ printf("attachment: [V %f,%f] [R %f] [D %s] %s\n",
+ GTS_POINT(attachment->p)->x,
+ GTS_POINT(attachment->p)->y,
+ attachment->r,
+ (attachment->dir == CCW) ? "CCW" : "CW",
+ point_visible(cur_layer, GTS_VERTEX(curpoint), GTS_VERTEX(attachment->p)) ? "(visible)" : ""
+
+ );
+
+
+ i = i->next;
+ }
+
+
+}
+*/
+GSList *
+box_constraint_vertices(ToporouterBBox *box)
+{
+ GSList *i = box->constraints;
+ GSList *r = NULL;
+
+ while(i) {
+ r = g_slist_insert_unique(r, GTS_SEGMENT(i->data)->v1);
+ r = g_slist_insert_unique(r, GTS_SEGMENT(i->data)->v2);
+ i = i->next;
+ }
+ return r;
+}
+
+typedef struct {
+ ToporouterVertex *key;
+ ToporouterVertex *result;
+}toporouter_heap_search_data_t;
+
+void
+toporouter_heap_search(gpointer data, gpointer user_data)
+{
+ ToporouterVertex *v = TOPOROUTER_VERTEX(data);
+ toporouter_heap_search_data_t *heap_search_data = (toporouter_heap_search_data_t *)user_data;
+ if(v == heap_search_data->key) heap_search_data->result = v;
+}
+
+void
+toporouter_heap_color(gpointer data, gpointer user_data)
+{
+ ToporouterVertex *v = TOPOROUTER_VERTEX(data);
+ v->flags |= (unsigned int) user_data;
+}
+
+void
+draw_route_status(toporouter_t *r, GSList *closelist, GtsEHeap *openlist,
+ ToporouterVertex *curpoint, toporouter_route_t *routedata, int count)
+{
+ GSList *i;
+ char buffer[256];
+ unsigned int mask = !(VERTEX_FLAG_RED | VERTEX_FLAG_GREEN);
+
+ LAYER_LOOP(PCB->Data, max_layer)
+ {
+ int tempnum = GetLayerNumber(PCB->Data, layer);
+ i = r->layers[tempnum].vertices;
+ while(i) {
+ TOPOROUTER_VERTEX(i->data)->flags &= mask;
+ i = i->next;
+ }
+ }
+ END_LOOP;
+
+ i = closelist;
+ while(i) {
+ TOPOROUTER_VERTEX(i->data)->flags |= VERTEX_FLAG_GREEN;
+ i = i->next;
+ }
+
+ gts_eheap_foreach(openlist,toporouter_heap_color, (gpointer) VERTEX_FLAG_GREEN);
+
+ while(curpoint) {
+ curpoint->flags |= VERTEX_FLAG_RED;
+ curpoint = curpoint->parent;
+ }
+
+ TOPOROUTER_VERTEX(routedata->src->point)->flags |= VERTEX_FLAG_BLUE;
+ TOPOROUTER_VERTEX(routedata->dest->point)->flags |= VERTEX_FLAG_BLUE;
+
+
+ LAYER_LOOP(PCB->Data, max_layer)
+ {
+ int tempnum = GetLayerNumber(PCB->Data, layer);
+ sprintf(buffer, "viz-layer%d-%d.png", tempnum, count);
+ toporouter_draw_surface(r->layers[tempnum].surface, buffer, 1280, 1280, 1, routedata);
+ }
+ END_LOOP;
+
+}
+
+GSList *
+all_region_points(ToporouterVertex *p)
+{
+ GSList *r = NULL, *i;
+ i = p->regions;
+ while(i) {
+ toporouter_vertex_region_t *region = TOPOROUTER_VERTEX_REGION(i->data);
+ GSList *j = region->points;
+ while(j) {
+ r = g_slist_insert_unique(r, j->data);
+ j = j->next;
+ }
+ i = i->next;
+ }
+
+// printf("all region points length = %d\n", g_slist_length(r));
+
+ return r;
+}
+
+ToporouterBBox *
+vertex_bbox(ToporouterVertex *v)
+{
+ GSList *i = v->boxes;
+
+ if(!i) return NULL;
+
+ if(g_slist_length(i) > 1) {
+ printf("WARNING: vertex with multiple bboxes\n");
+ }
+
+ return TOPOROUTER_BBOX(i->data);
+}
+
+inline gdouble
+angle_span(gdouble a1, gdouble a2)
+{
+ if(a1 > a2)
+ return ((2*M_PI)-a1 + a2);
+ return a2-a1;
+}
+
+gdouble
+region_span(toporouter_vertex_region_t *region)
+{
+ gdouble a1,a2;
+
+ g_assert(region->v1 != NULL);
+ g_assert(region->v2 != NULL);
+ g_assert(region->origin != NULL);
+
+ a1 = point_xangle(GTS_POINT(region->origin), GTS_POINT(region->v1));
+ a2 = point_xangle(GTS_POINT(region->origin), GTS_POINT(region->v2));
+
+ return angle_span(a1, a2);
+}
+
+gdouble
+vertex_net_thickness(ToporouterVertex *v)
+{
+ ToporouterBBox *box = vertex_bbox(v);
+ g_assert(box != NULL);
+ return lookup_thickness(box->style);
+}
+
+gdouble
+vertex_net_keepaway(ToporouterVertex *v)
+{
+ ToporouterBBox *box = vertex_bbox(v);
+ g_assert(box != NULL);
+ return lookup_keepaway(box->style);
+}
+
+gdouble
+edge_capacity(ToporouterEdge *e)
+{
+ GtsSegment *s = GTS_SEGMENT(e);
+
+ return gts_point_distance(GTS_POINT(s->v1), GTS_POINT(s->v2)) -
+ (vertex_net_thickness(TOPOROUTER_VERTEX(s->v1)) / 2.) -
+ (vertex_net_thickness(TOPOROUTER_VERTEX(s->v2)) / 2.);
+
+}
+
+ToporouterEdge *
+get_edge(ToporouterVertex *curpoint, ToporouterVertex *p)
+{
+ GSList *i = GTS_VERTEX(curpoint)->segments;
+
+ while(i) {
+ if(TOPOROUTER_IS_EDGE(i->data)) {
+ GtsSegment *s = GTS_SEGMENT(i->data);
+ if((s->v1 == GTS_VERTEX(curpoint) && s->v2 == GTS_VERTEX(p)) ||
+ (s->v1 == GTS_VERTEX(p) && s->v2 == GTS_VERTEX(curpoint))) {
+
+ return TOPOROUTER_EDGE(s);
+ }
+
+ }
+ i = i->next;
+ }
+
+ return NULL;
+}
+
+/*
+ * returns region in a spanning b
+ */
+toporouter_vertex_region_t *
+vertex_region_containing_point(ToporouterVertex *a, GtsPoint *b)
+{
+ GSList *i = a->regions;
+ guint len = g_slist_length(a->regions);
+ gdouble angle = point_xangle(GTS_POINT(a), b);
+
+ g_assert(len > 0);
+
+ if(len == 1)
+ return (toporouter_vertex_region_t *)a->regions->data;
+
+ while(i) {
+ toporouter_vertex_region_t *region = TOPOROUTER_VERTEX_REGION(i->data);
+ gdouble a1,a2;
+
+ g_assert(region->v1 != NULL);
+ g_assert(region->v2 != NULL);
+
+ a1 = point_xangle(GTS_POINT(a), GTS_POINT(region->v1));
+ a2 = point_xangle(GTS_POINT(a), GTS_POINT(region->v2));
+
+ if((a1 >= a2 && (angle > a1 || angle < a2)) || (angle > a1 && a2 > angle))
+ return region;
+
+ i = i->next;
+ }
+
+ return NULL;
+}
+
+GSList *
+segment_check_and_insert(GSList *r, ToporouterVertex *v, ToporouterVertex *dest, toporouter_route_t *data)
+{
+ GSList *i = g_hash_table_lookup(v->corridors, dest);
+ gdouble needed_capacity = lookup_thickness(data->src->style) + (2. * lookup_keepaway(data->src->style));
+
+ while(i) {
+ if(edge_capacity(TOPOROUTER_EDGE(i->data)) < needed_capacity)
+ return r;
+
+ i = i->next;
+ }
+
+ return g_slist_prepend(r, dest);
+}
+
+//#define TRACE_VISIBILITY TRUE
+
+GSList *
+compute_visible_points(toporouter_layer_t *l, ToporouterVertex *curpoint, toporouter_route_t *data)
+{
+ GSList *r = NULL, *i;
+ gdouble needed_capacity = lookup_thickness(data->src->style) + (2. * lookup_keepaway(data->src->style));
+
+ if(curpoint->parent == NULL) {
+ GSList *tempr = all_region_points(curpoint);
+ i = tempr;
+ while(i) {
+ if(TOPOROUTER_VERTEX(i->data)->flags & VERTEX_FLAG_ATTACH)
+ r = segment_check_and_insert(r, curpoint, TOPOROUTER_VERTEX(i->data), data);
+
+ i = i->next;
+ }
+
+ }else if(curpoint->flags & VERTEX_FLAG_ATTACH && vertex_bbox(curpoint) == data->dest) {
+ r = g_slist_prepend(r, data->dest->point);
+
+ }else if(curpoint->flags & VERTEX_FLAG_ATTACH && curpoint->parent == TOPOROUTER_VERTEX(data->src->point)) {
+ toporouter_vertex_region_t *region = vertex_region_containing_point(curpoint, GTS_POINT(curpoint->parent));
+ GSList *rr = NULL;
+ g_assert(region != NULL);
+
+ i = curpoint->regions;
+ while(i) {
+ toporouter_vertex_region_t *curregion = TOPOROUTER_VERTEX_REGION(i->data);
+ if(curregion == region) {
+ if(i->next) {
+ rr = g_slist_copy(((toporouter_vertex_region_t *)i->next->data)->points);
+ break;
+ }
+ }
+ if(i->next) {
+ if(((toporouter_vertex_region_t *)i->next->data) == region) {
+ rr = g_slist_copy(curregion->points);
+ break;
+ }
+ }
+ i = i->next;
+ }
+
+ g_assert(rr != NULL);
+
+ i = rr;
+ while(i) {
+ if(TOPOROUTER_VERTEX(i->data)->flags & VERTEX_FLAG_ATTACH) {
+ if(vertex_bbox(TOPOROUTER_VERTEX(i->data)) == data->dest)
+ r = segment_check_and_insert(r, curpoint, TOPOROUTER_VERTEX(i->data), data);
+ }else
+ r = segment_check_and_insert(r, curpoint, TOPOROUTER_VERTEX(i->data), data);
+ i = i->next;
+ }
+
+
+ }else{
+ gdouble a = point_xangle(GTS_POINT(curpoint), GTS_POINT(curpoint->parent));
+ guint len = g_slist_length(curpoint->regions);
+
+ g_assert(len > 0);
+
+ if(len == 1) {
+ toporouter_vertex_region_t *region = TOPOROUTER_VERTEX_REGION(curpoint->regions->data);
+ r = g_slist_copy(region->points);
+ goto compute_visible_points_finish;
+ }
+
+#ifdef TRACE_VISIBILITY
+ printf("* %d regions\n", g_slist_length(curpoint->regions));
+#endif
+
+ i = curpoint->regions;
+ while(i) {
+ toporouter_vertex_region_t *region = TOPOROUTER_VERTEX_REGION(i->data);
+ gdouble a1,a2;
+ gdouble parentangle, a1_p, p_a2, a1_a2;
+
+ g_assert(region->v1 != NULL);
+ g_assert(region->v2 != NULL);
+
+ a1 = point_xangle(GTS_POINT(curpoint), GTS_POINT(region->v1));
+ a2 = point_xangle(GTS_POINT(curpoint), GTS_POINT(region->v2));
+
+#ifdef TRACE_VISIBILITY
+ printf("a1 = %f a2 = %f\n", a1, a2);
+#endif
+ parentangle = point_xangle(GTS_POINT(curpoint), GTS_POINT(curpoint->parent));
+ a1_p = angle_span(a1, parentangle);
+ p_a2 = angle_span(parentangle, a2);
+ a1_a2 = angle_span(a1, a2);
+ if(a1_a2 == 0.) a1_a2 = 2. * M_PI;
+
+#ifdef TRACE_VISIBILITY
+ printf("a1_p = %f p_a2 = %f a1_a2 = %f\n",
+ a1_p, p_a2, a1_a2);
+ printf("parentangle = %f\n", parentangle);
+#endif
+ if((a1 >= a2 && (a > a1 || a < a2)) || (a > a1 && a2 > a)) {
+
+ if(p_a2 > M_PI) {
+ GSList *k = region->points;
+ //CW
+#ifdef TRACE_VISIBILITY
+ printf(" * CW\n");
+#endif
+
+#ifdef TRACE_VISIBILITY
+ printf("ZONE1:\n");
+#endif
+ while(k) {
+ gdouble tempangle = point_xangle(GTS_POINT(curpoint), GTS_POINT(k->data));
+ ToporouterEdge *e = get_edge(curpoint, TOPOROUTER_VERTEX(k->data));
+#ifdef TRACE_VISIBILITY
+ printf("p angle = %f\n", tempangle);
+#endif
+
+ if(angle_span(tempangle, a2) > p_a2 - M_PI) break;
+
+ if(e)
+ if(edge_capacity(e) < needed_capacity) {
+#ifdef TRACE_VISIBILITY
+ printf("\tfail capacity\n");
+#endif
+ g_slist_free(r);
+ r = NULL;
+ }
+ if(TOPOROUTER_VERTEX(k->data)->flags & VERTEX_FLAG_ATTACH) {
+ ToporouterBBox *vbox = vertex_bbox(TOPOROUTER_VERTEX(k->data));
+ if(vbox == data->src || vbox == data->dest)
+ r = segment_check_and_insert(r, curpoint, TOPOROUTER_VERTEX(k->data), data);
+ }else
+ r = segment_check_and_insert(r, curpoint, TOPOROUTER_VERTEX(k->data), data);
+ k = k->next;
+ }
+#ifdef TRACE_VISIBILITY
+ printf("ZONE2:\n");
+#endif
+ while(k) {
+ gdouble tempangle = point_xangle(GTS_POINT(curpoint), GTS_POINT(k->data));
+ ToporouterEdge *e = get_edge(curpoint, TOPOROUTER_VERTEX(k->data));
+#ifdef TRACE_VISIBILITY
+ printf("p angle = %f\n", tempangle);
+#endif
+
+ if(angle_span(a1, tempangle) <= a1_p) break;
+
+ if(e)
+ if(edge_capacity(e) < needed_capacity) {
+#ifdef TRACE_VISIBILITY
+ printf("\tfail capacity\n");
+#endif
+ g_slist_free(r);
+ r = NULL;
+ goto compute_visible_points_finish;
+ }
+ k = k->next;
+ }
+
+ return r;
+ }else if(a1_p > M_PI) {
+ //CCW
+ GSList *k = region->points;
+#ifdef TRACE_VISIBILITY
+ printf(" * CCW\n");
+#endif
+
+#ifdef TRACE_VISIBILITY
+ printf("ZONE1:\n");
+#endif
+ while(k) {
+ gdouble tempangle = point_xangle(GTS_POINT(curpoint), GTS_POINT(k->data));
+#ifdef TRACE_VISIBILITY
+ printf("p angle = %f\n", tempangle);
+#endif
+
+ if(angle_span(tempangle, a2) > a1_a2 - a1_p) break;
+
+ k = k->next;
+ }
+
+#ifdef TRACE_VISIBILITY
+ printf("ZONE2:\n");
+#endif
+ while(k) {
+ gdouble tempangle = point_xangle(GTS_POINT(curpoint), GTS_POINT(k->data));
+ ToporouterEdge *e = get_edge(curpoint, TOPOROUTER_VERTEX(k->data));
+#ifdef TRACE_VISIBILITY
+ printf("p angle = %f\n", tempangle);
+#endif
+
+ if(angle_span(a1, tempangle) <= a1_p - M_PI) break;
+
+ if(e)
+ if(edge_capacity(e) < needed_capacity) {
+#ifdef TRACE_VISIBILITY
+ printf("\tfail capacity\n");
+#endif
+ g_slist_free(r);
+ r = NULL;
+ goto compute_visible_points_finish;
+ }
+ k = k->next;
+ }
+
+#ifdef TRACE_VISIBILITY
+ printf("ZONE3:\n");
+#endif
+ while(k) {
+ ToporouterEdge *e = get_edge(curpoint, TOPOROUTER_VERTEX(k->data));
+#ifdef TRACE_VISIBILITY
+ gdouble tempangle = point_xangle(GTS_POINT(curpoint), GTS_POINT(k->data));
+ printf("p angle = %f\n", tempangle);
+#endif
+ if(TOPOROUTER_VERTEX(k->data)->flags & VERTEX_FLAG_ATTACH) {
+ ToporouterBBox *vbox = vertex_bbox(TOPOROUTER_VERTEX(k->data));
+ if(vbox == data->src || vbox == data->dest)
+ r = segment_check_and_insert(r, curpoint, TOPOROUTER_VERTEX(k->data), data);
+ }else
+ r = segment_check_and_insert(r, curpoint, TOPOROUTER_VERTEX(k->data), data);
+
+ if(e)
+ if(edge_capacity(e) < needed_capacity) {
+#ifdef TRACE_VISIBILITY
+ printf("\tfail capacity\n");
+#endif
+ goto compute_visible_points_finish;
+ }
+
+ k = k->next;
+ }
+
+ goto compute_visible_points_finish;
+ }
+ r = NULL;
+ goto compute_visible_points_finish;
+ }
+
+ i = i->next;
+ }
+
+ }
+
+compute_visible_points_finish:
+
+ {
+ ToporouterBBox *box = vertex_bbox(curpoint);
+ if(box)
+ if(vertex_bbox(curpoint)->netlist == data->src->netlist) {
+ i = curpoint->zlink;
+ while(i) {
+ if(TOPOROUTER_VERTEX(i->data) != curpoint)
+ r = g_slist_prepend(r, i->data);
+ i = i->next;
+ }
+ }
+ }
+
+ return r;
+}
+
+void
+vertex_visibility_remove(ToporouterVertex *a, ToporouterVertex *b)
+{
+ GSList *i;
+
+ i = a->regions;
+ while(i) {
+ toporouter_vertex_region_t *region = TOPOROUTER_VERTEX_REGION(i->data);
+
+ region->points = g_slist_remove(region->points, b);
+
+ i = i->next;
+ }
+
+}
+
+
+toporouter_attachment_t *
+attachment_new(ToporouterVertex *a, ToporouterVertex *b, ToporouterVertex *p, char *netlist, char *style)
+{
+ toporouter_attachment_t *r = malloc(sizeof(toporouter_attachment_t));
+ gdouble a1 = point_xangle(GTS_POINT(p), GTS_POINT(a));
+ gdouble a2 = point_xangle(GTS_POINT(p), GTS_POINT(b));
+
+ g_assert(a != NULL);
+ g_assert(b != NULL);
+ g_assert(p != NULL);
+
+ r->angle = MAX(angle_span(a1, a2), angle_span(a2,a1));
+ r->a = a;
+ r->b = b;
+ r->p = p;
+ r->netlist = netlist;
+ r->style = style;
+ return r;
+}
+
+gint
+attach_cmp(gconstpointer a, gconstpointer b)
+{
+ toporouter_attachment_t *aa = (toporouter_attachment_t *)a;
+ toporouter_attachment_t *ab = (toporouter_attachment_t *)b;
+
+ if(aa->angle < ab->angle) return -1;
+ if(aa->angle > ab->angle) return 1;
+ return 0;
+}
+
+
+/*
+ * inserts b into a's visibility
+ */
+void
+vertex_visibility_insert(ToporouterVertex *a, ToporouterVertex *b)
+{
+ GSList *i;
+ gdouble angle = point_xangle(GTS_POINT(a), GTS_POINT(b));
+ guint len = g_slist_length(a->regions);
+
+ g_assert(len > 0);
+
+ if(len == 1) {
+ toporouter_vertex_region_t *region = TOPOROUTER_VERTEX_REGION(a->regions->data);
+ if(!g_slist_find(region->points, b))
+ region->points = g_slist_insert_sorted_with_data(region->points, b, point_angle_cmp, a);
+ return;
+ }
+
+ i = a->regions;
+ while(i) {
+ toporouter_vertex_region_t *region = TOPOROUTER_VERTEX_REGION(i->data);
+ gdouble a1,a2;
+
+ g_assert(region->v1 != NULL);
+ g_assert(region->v2 != NULL);
+
+ a1 = point_xangle(GTS_POINT(a), GTS_POINT(region->v1));
+ a2 = point_xangle(GTS_POINT(a), GTS_POINT(region->v2));
+
+ if((a1 >= a2 && (angle >= a1 || angle <= a2)) || (angle >= a1 && a2 >= angle)) {
+ if(!g_slist_find(region->points, b))
+ region->points = g_slist_insert_sorted_with_data(region->points, b, point_angle_cmp, a);
+ }
+
+ i = i->next;
+ }
+
+}
+
+void
+compute_radii(ToporouterVertex *v)
+{
+ gdouble pthickness, pkeepaway, cthickness, ckeepaway;
+ ToporouterBBox *bbox = vertex_bbox(v);
+ GSList *i = v->attached;
+ gdouble raccum = 0.;
+
+ printf("compute_radii: \n");
+
+ if(bbox->type == PIN || bbox->type == VIA) {
+ PinType *pin = (PinType*)bbox->data;
+ pthickness = pin->Thickness / 2.;
+ pkeepaway = pin->Clearance;
+ }else{
+ pthickness = lookup_thickness(bbox->style) / 2.;
+ pkeepaway = lookup_keepaway(bbox->style);
+ }
+
+ while(i) {
+ toporouter_attachment_t *attach = (toporouter_attachment_t *)i->data;
+ cthickness = lookup_thickness(attach->style) / 2.;
+ ckeepaway = lookup_keepaway(attach->style);
+
+ attach->r = raccum + pthickness + cthickness + MAX(pkeepaway, ckeepaway);
+ raccum = attach->r;
+
+ printf("\t[R %f] [A %f]\n", raccum, attach->angle);
+
+ pthickness = cthickness;
+ pkeepaway = ckeepaway;
+ i = i->next;
+ }
+
+}
+
+void
+verify_attachments(toporouter_t *r)
+{
+ int n;
+ for(n=0;n<max_layer;n++) {
+ GSList *i = r->layers[n].vertices;
+
+ int count = 0;
+ while(i) {
+ if(TOPOROUTER_VERTEX(i->data)->flags & VERTEX_FLAG_ATTACH)
+ count++;
+
+ i = i->next;
+ }
+ printf("layer %d attach count = %d\n", n, count);
+ }
+}
+
+GSList *
+split_path(GSList *path)
+{
+ ToporouterVertex *pv = NULL;
+ GSList *curpath = NULL, *i, *paths = NULL;
+
+ i = path;
+ while(i) {
+ ToporouterVertex *v = TOPOROUTER_VERTEX(i->data);
+// printf("***\n");
+// if(v) printf("v = %f,%f\n", GTS_POINT(v)->x, GTS_POINT(v)->y);
+// if(pv) printf("pv = %f,%f\n", GTS_POINT(pv)->x, GTS_POINT(pv)->y);
+
+ if(pv)
+ if(GTS_POINT(v)->x == GTS_POINT(pv)->x && GTS_POINT(v)->y == GTS_POINT(pv)->y) {
+ paths = g_slist_prepend(paths, curpath);
+ curpath = NULL;
+ }
+ curpath = g_slist_prepend(curpath, v);
+
+ pv = v;
+ i = i->next;
+ }
+
+ if(g_slist_length(curpath) > 0)
+ paths = g_slist_prepend(paths, curpath);
+
+ return paths;
+}
+
+GSList *
+route(toporouter_t *r, toporouter_route_t *data)
+{
+ GtsEHeap *openlist = gts_eheap_new(route_heap_cmp, NULL);
+ GSList *closelist = NULL, *paths = NULL;
+ GSList *i, *j;
+ int count = 0;
+
+ toporouter_layer_t *cur_layer = &r->layers[data->src->layer];
+ toporouter_layer_t *dest_layer = &r->layers[data->dest->layer];
+ ToporouterVertex *curpoint = TOPOROUTER_VERTEX(data->src->point);
+ ToporouterVertex *destpoint = TOPOROUTER_VERTEX(data->dest->point);
+
+ printf(" * starting a*\n");
+ data->path = NULL;
+
+ curpoint->parent = NULL;
+ curpoint->gcost = 0.;
+ curpoint->hcost = simple_h_cost(curpoint, destpoint);
+ if(cur_layer != dest_layer) curpoint->hcost += r->viacost;
+ gts_eheap_insert(openlist, curpoint);
+
+ while(gts_eheap_size(openlist) > 0) {
+ GSList *visible;
+ data->curpoint = curpoint;
+ //draw_route_status(r, closelist, openlist, curpoint, data, count++);
+
+ curpoint = TOPOROUTER_VERTEX( gts_eheap_remove_top(openlist, NULL) );
+ if(curpoint->parent && curpoint->boxes) {
+ if(&r->layers[TOPOROUTER_BBOX(curpoint->boxes->data)->layer] != cur_layer) {
+ cur_layer = &r->layers[TOPOROUTER_BBOX(curpoint->boxes->data)->layer];
+ }
+ }
+
+ if(curpoint == destpoint) {
+ ToporouterVertex *temppoint = curpoint;
+ printf("destpoint reached\n");
+ data->path = NULL;
+ while(temppoint) {
+ data->path = g_slist_prepend(data->path, temppoint);
+ if(temppoint->flags & VERTEX_FLAG_ATTACH) printf("contains an attach\n");
+ temppoint = temppoint->parent;
+ }
+ goto route_finish;
+ }
+ closelist_insert(curpoint);
+
+// printf("*** COUNT = %d\n", count);
+ printf(".");
+ visible = compute_visible_points(cur_layer, curpoint, data);
+ /*********************
+ {
+ unsigned int mask = ~(VERTEX_FLAG_RED | VERTEX_FLAG_GREEN | VERTEX_FLAG_BLUE);
+ char buffer[256];
+
+ LAYER_LOOP(PCB->Data, max_layer)
+ {
+ int tempnum = GetLayerNumber(PCB->Data, layer);
+ i = r->layers[tempnum].vertices;
+ while(i) {
+ TOPOROUTER_VERTEX(i->data)->flags &= mask;
+ i = i->next;
+ }
+ }
+ END_LOOP;
+
+ i = visible;
+ while(i) {
+ TOPOROUTER_VERTEX(i->data)->flags |= VERTEX_FLAG_GREEN;
+ i = i->next;
+ }
+
+ curpoint->flags |= VERTEX_FLAG_BLUE;
+ if(curpoint->parent)
+ curpoint->parent->flags |= VERTEX_FLAG_RED;
+ LAYER_LOOP(PCB->Data, max_layer)
+ {
+ int tempnum = GetLayerNumber(PCB->Data, layer);
+ sprintf(buffer, "viz-layer%d-%d.png", tempnum, count);
+ toporouter_draw_surface(r->layers[tempnum].surface, buffer, 1280, 1280, 1, data);
+ }
+ END_LOOP;
+ }
+ *********************/
+ count++;
+ i = visible;
+ while(i) {
+ ToporouterVertex *temppoint = TOPOROUTER_VERTEX(i->data);
+ if(!g_slist_find(closelist, temppoint)) {
+
+ gdouble temp_g_cost = curpoint->gcost
+ + gts_point_distance(GTS_POINT(curpoint), GTS_POINT(temppoint));
+
+ toporouter_heap_search_data_t heap_search_data = { temppoint, NULL };
+
+ gts_eheap_foreach(openlist,toporouter_heap_search, &heap_search_data);
+
+ if(heap_search_data.result) {
+ if(temp_g_cost < temppoint->gcost) {
+
+ temppoint->gcost = temp_g_cost;
+ temppoint->parent = curpoint;
+ gts_eheap_update(openlist);
+ }
+ }else{
+ temppoint->parent = curpoint;
+ temppoint->gcost = temp_g_cost;
+ temppoint->hcost = simple_h_cost(temppoint, destpoint);
+ if(cur_layer != dest_layer) temppoint->hcost += r->viacost;
+ gts_eheap_insert(openlist, temppoint);
+ }
+
+ }
+ i = i->next;
+ }
+ g_slist_free(visible);
+ }
+
+ printf("ERROR: could not find path!\n");
+route_finish:
+ printf(" * finished a*\n");
+ LAYER_LOOP(PCB->Data, max_layer)
+ {
+ char buffer[256];
+ int tempnum = GetLayerNumber(PCB->Data, layer);
+ sprintf(buffer, "route%d.png", tempnum);
+ toporouter_draw_surface(r->layers[tempnum].surface, buffer, 1280, 1280, 2, data);
+ }
+ END_LOOP;
+
+ paths = split_path(data->path);
+
+ printf("%d paths\n", g_slist_length(paths));
+
+ /* set path stuff */
+ j = paths;
+ while(j) {
+ ToporouterVertex *pv = NULL, *nv = NULL;
+ i = (GSList *) j->data;
+ while(i) {
+ ToporouterVertex *v = TOPOROUTER_VERTEX(i->data);
+
+ if(i->next)
+ nv = TOPOROUTER_VERTEX(i->next->data);
+ else
+ nv = NULL;
+
+ if(nv && pv && !(v->flags & VERTEX_FLAG_ATTACH)) {
+ toporouter_attachment_t *attach = attachment_new(nv, pv, v, data->src->netlist, data->src->style);
+ v->attached = g_slist_insert_sorted(v->attached, attach, attach_cmp);
+ compute_radii(v);
+ }
+
+ pv = v;
+ i = i->next;
+ }
+ j = j->next;
+ }
+ return paths;
+}
+
+#define TRACE_EXPORT 1
+
+#define FARFARTV(x) ((x->next) ? ( (x->next->next) ? ((x->next->next->next) ? TOPOROUTER_VERTEX(x->next->next->next->data) : NULL ) : NULL ) : NULL)
+#define FARTV(x) ((x->next) ? ( (x->next->next) ? TOPOROUTER_VERTEX(x->next->next->data) : NULL ) : NULL)
+#define NEXTTV(x) ((x->next) ? TOPOROUTER_VERTEX(x->next->data) : NULL)
+#define CURTV(x) TOPOROUTER_VERTEX(x->data)
+
+#define FARPOINT(x) GTS_POINT(FARTV(x))
+#define NEXTPOINT(x) GTS_POINT(NEXTTV(x))
+#define CURPOINT(x) GTS_POINT(CURTV(x))
+
+void
+export_line(GtsPoint *p1, GtsPoint *p2, int layer, char *style)
+{
+ LineTypePtr line = CreateDrawnLineOnLayer( LAYER_PTR(layer), p1->x, p1->y, p2->x, p2->y,
+ lookup_thickness(style),
+ lookup_keepaway(style),
+ MakeFlags (AUTOFLAG | (TEST_FLAG (CLEARNEWFLAG, PCB) ? CLEARLINEFLAG : 0)));
+
+ AddObjectToCreateUndoList (LINE_TYPE, LAYER_PTR(layer), line, line);
+
+}
+
+void
+export_arc_to_term(GSList *path, char *style, gdouble startangle, gdouble radius, GtsPoint *fakepoint1, GtsPoint *fakepoint2)
+{
+#ifdef TRACE_EXPORT
+ printf("* export_term_to_arc\n");
+#endif
+
+
+
+}
+
+void
+export_arc_to_arc(GSList *path, char *style, gdouble startangle, gdouble radius, GtsPoint *fakepoint1, GtsPoint *fakepoint2)
+{
+#ifdef TRACE_EXPORT
+ printf("* export_term_to_arc\n");
+#endif
+
+
+
+}
+
+gdouble
+vertex_radius(ToporouterVertex*a, ToporouterVertex *v, ToporouterVertex *b)
+{
+ GSList *i = v->attached;
+
+ while(i) {
+ toporouter_attachment_t *attachment = (toporouter_attachment_t *) i->data;
+ if( (attachment->a == a && attachment->b == b) || (attachment->a == b && attachment->b == a) )
+ return attachment->r;
+ i = i->next;
+ }
+
+ if(v->flags & VERTEX_FLAG_ATTACH) {
+ return (vertex_net_thickness(v) / 2.) + vertex_net_keepaway(v);
+
+ }
+
+ printf("WARNING: vertex_radius: segments are not attached to this vertex\n");
+
+ return -1.;
+}
+
+#define BLANKPOINT gts_point_new(gts_point_class(), 0., 0., 0.)
+#define CLOSESTPOINT(x, a, b) ((gts_point_distance(GTS_POINT(x), GTS_POINT(a)) < gts_point_distance(GTS_POINT(x), GTS_POINT(b))) ? a : b)
+
+void
+export_term_to_arc(GSList *path)
+{
+ char *style = vertex_bbox(CURTV(path))->style;
+#ifdef TRACE_EXPORT
+ printf("* export_term_to_arc\n");
+#endif
+
+ if(NEXTTV(path)->flags & VERTEX_FLAG_ATTACH && TOPOROUTER_VERTEX(vertex_bbox(NEXTTV(path))->point) == CURTV(path)) {
+ /* straight line with fakepoint */
+ gdouble r = (lookup_thickness(style) / 2.) + lookup_keepaway(style);
+ gdouble m = perpendicular_gradient( point_gradient(CURPOINT(path), NEXTPOINT(path)) );
+ GtsPoint *temppoint[2] = { BLANKPOINT, BLANKPOINT };
+
+ points_on_line(NEXTPOINT(path), m, r, temppoint[0], temppoint[1]);
+
+ export_line(CURPOINT(path), NEXTPOINT(path), vertex_bbox(CURTV(path))->layer, style);
+
+ if(FARFARTV(path)) {
+ export_arc_to_arc(path->next, style, point_xangle(temppoint[0], NEXTPOINT(path)), r, temppoint[0], temppoint[1]);
+ }else{
+ export_arc_to_term(path->next, style, point_xangle(temppoint[0], NEXTPOINT(path)), r, temppoint[0], temppoint[1]);
+ }
+
+ }else{
+ gdouble m = point_gradient(CURPOINT(path), NEXTPOINT(path));
+ gdouble theta, x, y, r = vertex_radius(CURTV(path), NEXTTV(path), FARTV(path));
+ gdouble tempwind;
+ GtsPoint *temppoint[4] = { BLANKPOINT, BLANKPOINT, BLANKPOINT, BLANKPOINT };
+ GtsPoint *startpoint;
+
+ g_assert(r > 0);
+
+ theta = acos( r / gts_point_distance(CURPOINT(path), NEXTPOINT(path)));
+ x = r * cos( theta );
+ y = r * sin( theta );
+
+ points_on_line(NEXTPOINT(path), m, x, temppoint[0], temppoint[1]);
+ points_on_line(CLOSESTPOINT(CURPOINT(path), temppoint[0], temppoint[1]), perpendicular_gradient(m), y, temppoint[2], temppoint[3]);
+
+ gts_object_destroy (GTS_OBJECT (temppoint[0]));
+ gts_object_destroy (GTS_OBJECT (temppoint[1]));
+
+ tempwind = point_wind(CURPOINT(path), NEXTPOINT(path), FARPOINT(path));
+
+ if(tempwind == 0) {
+ toporouter_vertex_region_t *region1 = vertex_region_containing_point(CURTV(path), temppoint[2]);
+ toporouter_vertex_region_t *region2 = vertex_region_containing_point(CURTV(path), temppoint[3]);
+
+ if(region1 == region2) {
+ /* either one constraint or none */
+ if(region1->v2 == NULL) {
+ /* none, so by convention go clockwise */
+ if(point_wind(CURPOINT(path), temppoint[2], FARPOINT(path)) == 1)
+ startpoint = temppoint[2];
+ else
+ startpoint = temppoint[3];
+ }else{
+ /* one, so go same direction as constraints -> next seg */
+ if(point_wind(CURPOINT(path), temppoint[2], FARPOINT(path))
+ == point_wind(GTS_POINT(region1->v2), NEXTPOINT(path), FARPOINT(path)))
+ startpoint = temppoint[2];
+ else
+ startpoint = temppoint[3];
+ }
+ }else{
+ /* pick point in region with greatest span */
+ g_assert(region_span(region1) != region_span(region2));
+
+ if(region_span(region1) > region_span(region2)) startpoint = temppoint[2];
+ else startpoint = temppoint[3];
+ }
+ }else if(tempwind == point_wind(CURPOINT(path), temppoint[2], FARPOINT(path))) {
+ startpoint = temppoint[2];
+ }else{
+ startpoint = temppoint[3];
+ }
+
+ export_line(CURPOINT(path), startpoint, vertex_bbox(CURTV(path))->layer, style);
+
+ gts_object_destroy (GTS_OBJECT (temppoint[2]));
+ gts_object_destroy (GTS_OBJECT (temppoint[3]));
+
+ if(FARFARTV(path)) {
+ export_arc_to_arc(path->next, style, point_xangle(NEXTPOINT(path), startpoint), r, NULL, NULL);
+ }else{
+ export_arc_to_term(path->next, style, point_xangle(NEXTPOINT(path), startpoint), r, NULL, NULL);
+ }
+
+
+ }
+
+}
+
+
+void
+export_term_to_term(GSList *path)
+{
+#ifdef TRACE_EXPORT
+ printf("* export_term_to_term\n");
+#endif
+ export_line(CURPOINT(path), NEXTPOINT(path), vertex_bbox(TOPOROUTER_VERTEX(path->data))->layer, vertex_bbox(CURTV(path))->style);
+}
+
+void
+export(GSList *path)
+{
+ guint pathlength = g_slist_length(path);
+
+#ifdef TRACE_EXPORT
+ printf("* export\n");
+#endif
+
+ if(pathlength < 2) return ;
+ else if(pathlength == 2) export_term_to_term(path);
+ else export_term_to_arc(path);
+
+}
+
+static int
+toporouter (int argc, char **argv, int x, int y)
+{
+ toporouter_t *r;
+ GtsPoint *temppoint;
+
+ r = toporouter_new();
+
+// r->flags |= TOPOROUTER_FLAG_DEBUG_CDTS;
+ r->flags |= TOPOROUTER_FLAG_DUMP_CDTS;
+
+ if((r->debug = fopen("debug.txt", "w")) == NULL) {
+ printf("error opening debug output file\n");
+ return 0;
+ }
+ printf("Toporouter! %s\n", argc > 0 ? argv[0] : "");
+
+ if(r->flags & TOPOROUTER_FLAG_VERBOSE) {
+ printf("\nParameters:\n");
+ printf("\tVia max = %d\n", r->viamax);
+ printf("\tVia cost = %f\n", r->viacost);
+ printf("\tStub length = %f\n", r->stublength);
+ printf("\n");
+ }
+
+ import_geometry(r);
+
+ RAT_LOOP(PCB->Data);
+ {
+ if( TEST_FLAG(SELECTEDFLAG, line) ) {
+ toporouter_route_t data;
+
+ temppoint = gts_point_new(gts_point_class(), line->Point1.X, line->Point1.Y, 0.);
+ data.src = find_routebox(r, temppoint, line->group1);
+ temppoint->x = line->Point2.X;
+ temppoint->y = line->Point2.Y;
+ data.dest = find_routebox(r, temppoint, line->group2);
+
+ if(data.src) dump_routebox(data.src);
+ else printf("Routebox a NULL\n");
+ if(data.dest) dump_routebox(data.dest);
+ else printf("Routebox b NULL\n");
+
+ if(data.src && data.dest) {
+ GSList *paths = route(r, &data);
+ GSList *i = paths;
+ while(i) {
+ printf("exporting..\n");
+ export((GSList *)i->data);
+ g_slist_free(i->data);
+ i = i->next;
+ }
+ g_slist_free(paths);
+ }else{
+ printf("ERROR: one or both routeboxes NULL\n");
+
+ }
+
+ }
+ }
+ END_LOOP;
+
+ fclose(r->debug);
+ toporouter_free(r);
+
+ IncrementUndoSerialNumber ();
+ return 0;
+}
+
+static int
+global_toporouter (int argc, char **argv, int x, int y)
+{
+
+ printf("Global Toporouter! %s\n", argc > 0 ? argv[0] : "");
+
+ printf("sorry, global toporouter not implemented yet..\n");
+
+ IncrementUndoSerialNumber ();
+
+ return 0;
+}
+
+static HID_Action toporouter_action_list[] = {
+ {"Toporouter", "Select a net", toporouter, "Local topological autorouter", "Toporouter()"},
+ {"GlobalToporouter", NULL, global_toporouter, "Global topological autorouter", "GlobalToporouter()"}
+};
+
+REGISTER_ACTIONS (toporouter_action_list)
+
+void hid_toporouter_init()
+{
+ register_toporouter_action_list();
+}
+
diff --git a/src/toporouter.h b/src/toporouter.h
new file mode 100644
index 0000000..d65e737
--- /dev/null
+++ b/src/toporouter.h
@@ -0,0 +1,387 @@
+/*
+ * COPYRIGHT
+ *
+ * Topological Autorouter for
+ * PCB, interactive printed circuit board design
+ * Copyright (C) 2009 Anthony Blake
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Contact addresses for email:
+ * Anthony Blake, tonyb33@xxxxxxxxx
+ *
+ */
+
+#ifndef __TOPOROUTER_INCLUDED__
+#define __TOPOROUTER_INCLUDED__
+
+#include "data.h"
+#include "macro.h"
+#include "autoroute.h"
+#include "box.h"
+#include "create.h"
+#include "draw.h"
+#include "error.h"
+#include "find.h"
+#include "heap.h"
+#include "rtree.h"
+#include "misc.h"
+#include "mymem.h"
+#include "polygon.h"
+#include "rats.h"
+#include "remove.h"
+#include "thermal.h"
+#include "undo.h"
+#include "global.h"
+
+#include <gts.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#define TOPOROUTER_FLAG_VERBOSE (1<<0)
+#define TOPOROUTER_FLAG_DUMP_CDTS (1<<1)
+#define TOPOROUTER_FLAG_DEBUG_CDTS (1<<2)
+
+#define OUTPUT_ENABLED
+
+#ifdef OUTPUT_ENABLED
+ #include <cairo.h>
+#endif
+
+#define EPSILON 0.0001f
+
+/**
+ * TOPOROUTER_IS_BBOX:
+ * @obj: a #GtsObject.
+ *
+ * Evaluates to %TRUE if @obj is a #ToporouterBBox, %FALSE otherwise.
+ */
+#define TOPOROUTER_IS_BBOX(obj) (gts_object_is_from_class (obj,\
+ toporouter_bbox_class ()))
+/**
+ * TOPOROUTER_BBOX:
+ * @obj: a descendant of #ToporouterBBox.
+ *
+ * Casts @obj to #ToporouterBBox.
+ */
+#define TOPOROUTER_BBOX(obj) GTS_OBJECT_CAST (obj,\
+ ToporouterBBox,\
+ toporouter_bbox_class ())
+/**
+ * TOPOROUTER_BBOX_CLASS:
+ * @klass: a descendant of #ToporouterBBoxClass.
+ *
+ * Casts @klass to #ToporouterBBoxClass.
+ */
+#define TOPOROUTER_BBOX_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\
+ ToporouterBBoxClass,\
+ toporouter_bbox_class ())
+
+
+typedef enum {
+ PAD,
+ PIN,
+ VIA,
+ VIA_SHADOW,
+ LINE,
+ OTHER,
+ BOARD,
+ EXPANSION_AREA,
+ PLANE,
+ TEMP
+} toporouter_term_t;
+
+struct _ToporouterBBox {
+ GtsBBox b;
+
+ toporouter_term_t type;
+ void *data;
+ int layer;
+
+ GtsSurface *surface;
+ GtsTriangle *enclosing;
+
+ GSList *constraints;
+ GtsPoint *point,
+ *realpoint;
+
+ char *netlist, *style;
+};
+
+struct _ToporouterBBoxClass {
+ GtsBBoxClass parent_class;
+};
+
+typedef struct _ToporouterBBox ToporouterBBox;
+typedef struct _ToporouterBBoxClass ToporouterBBoxClass;
+
+/**
+ * TOPOROUTER_IS_EDGE:
+ * @obj: a #GtsObject.
+ *
+ * Evaluates to %TRUE if @obj is a #ToporouterEdge, %FALSE otherwise.
+ */
+#define TOPOROUTER_IS_EDGE(obj) (gts_object_is_from_class (obj,\
+ toporouter_edge_class ()))
+/**
+ * TOPOROUTER_EDGE:
+ * @obj: a descendant of #ToporouterEdge.
+ *
+ * Casts @obj to #ToporouterEdge.
+ */
+#define TOPOROUTER_EDGE(obj) GTS_OBJECT_CAST (obj,\
+ ToporouterEdge,\
+ toporouter_edge_class ())
+/**
+ * TOPOROUTER_EDGE_CLASS:
+ * @klass: a descendant of #ToporouterEdgeClass.
+ *
+ * Casts @klass to #ToporouterEdgeClass.
+ */
+#define TOPOROUTER_EDGE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\
+ ToporouterEdgeClass,\
+ toporouter_edge_class ())
+
+struct _ToporouterEdge {
+ GtsEdge e;
+ NetListType *netlist;
+};
+
+struct _ToporouterEdgeClass {
+ GtsEdgeClass parent_class;
+};
+
+typedef struct _ToporouterEdge ToporouterEdge;
+typedef struct _ToporouterEdgeClass ToporouterEdgeClass;
+
+/**
+ * TOPOROUTER_IS_VERTEX:
+ * @obj: a #GtsObject.
+ *
+ * Evaluates to %TRUE if @obj is a #ToporouterVertex, %FALSE otherwise.
+ */
+#define TOPOROUTER_IS_VERTEX(obj) (gts_object_is_from_class (obj,\
+ toporouter_vertex_class ()))
+/**
+ * TOPOROUTER_VERTEX:
+ * @obj: a descendant of #ToporouterVertex.
+ *
+ * Casts @obj to #ToporouterVertex.
+ */
+#define TOPOROUTER_VERTEX(obj) GTS_OBJECT_CAST (obj,\
+ ToporouterVertex,\
+ toporouter_vertex_class ())
+/**
+ * TOPOROUTER_VERTEX_CLASS:
+ * @klass: a descendant of #ToporouterVertexClass.
+ *
+ * Casts @klass to #ToporouterVertexClass.
+ */
+#define TOPOROUTER_VERTEX_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\
+ ToporouterVertexClass,\
+ toporouter_vertex_class ())
+/*
+#define TOPOROUTER_VERTEX_TYPE_NULL (1<<0)
+#define TOPOROUTER_VERTEX_TYPE_PIN (1<<1)
+#define TOPOROUTER_VERTEX_TYPE_PIN_POLY (1<<2)
+#define TOPOROUTER_VERTEX_TYPE_VIA (1<<3)
+#define TOPOROUTER_VERTEX_TYPE_VIA_POLY (1<<4)
+#define TOPOROUTER_VERTEX_TYPE_PAD (1<<5)
+#define TOPOROUTER_VERTEX_TYPE_CONSTRAINT (1<<6)
+#define TOPOROUTER_VERTEX_TYPE_BOARD (1<<7)
+#define TOPOROUTER_VERTEX_TYPE_LINE (1<<8)
+*/
+
+#define VERTEX_FLAG_ATTACH (1<<7)
+#define VERTEX_FLAG_VIZ (1<<1)
+#define VERTEX_FLAG_CCW (1<<2)
+#define VERTEX_FLAG_CW (1<<3)
+#define VERTEX_FLAG_RED (1<<4)
+#define VERTEX_FLAG_GREEN (1<<5)
+#define VERTEX_FLAG_BLUE (1<<6)
+
+struct _ToporouterVertex {
+ GtsVertex v;
+// void *data;
+// unsigned int type;
+ GSList *boxes;
+ struct _ToporouterVertex *parent;
+
+ //GSList *visible;
+ GSList *regions;
+ GSList *zlink;
+ //GSList *removed;
+ GHashTable* corridors;
+
+ guint flags;
+
+ gdouble gcost, hcost;
+
+ GSList *attached;
+
+};
+
+struct _ToporouterVertexClass {
+ GtsVertexClass parent_class;
+};
+
+typedef struct _ToporouterVertex ToporouterVertex;
+typedef struct _ToporouterVertexClass ToporouterVertexClass;
+
+/**
+ * TOPOROUTER_IS_CONSTRAINT:
+ * @obj: a #GtsObject.
+ *
+ * Evaluates to %TRUE if @obj is a #ToporouterConstraint, %FALSE otherwise.
+ */
+#define TOPOROUTER_IS_CONSTRAINT(obj) (gts_object_is_from_class (obj,\
+ toporouter_constraint_class ()))
+/**
+ * TOPOROUTER_CONSTRAINT:
+ * @obj: a descendant of #ToporouterConstraint.
+ *
+ * Casts @obj to #ToporouterConstraint.
+ */
+#define TOPOROUTER_CONSTRAINT(obj) GTS_OBJECT_CAST (obj,\
+ ToporouterConstraint,\
+ toporouter_constraint_class ())
+/**
+ * TOPOROUTER_CONSTRAINT_CLASS:
+ * @klass: a descendant of #ToporouterConstraintClass.
+ *
+ * Casts @klass to #ToporouterConstraintClass.
+ */
+#define TOPOROUTER_CONSTRAINT_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\
+ ToporouterConstraintClass,\
+ toporouter_constraint_class ())
+/*
+#define TOPOROUTER_CONSTRAINT_TYPE_NULL (1<<0)
+#define TOPOROUTER_CONSTRAINT_TYPE_PAD (1<<1)
+#define TOPOROUTER_CONSTRAINT_TYPE_PIN_POLY (1<<2)
+#define TOPOROUTER_CONSTRAINT_TYPE_VIA_POLY (1<<3)
+#define TOPOROUTER_CONSTRAINT_TYPE_BOARD (1<<4)
+#define TOPOROUTER_CONSTRAINT_TYPE_LINE (1<<5)
+*/
+
+struct _ToporouterConstraint {
+ GtsConstraint c;
+ ToporouterBBox *box;
+// void *data;
+// unsigned int type;
+// GSList *objectconstraints; /* list of all constraints for object */
+};
+
+struct _ToporouterConstraintClass {
+ GtsConstraintClass parent_class;
+};
+
+typedef struct {
+ gdouble x, y;
+} toporouter_spoint_t;
+
+typedef struct _ToporouterConstraint ToporouterConstraint;
+typedef struct _ToporouterConstraintClass ToporouterConstraintClass;
+
+typedef enum {
+ INCIDENT,
+ ATTACHED,
+ ATTACHED_TEMP
+} attachment_type_t;
+
+typedef enum {
+ CCW,
+ CW
+} toporouter_direction_t;
+
+typedef struct {
+ gdouble r;
+ gdouble angle;
+ ToporouterVertex *a, *b, *p;
+
+ char *netlist, *style;
+} toporouter_attachment_t;
+
+typedef struct {
+ ToporouterVertex *v;
+ GSList *edges;
+} toporouter_visibility_t;
+
+typedef struct {
+ GtsSurface *surface;
+// GtsTriangle *t;
+// GtsVertex *v1, *v2, *v3;
+
+ GSList *vertices;
+ GSList *constraints;
+ GSList *edges;
+
+} toporouter_layer_t;
+
+#define TOPOROUTER_VERTEX_REGION(x) ((toporouter_vertex_region_t *)x)
+typedef struct {
+
+ GSList *points;
+ ToporouterVertex *v1, *v2;
+ ToporouterVertex *origin;
+
+} toporouter_vertex_region_t;
+
+typedef struct {
+
+ ToporouterBBox *src, *dest;
+ /*
+ GSList *src_points,
+ *dest_points;
+
+ GSList *src_box_points;
+ */
+ ToporouterVertex *curpoint;
+
+ GSList *path;
+} toporouter_route_t;
+
+
+typedef struct {
+ GSList *bboxes;
+ GNode *bboxtree;
+
+ toporouter_layer_t *layers;
+
+ int flags;
+
+ /* settings: */
+ guint viamax;
+ gdouble viacost;
+ gdouble stublength;
+
+ FILE *debug;
+} toporouter_t;
+
+typedef struct {
+#ifdef CAIRO_H
+ cairo_t *cr;
+ cairo_surface_t *surface;
+#endif
+
+ double s; /* scale factor */
+
+ int mode;
+ void *data;
+
+ char *filename;
+ double iw, ih; /* image dimensions */
+} drawing_context_t;
+
+#endif /* __TOPOROUTER_INCLUDED__ */
_______________________________________________
geda-cvs mailing list
geda-cvs@xxxxxxxxxxxxxx
http://www.seul.org/cgi-bin/mailman/listinfo/geda-cvs