[Pkg-electronics-commits] [pcb-rnd] 03/26: New upstream version 1.1.4

Dima Kogan dima at secretsauce.net
Mon Jan 16 22:31:51 UTC 2017


This is an automated email from the git hooks/post-receive script.

dkogan pushed a commit to branch master
in repository pcb-rnd.

commit 7c6547c96228c299ca601b093baa6a124c7d4ac4
Author: Dima Kogan <dima at secretsauce.net>
Date:   Sun Dec 18 18:16:11 2016 -0800

    New upstream version 1.1.4
---
 Changelog                                      |   13 +
 Makefile                                       |    7 -
 Release_notes                                  |   17 +-
 doc-rnd/gsch2pcb-rnd.htm                       |   49 +
 scconfig/hooks.c                               |    8 +-
 scconfig/src/default/find_fscalls.c            |    7 +-
 src/Makefile.in                                |    3 +
 src/conf.c                                     |    2 +-
 src/netlist_act.c                              |   47 +-
 src_3rd/genregex/COPYING                       |    6 +
 src_3rd/genregex/regex.c                       |    5 +
 src_3rd/genregex/regex_be.c                    |    5 +
 src_3rd/genregex/regex_bei.c                   |    5 +
 src_3rd/genregex/regex_bt.c                    |    5 +
 src_3rd/genregex/regex_bti.c                   |    5 +
 src_3rd/genregex/regex_se.c                    |    5 +
 src_3rd/genregex/regex_sei.c                   |    5 +
 src_3rd/genregex/regex_st.c                    |    5 +
 src_3rd/genregex/regex_sti.c                   |    5 +
 src_3rd/genregex/tester.c                      |    5 +
 src_3rd/genregex/tester_main.c                 |    5 +
 src_3rd/gensexpr/gensexpr_impl.c               |   31 +-
 src_3rd/gensexpr/gensexpr_impl.h               |    4 +
 src_3rd/genvector/genvector_impl.c             |    4 +
 src_3rd/genvector/genvector_undef.h            |   19 +-
 src_3rd/gts/Makefile.dep                       |   36 +
 src_3rd/gts/Makefile.in                        |   74 +
 src_3rd/gts/bbtree.c                           | 1289 +++++++++++
 src_3rd/gts/boolean.c                          | 2048 ++++++++++++++++++
 src_3rd/gts/cdt.c                              | 1173 ++++++++++
 src_3rd/gts/container.c                        |  493 +++++
 src_3rd/gts/curvature.c                        |  621 ++++++
 src_3rd/gts/edge.c                             |  582 +++++
 src_3rd/gts/eheap.c                            |  461 ++++
 src_3rd/gts/face.c                             |  297 +++
 src_3rd/gts/fifo.c                             |  192 ++
 src_3rd/gts/graph.c                            | 1776 +++++++++++++++
 src_3rd/gts/gts-private.h                      |   37 +
 src_3rd/gts/gts.h                              | 2577 ++++++++++++++++++++++
 src_3rd/gts/heap.c                             |  258 +++
 src_3rd/gts/hsurface.c                         |  405 ++++
 src_3rd/gts/iso.c                              |  455 ++++
 src_3rd/gts/isotetra.c                         |  840 ++++++++
 src_3rd/gts/kdtree.c                           |  152 ++
 src_3rd/gts/matrix.c                           |  725 +++++++
 src_3rd/gts/misc.c                             |  692 ++++++
 src_3rd/gts/named.c                            |  188 ++
 src_3rd/gts/object.c                           |  345 +++
 src_3rd/gts/oocs.c                             |  387 ++++
 src_3rd/gts/partition.c                        | 1219 +++++++++++
 src_3rd/gts/pgraph.c                           |  584 +++++
 src_3rd/gts/point.c                            |  986 +++++++++
 src_3rd/gts/predicates.c                       | 2742 +++++++++++++++++++++++
 src_3rd/gts/predicates.h                       |   41 +
 src_3rd/gts/psurface.c                         |  471 ++++
 src_3rd/gts/refine.c                           |  418 ++++
 src_3rd/gts/rounding.h                         |   85 +
 src_3rd/gts/segment.c                          |  233 ++
 src_3rd/gts/split.c                            | 1840 ++++++++++++++++
 src_3rd/gts/stripe.c                           |  766 +++++++
 src_3rd/gts/surface.c                          | 2743 ++++++++++++++++++++++++
 src_3rd/gts/triangle.c                         | 1094 ++++++++++
 src_3rd/gts/tribox3.c                          |  192 ++
 src_3rd/gts/vertex.c                           |  780 +++++++
 src_3rd/gts/vopt.c                             |  521 +++++
 src_3rd/liblhtpers/lhtpers.c                   |   25 +
 src_3rd/liblhtpers/output.c                    |   25 +
 src_3rd/liblhtpers/pers_hash.c                 |   25 +
 src_3rd/liblhtpers/pers_list.c                 |   26 +
 src_3rd/liblhtpers/pers_table.c                |   26 +
 src_3rd/liblhtpers/pers_text.c                 |   26 +
 src_3rd/liblhtpers/tests/perstest.c            |   26 +
 src_3rd/sphash/sphash.c                        |    2 +-
 src_plugins/import_sch/import_sch.c            |   20 +-
 src_plugins/toporouter/Plug.tmpasm             |    3 +-
 util/Makefile                                  |   10 +-
 util/gnet-pcbrndfwd_elem.scm                   |  107 +
 util/gsch2pcb-rnd/Makefile.in                  |    9 +-
 util/gsch2pcb-rnd/glue.c                       |   61 +
 util/gsch2pcb-rnd/gsch2pcb.c                   | 1073 +--------
 util/gsch2pcb-rnd/gsch2pcb.h                   |   48 +
 util/gsch2pcb-rnd/gsch2pcb_rnd_conf.h          |    1 +
 util/gsch2pcb-rnd/help.c                       |   87 +-
 util/gsch2pcb-rnd/help.h                       |    1 -
 util/gsch2pcb-rnd/method.h                     |   15 +
 util/gsch2pcb-rnd/method_import.c              |  141 ++
 util/gsch2pcb-rnd/method_import.h              |    1 +
 util/gsch2pcb-rnd/{gsch2pcb.c => method_pcb.c} | 1180 +++-------
 util/gsch2pcb-rnd/method_pcb.h                 |    1 +
 util/gsch2pcb-rnd/netlister.c                  |  111 +
 util/gsch2pcb-rnd/netlister.h                  |   11 +
 util/gsch2pcb-rnd/run.c                        |  137 ++
 util/gsch2pcb-rnd/run.h                        |   13 +
 93 files changed, 32334 insertions(+), 1967 deletions(-)

diff --git a/Changelog b/Changelog
index 48c80d2..e8ee0c3 100644
--- a/Changelog
+++ b/Changelog
@@ -1,3 +1,16 @@
+pcb-rnd 1.1.4 (r5773)
+~~~~~~~~~~~~~~~~~~~~~
+	[gsch2pcb-rnd]
+		-Add: method infrastructure
+		-Add: a method that uses the same mechanism as import_sch
+		-Add: a method that uses import_sch's gnetlist backend for elements but a the usual PCB backend for the netlist
+	
+	[import_sch]
+		-Fix: install and use the .scm script from our own directory instead of guessing gnetlist's installation
+
+	[debian]
+		-Del: remove debian/ to ease official packaging
+
 pcb-rnd 1.1.3 (r4479)
 ~~~~~~~~~~~~~~~~~~~~~
 	[mods]
diff --git a/Makefile b/Makefile
index 9b94591..9bfe337 100644
--- a/Makefile
+++ b/Makefile
@@ -40,11 +40,4 @@ uninstall: FORCE
 	cd pcblib && make uninstall
 	cd doc-rnd && make uninstall
 
-deb: FORCE
-	fakeroot debian/rules clean
-	fakeroot debian/rules binary
-
-debclean: FORCE
-	fakeroot debian/rules clean
-
 FORCE:
diff --git a/Release_notes b/Release_notes
index ed827e5..e27e2d9 100644
--- a/Release_notes
+++ b/Release_notes
@@ -1,17 +1,4 @@
-pcb-rnd 1.1.3
+pcb-rnd 1.1.4
 ~~~~~~~~~~~~~~
-The main features of this release are kicad compatibility, the new
-query/advanced-search, a bunch of mainline and old plugins imported and
-that the lihata board format has reached production quality.
+This release is a backport of some critical fixes from 1.2.0 to 1.1.3.
 
-The lihata board format introduces a feature that is rare in this class
-of applications: it can load and save board files preserving comments,
-the original indentation, bracing style, units and even numerical
-formats. This reduces unwanted diffs.
-
-This is the last release before a major cleanup of internal data structures.
-The lihata board format had to be sorted out so that new core features
-can be saved and loaded without breaking the inflexible .pcb format. Since
-the cleanup will break the remaining binary compatibility with mainline,
-this was also the last moment we could import old plugins with reasonable
-effort.
diff --git a/doc-rnd/gsch2pcb-rnd.htm b/doc-rnd/gsch2pcb-rnd.htm
new file mode 100644
index 0000000..50dd864
--- /dev/null
+++ b/doc-rnd/gsch2pcb-rnd.htm
@@ -0,0 +1,49 @@
+<html>
+<head>
+	<title> pcb-rnd user manual </title>
+</head>
+<body>
+<h1> pcb-rnd - user manual </h1>
+
+<h2> 2. Utilities  </h2>
+<p>
+<h2> 2.1. gsch2pcb-rnd </h2>
+<p>
+Gsch2pcb-rnd is a standalone utility that can extract netlists and
+element data from a schematics produced by gschem. Thus it is a glue
+between gschem and pcb-rnd, doing <i>forward annotation</i>. It is ideal
+for automating the design process as it can be easily called from
+Makefiles, shell scripts or other programs/scripts, whereas the
+"import schematics" feautre (import_sch plugin) is more geared for the
+GUI user.
+<p>
+Multiple <i>methods</i> of operation are offered - the user can select
+one of these by name, using the -m command line argument (e.g.
+<i>gsch2pcb-rnd -m importsep foo.sch</i>):
+<table border="1">
+	<tr>
+		<td> method name
+		<td> description
+	<tr>
+		<td> pcb
+		<td> The classic approach: load the existing extract elements from
+		     the .sch file, load the .pcb file, compare element lists, load
+		     footprint libraries and create .new.pcb and .net and .cmd and let
+		     the user handle the rest. Does <b>not</b> work with anything else
+		     than the .pcb format. This method is <b>deprecated</b> and is
+		     provided only for compatibility with some old workflows.
+	<tr>
+		<td> import
+		<td> Runs the same gnetlist backend as the import_sch plugin. A single
+		     action command file generated with .cmd suffix. When executed
+		     (using action <i>ExcuteFile(foo.cmd)</i>), it syncs (replaces/creates)
+		     every element and sets up all nets. Pro: single-command import.
+		     Con: can't load netlist only or elements only.
+	<tr>
+		<td> importsep
+		<td> Similar to import, but produces two files: a .cmd file with element
+		     updates only (can be appled the same way as import's) and a
+		     separate .net netlist file that can be imported the same way as the
+		     .net of the pcb method. Pro: the user can update elements-only or
+		     nets-only. Con: requires two user actions to get a full impoty.
+</table>
diff --git a/scconfig/hooks.c b/scconfig/hooks.c
index c911e04..3b739e1 100644
--- a/scconfig/hooks.c
+++ b/scconfig/hooks.c
@@ -10,7 +10,7 @@
 #include "util/arg_auto_set.h"
 #include "Rev.h"
 
-#define version "1.1.3"
+#define version "1.1.4"
 
 #include "plugin_3state.h"
 
@@ -363,8 +363,12 @@ int hook_detect_target()
 	if (want_gtk)
 		want_glib = 1;
 
-	if (plug_is_enabled("toporouter"))
+	if (plug_is_enabled("toporouter")) {
+		put("/local/gts/enable", strue);
 		want_glib = 1;
+	}
+	else
+		put("/local/gts/enable", sfalse);
 
 	if (plug_is_enabled("export_dsn"))
 		want_glib = 1;
diff --git a/scconfig/src/default/find_fscalls.c b/scconfig/src/default/find_fscalls.c
index e7bc9a8..1c3408b 100644
--- a/scconfig/src/default/find_fscalls.c
+++ b/scconfig/src/default/find_fscalls.c
@@ -32,10 +32,15 @@
 int find_fs_realpath(int logdepth, int fatal)
 {
 	char *test_c =
+		NL "#include <limits.h>"
 		NL "#include <stdlib.h>"
 		NL "#include <stdio.h>"
 		NL "int main() {"
-		NL "	char out[1024];"
+		NL "#ifdef PATH_MAX"
+		NL "	char out[PATH_MAX];"
+		NL "#else"
+		NL "	char out[32768];"
+		NL "#endif"
 		NL "	if (realpath(\".\", out) == out)"
 		NL "		puts(\"OK\");"
 		NL "	return 0;"
diff --git a/src/Makefile.in b/src/Makefile.in
index 800e417..4857a2b 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -198,6 +198,9 @@ uniq /local/pcb/CFLAGS
 uniq /local/pcb/LDFLAGS
 uniq /local/pcb/LIBS
 uniq /local/pcb/ACTION_REG_SRC
+uniq /local/pcb/EXEDEPS
+uniq /local/pcb/CLEANFILES
+uniq /local/pcb/CLEANRULES
 put /local/pcb/SRCS /local/pcb/OBJS 
 append /local/pcb/SRCS ?/local/pcb/OBJS_C99
 gsub /local/pcb/SRCS {.o } {.c }
diff --git a/src/conf.c b/src/conf.c
index d9be64d..1bd67bf 100644
--- a/src/conf.c
+++ b/src/conf.c
@@ -646,7 +646,7 @@ typedef struct {
 #define GVT_SIZE_TYPE size_t
 #define GVT_DOUBLING_THRS 64
 #define GVT_START_SIZE 32
-/*#define GVT_FUNC static*/
+#define GVT_FUNC
 #define GVT_REALLOC(vect, ptr, size)  realloc(ptr, size)
 #define GVT_FREE(vect, ptr)           free(ptr)
 #include <genvector/genvector_impl.h>
diff --git a/src/netlist_act.c b/src/netlist_act.c
index 5880e4c..991d50d 100644
--- a/src/netlist_act.c
+++ b/src/netlist_act.c
@@ -114,30 +114,51 @@ quit:;
 
 /* The primary purpose of this action is to rebuild a netlist from a
    script, in conjunction with the clear action above.  */
-static int pcb_netlist_add(const char *netname, const char *pinname)
+static int pcb_netlist_add(int patch, const char *netname, const char *pinname)
 {
 	int ni, pi;
-	LibraryType *netlist = &PCB->NetlistLib[NETLIST_EDITED];
+	LibraryType *netlist = patch ? &PCB->NetlistLib[NETLIST_EDITED] : &PCB->NetlistLib[NETLIST_INPUT];
 	LibraryMenuType *net = NULL;
 	LibraryEntryType *pin = NULL;
 
-	for (ni = 0; ni < netlist->MenuN; ni++)
+	for (ni = 0; ni < netlist->MenuN; ni++) {
 		if (strcmp(netlist->Menu[ni].Name + 2, netname) == 0) {
 			net = &(netlist->Menu[ni]);
 			break;
 		}
+	}
+
+
 	if (net == NULL) {
-		net = CreateNewNet(netlist, (char *) netname, NULL);
+		if (!patch) {
+			net = CreateNewNet(netlist, (char *) netname, NULL);
+			net->Name = pcb_strdup_printf("  %s", netname);
+			net->flag = 1;
+			PCB->netlist_needs_update=1;
+		}
+		else {
+			abort(); /* applying this to net patch is not supported until 1.2.0 */
+		}
 	}
 
-	for (pi = 0; pi < net->EntryN; pi++)
+	for (pi = 0; pi < net->EntryN; pi++) {
 		if (strcmp(net->Entry[pi].ListEntry, pinname) == 0) {
 			pin = &(net->Entry[pi]);
 			break;
 		}
+	}
+
+
 	if (pin == NULL) {
-		pin = CreateNewConnection(net, (char *) pinname);
-		rats_patch_append_optimize(PCB, RATP_ADD_CONN, pin->ListEntry, net->Name + 2, NULL);
+		if (!patch) {
+			LibraryEntryType *entry = GetLibraryEntryMemory(net);
+			entry->ListEntry = pcb_strdup_printf("%s", pinname);
+			entry->ListEntry_dontfree = 0;
+			PCB->netlist_needs_update=1;
+		}
+		else {
+			abort(); /* applying this to net patch is not supported until 1.2.0 */
+		}
 	}
 
 	pcb_netlist_changed(0);
@@ -244,10 +265,11 @@ static int ActionNetlist(int argc, const char **argv, Coord x, Coord y)
 		return pcb_netlist_swap();
 	else if (strcasecmp(argv[0], "add") == 0) {
 		/* Add is different, because the net/pin won't already exist.  */
-		return pcb_netlist_add(ACTION_ARG(1), ACTION_ARG(2));
+		return pcb_netlist_add(0, ACTION_ARG(1), ACTION_ARG(2));
 	}
 	else if (strcasecmp(argv[0], "sort") == 0) {
 		pcb_sort_netlist();
+		rats_patch_make_edited(PCB);
 		return 0;
 	}
 	else if (strcasecmp(argv[0], "freeze") == 0) {
@@ -275,8 +297,8 @@ static int ActionNetlist(int argc, const char **argv, Coord x, Coord y)
 
 	if (argc > 1) {
 		use_re = 1;
-		for (i = 0; i < PCB->NetlistLib[NETLIST_EDITED].MenuN; i++) {
-			net = PCB->NetlistLib[NETLIST_EDITED].Menu + i;
+		for (i = 0; i < PCB->NetlistLib[NETLIST_INPUT].MenuN; i++) {
+			net = PCB->NetlistLib[NETLIST_INPUT].Menu + i;
 			if (strcasecmp(argv[1], net->Name + 2) == 0)
 				use_re = 0;
 		}
@@ -290,9 +312,10 @@ static int ActionNetlist(int argc, const char **argv, Coord x, Coord y)
 		}
 	}
 
-	for (i = PCB->NetlistLib[NETLIST_EDITED].MenuN - 1; i >= 0; i--) {
-		net = PCB->NetlistLib[NETLIST_EDITED].Menu + i;
 
+/* This code is for changing the netlist style */
+	for (i = PCB->NetlistLib[NETLIST_INPUT].MenuN - 1; i >= 0; i--) {
+		net = PCB->NetlistLib[NETLIST_INPUT].Menu + i;
 		if (argc > 1) {
 			if (use_re) {
 				if (re_sei_exec(regex, net->Name + 2) == 0)
diff --git a/src_3rd/genregex/COPYING b/src_3rd/genregex/COPYING
new file mode 100644
index 0000000..ab7ee70
--- /dev/null
+++ b/src_3rd/genregex/COPYING
@@ -0,0 +1,6 @@
+Based on Ozan S. Yigit's implementation; for more information on source,
+see regex_templ.c.
+
+Generalization done by  Tibor 'Igor2' Palinkas in 2012
+
+This project is placed in the Public Domain.
diff --git a/src_3rd/genregex/regex.c b/src_3rd/genregex/regex.c
index c272b24..fde3e4c 100644
--- a/src_3rd/genregex/regex.c
+++ b/src_3rd/genregex/regex.c
@@ -1,3 +1,8 @@
+/* generalized regex lib based on Ozan S. Yigit's implementation.
+   Generalization done by  Tibor 'Igor2' Palinkas in 2012
+   This file is placed in the Public Domain.
+*/
+
 #include "regex.h"
 
 static const char *msg[] = {
diff --git a/src_3rd/genregex/regex_be.c b/src_3rd/genregex/regex_be.c
index 86c94c4..62f3b6e 100644
--- a/src_3rd/genregex/regex_be.c
+++ b/src_3rd/genregex/regex_be.c
@@ -1,3 +1,8 @@
+/* generalized regex lib based on Ozan S. Yigit's implementation.
+   Generalization done by  Tibor 'Igor2' Palinkas in 2012
+   This file is placed in the Public Domain.
+*/
+
 #define RE_PRFX(name) re_be_ ## name
 #define RE_EXTEND 1
 #define RE_BIN_API 1
diff --git a/src_3rd/genregex/regex_bei.c b/src_3rd/genregex/regex_bei.c
index 2cedbef..3dfc75b 100644
--- a/src_3rd/genregex/regex_bei.c
+++ b/src_3rd/genregex/regex_bei.c
@@ -1,3 +1,8 @@
+/* generalized regex lib based on Ozan S. Yigit's implementation.
+   Generalization done by  Tibor 'Igor2' Palinkas in 2012
+   This file is placed in the Public Domain.
+*/
+
 #define RE_PRFX(name) re_bei_ ## name
 #define RE_EXTEND 1
 #define RE_BIN_API 1
diff --git a/src_3rd/genregex/regex_bt.c b/src_3rd/genregex/regex_bt.c
index e604515..ae05965 100644
--- a/src_3rd/genregex/regex_bt.c
+++ b/src_3rd/genregex/regex_bt.c
@@ -1,3 +1,8 @@
+/* generalized regex lib based on Ozan S. Yigit's implementation.
+   Generalization done by  Tibor 'Igor2' Palinkas in 2012
+   This file is placed in the Public Domain.
+*/
+
 #define RE_PRFX(name) re_bt_ ## name
 #define RE_EXTEND 0
 #define RE_BIN_API 1
diff --git a/src_3rd/genregex/regex_bti.c b/src_3rd/genregex/regex_bti.c
index cc2e3c6..894ccb8 100644
--- a/src_3rd/genregex/regex_bti.c
+++ b/src_3rd/genregex/regex_bti.c
@@ -1,3 +1,8 @@
+/* generalized regex lib based on Ozan S. Yigit's implementation.
+   Generalization done by  Tibor 'Igor2' Palinkas in 2012
+   This file is placed in the Public Domain.
+*/
+
 #define RE_PRFX(name) re_bti_ ## name
 #define RE_EXTEND 0
 #define RE_BIN_API 1
diff --git a/src_3rd/genregex/regex_se.c b/src_3rd/genregex/regex_se.c
index b2e6d74..c4828cd 100644
--- a/src_3rd/genregex/regex_se.c
+++ b/src_3rd/genregex/regex_se.c
@@ -1,3 +1,8 @@
+/* generalized regex lib based on Ozan S. Yigit's implementation.
+   Generalization done by  Tibor 'Igor2' Palinkas in 2012
+   This file is placed in the Public Domain.
+*/
+
 #define RE_PRFX(name) re_se_ ## name
 #define RE_EXTEND 1
 #define RE_BIN_API 0
diff --git a/src_3rd/genregex/regex_sei.c b/src_3rd/genregex/regex_sei.c
index 9d54a81..5e02159 100644
--- a/src_3rd/genregex/regex_sei.c
+++ b/src_3rd/genregex/regex_sei.c
@@ -1,3 +1,8 @@
+/* generalized regex lib based on Ozan S. Yigit's implementation.
+   Generalization done by  Tibor 'Igor2' Palinkas in 2012
+   This file is placed in the Public Domain.
+*/
+
 #define RE_PRFX(name) re_sei_ ## name
 #define RE_EXTEND 1
 #define RE_BIN_API 0
diff --git a/src_3rd/genregex/regex_st.c b/src_3rd/genregex/regex_st.c
index b75942d..b6db197 100644
--- a/src_3rd/genregex/regex_st.c
+++ b/src_3rd/genregex/regex_st.c
@@ -1,3 +1,8 @@
+/* generalized regex lib based on Ozan S. Yigit's implementation.
+   Generalization done by  Tibor 'Igor2' Palinkas in 2012
+   This file is placed in the Public Domain.
+*/
+
 #define RE_PRFX(name) re_st_ ## name
 #define RE_EXTEND 0
 #define RE_BIN_API 0
diff --git a/src_3rd/genregex/regex_sti.c b/src_3rd/genregex/regex_sti.c
index d70e32d..ddca2cb 100644
--- a/src_3rd/genregex/regex_sti.c
+++ b/src_3rd/genregex/regex_sti.c
@@ -1,3 +1,8 @@
+/* generalized regex lib based on Ozan S. Yigit's implementation.
+   Generalization done by  Tibor 'Igor2' Palinkas in 2012
+   This file is placed in the Public Domain.
+*/
+
 #define RE_PRFX(name) re_sti_ ## name
 #define RE_EXTEND 0
 #define RE_BIN_API 0
diff --git a/src_3rd/genregex/tester.c b/src_3rd/genregex/tester.c
index 2bfa2ce..4cbc2f3 100644
--- a/src_3rd/genregex/tester.c
+++ b/src_3rd/genregex/tester.c
@@ -1,3 +1,8 @@
+/* generalized regex lib based on Ozan S. Yigit's implementation.
+   Generalization done by  Tibor 'Igor2' Palinkas in 2012
+   This file is placed in the Public Domain.
+*/
+
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
diff --git a/src_3rd/genregex/tester_main.c b/src_3rd/genregex/tester_main.c
index 3b65288..9cc6150 100644
--- a/src_3rd/genregex/tester_main.c
+++ b/src_3rd/genregex/tester_main.c
@@ -1,3 +1,8 @@
+/* generalized regex lib based on Ozan S. Yigit's implementation.
+   Generalization done by  Tibor 'Igor2' Palinkas in 2012
+   This file is placed in the Public Domain.
+*/
+
 #ifdef BIN_API
 #define LINELEN ,linelen
 #define SUBSLEN ,subslen
diff --git a/src_3rd/gensexpr/gensexpr_impl.c b/src_3rd/gensexpr/gensexpr_impl.c
index 8228d88..4790058 100644
--- a/src_3rd/gensexpr/gensexpr_impl.c
+++ b/src_3rd/gensexpr/gensexpr_impl.c
@@ -66,6 +66,26 @@ static void setloc(GSX(node_t) *nd, gsx_parse_t *parse) {
 static void setloc(GSX(node_t) *nd, gsx_parse_t *parse) { }
 #endif
 
+/* if a parsing is cancelled in the middle, we have a random number of tree
+   nodes abusing their str fields for keeping track of last children. This
+   would cause double-free on uninit() so reset these strings to NULL. Any
+   node with children != NULL will have a last(), and won't have a real str */
+static void undo_last(GSX(node_t) *nd)
+{
+	GSX(node_t) *n;
+	last(nd) = NULL;
+	for(n = nd->children; n != NULL; n = n->next)
+		if (n->children != NULL)
+			undo_last(n);
+}
+
+static void cancel_parse(gsx_parse_t *pctx)
+{
+	GSX(dom_t) *ctx = (GSX(dom_t) *)pctx->user_ctx;
+	if ((ctx->root != NULL) && (ctx->root->children != NULL))
+		undo_last(ctx->root);
+}
+
 static void GSX(parser_ev)(gsx_parse_t *pctx, gsx_parse_event_t ev, const char *data)
 {
 	GSX(dom_t) *ctx = (GSX(dom_t) *)pctx->user_ctx;
@@ -111,8 +131,7 @@ static void GSX(parser_ev)(gsx_parse_t *pctx, gsx_parse_event_t ev, const char *
 			break;
 
 		case GSX_EV_ERROR:
-			if (ctx->parse_current != NULL)
-				last(ctx->parse_current) = NULL;
+			cancel_parse(pctx);
 			break;
 	}
 }
@@ -171,6 +190,11 @@ gsx_parse_res_t GSX(parse_char)(GSX(dom_t) *dom, int chr)
 	return gsx_parse_char(&dom->parse, chr);
 }
 
+void GSX(cancel_parse)(GSX(dom_t) *dom)
+{
+	cancel_parse(&dom->parse);
+}
+
 void GSX(dump_subtree)(GSX(dom_t) *dom, GSX(node_t) *node, void (*write)(void *wctx, const char *str), void *wctx)
 {
 	int compact = (node->str != NULL) && (node->children != NULL);
@@ -281,6 +305,9 @@ void GSX(compact_subtree)(GSX(dom_t) *dom, GSX(node_t) *nd)
 		nd->children = old_ch->next;
 		for(n = old_ch->next; n != NULL; n = n->next)
 			n->parent = nd;
+
+		old_ch->str = NULL;
+		old_ch->next = NULL;
 		dom->free(dom, old_ch);
 	}
 }
diff --git a/src_3rd/gensexpr/gensexpr_impl.h b/src_3rd/gensexpr/gensexpr_impl.h
index a41ee52..48f3037 100644
--- a/src_3rd/gensexpr/gensexpr_impl.h
+++ b/src_3rd/gensexpr/gensexpr_impl.h
@@ -41,6 +41,10 @@ void GSX(free)(GSX(dom_t) *dom);
 
 gsx_parse_res_t GSX(parse_char)(GSX(dom_t) *dom, int chr);
 
+/* This function must be called if the parse is cancelled before the root is
+   closed, else the tree is corrupt. */
+void GSX(cancel_parse)(GSX(dom_t) *dom);
+
 void GSX(dump_subtree)(GSX(dom_t) *dom, GSX(node_t) *node, void (*write)(void *wctx, const char *str), void *wctx);
 void GSX(dump_tree)(GSX(dom_t) *dom, void (*write)(void *wctx, const char *str), void *wctx);
 
diff --git a/src_3rd/genvector/genvector_impl.c b/src_3rd/genvector/genvector_impl.c
index 88768b2..2071190 100644
--- a/src_3rd/genvector/genvector_impl.c
+++ b/src_3rd/genvector/genvector_impl.c
@@ -36,6 +36,10 @@ Project page: http://repo.hu/projects/genvector
 #	define GVT_TERMSIZE 0
 #endif
 
+#ifndef GVT_FUNC
+#	define GVT_FUNC
+#endif
+
 GVT_FUNC void GVT(init)(GVTTYPE *vect)
 {
 	vect->used = vect->alloced = 0;
diff --git a/src_3rd/genvector/genvector_undef.h b/src_3rd/genvector/genvector_undef.h
index 6ffec84..e9f3a09 100644
--- a/src_3rd/genvector/genvector_undef.h
+++ b/src_3rd/genvector/genvector_undef.h
@@ -8,8 +8,19 @@
 #	undef GVT_START_SIZE
 #	undef GVT_TERM
 #	undef GVT_STRLEN
-# undef GVT_REALLOC
-# undef GVT_FREE
-# undef GVT_USER_FIELDS
-# undef GVT_NEED_APPEND_ARRAY
+#	undef GVT_REALLOC
+#	undef GVT_FREE
+#	undef GVT_USER_FIELDS
+#	undef GVT_NEED_APPEND_ARRAY
+#	undef GVT_FUNC
+#	undef GVT_SET_NEW_BYTES_TO
+#	undef GVT_ELEM_CONSTRUCTOR
+#	undef GVT_ELEM_COPY
+#	undef GVT_ELEM_DESTRUCTOR
+#	undef GVT_INIT_ELEM_FUNC
+#	undef GVT_OPTIONAL_NO_REALLOC
 #endif
+
+
+
+
diff --git a/src_3rd/gts/Makefile.dep b/src_3rd/gts/Makefile.dep
new file mode 100644
index 0000000..780ecd8
--- /dev/null
+++ b/src_3rd/gts/Makefile.dep
@@ -0,0 +1,36 @@
+### Generated file, do not edit, run make dep ###
+
+bbtree.o: bbtree.c gts.h
+boolean.o: boolean.c gts.h
+cdt.o: cdt.c ../../config.h gts.h
+container.o: container.c gts.h
+curvature.o: curvature.c gts.h
+edge.o: edge.c gts.h
+eheap.o: eheap.c gts.h
+face.o: face.c gts.h
+fifo.o: fifo.c gts.h
+graph.o: graph.c gts.h
+heap.o: heap.c gts.h
+hsurface.o: hsurface.c gts.h
+iso.o: iso.c gts.h
+isotetra.o: isotetra.c gts.h
+kdtree.o: kdtree.c gts.h
+matrix.o: matrix.c gts.h
+misc.o: misc.c gts.h gts-private.h ../../config.h
+named.o: named.c gts.h
+object.o: object.c gts.h gts-private.h
+oocs.o: oocs.c gts.h
+partition.o: partition.c gts.h
+pgraph.o: pgraph.c gts.h
+point.o: point.c gts.h gts-private.h predicates.h
+predicates.o: predicates.c predicates.h rounding.h ../../config.h
+psurface.o: psurface.c gts.h
+refine.o: refine.c gts.h
+segment.o: segment.c gts.h
+split.o: split.c gts.h
+stripe.o: stripe.c gts.h
+surface.o: surface.c gts.h gts-private.h
+triangle.o: triangle.c gts.h
+tribox3.o: tribox3.c
+vertex.o: vertex.c gts.h
+vopt.o: vopt.c gts.h
diff --git a/src_3rd/gts/Makefile.in b/src_3rd/gts/Makefile.in
new file mode 100644
index 0000000..261f19e
--- /dev/null
+++ b/src_3rd/gts/Makefile.in
@@ -0,0 +1,74 @@
+if /local/gts/enable
+then
+put /local/gts/CFLAGS [@-I. -I.. -I../.. -DG_LOG_DOMAIN=\"Gts\" @libs/sul/glib/cflags@@]
+put /local/gts/OBJS [@
+	object.o
+	point.o
+	vertex.o
+	segment.o
+	edge.o
+	triangle.o
+	face.o
+	kdtree.o
+	bbtree.o
+	misc.o
+	predicates.o
+	heap.o
+	eheap.o
+	fifo.o
+	matrix.o
+	surface.o
+	stripe.o
+	vopt.o
+	refine.o
+	iso.o
+	isotetra.o
+	split.o
+	psurface.o
+	hsurface.o
+	cdt.o
+	boolean.o
+	named.o
+	oocs.o
+	container.o
+	graph.o
+	pgraph.o
+	partition.o
+	curvature.o
+	tribox3.o
+@]
+
+put /tmpasm/OFS { }
+uniq /local/gts/OBJS
+put /local/gts/SRCS /local/gts/OBJS
+gsub /local/gts/SRCS {.o } {.c }
+
+print [@
+CFLAGS = @/local/gts/CFLAGS@
+OBJS = @/local/gts/OBJS@
+CC=@cc/cc@
+
+libgts.a: $(OBJS)
+	@/host/fstools/ar@ rvu libgts.a $(OBJS)
+
+clean:
+	-@/host/fstools/rm@ $(OBJS) libgts.a
+@]
+
+# generate explicit rules for .c -> .o
+put /local/comp/OBJS /local/gts/OBJS
+include {../../scconfig/Makefile.comp.inc}
+
+# generate deps
+put /local/dep/CFLAGS /local/gts/CFLAGS
+put /local/dep/SRCS /local/gts/SRCS
+include {../../scconfig/Makefile.dep.inc}
+else
+print [@
+all:
+
+libgts.a:
+
+clean:
+@]
+end
diff --git a/src_3rd/gts/bbtree.c b/src_3rd/gts/bbtree.c
new file mode 100644
index 0000000..cec93e4
--- /dev/null
+++ b/src_3rd/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/src_3rd/gts/boolean.c b/src_3rd/gts/boolean.c
new file mode 100644
index 0000000..79f3e0c
--- /dev/null
+++ b/src_3rd/gts/boolean.c
@@ -0,0 +1,2048 @@
+/* GTS - Library for the manipulation of triangulated surfaces
+ * Copyright (C) 1999--2002 St�phane Popinet
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <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;
+}
+
+#ifdef CHECK_ORIENTED
+static void check_object (GtsObject * o)
+{
+  g_assert (o->reserved == NULL);
+  g_assert (o->flags == 0);  
+}
+
+static void check_boundary (GtsEdge * e, GtsSurface * s)
+{
+  check_object (GTS_OBJECT (e));
+  check_object (GTS_OBJECT (GTS_SEGMENT (e)->v1));
+  check_object (GTS_OBJECT (GTS_SEGMENT (e)->v2));
+  g_assert (gts_edge_face_number (e, s) == 1);
+}
+
+static void check_interior (GtsEdge * e, GtsSurface * s)
+{
+  guint n;
+  check_object (GTS_OBJECT (e));
+  check_object (GTS_OBJECT (GTS_SEGMENT (e)->v1));
+  check_object (GTS_OBJECT (GTS_SEGMENT (e)->v2));
+
+  n = gts_edge_face_number (e, s);
+#ifdef DEBUG
+  if (n != 2)
+    gts_surface_print_stats (s, stderr);
+#endif /* DEBUG */
+  g_assert (n == 2);
+}
+
+static void check_boundary_interior_triangulation (GSList * boundary,
+						   GSList * interior,
+						   GtsSurface * surface)
+{
+  g_slist_foreach (boundary, (GFunc) check_boundary, surface);
+  g_slist_foreach (interior, (GFunc) check_interior, surface);
+}
+#endif /*ifdef CHECK_ORIENTED */
+
+static void merge_duplicate (GtsEdge * e)
+{
+  GtsEdge * dup = gts_edge_is_duplicate (e);
+
+  g_assert (dup);
+  gts_edge_replace (dup, e);
+  gts_object_destroy (GTS_OBJECT (dup));
+}
+
+static void triangulate_boundary_interior (GSList * boundary, 
+					   GSList * interior,
+					   GtsSurface * s,
+					   GtsPoint * o)
+{
+  GSList * iloops, * bloops, * i;
+
+  i = boundary;
+  while (i) {
+    SET (i->data, RELEVANT);
+    i = i->next;
+  }
+  i = interior;
+  while (i) {
+    SET (i->data, RELEVANT);
+    SET (i->data, INTERIOR);
+    i = i->next;
+  }
+
+  iloops = interior_loops (interior);
+  bloops = boundary_loops (boundary);
+
+  i = iloops;
+  while (i) {
+#ifdef DEBUG
+    fprintf (stderr, "--- interior loop ---\n");
+    print_loop (i->data, stderr);
+#endif /* DEBUG */
+    connect_interior_loop (i->data, &interior, &bloops, s, o);
+    i = i->next;
+  }
+  
+#ifdef DEBUG
+ {
+   FILE * fp = fopen ("/tmp/bloops", "w");
+   write_loops (bloops, fp);
+   fclose (fp);
+ }
+#endif /* DEBUG */
+
+  i = bloops;
+  while (i) {
+#ifdef DEBUG
+    fprintf (stderr, "--- boundary loop ---\n");
+    print_loop (i->data, stderr);
+#endif /* DEBUG */
+    triangulate_loop (i->data, s, o);
+    i = i->next;
+  }
+  
+  g_slist_foreach (interior, (GFunc) merge_duplicate, NULL);
+  g_slist_free (iloops);
+  g_slist_free (bloops);
+
+#ifdef CHECK_ORIENTED
+  check_boundary_interior_triangulation (boundary, interior, s);
+#endif /* CHECK_ORIENTED */
+}
+
+static void create_edges (GtsSegment * s, GtsSurface * surface)
+{
+  if (GTS_OBJECT (s)->reserved) {
+    GList * i = GTS_OBJECT (s)->reserved;
+    GtsVertex * v1 = i->data;
+
+    GTS_OBJECT (s)->reserved = g_list_prepend (i, 
+		      gts_edge_new (surface->edge_class, s->v1, v1));
+    while (i) {
+      GList * next = i->next;
+      GtsVertex * v2 = next ? next->data : s->v2;
+
+      GTS_OBJECT (i->data)->reserved = NULL;
+      i->data = gts_edge_new (surface->edge_class, v1, v2);
+      v1 = v2;
+      i = next;
+    }
+  }
+}
+
+static void add_boundary (GtsSegment * s, GtsSegment * next, 
+			  GSList ** boundary)
+{
+  if (GTS_OBJECT (s)->reserved == NULL)
+    *boundary = g_slist_prepend (*boundary, s);
+  else {
+    if (s->v2 == next->v2 || s->v2 == next->v1) {
+      GList * i = g_list_last (GTS_OBJECT (s)->reserved);
+
+      while (i) {
+	*boundary = g_slist_prepend (*boundary, i->data);
+	i = i->prev;
+      }
+    }
+    else {
+      GList * i = GTS_OBJECT (s)->reserved;
+
+      while (i) {
+	*boundary = g_slist_prepend (*boundary, i->data);
+	i = i->next;
+      }
+    }
+  }
+}
+
+static void triangulate_face (GtsTriangle * t, GtsSurface * surface)
+{
+  GSList * interior = GTS_OBJECT (t)->reserved;
+  GSList * boundary = NULL;
+  GtsSurface * s = gts_surface_new (gts_surface_class (),
+				    surface->face_class,
+				    surface->edge_class,
+				    surface->vertex_class);
+  gdouble x, y, z;
+  GtsPoint * p = GTS_POINT (GTS_SEGMENT (t->e1)->v1);
+  GtsPoint * o;
+
+  GTS_OBJECT (t)->reserved = NULL;  
+  gts_triangle_normal (t, &x, &y, &z);
+  g_assert (x != 0. || y != 0. || z != 0.);
+  o = gts_point_new (gts_point_class (), p->x + x, p->y + y, p->z + z);
+  add_boundary (GTS_SEGMENT (t->e3), GTS_SEGMENT (t->e1), &boundary);
+  add_boundary (GTS_SEGMENT (t->e2), GTS_SEGMENT (t->e3), &boundary);
+  add_boundary (GTS_SEGMENT (t->e1), GTS_SEGMENT (t->e2), &boundary);
+#ifdef DEBUG
+  {
+    static guint nt = 0;
+    char name[80];
+    FILE * fp;
+
+    fprintf (stderr, "%u: triangulating %p\n", nt, t);
+if (nt == 28)
+  fprintf (stderr, "tintin!!!!\n");
+    sprintf (name, "/tmp/oc.%u", nt++);
+    fp = fopen (name, "wt");
+    //    write_graph (boundary, interior, s, fp);
+    write_segments (boundary, interior, fp);
+    fclose (fp);
+  }
+#endif /* DEBUG */
+  triangulate_boundary_interior (boundary, interior, s, o);
+  g_slist_free (interior);
+  g_slist_free (boundary);
+  if (GTS_OBJECT (t)->klass->attributes)
+    gts_surface_foreach_face (s, (GtsFunc) gts_object_attributes, t);
+  gts_surface_merge (surface, s);
+  gts_object_destroy (GTS_OBJECT (s));
+  gts_object_destroy (GTS_OBJECT (o));
+}
+
+static void free_edge_list (GtsObject * o)
+{
+  g_list_free (o->reserved);
+  o->reserved = NULL;
+}
+
+/**
+ * gts_surface_inter_new:
+ * @klass: a #GtsSurfaceInterClass.
+ * @s1: a #GtsSurface.
+ * @s2: a #GtsSurface.
+ * @faces_tree1: a bounding box tree (see gts_bb_tree_new()) for
+ * the faces of @s1.
+ * @faces_tree2: a bounding box tree for the faces of @s2.
+ * @is_open1: whether @s1 is an "open" surface.
+ * @is_open2: whether @s2 is an "open" surface.
+ *
+ * When triangulating the cut faces, the new faces inherit the
+ * attributes of these original faces through their attributes()
+ * method.
+ *
+ * Returns: a new #GtsSurfaceInter describing the intersection of @s1
+ * and @s2.  
+ */
+GtsSurfaceInter * gts_surface_inter_new (GtsSurfaceInterClass * klass,
+					 GtsSurface * s1,
+					 GtsSurface * s2,
+					 GNode * faces_tree1,
+					 GNode * faces_tree2,
+					 gboolean is_open1,
+					 gboolean is_open2)
+{
+  GtsSurfaceInter * si;
+  GtsSurface * s;
+
+  g_return_val_if_fail (klass != NULL, NULL);
+  g_return_val_if_fail (s1 != NULL, NULL);
+  g_return_val_if_fail (s2 != NULL, NULL);
+  g_return_val_if_fail (faces_tree1 != NULL, NULL);
+  g_return_val_if_fail (faces_tree2 != NULL, NULL);
+
+  si = surface_inter_new (klass, s1, s2, faces_tree1, faces_tree2);
+
+  gts_surface_foreach_edge (si->s1, (GtsFunc) create_edges, si->s1);
+  gts_surface_foreach_edge (si->s2, (GtsFunc) create_edges, si->s2);
+
+#ifdef DEBUG
+  fprintf (stderr, "====== triangulating s1 ======\n");
+#endif /* DEBUG */
+  s = gts_surface_new (gts_surface_class (),
+		       s1->face_class,
+		       s1->edge_class,
+		       s1->vertex_class);
+  gts_surface_foreach_face (si->s1, (GtsFunc) triangulate_face, s);
+  gts_surface_foreach_edge (si->s1, (GtsFunc) free_edge_list, NULL);
+  gts_object_destroy (GTS_OBJECT (si->s1));
+  si->s1 = s;
+  GTS_OBJECT (si->s1)->reserved = s1;
+  
+#ifdef DEBUG
+  fprintf (stderr, "====== triangulating s2 ======\n");
+#endif /* DEBUG */
+  s = gts_surface_new (gts_surface_class (),
+		       s2->face_class,
+		       s2->edge_class,
+		       s2->vertex_class);
+  gts_surface_foreach_face (si->s2, (GtsFunc) triangulate_face, s);
+  gts_surface_foreach_edge (si->s2, (GtsFunc) free_edge_list, NULL);
+  gts_object_destroy (GTS_OBJECT (si->s2));
+  si->s2 = s;
+  GTS_OBJECT (si->s2)->reserved = s2;
+
+  return si;
+}
+
+static void check_surface_edge (GtsEdge * e, gpointer * data)
+{
+  gboolean * ok = data[0];
+  GtsSurface * s = data[1];
+  GtsSurface * bs = GTS_OBJECT (s)->reserved;
+  guint nf = gts_edge_face_number (e, s);
+
+  if (nf < 1 || nf > 2) {
+    *ok = FALSE;
+    g_return_if_fail (nf >= 1 && nf <= 2);
+  }
+  if (nf == 1 && gts_edge_face_number (e, bs) == 0) {
+    *ok = FALSE;
+    g_return_if_fail (gts_edge_face_number (e, bs) > 0);
+  }
+}
+
+static void mark_edge (GtsObject * o, gpointer data)
+{
+  o->reserved = data;
+}
+
+static gint triangle_orientation (GtsTriangle * t, GtsEdge * e)
+{
+  GtsSegment * s = GTS_SEGMENT (t->e1 == e ? t->e2 
+				: 
+				t->e2 == e ? t->e3 
+				: 
+				t->e1);
+  GtsVertex * v2 = GTS_SEGMENT (e)->v2;
+
+  if (s->v1 == v2 || s->v2 == v2)
+    return 1;
+  return -1;
+}
+
+static gboolean check_orientation (GtsEdge * e, GtsSurface * s)
+{
+  GtsTriangle * t1 = NULL, * t2 = NULL;
+  GSList * i = e->triangles;
+  gint o1 = 0, o2 = 0;
+
+  while (i) {
+    if (GTS_IS_FACE (i->data) && 
+	gts_face_has_parent_surface (i->data, s)) {
+      if (t1 == NULL) {
+	t1 = i->data;
+	o1 = triangle_orientation (t1, e);
+      }
+      else if (t2 == NULL) {
+	t2 = i->data;
+	o2 = triangle_orientation (t2, e);
+	g_return_val_if_fail (o1*o2 < 0, FALSE);
+      }
+      else
+	g_assert_not_reached ();
+    }
+    i = i->next;
+  }
+  g_return_val_if_fail (t1 && t2, FALSE);
+  return TRUE;
+}
+
+static void check_edge (GtsSegment * s, gpointer * data)
+{
+  gboolean * ok = data[0];
+  GtsSurfaceInter * si = data[1];
+  gboolean * closed = data[2];
+  GSList * j;
+  guint nn = 0;
+  
+  j = s->v1->segments;
+  while (j && *ok) {
+    GtsSegment * s1 = j->data;
+    
+    if (s1 != s && GTS_OBJECT (s1)->reserved == si) {
+      if (s1->v2 != s->v1)
+	*ok = FALSE;
+      nn++;
+    }
+    j = j->next;
+  }
+  j = s->v2->segments;
+  while (j && *ok) {
+    GtsSegment * s1 = j->data;
+    
+    if (s1 != s && GTS_OBJECT (s1)->reserved == si) {
+      if (s1->v1 != s->v2)
+	*ok = FALSE;
+      nn++;
+    }
+    j = j->next;
+  }
+  if (nn != 2)
+    *closed = FALSE;
+
+  if (!check_orientation (GTS_EDGE (s), si->s1))
+    *ok = FALSE;
+  if (!check_orientation (GTS_EDGE (s), si->s2))
+    *ok = FALSE;
+}
+
+/**
+ * gts_surface_inter_check:
+ * @si: a #GtsSurfaceInter.
+ * @closed: is set to %TRUE if @si->edges is a closed curve, %FALSE
+ * otherwise.
+ *
+ * Returns: %TRUE if the curve described by @si is an orientable
+ * manifold, %FALSE otherwise.  
+ */
+gboolean gts_surface_inter_check (GtsSurfaceInter * si,
+				  gboolean * closed)
+{
+  gboolean ok = TRUE;
+  gpointer data[3];
+
+  g_return_val_if_fail (si != NULL, FALSE);
+  g_return_val_if_fail (closed != NULL, FALSE);
+
+  *closed = si->edges ? TRUE : FALSE;
+
+  /* mark edges as used by si */
+  g_slist_foreach (si->edges, (GFunc) mark_edge, si);
+
+  data[0] = &ok;
+  data[1] = si;
+  data[2] = closed;
+  g_slist_foreach (si->edges, (GFunc) check_edge, data);
+  g_slist_foreach (si->edges, (GFunc) gts_object_reset_reserved, NULL);
+
+  /* check connectivity of the faces of @si */
+  if (*closed) {
+    gpointer data[2];
+
+    data[0] = &ok;
+    data[1] = si->s1;
+    gts_surface_foreach_edge (si->s1, (GtsFunc) check_surface_edge, data);
+    data[1] = si->s2;
+    gts_surface_foreach_edge (si->s2, (GtsFunc) check_surface_edge, data);
+  }
+
+  return ok;
+}
+
+/* Given @e and @f returns a #GtsFace compatible with @f and belonging to
+   @s1 or @s2 */
+static GtsFace * next_compatible_face (GtsEdge * e, 
+				       GtsFace * f, 
+				       GtsSurface * s1,
+				       GtsSurface * s2)
+{
+  GSList * i = e->triangles;
+  GtsFace * f2 = NULL, * f3 = NULL;
+
+  while (i) {
+    GtsFace * f1 = i->data;
+
+    if (f1 != f && GTS_IS_FACE (f1)) {
+      if (gts_face_has_parent_surface (f1, s1))
+	return f1;
+      if (gts_face_has_parent_surface (f1, s2)) {
+	if (f2 == NULL) f2 = f1;
+	else if (f3 == NULL) f3 = f1;
+	else g_assert_not_reached (); /* s2 is a non-manifold surface */
+      }
+    }
+    i = i->next;
+  }
+  if (f3 == NULL) {
+    if (gts_edge_is_boundary (e, s2))
+      return NULL;
+    return f2; 
+  }
+  g_assert (gts_face_has_parent_surface (f, s1));
+  if (gts_triangles_are_compatible (GTS_TRIANGLE (f), GTS_TRIANGLE (f2), e))
+    return f2;
+  return f3;
+}
+
+static void walk_faces (GtsEdge * e, GtsFace * f, 
+			GtsSurface * s1,
+			GtsSurface * s2,
+			GtsSurface * s)
+{
+  GtsFifo * faces = gts_fifo_new ();
+  GtsFifo * edges = gts_fifo_new ();
+
+  gts_fifo_push (faces, f);
+  gts_fifo_push (edges, e);
+  while ((f = gts_fifo_pop (faces)) && (e = gts_fifo_pop (edges))) {
+    if (!GTS_OBJECT (f)->reserved) {
+      GtsTriangle * t = GTS_TRIANGLE (f);
+      GtsFace * f1;
+
+      gts_surface_add_face (s, f);
+      GTS_OBJECT (f)->reserved = s;
+      if (t->e1 != e && !GTS_OBJECT (t->e1)->reserved &&
+	  (f1 = next_compatible_face (t->e1, f, s1, s2))) {
+	gts_fifo_push (faces, f1);
+	gts_fifo_push (edges, t->e1);
+      }	
+      if (t->e2 != e && !GTS_OBJECT (t->e2)->reserved &&
+	  (f1 = next_compatible_face (t->e2, f, s1, s2))) {
+	gts_fifo_push (faces, f1);
+	gts_fifo_push (edges, t->e2);
+      }	
+      if (t->e3 != e && !GTS_OBJECT (t->e3)->reserved &&
+	  (f1 = next_compatible_face (t->e3, f, s1, s2))) {
+	gts_fifo_push (faces, f1);
+	gts_fifo_push (edges, t->e3);
+      }	
+    }
+  }
+  gts_fifo_destroy (faces);
+  gts_fifo_destroy (edges);
+}
+
+/**
+ * gts_surface_inter_boolean:
+ * @si: a #GtsSurfaceInter.
+ * @surface: a #GtsSurface.
+ * @op: a #GtsBooleanOperation.
+ *
+ * Adds to @surface the part of the surface described by @si and @op.
+ */
+void gts_surface_inter_boolean (GtsSurfaceInter * si,
+				GtsSurface * surface,
+				GtsBooleanOperation op)
+{
+  GtsSurface * s = NULL;
+  gint orient = 1;
+  GSList * i;
+
+  g_return_if_fail (si != NULL);
+  g_return_if_fail (surface != NULL);
+
+  switch (op) {
+  case GTS_1_OUT_2: s = si->s1; orient = 1; break;
+  case GTS_1_IN_2: s = si->s1; orient = -1; break;
+  case GTS_2_OUT_1: s = si->s2; orient = -1; break;
+  case GTS_2_IN_1: s = si->s2; orient = 1; break;
+  default: g_assert_not_reached ();
+  }
+
+  /* mark edges as belonging to intersection */
+  g_slist_foreach (si->edges, (GFunc) mark_edge, si);
+
+  i = si->edges;
+  while (i) {
+    GtsEdge * e = i->data;
+    GSList * j = e->triangles;
+    
+    while (j) {
+      if (gts_face_has_parent_surface (j->data, s) &&
+	  orient*triangle_orientation (j->data, e) > 0) {
+#ifdef DEBUG_BOOLEAN
+	GtsFace * boundary = gts_edge_is_boundary (e, surface);
+
+	g_assert (!boundary || boundary == j->data);
+#endif /* DEBUG_BOOLEAN */
+	walk_faces (e, j->data, s, GTS_OBJECT (s)->reserved, surface);
+	break;
+      }
+      j = j->next;
+    }
+    i = i->next;
+  }
+  g_slist_foreach (si->edges, (GFunc) gts_object_reset_reserved, NULL);
+  gts_surface_foreach_face (surface, 
+			    (GtsFunc) gts_object_reset_reserved, NULL);
+}
+
+static void self_intersecting (GtsBBox * bb1, GtsBBox * bb2, 
+			       gpointer * d)
+{
+  GtsTriangle * t1 = bb1->bounded;
+  GtsTriangle * t2 = bb2->bounded;
+
+  if (t1 != t2) {
+    GtsSegment * s1 = GTS_SEGMENT (t1->e1);
+    GtsSegment * s2 = GTS_SEGMENT (t1->e2);
+    GtsSegment * s3 = GTS_SEGMENT (t1->e3);
+    GtsSegment * s4 = GTS_SEGMENT (t2->e1);
+    GtsSegment * s5 = GTS_SEGMENT (t2->e2);
+    GtsSegment * s6 = GTS_SEGMENT (t2->e3);
+    GtsPoint * pi;
+
+    if ((!gts_segments_touch (s4, s1) && 
+	 !gts_segments_touch (s4, s2) &&
+	 !gts_segments_touch (s4, s3) &&
+	 (pi = segment_triangle_intersection (s4, t1, gts_point_class ()))
+	 != NULL) ||
+	(!gts_segments_touch (s5, s1) && 
+	 !gts_segments_touch (s5, s2) &&
+	 !gts_segments_touch (s5, s3) &&
+	 (pi = segment_triangle_intersection (s5, t1, gts_point_class ())) 
+	 != NULL) ||
+	(!gts_segments_touch (s6, s1) && 
+	 !gts_segments_touch (s6, s2) &&
+	 !gts_segments_touch (s6, s3) &&
+	 (pi = segment_triangle_intersection (s6, t1, gts_point_class ())) 
+	 != NULL)) {
+      GtsBBTreeTraverseFunc func = d[0];
+      gpointer data = d[1];
+      gboolean * self_inter = d[2];
+
+      gts_object_destroy (GTS_OBJECT (pi));
+      *self_inter = TRUE;
+      (* func) (bb1, bb2, data);
+    }
+  }
+}
+
+/**
+ * gts_surface_foreach_intersecting_face:
+ * @s: a #GtsSurface.
+ * @func: a #GtsBBTreeTraverseFunc.
+ * @data: user data to pass to @func.
+ *
+ * Calls @func for each intersecting pair of faces of @s.
+ *
+ * Returns: %TRUE if @func was called at least once, %FALSE otherwise.
+ */
+gboolean gts_surface_foreach_intersecting_face (GtsSurface * s,
+						GtsBBTreeTraverseFunc func,
+						gpointer data)
+{
+  GNode * tree;
+  gpointer d[3];
+  gboolean self_inter = FALSE;
+
+  g_return_val_if_fail (s != NULL, FALSE);
+  g_return_val_if_fail (func != NULL, FALSE);
+
+  tree = gts_bb_tree_surface (s);
+  d[0] = func;
+  d[1] = data;
+  d[2] = &self_inter;
+  gts_bb_tree_traverse_overlapping (tree, tree, 
+				    (GtsBBTreeTraverseFunc) self_intersecting,
+				    d);
+  gts_bb_tree_destroy (tree, TRUE);
+
+  return self_inter;
+}
+
+static void add_intersecting (GtsBBox * bb1, GtsBBox * bb2, 
+			      GtsSurface * intersected)
+{
+  gts_surface_add_face (intersected, bb1->bounded);
+  gts_surface_add_face (intersected, bb2->bounded);
+}
+
+/**
+ * gts_surface_is_self_intersecting:
+ * @s: a #GtsSurface.
+ *
+ * Returns: a new #GtsSurface containing the faces of @s which are
+ * self-intersecting or %NULL if no faces of @s are self-intersecting.
+ */
+GtsSurface * gts_surface_is_self_intersecting (GtsSurface * s)
+{
+  GtsSurface * intersected;
+
+  g_return_val_if_fail (s != NULL, NULL);
+
+  intersected = gts_surface_new (GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass),
+				 s->face_class,
+				 s->edge_class,
+				 s->vertex_class);
+  if (!gts_surface_foreach_intersecting_face (s,
+		      (GtsBBTreeTraverseFunc) add_intersecting, intersected)) {
+    gts_object_destroy (GTS_OBJECT (intersected));
+    intersected = NULL;
+  }
+  return intersected;
+}
diff --git a/src_3rd/gts/cdt.c b/src_3rd/gts/cdt.c
new file mode 100644
index 0000000..088eb8e
--- /dev/null
+++ b/src_3rd/gts/cdt.c
@@ -0,0 +1,1173 @@
+/* GTS - Library for the manipulation of triangulated surfaces
+ * Copyright (C) 1999 St�phane Popinet
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+
+#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 */
+
+typedef struct _SFindClosest SFindClosest; 
+
+struct _SFindClosest {
+  gdouble dmin; 
+  GtsFace *closest;
+  GtsPoint * p;
+  gint stop;
+};
+
+#  if GLIB_CHECK_VERSION(2,4,0)
+/* finally, with g_hash_table_find we are able to stop iteration over the hash
+   table in the middle */
+
+static gboolean find_closest (gpointer key, gpointer value, gpointer user_data)
+{
+  SFindClosest * data = (SFindClosest *) user_data;
+  GtsFace * f = GTS_FACE (value);
+  
+  if (gts_triangle_orientation (GTS_TRIANGLE (f)) > 0.) {
+    GtsPoint * p1 = GTS_POINT (GTS_SEGMENT (GTS_TRIANGLE (f)->e1)->v1);
+    gdouble d = ((data->p->x - p1->x)*(data->p->x - p1->x) + 
+		 (data->p->y - p1->y)*(data->p->y - p1->y));
+
+    if (d < data->dmin) {
+      data->dmin = d;
+      data->closest = f;
+    }
+  }
+  data->stop--;
+  return !(data->stop > 0);
+}
+
+static GtsFace * closest_face (GtsSurface * s, GtsPoint * p)
+{
+  SFindClosest fc;
+
+  fc.dmin = G_MAXDOUBLE;
+  fc.closest = NULL;
+  fc.p = p;
+  fc.stop = (gint) exp (log ((gdouble) g_hash_table_size (s->faces))/3.);
+  g_hash_table_find (s->faces, find_closest, &fc);
+  
+  return fc.closest;
+}
+
+#  else /* VERSION < 2.4.0 */
+
+static void
+find_closest (gpointer key, gpointer value, gpointer user_data)
+{
+  SFindClosest * data = (SFindClosest *) user_data;
+  GtsFace * f = GTS_FACE (value);
+
+  if (gts_triangle_orientation (GTS_TRIANGLE (f)) > 0.) {
+    GtsPoint * p1 = GTS_POINT (GTS_SEGMENT (GTS_TRIANGLE (f)->e1)->v1);
+    gdouble d = ((data->p->x - p1->x)*(data->p->x - p1->x) +
+		 (data->p->y - p1->y)*(data->p->y - p1->y));
+
+    if (d < data->dmin) {
+      data->dmin = d;
+      data->closest = f;
+    }
+  }
+  data->stop--;
+}
+
+/* select the face closest to @p among n^1/3 randomly picked faces
+ * of @surface */
+static GtsFace * closest_face (GtsSurface * s, GtsPoint * p)
+{
+  SFindClosest fc;
+
+  if (!g_hash_table_size (s->faces))
+    return NULL;
+
+  fc.dmin = G_MAXDOUBLE;
+  fc.closest = NULL;
+  fc.p = p;
+  fc.stop = (gint) exp (log ((gdouble) g_hash_table_size (s->faces))/3.);
+  g_hash_table_foreach (s->faces, find_closest, &fc);
+  return fc.closest;
+}
+#  endif /* VERSION < 2.4.0 */
+#endif /* not USE_SURFACE_BTREE */
+
+/* returns the face belonging to @surface and neighbor of @f via @e */
+static GtsFace * neighbor (GtsFace * f,
+			   GtsEdge * e,
+			   GtsSurface * surface)
+{
+  GSList * i = e->triangles;
+  GtsTriangle * t = GTS_TRIANGLE (f);
+
+  while (i) {
+    GtsTriangle * t1 = i->data;
+    if (t1 != t &&
+	GTS_IS_FACE (t1) &&
+	gts_face_has_parent_surface (GTS_FACE (t1), surface))
+      return GTS_FACE (t1);
+    i = i->next;
+  }
+  return NULL;
+}
+
+/* given a triangle @t and a segment s (@o -> @p). 
+   @o must be in @t. Returns the
+   edge of @t which is intersected by s or %NULL if @p is also
+   contained in @t (on_summit is set to %FALSE) or if s intersects @t 
+   exactly on one of its summit (on_summit is set to %TRUE). */
+static GtsEdge * triangle_next_edge (GtsTriangle * t,
+				     GtsPoint * o, GtsPoint * p,
+				     gboolean * on_summit)
+{
+  GtsVertex * v1, * v2, * v3;
+  GtsEdge * e1, * e2, * e3;
+  gdouble orient = 0.0;
+  
+  gts_triangle_vertices_edges (t, NULL,
+			       &v1, &v2, &v3, 
+			       &e1, &e2, &e3);
+
+  *on_summit = FALSE;
+  orient = gts_point_orientation (o, GTS_POINT (v1), p);
+  if (orient > 0.0) {
+    orient = gts_point_orientation (o, GTS_POINT (v2), p);
+    if (orient > 0.0) {
+      if (gts_point_orientation (GTS_POINT (v2), GTS_POINT (v3), p) >= 0.0)
+	return NULL;
+      return e2;
+    }
+    if (orient < 0.0) {
+      if (gts_point_orientation (GTS_POINT (v1), GTS_POINT (v2), p) >= 0.0)
+	return NULL;
+      return e1;
+    }
+    if (gts_point_orientation (GTS_POINT (v1), GTS_POINT (v2), p) < 0.0)
+      *on_summit = TRUE;
+    return NULL;
+  }
+
+  if (orient < 0.0) {
+    orient = gts_point_orientation (o, GTS_POINT (v3), p);
+    if (orient > 0.0) {
+      if (gts_point_orientation (GTS_POINT (v3), GTS_POINT (v1), p) >= 0.0)
+	return NULL;
+      return e3;
+    }
+    if (orient < 0.0) {
+      if (gts_point_orientation (GTS_POINT (v2), GTS_POINT (v3), p) >= 0.0)
+	return NULL;
+      return e2;
+    }
+    if (gts_point_orientation (GTS_POINT (v3), GTS_POINT (v1), p) < 0.0)
+      *on_summit = TRUE;
+    return NULL;
+  }
+  
+  if (gts_point_orientation (GTS_POINT (v2), GTS_POINT (v3), p) < 0.0)
+    return e2;
+  if (gts_point_orientation (GTS_POINT (v1), GTS_POINT (v2), p) < 0.0)
+    *on_summit = TRUE;
+  return NULL;
+}
+
+static void triangle_barycenter (GtsTriangle * t, GtsPoint * b)
+{
+  GtsPoint * p = GTS_POINT (gts_triangle_vertex (t));
+  b->x = (p->x + 
+	  GTS_POINT (GTS_SEGMENT(t->e1)->v1)->x +
+	  GTS_POINT (GTS_SEGMENT(t->e1)->v2)->x)/3.;
+  b->y = (p->y + 
+	  GTS_POINT (GTS_SEGMENT(t->e1)->v1)->y +
+	  GTS_POINT (GTS_SEGMENT(t->e1)->v2)->y)/3.;
+}
+
+static GtsFace * point_locate (GtsPoint * o,
+			       GtsPoint * p,
+			       GtsFace * f,
+			       GtsSurface * surface)
+{
+  GtsEdge * prev;
+  gboolean on_summit;
+  GtsVertex * v1, * v2, * v3;
+  GtsEdge * e2, * e3;    
+  
+  prev = triangle_next_edge (GTS_TRIANGLE (f), o, p, &on_summit);
+
+  if (!prev) {
+    GtsFace * f1;
+
+    if (!on_summit)
+      return f; /* p is inside f */
+
+    /* s intersects f exactly on a summit: restarts from a neighbor of f */
+    if ((f1 = neighbor (f, GTS_TRIANGLE (f)->e1, surface)) ||
+	(f1 = neighbor (f, GTS_TRIANGLE (f)->e2, surface)) ||
+	(f1 = neighbor (f, GTS_TRIANGLE (f)->e3, surface))) {
+      triangle_barycenter (GTS_TRIANGLE (f1), o);
+      return point_locate (o, p, f1, surface);
+    }
+    return NULL;
+  }
+  
+  f = neighbor (f, prev, surface);
+  if (f)
+    gts_triangle_vertices_edges (GTS_TRIANGLE (f), prev, 
+				 &v1, &v2, &v3, &prev, &e2, &e3);
+  while (f) {
+    gdouble orient = gts_point_orientation (o, GTS_POINT (v3), p);
+
+    if (orient < 0.0) {
+      if (gts_point_orientation (GTS_POINT (v2), GTS_POINT (v3), p) >= 0.0)
+	return f; /* p is inside f */
+      f = neighbor (f, e2, surface);
+      prev = e2;
+      v1 = v3;      
+    }
+    else if (orient > 0.0) {
+      if (gts_point_orientation (GTS_POINT (v3), GTS_POINT (v1), p) >= 0.0)
+	return f; /* p is inside f */
+      f = neighbor (f, e3, surface);
+      prev = e3;
+      v2 = v3;
+    }
+    else {
+      GtsFace * f1;
+
+      if (gts_point_orientation (GTS_POINT (v2), GTS_POINT (v3), p) >= 0.0)
+	return f; /* p is inside f */
+
+      /* s intersects f exactly on v3: restarts from a neighbor of f */
+      if ((f1 = neighbor (f, e2, surface)) ||
+	  (f1 = neighbor (f, e3, surface))) {
+	triangle_barycenter (GTS_TRIANGLE (f1), o);
+	return point_locate (o, p, f1, surface);
+      }
+      return NULL;
+    }
+    /* update e2, e3, v3 for the new triangle */
+    if (f) {
+      if (prev == GTS_TRIANGLE (f)->e1) {
+	e2 = GTS_TRIANGLE (f)->e2; e3 = GTS_TRIANGLE (f)->e3;
+      }
+      else if (prev == GTS_TRIANGLE (f)->e2) {
+	e2 = GTS_TRIANGLE (f)->e3; e3 = GTS_TRIANGLE (f)->e1;
+      }
+      else {
+	e2 = GTS_TRIANGLE (f)->e1; e3 = GTS_TRIANGLE (f)->e2;
+      }
+      if (GTS_SEGMENT (e2)->v1 == v1 || GTS_SEGMENT (e2)->v1 == v2)
+	v3 = GTS_SEGMENT (e2)->v2;
+      else
+	v3 = GTS_SEGMENT (e2)->v1;
+    }
+  }
+  return NULL;
+}
+
+/**
+ * gts_point_locate:
+ * @p: a #GtsPoint.
+ * @surface: a #GtsSurface.
+ * @guess: %NULL or a face of @surface close to @p.
+ *
+ * Locates the face of the planar projection of @surface containing
+ * @p. The planar projection of @surface must define a connected set
+ * of triangles without holes and bounded by a convex boundary. The
+ * algorithm is randomized and performs in O(n^1/3) expected time
+ * where n is the number of triangles of @surface.
+ *
+ * If a good @guess is given the point location can be significantly faster.
+ *
+ * Returns: a #GtsFace of @surface containing @p or %NULL if @p is not
+ * contained within the boundary of @surface.  
+ */
+GtsFace * gts_point_locate (GtsPoint * p, 
+			    GtsSurface * surface,
+			    GtsFace * guess)
+{
+  GtsFace * fr;
+  GtsPoint * o;
+
+  g_return_val_if_fail (p != NULL, NULL);
+  g_return_val_if_fail (surface != NULL, NULL);
+  g_return_val_if_fail (guess == NULL || 
+			gts_face_has_parent_surface (guess, surface), NULL);
+
+  if (guess == NULL)
+    guess = closest_face (surface, p);
+  else
+    g_return_val_if_fail (gts_triangle_orientation (GTS_TRIANGLE (guess)) > 0., NULL);
+
+  if (guess == NULL)
+    return NULL;
+
+  o = GTS_POINT (gts_object_new (GTS_OBJECT_CLASS (gts_point_class ())));
+  triangle_barycenter (GTS_TRIANGLE (guess), o);
+  fr = point_locate (o, p, guess, surface);
+  gts_object_destroy (GTS_OBJECT (o));
+
+  return fr;
+}
+
+
+/**
+ * gts_constraint_class:
+ *
+ * Returns: the #GtsConstraintClass.
+ */
+GtsConstraintClass * gts_constraint_class (void)
+{
+  static GtsConstraintClass * klass = NULL;
+
+  if (klass == NULL) {
+    GtsObjectClassInfo constraint_info = {
+      "GtsConstraint",
+      sizeof (GtsConstraint),
+      sizeof (GtsConstraintClass),
+      (GtsObjectClassInitFunc) NULL,
+      (GtsObjectInitFunc) NULL,
+      (GtsArgSetFunc) NULL,
+      (GtsArgGetFunc) NULL
+    };
+    klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_edge_class ()), 
+				  &constraint_info);
+  }
+
+  return klass;
+}
+
+static void split_list (GtsListFace * f, GtsListFace * f1, GtsListFace * f2, 
+			GtsPoint * p1, GtsPoint * p2,
+			GSList ** last1, GSList ** last2)
+{
+  GSList * i = f->points, * l1 = *last1, * l2 = *last2;
+
+  while (i) {
+    GtsPoint * p = i->data;
+    
+    if (gts_point_orientation (p1, p2, p) >= 0.) {
+      if (l1) l1->next = i; else f1->points = i;
+      l1 = i;
+    }
+    else {
+      if (l2) l2->next = i; else f2->points = i;
+      l2 = i;
+    }
+    i = i->next;
+  }
+  f->points = NULL;
+  *last1 = l1;
+  *last2 = l2;
+}
+
+/* cf. figure misc/swap.fig */
+static void swap_if_in_circle (GtsFace * f1,
+			       GtsVertex * v1, 
+			       GtsVertex * v2, 
+			       GtsVertex * v3,
+			       GtsEdge * e1, 
+			       GtsEdge * e2, 
+			       GtsEdge * e3,
+			       GtsSurface * surface)
+{
+  GtsFace * f2;
+  GtsEdge * e4, *e5;
+  GtsVertex * v4;
+
+  if (GTS_IS_CONSTRAINT (e1)) /* @e1 is a constraint can not swap */
+    return;
+
+  f2 = neighbor (f1, e1, surface);
+  if (f2 == NULL) /* @e1 is a boundary of @surface */
+    return;
+
+  if (GTS_TRIANGLE (f2)->e1 == e1) {
+    e4 = GTS_TRIANGLE (f2)->e2; e5 = GTS_TRIANGLE (f2)->e3;
+  }
+  else if (GTS_TRIANGLE (f2)->e2 == e1) {
+    e4 = GTS_TRIANGLE (f2)->e3; e5 = GTS_TRIANGLE (f2)->e1;
+  }
+  else {
+    e4 = GTS_TRIANGLE (f2)->e1; e5 = GTS_TRIANGLE (f2)->e2;
+  }
+  if (GTS_SEGMENT (e4)->v1 == GTS_SEGMENT (e1)->v1 || 
+      GTS_SEGMENT (e4)->v1 == GTS_SEGMENT (e1)->v2)
+    v4 = GTS_SEGMENT (e4)->v2;
+  else
+    v4 = GTS_SEGMENT (e4)->v1;
+
+  if (gts_point_in_circle (GTS_POINT (v4), GTS_POINT (v1), 
+			   GTS_POINT (v2), GTS_POINT (v3)) > 0.0) {
+    GtsEdge * en;
+    GtsSegment * sn = gts_vertices_are_connected (v3, v4);
+    GtsFace * f3, * f4;
+
+    if (!GTS_IS_EDGE (sn))
+      en = gts_edge_new (surface->edge_class, v3, v4);
+    else
+      en = GTS_EDGE (sn);
+
+    f3 = gts_face_new (surface->face_class, en, e5, e2);
+    gts_object_attributes (GTS_OBJECT (f3), GTS_OBJECT (f1));
+    f4 = gts_face_new (surface->face_class, en, e3, e4);
+    gts_object_attributes (GTS_OBJECT (f4), GTS_OBJECT (f2));
+    
+    if (GTS_IS_LIST_FACE (f3)) {
+      GSList * last3 = NULL, * last4 = NULL;
+
+      if (GTS_IS_LIST_FACE (f1))
+	split_list (GTS_LIST_FACE (f1), GTS_LIST_FACE (f3), GTS_LIST_FACE (f4),
+		    GTS_POINT (v3), GTS_POINT (v4), &last3, &last4);
+      if (GTS_IS_LIST_FACE (f2))
+	split_list (GTS_LIST_FACE (f2), GTS_LIST_FACE (f3), GTS_LIST_FACE (f4),
+		    GTS_POINT (v3), GTS_POINT (v4), &last3, &last4);
+      if (last3) last3->next = NULL;
+      if (last4) last4->next = NULL;
+    }
+
+    gts_surface_remove_face (surface, f1);
+    gts_surface_remove_face (surface, f2);
+    gts_surface_add_face (surface, f3);
+    gts_surface_add_face (surface, f4);
+
+    swap_if_in_circle (f3, v4, v2, v3, e5, e2, en, surface);
+    swap_if_in_circle (f4, v1, v4, v3, e4, en, e3, surface);
+  }
+}
+
+/**
+ * gts_delaunay_add_vertex_to_face:
+ * @surface: a #GtsSurface.
+ * @v: a #GtsVertex.
+ * @f: a #GtsFace belonging to @surface.
+ *
+ * Adds vertex @v to the face @f of the Delaunay triangulation defined
+ * by @surface.
+ *
+ * Returns: %NULL is @v has been successfully added to @surface or was
+ * already contained in @surface or a #GtsVertex having the same x and
+ * y coordinates as @v.  
+ */
+GtsVertex * gts_delaunay_add_vertex_to_face (GtsSurface * surface, 
+					     GtsVertex * v,
+					     GtsFace * f)
+{
+  GtsEdge * e1, * e2, * e3;
+  GtsSegment * s4, * s5, * s6;
+  GtsEdge * e4, * e5, * e6;
+  GtsVertex * v1, * v2, * v3;
+  GtsFace * nf[3];
+
+  g_return_val_if_fail (surface != NULL, v);
+  g_return_val_if_fail (v != NULL, v);
+  g_return_val_if_fail (f != NULL, v);
+
+  gts_triangle_vertices_edges (GTS_TRIANGLE (f), NULL, 
+			       &v1, &v2, &v3, &e1, &e2, &e3);
+  if (v == v1 || v == v2 || v == v3) /* v already in @surface */
+    return NULL;
+  if (GTS_POINT (v)->x == GTS_POINT (v1)->x &&
+      GTS_POINT (v)->y == GTS_POINT (v1)->y)
+    return v1;
+  if (GTS_POINT (v)->x == GTS_POINT (v2)->x &&
+      GTS_POINT (v)->y == GTS_POINT (v2)->y)
+    return v2;
+  if (GTS_POINT (v)->x == GTS_POINT (v3)->x &&
+      GTS_POINT (v)->y == GTS_POINT (v3)->y)
+    return v3;
+
+  s4 = gts_vertices_are_connected (v, v1);
+  if (!GTS_IS_EDGE (s4))
+    e4 = gts_edge_new (surface->edge_class, v, v1);
+  else
+    e4 = GTS_EDGE (s4);
+  s5 = gts_vertices_are_connected (v, v2);
+  if (!GTS_IS_EDGE (s5))
+    e5 = gts_edge_new (surface->edge_class, v, v2);
+  else
+    e5 = GTS_EDGE (s5);
+  s6 = gts_vertices_are_connected (v, v3);
+  if (!GTS_IS_EDGE (s6))
+    e6 = gts_edge_new (surface->edge_class, v, v3);
+  else
+    e6 = GTS_EDGE (s6);
+
+  /* cf. figure misc/swap.fig */
+  nf[0] = gts_face_new (surface->face_class, e4, e1, e5);
+  gts_object_attributes (GTS_OBJECT (nf[0]), GTS_OBJECT (f));
+  nf[1] = gts_face_new (surface->face_class, e5, e2, e6);
+  gts_object_attributes (GTS_OBJECT (nf[1]), GTS_OBJECT (f));
+  nf[2] = gts_face_new (surface->face_class, e6, e3, e4);
+  gts_object_attributes (GTS_OBJECT (nf[2]), GTS_OBJECT (f));
+
+  if (GTS_IS_LIST_FACE (f) && GTS_IS_LIST_FACE (nf[0])) {
+    GSList * i = GTS_LIST_FACE (f)->points, * last[3] = { NULL, NULL, NULL };
+
+    while (i) {
+      GtsPoint * p = i->data;
+      GSList * next = i->next;
+      guint j;
+      
+      if (p != GTS_POINT (v)) {
+	if (gts_point_orientation (GTS_POINT (v), GTS_POINT (v1), p) >= 0.) {
+	  gdouble o = gts_point_orientation (GTS_POINT (v), GTS_POINT (v2), p);
+
+	  if (o != 0.)
+	    j = o > 0. ? 1 : 0;
+	  else
+	    j = gts_point_orientation (GTS_POINT (v), GTS_POINT (v3), p) 
+	      > 0. ? 0 : 1;
+	}
+	else if (gts_point_orientation (GTS_POINT (v), GTS_POINT (v3), p) > 0.)
+	  j = 2;
+	else
+	  j = 1;
+	if (last[j])
+	  last[j]->next = i; 
+	else 
+	  GTS_LIST_FACE (nf[j])->points = i;
+	last[j] = i;
+      }
+      else
+	g_slist_free_1 (i);
+      i = next;
+    }
+    GTS_LIST_FACE (f)->points = NULL;
+    if (last[0]) last[0]->next = NULL;
+    if (last[1]) last[1]->next = NULL;
+    if (last[2]) last[2]->next = NULL;
+  }
+
+  gts_surface_remove_face (surface, f);
+  gts_surface_add_face (surface, nf[0]);
+  gts_surface_add_face (surface, nf[1]);
+  gts_surface_add_face (surface, nf[2]);
+
+  swap_if_in_circle (nf[0], v1, v2, v, e1, e5, e4, surface);
+  swap_if_in_circle (nf[1], v2, v3, v, e2, e6, e5, surface);
+  swap_if_in_circle (nf[2], v3, v1, v, e3, e4, e6, surface);
+
+  return NULL;
+}
+
+/** 
+ * gts_delaunay_add_vertex: 
+ * @surface: a #GtsSurface.  
+ * @v: a #GtsVertex.  
+ * @guess: %NULL or a #GtsFace belonging to @surface to be used as an initial
+ * guess for point location.
+ *
+ * Adds vertex @v to the Delaunay triangulation defined by
+ * @surface. If @v is not contained in the convex hull bounding
+ * @surface, @v is not added to the triangulation.
+ *
+ * Returns: %NULL is @v has been successfully added to @surface or was
+ * already contained in @surface, @v if @v is not contained in the
+ * convex hull bounding surface or a #GtsVertex having the same x and
+ * y coordinates as @v.  
+ */
+GtsVertex * gts_delaunay_add_vertex (GtsSurface * surface, 
+				     GtsVertex * v,
+				     GtsFace * guess)
+{
+  GtsFace * f;
+
+  g_return_val_if_fail (surface != NULL, v);
+  g_return_val_if_fail (v != NULL, v);
+
+  if (!(f = gts_point_locate (GTS_POINT (v), surface, guess)))
+    return v;
+  return gts_delaunay_add_vertex_to_face (surface, v, f);
+}
+
+static gboolean polygon_in_circle (GSList * poly,
+				   GtsPoint * p1, 
+				   GtsPoint * p2,
+				   GtsPoint * p3)
+{
+  GtsVertex * v1 = NULL, * v2 = NULL;
+
+  while (poly) {
+    GtsSegment * s = poly->data;
+    GtsVertex * v;
+    v = s->v1;
+    if (v != v1 && v != v2 &&
+	v != GTS_VERTEX (p1) &&
+	v != GTS_VERTEX (p2) &&
+	v != GTS_VERTEX (p3) &&
+	gts_point_in_circle (GTS_POINT (v), p1, p2, p3) > 0.)
+      return TRUE;
+    v = s->v2;
+    if (v != v1 && v != v2 &&
+	v != GTS_VERTEX (p1) &&
+	v != GTS_VERTEX (p2) &&
+	v != GTS_VERTEX (p3) &&
+	gts_point_in_circle (GTS_POINT (v), p1, p2, p3) > 0.)
+      return TRUE;
+    v1 = s->v1;
+    v2 = s->v2;
+    poly = poly->next;
+  }
+  return FALSE;
+}
+
+static void triangulate_polygon (GSList * poly, 
+				 GtsSurface * surface,
+				 GtsFace * ref)
+{
+  GSList * i, * poly1, * poly2;
+  GtsVertex * v1, * v2, * v3 = NULL;
+  gboolean found = FALSE;
+  GtsSegment * s, * s1, * s2;
+  GtsEdge * e1, * e2;
+  GtsFace * f;
+
+  if (poly == NULL || poly->next == NULL) {
+    g_slist_free (poly);
+    return;
+  }
+
+  s = poly->data;
+  s1 = poly->next->data;
+  if (s->v1 == s1->v1 || s->v1 == s1->v2) {
+    v1 = s->v2;
+    v2 = s->v1;
+  }
+  else {
+    g_assert (s->v2 == s1->v1 || s->v2 == s1->v2);
+    v1 = s->v1;
+    v2 = s->v2;
+  }
+
+  i = poly->next;
+  v3 = v2;
+  while (i && !found) {
+    s1 = i->data;
+    if (s1->v1 == v3)
+      v3 = s1->v2;
+    else {
+      g_assert (s1->v2 == v3);
+      v3 = s1->v1;
+    }
+    if (v3 != v1 &&
+	gts_point_orientation (GTS_POINT (v1), 
+			       GTS_POINT (v2), 
+			       GTS_POINT (v3)) >= 0. &&
+	!polygon_in_circle (poly, 
+			    GTS_POINT (v1), 
+			    GTS_POINT (v2), 
+			    GTS_POINT (v3)))
+      found = TRUE;
+    else 
+      i = i->next;
+  }
+
+  if (!found) {
+    g_slist_free (poly);
+    return;
+  }
+
+  s1 = gts_vertices_are_connected (v2, v3);
+  if (!GTS_IS_EDGE (s1))
+    e1 = gts_edge_new (surface->edge_class, v2, v3);
+  else
+    e1 = GTS_EDGE (s1);
+  s2 = gts_vertices_are_connected (v3, v1);
+  if (!GTS_IS_EDGE (s2))
+    e2 = gts_edge_new (surface->edge_class, v3, v1);
+  else
+    e2 = GTS_EDGE (s2);
+  f = gts_face_new (surface->face_class, GTS_EDGE (s), e1, e2);
+  gts_object_attributes (GTS_OBJECT (f), GTS_OBJECT (ref));
+  gts_surface_add_face (surface, f);
+
+  poly1 = poly->next;
+  g_slist_free_1 (poly);
+  if (i->next && e2 != i->next->data)
+    poly2 = g_slist_prepend (i->next, e2);
+  else
+    poly2 = i->next;
+  if (e1 != i->data)
+    i->next = g_slist_prepend (NULL, e1);
+  else
+    i->next = NULL;
+
+ triangulate_polygon (poly1, surface, ref);
+ triangulate_polygon (poly2, surface, ref);
+}
+
+/**
+ * gts_delaunay_remove_vertex:
+ * @surface: a #GtsSurface.
+ * @v: a #GtsVertex.
+ *
+ * Removes @v from the Delaunay triangulation defined by @surface and
+ * restores the Delaunay property. Vertex @v must not be used by any
+ * constrained edge otherwise the triangulation is not guaranteed to
+ * be Delaunay.  
+ */
+void gts_delaunay_remove_vertex (GtsSurface * surface, GtsVertex * v)
+{
+  GSList * triangles, * i;
+  GtsFace * ref = NULL;
+
+  g_return_if_fail (surface != NULL);
+  g_return_if_fail (v != NULL);
+
+  i = triangles = gts_vertex_triangles (v, NULL);
+  while (i && !ref) {
+    if (GTS_IS_FACE (i->data) &&
+	gts_face_has_parent_surface (i->data, surface))
+      ref = i->data;
+    i = i->next;
+  }
+  if (!ref) {
+    g_slist_free (triangles);
+    g_return_if_fail (ref);
+  }
+  triangulate_polygon (gts_vertex_fan_oriented (v, surface), surface, ref);
+  i = triangles;
+  while (i) {
+    if (GTS_IS_FACE (i->data) &&
+	gts_face_has_parent_surface (i->data, surface))
+      gts_surface_remove_face (surface, i->data);
+    i = i->next;
+  }
+  g_slist_free (triangles);
+}
+
+#define NEXT_CUT(edge, edge1, list) { next = neighbor (f, edge, surface);\
+                                      remove_triangles (e, surface);\
+                                      if (!constraint && !e->triangles)\
+				        gts_object_destroy (GTS_OBJECT (e));\
+                                      g_assert (next);\
+				      *list = g_slist_prepend (*list, edge1);\
+                                      return g_slist_concat (constraint,\
+                                        remove_intersected_edge (s, edge,\
+					       next, surface, left, right));\
+                                    }
+
+static void remove_triangles (GtsEdge * e, GtsSurface * s)
+{
+  GSList * i = e->triangles;
+
+  while (i) {
+    GSList * next = i->next;
+
+    if (GTS_IS_FACE (i->data) && gts_face_has_parent_surface (i->data, s))
+      gts_surface_remove_face (s, i->data);
+    i = next;
+  }
+}
+
+static GSList * 
+remove_intersected_edge (GtsSegment * s,
+			 GtsEdge * e,
+			 GtsFace * f,
+			 GtsSurface * surface,
+			 GSList ** left, GSList ** right)
+{
+  GtsVertex * v1, * v2, * v3;
+  GtsEdge * e1, * e2;
+  gdouble o1, o2;
+  GtsFace * next;
+  GSList * constraint = NULL;
+
+  if (GTS_IS_CONSTRAINT (e))
+    constraint = g_slist_prepend (NULL, e);
+
+  gts_triangle_vertices_edges (GTS_TRIANGLE (f), e, 
+			       &v1, &v2, &v3, &e, &e1, &e2);
+  
+  o1 = gts_point_orientation (GTS_POINT (v2), GTS_POINT (v3), 
+			      GTS_POINT (s->v2));
+  o2 = gts_point_orientation (GTS_POINT (v3), GTS_POINT (v1), 
+			      GTS_POINT (s->v2));
+
+  if (o1 == 0. && o2 == 0.) {
+/*    if(o2 != 0.) {
+      fprintf(stderr, "o1 = %f o2 = %f\n", o1, o2);
+      fprintf(stderr, "v1 = %f, %f\n", GTS_POINT(v1)->x, GTS_POINT(v1)->y);
+      fprintf(stderr, "v2 = %f, %f\n", GTS_POINT(v2)->x, GTS_POINT(v2)->y);
+      fprintf(stderr, "v3 = %f, %f\n", GTS_POINT(v3)->x, GTS_POINT(v3)->y);
+      fprintf(stderr, "s->v2 = %f, %f\n", GTS_POINT(s->v2)->x, GTS_POINT(s->v2)->y);
+
+      g_assert (o2 == 0.);
+    }*/
+ //   if(o2 == 0.) {
+      remove_triangles (e, surface);
+      if (!constraint && !e->triangles)
+        gts_object_destroy (GTS_OBJECT (e));
+      *left = g_slist_prepend (*left, e2);
+      *right = g_slist_prepend (*right, e1);
+//    }
+  }
+  else if (o1 > 0.) {
+    g_assert (o2 <= 0.);
+    NEXT_CUT (e2, e1, right)
+  }
+  else if (o2 >= 0.)
+    NEXT_CUT (e1, e2, left)
+  else {
+    gdouble o3 = gts_point_orientation (GTS_POINT (s->v1), GTS_POINT (s->v2),
+					GTS_POINT (v3));
+    if (o3 > 0.)
+      NEXT_CUT (e1, e2, left)
+    else
+      NEXT_CUT (e2, e1, right)
+  }
+  return constraint;
+}
+
+static GSList * 
+remove_intersected_vertex (GtsSegment * s,
+			   GtsVertex * v,
+			   GtsSurface * surface,
+			   GSList ** left,
+			   GSList ** right,
+			   GtsFace ** ref)
+{
+  GSList * triangles = gts_vertex_triangles (v, NULL);
+  GSList * i;
+
+  i = triangles;
+  while (i) {
+    GtsTriangle * t = i->data;
+    if (GTS_IS_FACE (t) && 
+	gts_face_has_parent_surface (GTS_FACE (t), surface)) {
+      GtsVertex * v1, * v2, * v3;
+      gdouble o1, o2;
+
+      gts_triangle_vertices (t, &v1, &v2, &v3);
+      if (v == v2) {
+	v2 = v3;
+	v3 = v1;
+      }
+      else if (v == v3) {
+	v3 = v2;
+	v2 = v1;	
+      }
+      else
+	g_assert (v == v1);
+
+      if ((o1 = gts_point_orientation (GTS_POINT (v), GTS_POINT (v2),
+				       GTS_POINT (s->v2))) >= 0. &&
+	  (o2 = gts_point_orientation (GTS_POINT (v3), GTS_POINT (v),
+				       GTS_POINT (s->v2))) >= 0.) {
+	gdouble o3 = gts_point_orientation (GTS_POINT (v2), GTS_POINT (v3),
+					    GTS_POINT (s->v2));
+	GtsEdge * e = gts_triangle_edge_opposite (t, v);
+	GtsEdge * e1, * e2;
+	GtsFace * next = neighbor (GTS_FACE (t), e, surface);
+
+	*ref = GTS_FACE (t);
+	gts_triangle_vertices_edges (t, e, &v2, &v3, &v, &e, &e2, &e1);
+
+	g_slist_free (triangles);
+
+	if (o3 >= 0.) /* @s->v2 is inside (or on the edge) of t */
+	  return NULL;
+
+	gts_allow_floating_faces = TRUE;
+	gts_surface_remove_face (surface, GTS_FACE (t));
+	gts_allow_floating_faces = FALSE;
+
+	*left = g_slist_prepend (*left, e2);
+	*right = g_slist_prepend (*right, e1);
+
+	g_assert (next);
+	return remove_intersected_edge (s, e, next, surface, left, right);
+      }
+    }
+    i = i->next;
+  }
+
+  g_assert_not_reached ();
+  return NULL;
+}
+
+/**
+ * gts_delaunay_add_constraint:
+ * @surface: a #GtsSurface.
+ * @c: a #GtsConstraint.
+ *
+ * Add constraint @c to the constrained Delaunay triangulation defined by
+ * @surface.
+ *
+ * Returns: a list of #GtsConstraint conflicting (i.e. intersecting) with @c 
+ * which were removed from @surface (%NULL if there was none).
+ */
+GSList * gts_delaunay_add_constraint (GtsSurface * surface,
+				      GtsConstraint * c)
+{
+  GSList * constraints;
+  GtsVertex * v1; //, * v2;
+  GSList * left = NULL, * right = NULL;
+  GtsFace * ref = NULL;
+
+  g_return_val_if_fail (surface != NULL, NULL);
+  g_return_val_if_fail (c != NULL, NULL);
+  g_return_val_if_fail (GTS_IS_CONSTRAINT (c), NULL);
+  
+  v1 = GTS_SEGMENT (c)->v1;
+  //v2 = GTS_SEGMENT (c)->v2;
+  
+  gts_allow_floating_edges = TRUE;
+  constraints = remove_intersected_vertex (GTS_SEGMENT (c), v1, surface,
+					   &left, &right, &ref);
+  gts_allow_floating_edges = FALSE;
+#if 1
+  triangulate_polygon (g_slist_prepend (g_slist_reverse (right), c), 
+		       surface, ref);
+  triangulate_polygon (g_slist_prepend (left, c), 
+		       surface, ref);
+#else
+  right = g_slist_prepend (g_slist_reverse (right), c);
+  left = g_slist_prepend (left, c);
+  {
+    FILE * fp0 = fopen ("hole", "wt");
+    FILE * fp1 = fopen ("right", "wt");
+    FILE * fp2 = fopen ("left", "wt");
+    GSList * i = left;
+
+    gts_surface_write (surface, fp0);
+    fclose (fp0);
+ 
+    fprintf (fp2, "LIST {\n");
+    while (i) {
+      GtsSegment * s = i->data;
+      fprintf (fp2, 
+	       "# %p: %p->%p\n"
+	       "VECT 1 2 0 2 0 %g %g 0 %g %g 0\n",
+	       s, s->v1, s->v2,
+	       GTS_POINT (s->v1)->x, GTS_POINT (s->v1)->y,
+	       GTS_POINT (s->v2)->x, GTS_POINT (s->v2)->y);
+      i = i->next;
+    }
+    fprintf (fp2, "}\n");
+    fprintf (fp1, "LIST {\n");
+    i = right;
+    while (i) {
+      GtsSegment * s = i->data;
+      fprintf (fp1, 
+	       "# %p: %p->%p\n"
+	       "VECT 1 2 0 2 0 %g %g 0 %g %g 0\n",
+	       s, s->v1, s->v2,
+	       GTS_POINT (s->v1)->x, GTS_POINT (s->v1)->y,
+	       GTS_POINT (s->v2)->x, GTS_POINT (s->v2)->y);
+      i = i->next;
+    }
+    fprintf (fp1, "}\n");
+    fclose (fp1);
+    fclose (fp2);
+  }
+  triangulate_polygon (right, surface);
+  triangulate_polygon (left, surface);
+#endif
+  if (ref && !ref->surfaces) {
+    gts_allow_floating_edges = TRUE;
+    gts_object_destroy (GTS_OBJECT (ref));
+    gts_allow_floating_edges = FALSE;
+  }
+  return constraints;
+}
+
+static void delaunay_check (GtsTriangle * t, gpointer * data)
+{
+  GtsSurface * surface = data[0];
+  GtsFace ** face = data[1];
+
+  if (*face == NULL) {
+    GSList * i, * list;
+    GtsVertex * v1, * v2, * v3;
+
+    gts_triangle_vertices (t, &v1, &v2, &v3);
+    list = gts_vertex_neighbors (v1, NULL, surface);
+    list = gts_vertex_neighbors (v2, list, surface);
+    list = gts_vertex_neighbors (v3, list, surface);
+    i = list;
+    while (i && *face == NULL) {
+      GtsVertex * v = i->data;
+      if (v != v1 && v != v2 && v != v3 &&
+	  gts_point_in_circle (GTS_POINT (v), 
+			       GTS_POINT (v1),
+			       GTS_POINT (v2),  
+			       GTS_POINT (v3)) > 0.)
+	*face = GTS_FACE (t);
+      i = i->next;
+    }
+    g_slist_free (list);
+  }
+}
+
+/**
+ * gts_delaunay_check:
+ * @surface: a #GtsSurface.
+ *
+ * Returns: %NULL if the planar projection of @surface is a Delaunay 
+ * triangulation (unconstrained), a #GtsFace violating the Delaunay
+ * property otherwise.
+ */
+GtsFace * gts_delaunay_check (GtsSurface * surface)
+{
+  GtsFace * face = NULL;
+  gpointer data[2];
+
+  g_return_val_if_fail (surface != NULL, FALSE);
+
+  data[0] = surface;
+  data[1] = &face;
+  gts_surface_foreach_face (surface, (GtsFunc) delaunay_check, data);
+
+  return face;
+}
+
+/**
+ * gts_delaunay_remove_hull:
+ * @surface: a #GtsSurface.
+ *
+ * Removes all the edges of the boundary of @surface which are not
+ * constraints.  
+ */
+void gts_delaunay_remove_hull (GtsSurface * surface)
+{
+  GSList * boundary;
+
+  g_return_if_fail (surface != NULL);
+
+  boundary = gts_surface_boundary (surface);
+  gts_allow_floating_edges = TRUE;
+  while (boundary) {
+    GSList * i = boundary;
+    GtsEdge * e = i->data;
+
+    boundary = i->next;
+    g_slist_free_1 (i);
+    if (!GTS_IS_CONSTRAINT (e)) {
+      GtsTriangle * t = GTS_TRIANGLE (gts_edge_is_boundary (e, surface));
+
+      if (t != NULL) {
+	if (t->e1 != e && !GTS_IS_CONSTRAINT (t->e1) &&
+	    !gts_edge_is_boundary (t->e1, surface))
+	  boundary = g_slist_prepend (boundary, t->e1);
+	if (t->e2 != e && !GTS_IS_CONSTRAINT (t->e2) &&
+	    !gts_edge_is_boundary (t->e2, surface))
+	  boundary = g_slist_prepend (boundary, t->e2);
+	if (t->e3 != e && !GTS_IS_CONSTRAINT (t->e3) &&
+	    !gts_edge_is_boundary (t->e3, surface))
+	  boundary = g_slist_prepend (boundary, t->e3);
+	gts_surface_remove_face (surface, GTS_FACE (t));
+      }
+      if (!e->triangles)
+	gts_object_destroy (GTS_OBJECT (e));
+    }
+  }
+  gts_allow_floating_edges = FALSE;
+}
+
+/* GtsListFace: Object */
+
+static void gts_list_face_destroy (GtsObject * object)
+{
+  g_slist_free (GTS_LIST_FACE (object)->points);
+
+  (* GTS_OBJECT_CLASS (gts_list_face_class ())->parent_class->destroy) 
+    (object);
+}
+
+static void gts_list_face_class_init (GtsFaceClass * klass)
+{
+  GTS_OBJECT_CLASS (klass)->destroy = gts_list_face_destroy;
+}
+
+GtsFaceClass * gts_list_face_class (void)
+{
+  static GtsFaceClass * klass = NULL;
+
+  if (klass == NULL) {
+    GtsObjectClassInfo gts_list_face_info = {
+      "GtsListFace",
+      sizeof (GtsListFace),
+      sizeof (GtsFaceClass),
+      (GtsObjectClassInitFunc) gts_list_face_class_init,
+      (GtsObjectInitFunc) NULL,
+      (GtsArgSetFunc) NULL,
+      (GtsArgGetFunc) NULL
+    };
+    klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_face_class ()),
+				  &gts_list_face_info);
+  }
+
+  return klass;
+}
diff --git a/src_3rd/gts/container.c b/src_3rd/gts/container.c
new file mode 100644
index 0000000..e1dc0fa
--- /dev/null
+++ b/src_3rd/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/src_3rd/gts/curvature.c b/src_3rd/gts/curvature.c
new file mode 100644
index 0000000..70f6af2
--- /dev/null
+++ b/src_3rd/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/src_3rd/gts/edge.c b/src_3rd/gts/edge.c
new file mode 100644
index 0000000..708c06c
--- /dev/null
+++ b/src_3rd/gts/edge.c
@@ -0,0 +1,582 @@
+/* GTS - Library for the manipulation of triangulated surfaces
+ * Copyright (C) 1999 St�phane Popinet
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gts.h"
+
+gboolean gts_allow_floating_edges = FALSE;
+
+static void edge_destroy (GtsObject * object)
+{
+  GtsEdge * edge = GTS_EDGE (object);
+  GSList * i;
+
+  i = edge->triangles;
+  while (i) {
+    GSList * next = i->next;
+    gts_object_destroy (i->data);
+    i = next;
+  }
+  g_assert (edge->triangles == NULL);
+
+  (* GTS_OBJECT_CLASS (gts_edge_class ())->parent_class->destroy) (object);
+}
+
+static void edge_clone (GtsObject * clone, GtsObject * object)
+{
+  (* GTS_OBJECT_CLASS (gts_edge_class ())->parent_class->clone) (clone,
+								 object);
+  GTS_SEGMENT (clone)->v1 = GTS_SEGMENT (clone)->v2 = NULL;
+  GTS_EDGE (clone)->triangles = NULL;
+}
+
+static void edge_class_init (GtsObjectClass * klass)
+{
+  klass->clone = edge_clone;
+  klass->destroy = edge_destroy;
+}
+
+static void edge_init (GtsEdge * edge)
+{
+  edge->triangles = NULL;
+}
+
+/**
+ * gts_edge_class:
+ *
+ * Returns: the #GtsEdgeClass.
+ */
+GtsEdgeClass * gts_edge_class (void)
+{
+  static GtsEdgeClass * klass = NULL;
+
+  if (klass == NULL) {
+    GtsObjectClassInfo edge_info = {
+      "GtsEdge",
+      sizeof (GtsEdge),
+      sizeof (GtsEdgeClass),
+      (GtsObjectClassInitFunc) edge_class_init,
+      (GtsObjectInitFunc) edge_init,
+      (GtsArgSetFunc) NULL,
+      (GtsArgGetFunc) NULL
+    };
+    klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_segment_class ()), 
+				  &edge_info);
+  }
+
+  return klass;
+}
+
+/**
+ * gts_edge_new:
+ * @klass: a #GtsEdgeClass.
+ * @v1: a #GtsVertex.
+ * @v2: a #GtsVertex.
+ *
+ * Returns: a new #GtsEdge linking @v1 and @v2.
+ */
+GtsEdge * gts_edge_new (GtsEdgeClass * klass,
+			GtsVertex * v1, GtsVertex * v2)
+{
+  return GTS_EDGE (gts_segment_new (GTS_SEGMENT_CLASS (klass), v1, v2));
+}
+
+void gts_edge_remove(GtsEdge *edge) 
+{
+  edge->segment.v1->segments = g_slist_remove(edge->segment.v1->segments, &edge->segment);
+  edge->segment.v2->segments = g_slist_remove(edge->segment.v2->segments, &edge->segment);
+  edge_destroy(GTS_OBJECT (edge));
+}
+
+/**
+ * gts_edge_replace:
+ * @e: a #GtsEdge.
+ * @with: a #GtsEdge.
+ *
+ * Replaces @e with @with. For each triangle which uses @e as an
+ * edge, @e is replaced with @with. The @with->triangles list is
+ * updated appropriately and the @e->triangles list is freed and set
+ * to %NULL.
+ */
+void gts_edge_replace (GtsEdge * e, GtsEdge * with)
+{
+  GSList * i;
+
+  g_return_if_fail (e != NULL && with != NULL && e != with);
+
+  i = e->triangles;
+  while (i) {
+    GtsTriangle * t = i->data;
+    if (t->e1 == e) t->e1 = with;
+    if (t->e2 == e) t->e2 = with;
+    if (t->e3 == e) t->e3 = with;
+    if (!g_slist_find (with->triangles, t))
+      with->triangles = g_slist_prepend (with->triangles, t);
+    i = i->next;
+  }
+  g_slist_free (e->triangles);
+  e->triangles = NULL;
+}
+
+/**
+ * gts_edge_has_parent_surface:
+ * @e: a #GtsEdge.
+ * @surface: a #GtsSurface.
+ * 
+ * Returns: a #GtsFace of @surface having @e as an edge, %NULL otherwise.
+ */
+GtsFace * gts_edge_has_parent_surface (GtsEdge * e, GtsSurface * surface)
+{
+  GSList * i;
+
+  g_return_val_if_fail (e != NULL, NULL);
+
+  i = e->triangles;
+  while (i) {
+    if (GTS_IS_FACE (i->data) && 
+	gts_face_has_parent_surface (i->data, surface))
+      return i->data;
+    i = i->next;
+  }
+  return NULL;
+}
+
+/**
+ * gts_edge_has_any_parent_surface:
+ * @e: a #GtsEdge.
+ * 
+ * Returns: %NULL if @e is not an edge of any triangle or if all the
+ * faces having @e has an edge do not belong to any surface,
+ * a #GtsFace belonging to a surface and having @e as an edge.
+ */
+GtsFace * gts_edge_has_any_parent_surface (GtsEdge * e)
+{
+  GSList * i;
+
+  g_return_val_if_fail (e != NULL, NULL);
+
+  i = e->triangles;
+  while (i) {
+    GtsTriangle * t = i->data;
+    if (GTS_IS_FACE (t) && GTS_FACE (t)->surfaces != NULL)
+      return GTS_FACE (t);
+    i = i->next;
+  }
+  return NULL;
+}
+
+/**
+ * gts_edge_is_boundary:
+ * @e: a #GtsEdge.
+ * @surface: a #GtsSurface or %NULL.
+ * 
+ * Returns: the unique #GtsFace (which belongs to @surface) and which
+ * has @e as an edge (i.e. @e is a boundary edge (of @surface)) or %NULL 
+ * if there is more than one or no faces (belonging to @surface) and
+ * with @e as an edge.
+ */
+GtsFace * gts_edge_is_boundary (GtsEdge * e, GtsSurface * surface)
+{
+  GSList * i;
+  GtsFace * f = NULL;
+  
+  g_return_val_if_fail (e != NULL, NULL);
+  
+  i = e->triangles;
+  while (i) {
+    if (GTS_IS_FACE (i->data)) {
+      if (!surface || gts_face_has_parent_surface (i->data, surface)) {
+	if (f != NULL)
+	  return NULL;
+	f = i->data;
+      }
+    }
+    i = i->next;    
+  }
+  return f;
+}
+
+/**
+ * gts_edges_from_vertices:
+ * @vertices: a list of #GtsVertex.
+ * @parent: a #GtsSurface.
+ * 
+ * Returns: a list of unique #GtsEdge which have one of their vertices in 
+ * @vertices and are used by a face of @parent. 
+ */
+GSList * gts_edges_from_vertices (GSList * vertices, GtsSurface * parent)
+{
+  GHashTable * hash;
+  GSList * edges = NULL, * i;
+
+  g_return_val_if_fail (parent != NULL, NULL);
+  
+  hash = g_hash_table_new (NULL, NULL);
+  i = vertices;
+  while (i) {
+    GSList * j = GTS_VERTEX (i->data)->segments;
+    while (j) {
+      GtsSegment * s = j->data;
+      if (GTS_IS_EDGE (s) &&
+	  gts_edge_has_parent_surface (GTS_EDGE (s), parent) && 
+	  g_hash_table_lookup (hash, s) == NULL) {
+	edges = g_slist_prepend (edges, s);
+	g_hash_table_insert (hash, s, i);
+      }
+      j = j->next;
+    }
+    i = i->next;
+  }
+  g_hash_table_destroy (hash);
+  return edges;
+}
+
+/**
+ * gts_edge_face_number:
+ * @e: a #GtsEdge.
+ * @s: a #GtsSurface.
+ *
+ * Returns: the number of faces using @e and belonging to @s.
+ */
+guint gts_edge_face_number (GtsEdge * e, GtsSurface * s)
+{
+  GSList * i;
+  guint nt = 0;
+
+  g_return_val_if_fail (e != NULL, 0);
+  g_return_val_if_fail (s != NULL, 0);
+
+  i = e->triangles;
+  while (i) {
+    if (GTS_IS_FACE (i->data) && 
+	gts_face_has_parent_surface (GTS_FACE (i->data), s))
+      nt++;
+    i = i->next;
+  }
+  return nt;
+}
+
+/**
+ * gts_edge_is_duplicate:
+ * @e: a #GtsEdge.
+ *
+ * Returns: the first #GtsEdge different from @e which shares the
+ * same endpoints or %NULL if there is none.
+ */
+GtsEdge * gts_edge_is_duplicate (GtsEdge * e)
+{
+  GSList * i;
+  GtsVertex * v2;
+
+  g_return_val_if_fail (e != NULL, NULL);
+
+  v2 = GTS_SEGMENT (e)->v2;
+  i = GTS_SEGMENT (e)->v1->segments;
+  if (GTS_SEGMENT (e)->v1 == v2) /* e is degenerate: special treatment */
+    while (i) {
+      GtsSegment * s = i->data;
+      if (s != GTS_SEGMENT (e) &&
+	  GTS_IS_EDGE (s) && 
+	  s->v1 == v2 && s->v2 == v2)
+	return GTS_EDGE (s);
+      i = i->next;
+    }
+  else /* e is not degenerate */
+    while (i) {
+      GtsSegment * s = i->data;
+      if (s != GTS_SEGMENT (e) &&
+	  GTS_IS_EDGE (s) && 
+	  (s->v1 == v2 || s->v2 == v2))
+	return GTS_EDGE (s);
+      i = i->next;
+    }
+  return NULL;
+}
+
+/**
+ * gts_edges_merge:
+ * @edges: a list of #GtsEdge.
+ *
+ * For each edge in @edges check if it is duplicated (as
+ * returned by gts_edge_is_duplicate()). If it is replace it by its
+ * duplicate, destroy it and remove it from the list.
+ *
+ * Returns: the updated @edges list.
+ */
+GList * gts_edges_merge (GList * edges)
+{
+  GList * i = edges;
+
+  /* we want to control edge destruction */
+  gts_allow_floating_edges = TRUE;
+  while (i) {
+    GtsEdge * e = i->data;
+    GtsEdge * de = gts_edge_is_duplicate (e);
+    if (de) {
+      GList * next = i->next;
+      edges = g_list_remove_link (edges, i);
+      g_list_free_1 (i);
+      i = next;
+      gts_edge_replace (e, de);
+      gts_object_destroy (GTS_OBJECT (e));
+    }
+    else
+      i = i->next;
+  }
+  gts_allow_floating_edges = FALSE;;
+
+  return edges;
+}
+
+static void triangle_vertices_edges (GtsTriangle * t, 
+				     GtsEdge * e,
+				     GtsVertex ** v,
+				     GtsEdge ** ee1,
+				     GtsEdge ** ee2)
+{
+  GtsEdge * e1 = t->e1, * e2 = t->e2, * e3 = t->e3;
+  GtsVertex * v1 = GTS_SEGMENT (e)->v1;
+
+  if (e1 == e)        e1 = e3;
+  else if (e2 == e)   e2 = e3;
+  else                g_assert (e3 == e);
+
+  if (GTS_SEGMENT (e2)->v1 == v1 || GTS_SEGMENT (e2)->v2 == v1) {
+    e3 = e1; e1 = e2; e2 = e3;
+  }
+  if (GTS_SEGMENT (e1)->v1 == v1)
+    *v = GTS_SEGMENT (e1)->v2;
+  else
+    *v = GTS_SEGMENT (e1)->v1;
+  *ee1 = e1;
+  *ee2 = e2;
+}
+
+/**
+ * gts_edge_belongs_to_tetrahedron:
+ * @e: a #GtsEdge.
+ *
+ * Returns: %TRUE if @e is used by faces forming a tetrahedron, %FALSE
+ * otherwise.
+ */
+gboolean gts_edge_belongs_to_tetrahedron (GtsEdge * e)
+{
+  GSList * i;
+
+  g_return_val_if_fail (e != NULL, FALSE);
+
+  i = e->triangles;
+  while (i) {
+    GtsEdge * e1, * e2;
+    GtsVertex * vt1;
+    GSList * j = i->next;
+    triangle_vertices_edges (i->data, e, &vt1, &e1, &e2);
+    while (j) {      
+      GtsSegment * s5;
+      GtsEdge * e3, * e4;
+      GtsVertex * vt2;
+
+      triangle_vertices_edges (j->data, e, &vt2, &e3, &e4);
+      s5 = gts_vertices_are_connected (vt1, vt2);
+      if (GTS_IS_EDGE (s5) &&
+	  gts_triangle_use_edges (e1, e3, GTS_EDGE (s5)) &&
+	  gts_triangle_use_edges (e2, e4, GTS_EDGE (s5)))
+	return TRUE;
+      j = j->next;
+    }
+    i = i->next;
+  }
+
+  return FALSE;
+}
+
+#define edge_use_vertex(e, v) (GTS_SEGMENT(e)->v1 == v ||\
+			       GTS_SEGMENT(e)->v2 == v)
+
+static GtsEdge * next_edge (GtsTriangle * t,
+			    GtsEdge * e1,
+			    GtsEdge * e)
+{
+  GtsVertex * v1 = GTS_SEGMENT (e)->v1;
+  GtsVertex * v2 = GTS_SEGMENT (e)->v2;
+  
+  if (t->e1 != e1 && t->e1 != e && 
+      (edge_use_vertex (t->e1, v1) || edge_use_vertex (t->e1, v2)))
+    return t->e1;
+  else if (t->e2 != e1 && t->e2 != e && 
+	   (edge_use_vertex (t->e2, v1) || edge_use_vertex (t->e2, v2)))
+    return t->e2;
+  else if (t->e3 != e1 && t->e3 != e && 
+	   (edge_use_vertex (t->e3, v1) || edge_use_vertex (t->e3, v2)))
+    return t->e3;
+  g_assert_not_reached ();
+  return NULL;
+}
+
+static void triangle_next (GtsEdge * e1, GtsEdge * e)
+{
+  GSList * i;
+
+  i = e1->triangles;
+  while (i) {
+    GtsTriangle * t = i->data;
+    if (GTS_OBJECT (t)->reserved) {
+      GTS_OBJECT (t)->reserved = NULL;
+      triangle_next (next_edge (t, e1, e), e);
+    }
+    i = i->next;
+  }
+}
+
+/** 
+ * gts_edge_is_contact: 
+ * @e: a #GtsEdge.  
+ *
+ * Returns: the number of sets of connected triangles sharing @e as a
+ * contact edge.  
+ */
+guint gts_edge_is_contact (GtsEdge * e)
+{
+  GSList * i, * triangles;
+  guint ncomponent = 0;
+
+  g_return_val_if_fail (e != NULL, 0);
+
+  triangles = gts_vertex_triangles (GTS_SEGMENT (e)->v1, NULL);
+  i = triangles = gts_vertex_triangles (GTS_SEGMENT (e)->v2, triangles);
+  while (i) {
+    GTS_OBJECT (i->data)->reserved = i;
+    i = i->next;
+  }
+
+  i = e->triangles;
+  while (i) {
+    GtsTriangle * t = i->data;
+    if (GTS_OBJECT (t)->reserved) {
+      GtsEdge * e1;
+      GTS_OBJECT (t)->reserved = NULL;
+      e1 = next_edge (t, NULL, e);
+      triangle_next (e1, e);
+      triangle_next (next_edge (t, e1, e), e);
+      ncomponent++;
+    }
+    i = i->next;
+  }
+   
+  g_slist_foreach (triangles, (GFunc) gts_object_reset_reserved, NULL);
+  g_slist_free (triangles);
+
+  return ncomponent;
+}
+
+/**
+ * gts_edge_swap:
+ * @e: a #GtsEdge.
+ * @s: a #GtsSurface.
+ *
+ * Performs an "edge swap" on the two triangles sharing @e and
+ * belonging to @s.
+ */
+void gts_edge_swap (GtsEdge * e, GtsSurface * s)
+{
+  GtsTriangle * t1 = NULL, * t2 = NULL, * t;
+  GtsFace * f;
+  GSList * i;
+  GtsVertex * v1, * v2, * v3, * v4, * v5, * v6;
+  GtsEdge * e1, * e2, * e3, * e4;
+  GtsSegment * v3v6;
+
+  g_return_if_fail (e != NULL);
+  g_return_if_fail (s != NULL);
+
+  i = e->triangles;
+  while (i) {
+    if (GTS_IS_FACE (i->data) && gts_face_has_parent_surface (i->data, s)) {
+      if (!t1)
+	t1 = i->data;
+      else if (!t2)
+	t2 = i->data;
+      else
+	g_return_if_fail (gts_edge_face_number (e, s) == 2);
+    }
+    i = i->next;
+  }
+  g_assert (t1 && t2);
+
+  gts_triangle_vertices_edges (t1, e, &v1, &v2, &v3, &e, &e1, &e2);
+  gts_triangle_vertices_edges (t2, e, &v4, &v5, &v6, &e, &e3, &e4);
+  g_assert (v2 == v4 && v1 == v5);
+
+  v3v6 = gts_vertices_are_connected (v3, v6);
+  if (!GTS_IS_EDGE (v3v6))
+    v3v6 = GTS_SEGMENT (gts_edge_new (s->edge_class, v3, v6));
+  f = gts_face_new (s->face_class, e1, GTS_EDGE (v3v6), e4);
+  if ((t = gts_triangle_is_duplicate (GTS_TRIANGLE (f))) &&
+      GTS_IS_FACE (t)) {
+    gts_object_destroy (GTS_OBJECT (f));
+    f = GTS_FACE (t);
+  }
+  gts_surface_add_face (s, f);
+
+  f = gts_face_new (s->face_class, GTS_EDGE (v3v6), e2, e3);
+  if ((t = gts_triangle_is_duplicate (GTS_TRIANGLE (f))) &&
+      GTS_IS_FACE (t)) {
+    gts_object_destroy (GTS_OBJECT (f));
+    f = GTS_FACE (t);
+  }
+  gts_surface_add_face (s, f);
+
+  gts_surface_remove_face (s, GTS_FACE (t1));
+  gts_surface_remove_face (s, GTS_FACE (t2));
+}
+
+/**
+ * gts_edge_manifold_faces:
+ * @e: a #GtsEdge.
+ * @s: a #GtsSurface.
+ * @f1: pointer for first face.
+ * @f2: pointer for second face.
+ *
+ * If @e is a manifold edge of surface @s, fills @f1 and @f2 with the
+ * faces belonging to @s and sharing @e.
+ *
+ * Returns: %TRUE if @e is a manifold edge, %FALSE otherwise.
+ */
+gboolean gts_edge_manifold_faces (GtsEdge * e, GtsSurface * s,
+				  GtsFace ** f1, GtsFace ** f2)
+{
+  GSList * i;
+
+  g_return_val_if_fail (e != NULL, FALSE);
+  g_return_val_if_fail (s != NULL, FALSE);
+  g_return_val_if_fail (f1 != NULL, FALSE);
+  g_return_val_if_fail (f2 != NULL, FALSE);
+
+  *f1 = *f2 = NULL;
+  i = e->triangles;
+  while (i) {
+    if (GTS_IS_FACE (i->data) && gts_face_has_parent_surface (i->data, s)) {
+      if (!(*f1)) *f1 = i->data;
+      else if (!(*f2)) *f2 = i->data;
+      else return FALSE;
+    }
+    i = i->next;
+  }
+
+  return (*f1 && *f2);
+}
diff --git a/src_3rd/gts/eheap.c b/src_3rd/gts/eheap.c
new file mode 100644
index 0000000..29f462d
--- /dev/null
+++ b/src_3rd/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/src_3rd/gts/face.c b/src_3rd/gts/face.c
new file mode 100644
index 0000000..f6009f1
--- /dev/null
+++ b/src_3rd/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/src_3rd/gts/fifo.c b/src_3rd/gts/fifo.c
new file mode 100644
index 0000000..8b3d2b6
--- /dev/null
+++ b/src_3rd/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/src_3rd/gts/graph.c b/src_3rd/gts/graph.c
new file mode 100644
index 0000000..1566c95
--- /dev/null
+++ b/src_3rd/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 (&degree);
+  data[0] = g;
+  data[1] = °ree;
+  gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) compute_degree, data);
+  gts_range_update (&degree);
+  gts_range_print (&degree, 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/src_3rd/gts/gts-private.h b/src_3rd/gts/gts-private.h
new file mode 100644
index 0000000..59246d1
--- /dev/null
+++ b/src_3rd/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/src_3rd/gts/gts.h b/src_3rd/gts/gts.h
new file mode 100644
index 0000000..9397230
--- /dev/null
+++ b/src_3rd/gts/gts.h
@@ -0,0 +1,2577 @@
+/* GTS - Library for the manipulation of triangulated surfaces
+ * Copyright (C) 1999 St�phane Popinet
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTS_H__
+#define __GTS_H__
+
+#include <stdio.h>
+#include <glib.h>
+
+#define GTS_MAJOR_VERSION 0
+#define GTS_MINOR_VERSION 7
+#define GTS_MICRO_VERSION 6
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Added based on glib.h by M J Loehr 01/01/01 */
+/* GTS version.
+ * we prefix variable declarations so they can
+ * properly get exported in windows dlls.
+ */
+#ifdef NATIVE_WIN32
+#  ifdef GTS_COMPILATION
+#    define GTS_C_VAR __declspec(dllexport)
+#  else /* not GTS_COMPILATION */
+#    define GTS_C_VAR extern __declspec(dllimport)
+#  endif /* not GTS_COMPILATION */
+#else /* not NATIVE_WIN32 */
+#  define GTS_C_VAR extern
+#endif /* not NATIVE_WIN32 */
+
+GTS_C_VAR const guint gts_major_version;
+GTS_C_VAR const guint gts_minor_version;
+GTS_C_VAR const guint gts_micro_version;
+GTS_C_VAR const guint gts_interface_age;
+GTS_C_VAR const guint gts_binary_age;
+
+#define GTS_CHECK_VERSION(major,minor,micro)    \
+    (gts_major_version > (major) || \
+    (gts_major_version == (major) && gts_minor_version > (minor)) || \
+    (gts_major_version == (major) && gts_minor_version == (minor) && \
+     gts_micro_version >= (micro)))
+
+#define GTS_COMMENTS  "#!"
+#define GTS_MAINTAINER "popinet at users.sourceforge.net"
+
+
+
+
+void gts_predicates_init();
+
+
+/* Class declarations for base types */
+
+typedef struct _GtsObjectClassInfo     GtsObjectClassInfo;
+typedef struct _GtsObject        GtsObject;
+typedef struct _GtsObjectClass   GtsObjectClass;
+typedef struct _GtsPoint         GtsPoint;
+typedef struct _GtsPointClass    GtsPointClass;
+typedef struct _GtsVertex        GtsVertex;
+typedef struct _GtsVertexClass   GtsVertexClass;
+typedef struct _GtsSegment       GtsSegment;
+typedef struct _GtsSegmentClass  GtsSegmentClass;
+typedef struct _GtsEdge          GtsEdge;
+typedef struct _GtsEdgeClass     GtsEdgeClass;
+typedef struct _GtsTriangle      GtsTriangle;
+typedef struct _GtsTriangleClass GtsTriangleClass;
+typedef struct _GtsFace          GtsFace;
+typedef struct _GtsFaceClass     GtsFaceClass;
+typedef struct _GtsBBox          GtsBBox;
+typedef struct _GtsBBoxClass     GtsBBoxClass;
+typedef struct _GtsSurface       GtsSurface;
+typedef struct _GtsSurfaceClass  GtsSurfaceClass;
+
+typedef void         (*GtsObjectClassInitFunc) (GtsObjectClass * objclass);
+typedef void         (*GtsObjectInitFunc)      (GtsObject * obj);
+typedef void         (*GtsArgSetFunc)          (GtsObject * obj);
+typedef void         (*GtsArgGetFunc)          (GtsObject * obj);
+
+typedef gdouble                  GtsVector[3];
+typedef gdouble                  GtsVector4[4];
+typedef GtsVector4               GtsMatrix;
+/**
+ * GtsKeyFunc:
+ * @item: A pointer to an item to be stored in the heap.
+ * @data: User data passed to gts_eheap_new().
+ *
+ * Returns: the value of the key for the given item.
+ */
+typedef gdouble                  (*GtsKeyFunc)    (gpointer item,
+						   gpointer data);
+typedef enum 
+{ 
+  GTS_OUT = -1,
+  GTS_ON = 0,
+  GTS_IN = 1
+} GtsIntersect;
+
+typedef struct _GtsColor         GtsColor;
+
+struct _GtsColor {
+  gfloat r, g, b;
+};
+
+typedef gint   (*GtsFunc)              (gpointer item,
+					gpointer data);
+
+/* misc.c */
+
+typedef struct _GtsFile GtsFile;
+
+typedef enum {
+  GTS_NONE   = 1 << 8,
+  GTS_INT    = 1 << 9,
+  GTS_UINT   = 1 << 10,
+  GTS_FLOAT  = 1 << 11,
+  GTS_DOUBLE = 1 << 12,
+  GTS_STRING = 1 << 13,
+  GTS_FILE   = 1 << 14,
+  GTS_ERROR  = 1 << 15
+} GtsTokenType;
+
+struct _GtsFile {
+  FILE * fp;
+  gchar * s, * s1;
+  guint line, pos;
+  GString * token;
+  GtsTokenType type;
+  gchar * error;
+
+  guint curline, curpos;
+  guint scope, scope_max;
+  gint next_token;
+  gchar * delimiters;
+  gchar * comments;
+  gchar * tokens;
+};
+
+typedef struct _GtsFileVariable GtsFileVariable;
+
+struct _GtsFileVariable {
+  GtsTokenType type;
+  gchar name[30];
+  gboolean unique;
+  gpointer data;
+  gboolean set;
+  guint line, pos;
+};
+
+
+GtsFile *      gts_file_new               (FILE * fp);
+GtsFile *      gts_file_new_from_string   (const gchar * s);
+void           gts_file_verror            (GtsFile * f,
+					   const gchar * format,
+					   va_list args);
+void           gts_file_error             (GtsFile * f,
+					   const gchar * format,
+					   ...);
+gint           gts_file_getc              (GtsFile * f);
+guint          gts_file_read              (GtsFile * f, 
+					   gpointer ptr, 
+					   guint size, 
+					   guint nmemb);
+gint           gts_file_getc_scope        (GtsFile * f);
+void           gts_file_next_token        (GtsFile * f);
+void           gts_file_first_token_after (GtsFile * f, 
+					   GtsTokenType type);
+void           gts_file_assign_start      (GtsFile * f, 
+					   GtsFileVariable * vars);
+GtsFileVariable * gts_file_assign_next    (GtsFile * f, 
+					   GtsFileVariable * vars);
+void           gts_file_assign_variables  (GtsFile * f, 
+					   GtsFileVariable * vars);
+void           gts_file_variable_error    (GtsFile * f, 
+					   GtsFileVariable * vars,
+					   const gchar * name,
+					   const gchar * format,
+					   ...);
+void           gts_file_destroy           (GtsFile * f);
+
+/* Objects: object.c */
+
+#ifdef GTS_CHECK_CASTS
+#  define GTS_OBJECT_CAST(obj, type, klass) ((type *) gts_object_check_cast (obj, klass))
+#  define GTS_OBJECT_CLASS_CAST(objklass, type, klass) ((type *) gts_object_class_check_cast (objklass, klass))
+#else  /* not GTS_CHECK_CASTS */
+#  define GTS_OBJECT_CAST(obj, type, klass)             ((type *) (obj))
+#  define GTS_OBJECT_CLASS_CAST(objklass, type, klass)  ((type *) (objklass))
+#endif /* not GTS_CHECK_CASTS */
+
+#define GTS_CLASS_NAME_LENGTH 40
+#define GTS_OBJECT(obj)          GTS_OBJECT_CAST (obj,\
+						  GtsObject,\
+						  gts_object_class ())
+#define GTS_OBJECT_CLASS(klass)  GTS_OBJECT_CLASS_CAST (klass,\
+							GtsObjectClass,\
+							gts_object_class())
+#define GTS_IS_OBJECT(obj) (gts_object_is_from_class (obj,\
+						      gts_object_class ()))
+
+typedef enum
+{
+  GTS_DESTROYED         = 1 << 0,
+  GTS_USER_FLAG         = 1 /* user flags start from here */
+} GtsObjectFlags;
+
+#define GTS_OBJECT_FLAGS(obj)             (GTS_OBJECT (obj)->flags)
+#define GTS_OBJECT_DESTROYED(obj)         ((GTS_OBJECT_FLAGS (obj) & GTS_DESTROYED) != 0)
+#define GTS_OBJECT_SET_FLAGS(obj,flag)	  G_STMT_START{ (GTS_OBJECT_FLAGS (obj) |= (flag)); }G_STMT_END
+#define GTS_OBJECT_UNSET_FLAGS(obj,flag)  G_STMT_START{ (GTS_OBJECT_FLAGS (obj) &= ~(flag)); }G_STMT_END
+
+struct _GtsObjectClassInfo {
+  gchar name[GTS_CLASS_NAME_LENGTH];
+  guint object_size;
+  guint class_size;
+  GtsObjectClassInitFunc class_init_func;
+  GtsObjectInitFunc object_init_func;
+  GtsArgSetFunc arg_set_func;
+  GtsArgGetFunc arg_get_func;
+};
+
+struct _GtsObject {
+  GtsObjectClass * klass;
+
+  gpointer reserved;
+  guint32 flags;
+};
+
+struct _GtsObjectClass {
+  GtsObjectClassInfo info;
+  GtsObjectClass * parent_class;
+
+  void        (* clone)      (GtsObject *, GtsObject *);
+  void        (* destroy)    (GtsObject *);
+  void        (* read)       (GtsObject **, GtsFile *);
+  void        (* write)      (GtsObject *, FILE *);
+  GtsColor    (* color)      (GtsObject *);
+  void        (* attributes) (GtsObject *, GtsObject *);
+};
+
+gpointer         gts_object_class_new      (GtsObjectClass * parent_class,
+					    GtsObjectClassInfo * info);
+GtsObjectClass * gts_object_class          (void);
+gpointer         gts_object_check_cast     (gpointer object, 
+					    gpointer klass);
+gpointer         gts_object_class_check_cast (gpointer klass, 
+					      gpointer from);
+
+static inline
+gpointer gts_object_is_from_class (gpointer object,
+				   gpointer klass)
+{
+  GtsObjectClass * c;
+
+  g_return_val_if_fail (klass != NULL, NULL);
+
+  if (object == NULL)
+    return NULL;
+
+  c = ((GtsObject *) object)->klass;
+
+  g_return_val_if_fail (c != NULL, NULL);
+
+  while (c) {
+    if (c == klass)
+      return object;
+    c = c->parent_class;
+  }
+
+  return NULL;
+}
+
+static inline
+gpointer gts_object_class_is_from_class (gpointer klass,
+					 gpointer from)
+{
+  GtsObjectClass * c;
+
+  g_return_val_if_fail (klass != NULL, NULL);
+  g_return_val_if_fail (from != NULL, NULL);
+
+  c = (GtsObjectClass *) klass;
+  while (c) {
+    if (c == from)
+      return klass;
+    c = c->parent_class;
+  }
+
+  return NULL;
+}
+
+GtsObjectClass * gts_object_class_from_name     (const gchar * name);
+
+GtsObject *      gts_object_new                 (GtsObjectClass * klass);
+GtsObject *      gts_object_clone               (GtsObject * object);
+void             gts_object_attributes          (GtsObject * object, 
+						 GtsObject * from);
+void             gts_object_init                (GtsObject * object, 
+						 GtsObjectClass * klass);
+void             gts_object_reset_reserved      (GtsObject * object);
+void             gts_object_destroy             (GtsObject * object);
+void             gts_finalize                   (void);
+
+/* Ranges: surface.c */
+typedef struct _GtsRange               GtsRange;
+
+struct _GtsRange {
+  gdouble min, max, sum, sum2, mean, stddev;
+  guint n;
+};
+
+void gts_range_init         (GtsRange * r);
+void gts_range_reset        (GtsRange * r);
+void gts_range_add_value    (GtsRange * r, 
+			     gdouble val);
+void gts_range_update       (GtsRange * r);
+void gts_range_print        (GtsRange * r, 
+			     FILE * fptr);
+
+/* Points: point.c */
+
+#define GTS_IS_POINT(obj) (gts_object_is_from_class (obj,\
+						     gts_point_class ()))
+#define GTS_POINT(obj)              GTS_OBJECT_CAST (obj,\
+						     GtsPoint,\
+						     gts_point_class ())
+#define GTS_POINT_CLASS(klass)      GTS_OBJECT_CLASS_CAST (klass,\
+							   GtsPointClass,\
+							   gts_point_class ())
+
+struct _GtsPoint {
+  GtsObject object;
+
+  gdouble x, y, z; /* must be contiguous (cast to robust functions) */
+};
+
+struct _GtsPointClass {
+  GtsObjectClass parent_class;
+  gboolean binary;
+};
+
+GtsPointClass * gts_point_class                      (void);
+GtsPoint *    gts_point_new                          (GtsPointClass * klass,
+						      gdouble x, 
+						      gdouble y, 
+						      gdouble z);
+void          gts_point_set                          (GtsPoint * p, 
+						      gdouble x, 
+						      gdouble y, 
+						      gdouble z);
+#define       gts_point_is_in_rectangle(p, p1, p2)   ((p)->x >= (p1)->x &&\
+						      (p)->x <= (p2)->x &&\
+						      (p)->y >= (p1)->y &&\
+						      (p)->y <= (p2)->y &&\
+						      (p)->z >= (p1)->z &&\
+						      (p)->z <= (p2)->z)
+GtsPoint *    gts_segment_triangle_intersection      (GtsSegment * s,
+						      GtsTriangle * t,
+						      gboolean boundary,
+						      GtsPointClass * klass);
+void          gts_point_transform                    (GtsPoint * p, 
+						      GtsMatrix * m);
+gdouble       gts_point_distance                     (GtsPoint * p1,
+						      GtsPoint * p2);
+gdouble       gts_point_distance2                    (GtsPoint * p1,
+						      GtsPoint * p2);
+gdouble       gts_point_orientation_3d               (GtsPoint * p1,
+						      GtsPoint * p2,
+						      GtsPoint * p3,
+						      GtsPoint * p4);
+gint          gts_point_orientation_3d_sos           (GtsPoint * p1,
+						      GtsPoint * p2,
+						      GtsPoint * p3,
+						      GtsPoint * p4);
+GtsIntersect  gts_point_is_in_triangle               (GtsPoint * p,
+						      GtsTriangle * t);
+gdouble       gts_point_in_circle                    (GtsPoint * p, 
+						      GtsPoint * p1,
+						      GtsPoint * p2,
+						      GtsPoint * p3);
+gdouble       gts_point_in_sphere                    (GtsPoint * p, 
+						      GtsPoint * p1,
+						      GtsPoint * p2,
+						      GtsPoint * p3,
+						      GtsPoint * p4);
+gdouble       gts_point_in_triangle_circle           (GtsPoint * p, 
+						      GtsTriangle * t);
+gdouble       gts_point_orientation                  (GtsPoint * p1,
+						      GtsPoint * p2,
+						      GtsPoint * p3);
+gint          gts_point_orientation_sos              (GtsPoint * p1,
+						      GtsPoint * p2,
+						      GtsPoint * p3);
+gdouble       gts_point_segment_distance2            (GtsPoint * p, 
+						      GtsSegment * s);
+gdouble       gts_point_segment_distance             (GtsPoint * p, 
+						      GtsSegment * s);
+void          gts_point_segment_closest              (GtsPoint * p, 
+						      GtsSegment * s,
+						      GtsPoint * closest);
+gdouble       gts_point_triangle_distance2           (GtsPoint * p, 
+						      GtsTriangle * t);
+gdouble       gts_point_triangle_distance            (GtsPoint * p, 
+						      GtsTriangle * t);
+void          gts_point_triangle_closest             (GtsPoint * p,
+						      GtsTriangle * t,
+						      GtsPoint * closest);
+gboolean      gts_point_is_inside_surface            (GtsPoint * p, 
+						      GNode * tree,
+						      gboolean is_open);
+
+/* Vertices: vertex.c */
+
+#define GTS_IS_VERTEX(obj)   (gts_object_is_from_class (obj,\
+							gts_vertex_class ()))
+#define GTS_VERTEX(obj)             GTS_OBJECT_CAST (obj,\
+						     GtsVertex,\
+						     gts_vertex_class ())
+#define GTS_VERTEX_CLASS(klass)     GTS_OBJECT_CLASS_CAST (klass,\
+							   GtsVertexClass,\
+							   gts_vertex_class ())
+struct _GtsVertex {
+  GtsPoint p;
+  
+  GSList * segments;
+};
+
+struct _GtsVertexClass {
+  GtsPointClass parent_class;
+
+  void        (* intersection_attributes) (GtsVertex *, 
+					   GtsObject *, 
+					   GtsObject *);
+};
+
+GTS_C_VAR 
+gboolean      gts_allow_floating_vertices;
+
+GtsVertexClass * gts_vertex_class          (void);
+GtsVertex *   gts_vertex_new               (GtsVertexClass * klass,
+					    gdouble x,
+					    gdouble y,
+					    gdouble z);
+void          gts_vertex_replace           (GtsVertex * v, 
+					    GtsVertex * with);
+gboolean      gts_vertex_is_unattached     (GtsVertex * v);
+GtsSegment *  gts_vertices_are_connected   (GtsVertex * v1,
+					    GtsVertex * v2);
+GSList *      gts_vertex_triangles         (GtsVertex * v,
+					    GSList * list);
+GSList *      gts_vertex_faces             (GtsVertex * v,
+					    GtsSurface * surface,
+					    GSList * list);
+GSList *      gts_vertex_neighbors         (GtsVertex * v, 
+					    GSList * list,
+					    GtsSurface * surface);
+GSList *      gts_vertices_from_segments   (GSList * segments);
+gboolean      gts_vertex_is_boundary       (GtsVertex * v, 
+					    GtsSurface * surface);
+GList *       gts_vertices_merge           (GList * vertices, 
+					    gdouble epsilon,
+					    gboolean (* check) (GtsVertex *, GtsVertex *));
+GSList *      gts_vertex_fan_oriented      (GtsVertex * v, 
+					    GtsSurface * surface);
+guint         gts_vertex_is_contact        (GtsVertex * v, gboolean sever);
+
+/* GtsVertexNormal: Header */
+
+typedef struct _GtsVertexNormal         GtsVertexNormal;
+
+struct _GtsVertexNormal {
+  /*< private >*/
+  GtsVertex parent;
+
+  /*< public >*/
+  GtsVector n;
+};
+
+#define GTS_VERTEX_NORMAL(obj)            GTS_OBJECT_CAST (obj,\
+					         GtsVertexNormal,\
+					         gts_vertex_normal_class ())
+#define GTS_IS_VERTEX_NORMAL(obj)         (gts_object_is_from_class (obj,\
+						 gts_vertex_normal_class ()))
+
+GtsVertexClass * gts_vertex_normal_class  (void);
+
+/* GtsColorVertex: Header */
+
+typedef struct _GtsColorVertex         GtsColorVertex;
+
+struct _GtsColorVertex {
+  /*< private >*/
+  GtsVertex parent;
+
+  /*< public >*/
+  GtsColor c;
+};
+
+#define GTS_COLOR_VERTEX(obj)            GTS_OBJECT_CAST (obj,\
+					         GtsColorVertex,\
+					         gts_color_vertex_class ())
+#define GTS_IS_COLOR_VERTEX(obj)         (gts_object_is_from_class (obj,\
+						 gts_color_vertex_class ()))
+
+GtsVertexClass * gts_color_vertex_class  (void);
+
+/* Segments: segment.c */
+
+#define GTS_IS_SEGMENT(obj) (gts_object_is_from_class (obj,\
+						       gts_segment_class ()))
+#define GTS_SEGMENT(obj)          GTS_OBJECT_CAST (obj,\
+						   GtsSegment,\
+						   gts_segment_class ())
+#define GTS_SEGMENT_CLASS(klass)  GTS_OBJECT_CLASS_CAST (klass,\
+							 GtsSegmentClass,\
+							 gts_segment_class ())
+
+struct _GtsSegment {
+  GtsObject object;
+
+  GtsVertex * v1;
+  GtsVertex * v2;
+};
+
+struct _GtsSegmentClass {
+  GtsObjectClass parent_class;
+};
+
+GtsSegmentClass * gts_segment_class                  (void);
+GtsSegment *  gts_segment_new                        (GtsSegmentClass * klass,
+						      GtsVertex * v1, 
+						      GtsVertex * v2);
+#define       gts_segment_connect(s, e1, e2)         (((s)->v1 == e1 &&\
+                                                       (s)->v2 == e2) || \
+                                                      ((s)->v1 == e2 &&\
+                                                       (s)->v2 == e1))
+#define       gts_segments_are_identical(s1, s2)     (((s1)->v1 == (s2)->v1 &&\
+						       (s1)->v2 == (s2)->v2)\
+						      ||\
+						      ((s1)->v1 == (s2)->v2 &&\
+						       (s1)->v2 == (s2)->v1))
+#define       gts_segments_touch(s1, s2)             ((s1)->v1 == (s2)->v1 ||\
+						      (s1)->v1 == (s2)->v2 ||\
+						      (s1)->v2 == (s2)->v1 ||\
+						      (s1)->v2 == (s2)->v2)
+GtsIntersect  gts_segments_are_intersecting          (GtsSegment * s1,
+						      GtsSegment * s2);
+GtsSegment *  gts_segment_is_duplicate               (GtsSegment * s);
+GtsVertex *   gts_segment_midvertex                  (GtsSegment * s,
+						      GtsVertexClass * klass);
+GSList *      gts_segments_from_vertices             (GSList * vertices);
+gboolean      gts_segment_is_ok                      (GtsSegment * s);
+
+/* Edges: edge.c */
+
+#define GTS_IS_EDGE(obj)  (gts_object_is_from_class (obj,\
+						     gts_edge_class ()))
+#define GTS_EDGE(obj)            GTS_OBJECT_CAST (obj,\
+						  GtsEdge,\
+						  gts_edge_class ())
+#define GTS_EDGE_CLASS(klass)    GTS_OBJECT_CLASS_CAST (klass,\
+							GtsEdgeClass,\
+							gts_edge_class ())
+
+struct _GtsEdge {
+  GtsSegment segment;
+
+  GSList * triangles;
+};
+
+struct _GtsEdgeClass {
+  GtsSegmentClass parent_class;
+};
+
+GTS_C_VAR 
+gboolean      gts_allow_floating_edges;
+
+GtsEdgeClass * gts_edge_class                     (void);
+GtsEdge *     gts_edge_new                        (GtsEdgeClass * klass,
+						   GtsVertex * v1,
+						   GtsVertex * v2);
+/**
+ * gts_edge_is_unattached:
+ * @s: a #GtsEdge.
+ *
+ * Evaluates to %TRUE if no triangles uses @s as an edge, %FALSE otherwise.
+ */
+#define       gts_edge_is_unattached(s) ((s)->triangles == NULL ? TRUE : FALSE)
+GtsFace *     gts_edge_has_parent_surface         (GtsEdge * e, 
+						   GtsSurface * surface);
+GtsFace *     gts_edge_has_any_parent_surface     (GtsEdge * e);
+GtsFace *     gts_edge_is_boundary                (GtsEdge * e, 
+						   GtsSurface * surface);
+void          gts_edge_replace                    (GtsEdge * e,
+						   GtsEdge * with);
+GSList *      gts_edges_from_vertices             (GSList * vertices,
+						   GtsSurface * parent);
+guint         gts_edge_face_number                (GtsEdge * e,
+						   GtsSurface * s);
+gboolean      gts_edge_collapse_is_valid          (GtsEdge * e);
+gboolean      gts_edge_collapse_creates_fold      (GtsEdge * e, 
+						   GtsVertex * v,
+						   gdouble max);
+GtsEdge *     gts_edge_is_duplicate               (GtsEdge * e);
+GList *       gts_edges_merge                     (GList * edges);
+gboolean      gts_edge_belongs_to_tetrahedron     (GtsEdge * e);
+guint         gts_edge_is_contact                 (GtsEdge * e);
+void          gts_edge_swap                       (GtsEdge * e, 
+						   GtsSurface * s);
+gboolean      gts_edge_manifold_faces             (GtsEdge * e, 
+						   GtsSurface * s,
+						   GtsFace ** f1, 
+						   GtsFace ** f2);
+
+/* Triangles: triangle.c */
+
+#define GTS_IS_TRIANGLE(obj) (gts_object_is_from_class (obj,\
+							gts_triangle_class ()))
+#define GTS_TRIANGLE(obj)         GTS_OBJECT_CAST (obj,\
+						   GtsTriangle,\
+						   gts_triangle_class ())
+#define GTS_TRIANGLE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\
+							 GtsTriangleClass,\
+							 gts_triangle_class ())
+
+struct _GtsTriangle {
+  GtsObject object;
+
+  GtsEdge * e1;
+  GtsEdge * e2;
+  GtsEdge * e3;
+};
+
+struct _GtsTriangleClass {
+  GtsObjectClass parent_class;
+};
+
+GtsTriangleClass * gts_triangle_class        (void);
+void        gts_triangle_set                 (GtsTriangle * triangle, 
+					      GtsEdge * e1, 
+					      GtsEdge * e2,
+					      GtsEdge * e3);
+GtsTriangle * gts_triangle_new               (GtsTriangleClass * klass, 
+					      GtsEdge * e1, 
+					      GtsEdge * e2,
+					      GtsEdge * e3);
+#define     gts_triangle_vertex(t) (GTS_SEGMENT (GTS_TRIANGLE (t)->e1)->v1 ==\
+                                    GTS_SEGMENT (GTS_TRIANGLE (t)->e2)->v1 || \
+                                    GTS_SEGMENT (GTS_TRIANGLE (t)->e1)->v2 ==\
+                                    GTS_SEGMENT (GTS_TRIANGLE (t)->e2)->v1 ? \
+                                    GTS_SEGMENT (GTS_TRIANGLE (t)->e2)->v2 :\
+                                    GTS_SEGMENT (GTS_TRIANGLE (t)->e2)->v1)
+GtsVertex *   gts_triangle_vertex_opposite  (GtsTriangle * t, 
+					     GtsEdge * e);
+GtsEdge *     gts_triangle_edge_opposite    (GtsTriangle * t, 
+					     GtsVertex * v);
+gdouble       gts_triangles_angle           (GtsTriangle * t1,
+					     GtsTriangle * t2);
+gboolean      gts_triangles_are_compatible  (GtsTriangle * t1, 
+					     GtsTriangle * t2,
+					     GtsEdge * e);
+gdouble       gts_triangle_area             (GtsTriangle * t);
+gdouble       gts_triangle_perimeter        (GtsTriangle * t);
+gdouble       gts_triangle_quality          (GtsTriangle * t);
+void          gts_triangle_normal           (GtsTriangle * t, 
+					     gdouble * x, 
+					     gdouble * y, 
+					     gdouble * z);
+gdouble       gts_triangle_orientation      (GtsTriangle * t);
+void          gts_triangle_revert           (GtsTriangle * t);
+GSList *      gts_triangles_from_edges      (GSList * edges);
+void          gts_triangle_vertices_edges   (GtsTriangle * t, 
+					     GtsEdge * e,
+					     GtsVertex ** v1, 
+					     GtsVertex ** v2, 
+					     GtsVertex ** v3,
+					     GtsEdge ** e1,
+					     GtsEdge ** e2,
+					     GtsEdge ** e3);
+GtsTriangle * gts_triangle_enclosing        (GtsTriangleClass * klass,
+					     GSList * points, 
+					     gdouble scale);
+guint         gts_triangle_neighbor_number  (GtsTriangle * t);
+GSList *      gts_triangle_neighbors        (GtsTriangle * t);
+GtsEdge *     gts_triangles_common_edge     (GtsTriangle * t1,
+					     GtsTriangle * t2);
+GtsTriangle * gts_triangle_is_duplicate     (GtsTriangle * t);
+GtsTriangle * gts_triangle_use_edges        (GtsEdge * e1,
+					     GtsEdge * e2,
+					     GtsEdge * e3);
+gboolean      gts_triangle_is_ok            (GtsTriangle * t);
+void          gts_triangle_vertices         (GtsTriangle * t,
+					     GtsVertex ** v1,
+					     GtsVertex ** v2,
+					     GtsVertex ** v3);
+GtsPoint *    gts_triangle_circumcircle_center (GtsTriangle * t,
+						GtsPointClass * point_class);
+gboolean      gts_triangles_are_folded      (GSList * triangles,
+					     GtsVertex * A, GtsVertex * B,
+					     gdouble max);
+GtsObject *   gts_triangle_is_stabbed       (GtsTriangle * t,
+					     GtsPoint * p,
+					     gdouble * orientation);
+void          gts_triangle_interpolate_height (GtsTriangle * t, 
+					       GtsPoint * p);
+
+/* Faces: face.c */
+
+#define GTS_IS_FACE(obj) (gts_object_is_from_class (obj,\
+						    gts_face_class ()))
+#define GTS_FACE(obj)          GTS_OBJECT_CAST (obj,\
+						GtsFace,\
+						gts_face_class ())
+#define GTS_FACE_CLASS(klass)  GTS_OBJECT_CLASS_CAST (klass,\
+						      GtsFaceClass,\
+						      gts_face_class ())
+
+struct _GtsFace {
+  GtsTriangle triangle;
+
+  GSList * surfaces;
+};
+
+struct _GtsFaceClass {
+  GtsTriangleClass parent_class;
+};
+
+GTS_C_VAR 
+gboolean      gts_allow_floating_faces;
+
+GtsFaceClass * gts_face_class                       (void);
+GtsFace *     gts_face_new                          (GtsFaceClass * klass,
+						     GtsEdge * e1,
+						     GtsEdge * e2,
+						     GtsEdge * e3);
+gboolean      gts_face_has_parent_surface           (GtsFace * f,
+						     GtsSurface * s);
+GSList *      gts_faces_from_edges                  (GSList * edges, 
+						     GtsSurface * s);
+guint         gts_face_neighbor_number              (GtsFace * f, 
+						     GtsSurface * s);
+GSList *      gts_face_neighbors                    (GtsFace * f, 
+						     GtsSurface * s);
+void          gts_face_foreach_neighbor             (GtsFace * f, 
+						     GtsSurface * s, 
+						     GtsFunc func,
+						     gpointer data);
+gboolean      gts_face_is_compatible                (GtsFace * f, 
+						     GtsSurface * s);
+
+/* Matrices: matrix.c */
+
+#define       gts_vector_cross(C,A,B) ((C)[0] = (A)[1]*(B)[2] - (A)[2]*(B)[1],\
+			               (C)[1] = (A)[2]*(B)[0] - (A)[0]*(B)[2],\
+			               (C)[2] = (A)[0]*(B)[1] - (A)[1]*(B)[0])
+
+#define       gts_vector_init(v, p1, p2)   ((v)[0] = (p2)->x - (p1)->x,\
+					    (v)[1] = (p2)->y - (p1)->y,\
+					    (v)[2] = (p2)->z - (p1)->z)
+#define       gts_vector_scalar(v1, v2)    ((v1)[0]*(v2)[0] +\
+					    (v1)[1]*(v2)[1] +\
+					    (v1)[2]*(v2)[2])
+#define       gts_vector_norm(v)   (sqrt ((v)[0]*(v)[0] +\
+                                          (v)[1]*(v)[1] +\
+                                          (v)[2]*(v)[2]))
+#define       gts_vector_normalize(v) {\
+  gdouble __gts_n = gts_vector_norm (v);\
+  if (__gts_n > 0.) {\
+    (v)[0] /= __gts_n;\
+    (v)[1] /= __gts_n;\
+    (v)[2] /= __gts_n;\
+  }\
+}
+GtsMatrix * gts_matrix_new (gdouble a00, gdouble a01, gdouble a02, gdouble a03,
+			    gdouble a10, gdouble a11, gdouble a12, gdouble a13,
+			    gdouble a20, gdouble a21, gdouble a22, gdouble a23,
+			    gdouble a30, gdouble a31, gdouble a32, gdouble a33);
+void gts_matrix_assign (GtsMatrix * m,
+			gdouble a00, gdouble a01, gdouble a02, gdouble a03,
+			gdouble a10, gdouble a11, gdouble a12, gdouble a13,
+			gdouble a20, gdouble a21, gdouble a22, gdouble a23,
+			gdouble a30, gdouble a31, gdouble a32, gdouble a33);
+GtsMatrix *   gts_matrix_projection                  (GtsTriangle * t);
+GtsMatrix *   gts_matrix_transpose                   (GtsMatrix * m);
+gdouble       gts_matrix_determinant                 (GtsMatrix * m);
+GtsMatrix *   gts_matrix_inverse                     (GtsMatrix * m);
+GtsMatrix *   gts_matrix3_inverse                    (GtsMatrix * m);
+void          gts_matrix_print                       (GtsMatrix * m, 
+						      FILE * fptr);
+guint         gts_matrix_compatible_row              (GtsMatrix * A,
+						      GtsVector b,
+						      guint n,
+						      GtsVector A1,
+						      gdouble b1);
+guint         gts_matrix_quadratic_optimization      (GtsMatrix * A,
+						      GtsVector b,
+						      guint n,
+						      GtsMatrix * H,
+						      GtsVector c);
+GtsMatrix *   gts_matrix_product                     (GtsMatrix * m1, 
+						      GtsMatrix * m2);
+GtsMatrix *   gts_matrix_zero                        (GtsMatrix * m);
+GtsMatrix *   gts_matrix_identity                    (GtsMatrix * m);
+GtsMatrix *   gts_matrix_scale                       (GtsMatrix * m, 
+						      GtsVector s);
+GtsMatrix *   gts_matrix_translate                   (GtsMatrix * m, 
+						      GtsVector t);
+GtsMatrix *   gts_matrix_rotate                      (GtsMatrix * m,
+						      GtsVector r,
+						      gdouble angle);
+void          gts_matrix_destroy                     (GtsMatrix * m);
+void          gts_vector_print                       (GtsVector v,
+						      FILE * fptr);
+void          gts_vector4_print                      (GtsVector4 v, 
+						      FILE * fptr);
+
+/* Kdtrees: kdtree.c */
+
+#define       gts_kdtree_destroy(tree)               g_node_destroy(tree)
+
+GNode *       gts_kdtree_new                         (GPtrArray * points,
+						      int (*compare)
+						      (const void *, 
+						       const void *));
+GSList *      gts_kdtree_range                       (GNode * tree,
+						      GtsBBox * bbox,
+						      int (*compare)
+						      (const void *, 
+						      const void *));
+
+/* Bboxtrees: bbtree.c */
+
+/**
+ * GtsBBTreeTraverseFunc:
+ * @bb1: a #GtsBBox.
+ * @bb2: another #GtsBBox.
+ * @data: user data passed to the function.
+ *
+ * User function called for each pair of overlapping bounding
+ * boxes. See gts_bb_tree_traverse_overlapping().
+ */
+typedef void   (*GtsBBTreeTraverseFunc)          (GtsBBox * bb1,
+						  GtsBBox * bb2,
+						  gpointer data);
+/**
+ * GtsBBoxDistFunc:
+ * @p: a #GtsPoint.
+ * @bounded: an object bounded by a #GtsBBox.
+ *
+ * User function returning the (minimum) distance between the object
+ * defined by @bounded and point @p.
+ *
+ * Returns: the distance between @p and @bounded.
+ */
+typedef gdouble (*GtsBBoxDistFunc)               (GtsPoint * p,
+						  gpointer bounded);
+/**
+ * GtsBBoxClosestFunc:
+ * @p: a #GtsPoint.
+ * @bounded: an object bounded by a #GtsBBox.
+ * 
+ * User function returning a #GtsPoint belonging to the object defined
+ * by @bounded and closest to @p.
+ *
+ * Returns: a #GtsPoint.
+ */
+typedef GtsPoint * (*GtsBBoxClosestFunc)         (GtsPoint * p,
+						  gpointer bounded);
+
+/**
+ * GTS_IS_BBOX:
+ * @obj: a #GtsObject.
+ *
+ * Evaluates to %TRUE if @obj is a #GtsBBox, %FALSE otherwise.
+ */
+#define GTS_IS_BBOX(obj)  (gts_object_is_from_class (obj,\
+						     gts_bbox_class ()))
+/**
+ * GTS_BBOX:
+ * @obj: a #GtsObject.
+ *
+ * Casts @obj to #GtsBBox.
+ */
+#define GTS_BBOX(obj)         GTS_OBJECT_CAST (obj,\
+					       GtsBBox,\
+					       gts_bbox_class ())
+/**
+ * GTS_BBOX_CLASS:
+ * @klass: a descendant of #GtsBBoxClass.
+ *
+ * Casts @klass to #GtsBBoxClass.
+ */
+#define GTS_BBOX_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\
+						     GtsBBoxClass,\
+						     gts_bbox_class ())
+
+struct _GtsBBox {
+  GtsObject object;
+  gpointer bounded;
+  gdouble x1, y1, z1;
+  gdouble x2, y2, z2;
+};
+
+struct _GtsBBoxClass {
+  GtsObjectClass parent_class;
+};
+
+GtsBBoxClass * gts_bbox_class                (void);
+GtsBBox *  gts_bbox_new                      (GtsBBoxClass * klass,
+					      gpointer bounded,
+					      gdouble x1, 
+					      gdouble y1, 
+					      gdouble z1,
+					      gdouble x2, 
+					      gdouble y2, 
+					      gdouble z2);
+void       gts_bbox_set                      (GtsBBox * bbox,
+					      gpointer bounded,
+					      gdouble x1, 
+					      gdouble y1, 
+					      gdouble z1,
+					      gdouble x2, 
+					      gdouble y2, 
+					      gdouble z2);
+GtsBBox *  gts_bbox_segment                  (GtsBBoxClass * klass,
+					      GtsSegment * s);
+GtsBBox *  gts_bbox_triangle                 (GtsBBoxClass * klass,
+					      GtsTriangle * t);
+GtsBBox *  gts_bbox_surface                  (GtsBBoxClass * klass, 
+					      GtsSurface * surface);
+GtsBBox *  gts_bbox_bboxes                   (GtsBBoxClass * klass,
+					      GSList * bboxes);
+GtsBBox *  gts_bbox_points                   (GtsBBoxClass * klass,
+					      GSList * points);
+/**
+ * gts_bbox_point_is_inside:
+ * @bbox: a #GtsBBox.
+ * @p: a #GtsPoint.
+ *
+ * Evaluates to %TRUE if @p is inside (or on the boundary) of @bbox, %FALSE otherwise.
+ */
+#define    gts_bbox_point_is_inside(bbox, p) ((p)->x >= (bbox)->x1 &&\
+					     (p)->y >= (bbox)->y1 &&\
+                                             (p)->z >= (bbox)->z1 &&\
+                                             (p)->x <= (bbox)->x2 &&\
+					     (p)->y <= (bbox)->y2 &&\
+                                             (p)->z <= (bbox)->z2)
+gboolean   gts_bboxes_are_overlapping        (GtsBBox * bb1, 
+					      GtsBBox * bb2);
+void       gts_bbox_draw                     (GtsBBox * bb, 
+					      FILE * fptr);
+gdouble    gts_bbox_diagonal2                (GtsBBox * bb);
+void       gts_bbox_point_distance2          (GtsBBox * bb, 
+					      GtsPoint * p,
+					      gdouble * min,
+					      gdouble * max);
+gboolean   gts_bbox_is_stabbed               (GtsBBox * bb, 
+					      GtsPoint * p);
+gboolean   gts_bbox_overlaps_triangle        (GtsBBox * bb,
+					      GtsTriangle * t);
+gboolean   gts_bbox_overlaps_segment         (GtsBBox * bb, 
+					      GtsSegment * s);
+
+GNode *    gts_bb_tree_new                   (GSList * bboxes);
+GNode *    gts_bb_tree_surface               (GtsSurface * s);
+GSList *   gts_bb_tree_stabbed               (GNode * tree, 
+					      GtsPoint * p);
+GSList *   gts_bb_tree_overlap               (GNode * tree, 
+					      GtsBBox * bbox);
+gboolean   gts_bb_tree_is_overlapping        (GNode * tree, 
+					      GtsBBox * bbox);
+void       gts_bb_tree_traverse_overlapping  (GNode * tree1, 
+					      GNode * tree2,
+					      GtsBBTreeTraverseFunc func,
+					      gpointer data);
+void       gts_bb_tree_draw                  (GNode * tree, 
+					      guint depth, 
+					      FILE * fptr);
+GSList *   gts_bb_tree_point_closest_bboxes  (GNode * tree, 
+					      GtsPoint * p);
+gdouble    gts_bb_tree_point_distance        (GNode * tree, 
+					      GtsPoint * p,
+					      GtsBBoxDistFunc distance,
+					      GtsBBox ** bbox);
+GtsPoint * gts_bb_tree_point_closest         (GNode * tree, 
+					      GtsPoint * p,
+					      GtsBBoxClosestFunc closest,
+					      gdouble * distance);
+void       gts_bb_tree_segment_distance      (GNode * tree, 
+					      GtsSegment * s,
+					      GtsBBoxDistFunc distance,
+					      gdouble delta,
+					      GtsRange * range);
+void       gts_bb_tree_triangle_distance     (GNode * tree, 
+					      GtsTriangle * t,
+					      GtsBBoxDistFunc distance,
+					      gdouble delta,
+					      GtsRange * range);
+void       gts_bb_tree_surface_distance      (GNode * tree,
+					      GtsSurface * s,
+					      GtsBBoxDistFunc distance,
+					      gdouble delta,
+					      GtsRange * range);
+void       gts_bb_tree_surface_boundary_distance 
+                                             (GNode * tree,
+					      GtsSurface * s,
+					      GtsBBoxDistFunc distance,
+					      gdouble delta,
+					      GtsRange * range);
+void       gts_bb_tree_destroy               (GNode * tree, 
+					      gboolean free_leaves);
+
+/* Surfaces: surface.c */
+
+typedef struct _GtsSurfaceStats        GtsSurfaceStats;
+typedef struct _GtsSurfaceQualityStats GtsSurfaceQualityStats;
+typedef GtsVertex * (*GtsRefineFunc)   (GtsEdge * e,
+					GtsVertexClass * klass,
+					gpointer data);
+typedef GtsVertex * (*GtsCoarsenFunc)  (GtsEdge * e,
+					GtsVertexClass * klass,
+					gpointer data);
+typedef gboolean    (*GtsStopFunc)     (gdouble cost,
+					guint nedge,
+					gpointer data);
+
+struct _GtsSurfaceStats {
+  guint n_faces;
+  guint n_incompatible_faces;
+  guint n_duplicate_faces;
+  guint n_duplicate_edges;
+  guint n_boundary_edges;
+  guint n_non_manifold_edges;
+  GtsRange edges_per_vertex, faces_per_edge;
+  GtsSurface * parent;
+};
+
+struct _GtsSurfaceQualityStats {
+  GtsRange face_quality;
+  GtsRange face_area;
+  GtsRange edge_length;
+  GtsRange edge_angle;
+  GtsSurface * parent;
+};
+
+struct _GtsSurface {
+  GtsObject object;
+
+#ifdef USE_SURFACE_BTREE
+  GTree * faces;
+#else /* not USE_SURFACE_BTREE */
+  GHashTable * faces;
+#endif /* not USE_SURFACE_BTREE */
+  GtsFaceClass * face_class;
+  GtsEdgeClass * edge_class;
+  GtsVertexClass * vertex_class;
+  gboolean keep_faces;
+};
+
+struct _GtsSurfaceClass {
+  GtsObjectClass parent_class;
+
+  void (* add_face)    (GtsSurface *, GtsFace *);
+  void (* remove_face) (GtsSurface *, GtsFace *);
+};
+
+#define GTS_IS_SURFACE(obj) (gts_object_is_from_class (obj,\
+						       gts_surface_class ()))
+#define GTS_SURFACE(obj)         GTS_OBJECT_CAST (obj,\
+						  GtsSurface,\
+						  gts_surface_class ())
+#define GTS_SURFACE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\
+							GtsSurfaceClass,\
+							gts_surface_class ())
+
+GtsSurfaceClass * gts_surface_class        (void);
+GtsSurface * gts_surface_new               (GtsSurfaceClass * klass,
+					    GtsFaceClass * face_class,
+					    GtsEdgeClass * edge_class,
+					    GtsVertexClass * vertex_class);
+void         gts_surface_add_face          (GtsSurface * s, 
+					    GtsFace * f);
+void         gts_surface_remove_face       (GtsSurface * s, 
+					    GtsFace * f);
+guint        gts_surface_read              (GtsSurface * surface,
+					    GtsFile * f);
+gdouble      gts_surface_area              (GtsSurface * s);
+void         gts_surface_stats             (GtsSurface * s, 
+					    GtsSurfaceStats * stats);
+void         gts_surface_quality_stats     (GtsSurface * s, 
+					    GtsSurfaceQualityStats * stats);
+void         gts_surface_print_stats       (GtsSurface * s, 
+					    FILE * fptr);
+void         gts_surface_write             (GtsSurface * s, 
+					    FILE * fptr);
+void         gts_surface_write_oogl        (GtsSurface * s, 
+					    FILE * fptr);
+void         gts_surface_write_vtk         (GtsSurface * s, 
+					    FILE * fptr);
+void         gts_surface_write_oogl_boundary (GtsSurface * s, 
+					      FILE * fptr);
+void         gts_surface_foreach_vertex    (GtsSurface * s, 
+					    GtsFunc func, 
+					    gpointer data);
+void         gts_surface_foreach_edge      (GtsSurface * s, 
+					    GtsFunc func, 
+					    gpointer data);
+void         gts_surface_foreach_face      (GtsSurface * s,
+					    GtsFunc func, 
+					    gpointer data);
+guint        gts_surface_foreach_face_remove (GtsSurface * s,
+					      GtsFunc func, 
+					      gpointer data);
+typedef struct _GtsSurfaceTraverse GtsSurfaceTraverse;
+GtsSurfaceTraverse * gts_surface_traverse_new (GtsSurface * s,
+					       GtsFace * f);
+GtsFace *    gts_surface_traverse_next     (GtsSurfaceTraverse * t,
+					    guint * level);
+void         gts_surface_traverse_destroy  (GtsSurfaceTraverse * t);
+void         gts_surface_refine            (GtsSurface * surface,
+					    GtsKeyFunc cost_func,
+					    gpointer cost_data,
+					    GtsRefineFunc refine_func,
+					    gpointer refine_data,
+					    GtsStopFunc stop_func,
+					    gpointer stop_data);
+gboolean     gts_edge_collapse_is_valid    (GtsEdge * e);
+void         gts_surface_coarsen           (GtsSurface * surface,
+					    GtsKeyFunc cost_func,
+					    gpointer cost_data,
+					    GtsCoarsenFunc coarsen_func,
+					    gpointer coarsen_data,
+					    GtsStopFunc stop_func,
+					    gpointer stop_data,
+					    gdouble minangle);
+gboolean     gts_coarsen_stop_number       (gdouble cost, 
+					    guint nedge, 
+					    guint * min_number);
+gboolean     gts_coarsen_stop_cost         (gdouble cost, 
+					    guint nedge, 
+					    gdouble * max_cost);
+void         gts_surface_tessellate        (GtsSurface * s,
+					    GtsRefineFunc refine_func,
+					    gpointer refine_data);
+GtsSurface * gts_surface_generate_sphere   (GtsSurface * s,
+					    guint geodesation_order);
+GtsSurface * gts_surface_copy              (GtsSurface * s1,
+					    GtsSurface * s2);
+void         gts_surface_merge             (GtsSurface * s, 
+					    GtsSurface * with);
+gboolean     gts_surface_is_manifold       (GtsSurface * s);
+gboolean     gts_surface_is_closed         (GtsSurface * s);
+gboolean     gts_surface_is_orientable     (GtsSurface * s);
+gdouble      gts_surface_volume            (GtsSurface * s);
+gdouble      gts_surface_center_of_mass    (GtsSurface * s,
+					    GtsVector cm);
+gdouble      gts_surface_center_of_area    (GtsSurface * s,
+					    GtsVector cm);
+guint        gts_surface_vertex_number     (GtsSurface * s);
+guint        gts_surface_edge_number       (GtsSurface * s);
+guint        gts_surface_face_number       (GtsSurface * s);
+void         gts_surface_distance          (GtsSurface * s1, 
+					    GtsSurface * s2, 
+					    gdouble delta,
+					    GtsRange * face_range, 
+					    GtsRange * boundary_range);
+GSList *     gts_surface_boundary          (GtsSurface * surface);
+GSList *     gts_surface_split             (GtsSurface * s);
+
+/* Discrete differential operators: curvature.c */
+
+gboolean gts_vertex_mean_curvature_normal  (GtsVertex * v, 
+					    GtsSurface * s, 
+					    GtsVector Kh);
+gboolean gts_vertex_gaussian_curvature     (GtsVertex * v, 
+					    GtsSurface * s, 
+					    gdouble * Kg);
+void     gts_vertex_principal_curvatures   (gdouble Kh, 
+					    gdouble Kg, 
+					    gdouble * K1, 
+					    gdouble * K2);
+void     gts_vertex_principal_directions   (GtsVertex * v, 
+					    GtsSurface * s, 
+					    GtsVector Kh,
+                                            gdouble Kg,
+					    GtsVector e1, 
+					    GtsVector e2);
+
+/* Volume optimization: vopt.c */
+typedef struct _GtsVolumeOptimizedParams   GtsVolumeOptimizedParams;
+
+struct _GtsVolumeOptimizedParams {
+  gdouble volume_weight;
+  gdouble boundary_weight;
+  gdouble shape_weight;
+};
+
+GtsVertex *  gts_volume_optimized_vertex   (GtsEdge * edge,
+					    GtsVertexClass * klass,
+					    GtsVolumeOptimizedParams * params);
+gdouble      gts_volume_optimized_cost     (GtsEdge * e,
+					    GtsVolumeOptimizedParams * params);
+
+/* bool operations: boolean.c */
+
+GSList *     gts_surface_intersection      (GtsSurface * s1,
+					    GtsSurface * s2,
+					    GNode * faces_tree1,
+					    GNode * faces_tree2);
+
+typedef struct _GtsSurfaceInter         GtsSurfaceInter;
+typedef struct _GtsSurfaceInterClass    GtsSurfaceInterClass;
+/**
+ * GtsBooleanOperation:
+ * @GTS_1_OUT_2: identifies the part of the first surface which lies
+ * outside the second surface.
+ * @GTS_1_IN_2: identifies the part of the first surface which lies
+ * inside the second surface.
+ * @GTS_2_OUT_1: identifies the part of the second surface which lies
+ * outside the first surface.
+ * @GTS_2_IN_1: identifies the part of the second surface which lies
+ * inside the first surface.
+ */
+typedef enum { GTS_1_OUT_2, 
+	       GTS_1_IN_2, 
+	       GTS_2_OUT_1, 
+	       GTS_2_IN_1 }             GtsBooleanOperation;
+
+/**
+ * GTS_IS_SURFACE_INTER:
+ * @obj: a #GtsObject.
+ *
+ * Evaluates to %TRUE if @obj is a #GtsSurfaceInter, %FALSE otherwise.
+ */
+#define GTS_IS_SURFACE_INTER(obj) (gts_object_is_from_class (obj,\
+					      gts_surface_inter_class ()))
+/**
+ * GTS_SURFACE_INTER:
+ * @obj: a descendant of #GtsSurfaceInter.
+ *
+ * Casts @obj to #GtsSurfaceInter.
+ */
+#define GTS_SURFACE_INTER(obj)         GTS_OBJECT_CAST (obj,\
+						  GtsSurfaceInter,\
+						  gts_surface_inter_class ())
+/**
+ * GTS_SURFACE_INTER_CLASS:
+ * @klass: a descendant of #GtsSurfaceInterClass.
+ *
+ * Casts @klass to #GtsSurfaceInterClass.
+ */
+#define GTS_SURFACE_INTER_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\
+						 GtsSurfaceInterClass,\
+						 gts_surface_inter_class ())
+
+struct _GtsSurfaceInter {
+  GtsObject object;
+
+  GtsSurface * s1;
+  GtsSurface * s2;
+  GSList * edges;
+};
+
+struct _GtsSurfaceInterClass {
+  GtsObjectClass parent_class;
+};
+
+GtsSurfaceInterClass *
+gts_surface_inter_class          (void);
+GtsSurfaceInter *
+gts_surface_inter_new            (GtsSurfaceInterClass * klass,
+				  GtsSurface * s1,
+				  GtsSurface * s2,
+				  GNode * faces_tree1,
+				  GNode * faces_tree2,
+				  gboolean is_open1,
+				  gboolean is_open2);
+gboolean 
+gts_surface_inter_check          (GtsSurfaceInter * si,
+				  gboolean * closed);
+void 
+gts_surface_inter_boolean        (GtsSurfaceInter * si, 
+				  GtsSurface * surface,
+				  GtsBooleanOperation op);
+gboolean gts_surface_foreach_intersecting_face (GtsSurface * s,
+					    GtsBBTreeTraverseFunc func,
+					    gpointer data);
+GtsSurface * 
+gts_surface_is_self_intersecting (GtsSurface * s);
+
+/* Binary Heap: heap.c */
+
+typedef struct _GtsHeap         GtsHeap;
+
+GtsHeap *    gts_heap_new          (GCompareFunc compare_func);
+void         gts_heap_insert       (GtsHeap * heap, gpointer p);
+gpointer     gts_heap_remove_top   (GtsHeap * heap);
+gpointer     gts_heap_top          (GtsHeap * heap);
+void         gts_heap_thaw         (GtsHeap * heap);
+void         gts_heap_foreach      (GtsHeap * heap, 
+				    GFunc func,
+				    gpointer user_data);
+void         gts_heap_freeze       (GtsHeap * heap);
+guint        gts_heap_size         (GtsHeap * heap);
+void         gts_heap_destroy      (GtsHeap * heap);
+
+/* Extended Binary Heap: eheap.c */
+
+typedef struct _GtsEHeap         GtsEHeap;
+typedef struct _GtsEHeapPair     GtsEHeapPair;
+
+struct _GtsEHeap {
+  GPtrArray * elts;
+  GtsKeyFunc func;
+  gpointer data;
+  gboolean frozen, randomized;
+};
+
+/**
+ * _GtsEHeapPair:
+ * @data: Points to the item stored in the heap.
+ * @key: Value of the key for this item.
+ * @pos: Private field.
+ */
+struct _GtsEHeapPair {
+  gpointer data;
+  gdouble key;
+  guint pos;
+};
+
+GtsEHeap *     gts_eheap_new          (GtsKeyFunc key_func,
+				       gpointer data);
+GtsEHeapPair * gts_eheap_insert       (GtsEHeap * heap, 
+				       gpointer p);
+GtsEHeapPair * gts_eheap_insert_with_key (GtsEHeap * heap, 
+					  gpointer p, 
+					  gdouble key);
+gpointer       gts_eheap_remove_top   (GtsEHeap * heap,
+				       gdouble * key);
+gpointer       gts_eheap_top          (GtsEHeap * heap, 
+				       gdouble * key);
+void           gts_eheap_thaw         (GtsEHeap * heap);
+void           gts_eheap_foreach      (GtsEHeap * heap, 
+				       GFunc func,
+				       gpointer data);
+gpointer       gts_eheap_remove       (GtsEHeap * heap, 
+				       GtsEHeapPair * p);
+void           gts_eheap_decrease_key (GtsEHeap * heap,
+				       GtsEHeapPair * p,
+				       gdouble new_key);
+void           gts_eheap_freeze       (GtsEHeap * heap);
+guint          gts_eheap_size         (GtsEHeap * heap);
+void           gts_eheap_update       (GtsEHeap * heap);
+gdouble        gts_eheap_key          (GtsEHeap * heap,
+				       gpointer p);
+void           gts_eheap_randomized   (GtsEHeap * heap, 
+				       gboolean randomized);
+void           gts_eheap_destroy      (GtsEHeap * heap);
+
+/* FIFO queues: fifo.c */
+
+typedef struct _GtsFifo GtsFifo;
+
+GtsFifo *      gts_fifo_new           (void);
+void           gts_fifo_write         (GtsFifo * fifo, 
+				       FILE * fp);
+void           gts_fifo_push          (GtsFifo * fifo, 
+				       gpointer data);
+gpointer       gts_fifo_pop           (GtsFifo * fifo);
+gpointer       gts_fifo_top           (GtsFifo * fifo);
+guint          gts_fifo_size          (GtsFifo * fifo);
+gboolean       gts_fifo_is_empty      (GtsFifo * fifo);
+void           gts_fifo_foreach       (GtsFifo * fifo, 
+				       GtsFunc func, 
+				       gpointer data);
+void           gts_fifo_reverse       (GtsFifo * fifo);
+void           gts_fifo_destroy       (GtsFifo * fifo);
+
+/* Progressive surfaces */
+
+/* split.c */
+
+typedef struct _GtsSplit      GtsSplit;
+typedef struct _GtsSplitClass GtsSplitClass;
+typedef struct _GtsSplitCFace GtsSplitCFace;
+
+struct _GtsSplit {
+  GtsObject object;
+
+  GtsVertex * v;
+  GtsObject * v1;
+  GtsObject * v2;
+  GtsSplitCFace * cfaces;
+  guint ncf;
+};
+
+struct _GtsSplitClass {
+  GtsObjectClass parent_class;
+};
+
+#define GTS_IS_SPLIT(obj)    (gts_object_is_from_class (obj,\
+							gts_split_class ()))
+#define GTS_SPLIT(obj)              GTS_OBJECT_CAST (obj,\
+						     GtsSplit,\
+						     gts_split_class ())
+#define GTS_SPLIT_CLASS(klass)      GTS_OBJECT_CLASS_CAST (klass,\
+						     GtsSplitClass,\
+						     gts_split_class ())
+#define GTS_SPLIT_V1(vs)            (GTS_IS_SPLIT ((vs)->v1) ?\
+				     GTS_SPLIT ((vs)->v1)->v :\
+				     GTS_VERTEX ((vs)->v1))
+#define GTS_SPLIT_V2(vs)            (GTS_IS_SPLIT ((vs)->v2) ?\
+				     GTS_SPLIT ((vs)->v2)->v :\
+				     GTS_VERTEX ((vs)->v2))
+
+GtsSplitClass *  gts_split_class          (void);
+GtsSplit *       gts_split_new            (GtsSplitClass * klass,
+					   GtsVertex * v,
+					   GtsObject * o1,
+					   GtsObject * o2);
+void             gts_split_collapse       (GtsSplit * vs,
+					   GtsEdgeClass * klass,
+					   GtsEHeap * heap);
+void             gts_split_expand         (GtsSplit * vs, 
+					   GtsSurface * s,
+					   GtsEdgeClass * klass);
+typedef gboolean (*GtsSplitTraverseFunc)  (GtsSplit * vs,
+					   gpointer data);
+void             gts_split_traverse       (GtsSplit * root,
+					   GTraverseType        order,
+					   gint                 depth,
+					   GtsSplitTraverseFunc func,
+					   gpointer             data);
+guint            gts_split_height         (GtsSplit * root);
+
+/* psurface.c */
+
+typedef struct _GtsPSurface         GtsPSurface;
+typedef struct _GtsPSurfaceClass    GtsPSurfaceClass;
+
+struct _GtsPSurface {
+  GtsObject object;
+
+  GtsSurface * s;
+  GPtrArray * split;
+  GtsSplitClass * split_class;
+  guint pos, min;
+
+  GPtrArray * vertices;
+  GPtrArray * faces;
+};
+
+struct _GtsPSurfaceClass {
+  GtsObjectClass parent_class;
+};
+
+#define GTS_IS_PSURFACE(obj) (gts_object_is_from_class (obj,\
+							gts_psurface_class ()))
+#define GTS_PSURFACE(obj)           GTS_OBJECT_CAST (obj,\
+						     GtsPSurface,\
+						     gts_psurface_class ())
+#define GTS_PSURFACE_CLASS(klass)     GTS_OBJECT_CLASS_CAST (klass,\
+						     GtsPSurfaceClass,\
+						     gts_psurface_class ())
+#define GTS_PSURFACE_IS_CLOSED(ps)  (!(ps)->vertices)
+
+GtsPSurfaceClass * gts_psurface_class         (void);
+GtsPSurface * gts_psurface_new                (GtsPSurfaceClass * klass,
+					       GtsSurface * surface,
+					       GtsSplitClass * split_class,
+					       GtsKeyFunc cost_func,
+					       gpointer cost_data,
+					       GtsCoarsenFunc coarsen_func,
+					       gpointer coarsen_data,
+					       GtsStopFunc stop_func,
+					       gpointer stop_data,
+					       gdouble minangle);
+GtsSplit *    gts_psurface_add_vertex         (GtsPSurface * ps);
+GtsSplit *    gts_psurface_remove_vertex      (GtsPSurface * ps);
+guint         gts_psurface_max_vertex_number  (GtsPSurface * ps);
+guint         gts_psurface_min_vertex_number  (GtsPSurface * ps);
+void          gts_psurface_set_vertex_number  (GtsPSurface * ps, 
+					       guint n);
+guint         gts_psurface_get_vertex_number  (GtsPSurface * ps);
+void          gts_psurface_write              (GtsPSurface * ps,
+					       FILE * fptr);
+GtsPSurface * gts_psurface_open               (GtsPSurfaceClass * klass,
+					       GtsSurface * s,
+					       GtsSplitClass * split_class,
+					       GtsFile * f);
+GtsSplit *    gts_psurface_read_vertex        (GtsPSurface * ps, 
+					       GtsFile * fp);
+void          gts_psurface_close              (GtsPSurface * ps);
+void          gts_psurface_foreach_vertex     (GtsPSurface * ps, 
+					       GtsFunc func, 
+					       gpointer data);
+
+/* hsurface.c */
+
+typedef struct _GtsHSplit        GtsHSplit;
+typedef struct _GtsHSplitClass   GtsHSplitClass;
+typedef struct _GtsHSurface      GtsHSurface;
+typedef struct _GtsHSurfaceClass GtsHSurfaceClass;
+
+struct _GtsHSplit {
+  GtsSplit split;
+
+  GtsEHeapPair * index;
+  GtsHSplit * parent;
+  guint nchild;
+};
+
+struct _GtsHSplitClass {
+  GtsSplitClass parent_class;
+};
+
+#define GTS_IS_HSPLIT(obj) (gts_object_is_from_class (obj,\
+						      gts_hsplit_class ()))
+#define GTS_HSPLIT(obj)           GTS_OBJECT_CAST (obj,\
+						   GtsHSplit,\
+						   gts_hsplit_class ())
+#define GTS_HSPLIT_CLASS(klass)     GTS_OBJECT_CLASS_CAST (klass,\
+						   GtsHSplitClass,\
+						   gts_hsplit_class ())
+
+GtsHSplitClass * gts_hsplit_class             (void);
+GtsHSplit *   gts_hsplit_new                  (GtsHSplitClass * klass, 
+					       GtsSplit * vs);
+void          gts_hsplit_collapse             (GtsHSplit * hs,
+					       GtsHSurface * hsurface);
+void          gts_hsplit_expand               (GtsHSplit * hs,
+					       GtsHSurface * hsurface);
+void          gts_hsplit_force_expand         (GtsHSplit * hs,
+					       GtsHSurface * hsurface);
+
+struct _GtsHSurface {
+  GtsObject object;
+
+  GtsSurface * s;
+  GSList * roots;
+  GtsEHeap * expandable;
+  GtsEHeap * collapsable;
+  GPtrArray * split;
+  guint nvertex;
+};
+
+struct _GtsHSurfaceClass {
+  GtsObjectClass parent_class;
+};
+
+#define GTS_IS_HSURFACE(obj) (gts_object_is_from_class (obj,\
+							gts_hsurface_class ()))
+#define GTS_HSURFACE(obj)           GTS_OBJECT_CAST (obj,\
+						     GtsHSurface,\
+						     gts_hsurface_class ())
+#define GTS_HSURFACE_CLASS(klass)   GTS_OBJECT_CLASS_CAST (klass,\
+						     GtsHSurfaceClass,\
+						     gts_hsurface_class ())
+
+GtsHSurfaceClass * gts_hsurface_class    (void);
+GtsHSurface * gts_hsurface_new           (GtsHSurfaceClass * klass,
+					  GtsHSplitClass *   hsplit_class,
+					  GtsPSurface *      psurface,
+					  GtsKeyFunc         expand_key,
+					  gpointer           expand_data,
+					  GtsKeyFunc         collapse_key,
+					  gpointer           collapse_data);
+void          gts_hsurface_traverse      (GtsHSurface *        hsurface,
+					  GTraverseType        order,
+					  gint                 depth,
+					  GtsSplitTraverseFunc func,
+					  gpointer             data);
+void          gts_hsurface_foreach       (GtsHSurface *        hsurface,
+					  GTraverseType        order,
+					  GtsFunc              func,
+					  gpointer             data);
+guint         gts_hsurface_height        (GtsHSurface *        hsurface);
+
+/* Constrained Delaunay triangulation: cdt.c */
+
+/**
+ * GTS_IS_CONSTRAINT:
+ * @obj: a #GtsObject.
+ *
+ * Evaluates to %TRUE if @obj is a #GtsConstraint, %FALSE otherwise.
+ */
+#define GTS_IS_CONSTRAINT(obj)      (gts_object_is_from_class (obj,\
+						    gts_constraint_class ()))
+/**
+ * GTS_CONSTRAINT:
+ * @obj: a descendant of #GtsConstraint.
+ *
+ * Casts @obj to #GtsConstraint.
+ */
+#define GTS_CONSTRAINT(obj)          GTS_OBJECT_CAST (obj,\
+						  GtsConstraint,\
+						  gts_constraint_class ())
+/**
+ * GTS_CONSTRAINT_CLASS:
+ * @klass: a desscendant of #GtsConstraintClass.
+ *
+ * Casts @klass to #GtsConstraintClass.
+ */
+#define GTS_CONSTRAINT_CLASS(klass)  GTS_OBJECT_CLASS_CAST (klass,\
+						  GtsConstraintClass,\
+						  gts_constraint_class ())
+
+struct _GtsConstraint {
+  GtsEdge edge;
+};
+
+struct _GtsConstraintClass {
+  GtsEdgeClass parent_class;
+};
+
+typedef struct _GtsConstraint        GtsConstraint;
+typedef struct _GtsConstraintClass   GtsConstraintClass;
+
+GtsConstraintClass * gts_constraint_class        (void);
+
+GtsFace *            gts_point_locate            (GtsPoint * p, 
+						  GtsSurface * surface,
+						  GtsFace * guess);
+GtsVertex *          gts_delaunay_add_vertex_to_face (GtsSurface * surface, 
+						      GtsVertex * v,
+						      GtsFace * f);
+GtsVertex *          gts_delaunay_add_vertex     (GtsSurface * surface, 
+						  GtsVertex * v,
+						  GtsFace * guess);
+void                 gts_delaunay_remove_vertex  (GtsSurface * surface, 
+						  GtsVertex * v);
+GtsFace *            gts_delaunay_check          (GtsSurface * surface);
+GSList *             gts_delaunay_add_constraint (GtsSurface * surface,
+						  GtsConstraint * c);
+void                 gts_delaunay_remove_hull    (GtsSurface * surface);
+
+/* GtsListFace: Header */
+
+typedef struct _GtsListFace         GtsListFace;
+
+struct _GtsListFace {
+  /*< private >*/
+  GtsFace parent;
+
+  /*< public >*/
+  GSList * points;
+};
+
+#define GTS_LIST_FACE(obj)            GTS_OBJECT_CAST (obj,\
+					         GtsListFace,\
+					         gts_list_face_class ())
+#define GTS_IS_LIST_FACE(obj)         (gts_object_is_from_class (obj,\
+						 gts_list_face_class ()))
+
+GtsFaceClass *       gts_list_face_class         (void);
+
+/* Constrained Delaunay refinement: refine.c */
+
+typedef gboolean   (* GtsEncroachFunc)           (GtsVertex * v,
+						  GtsEdge * e,
+						  GtsSurface * s,
+						  gpointer data);
+
+gboolean             gts_vertex_encroaches_edge  (GtsVertex * v, 
+						  GtsEdge * e);
+GtsVertex *          gts_edge_is_encroached      (GtsEdge * e,
+						  GtsSurface * s,
+						  GtsEncroachFunc encroaches,
+						  gpointer data);
+guint                gts_delaunay_conform        (GtsSurface * surface,
+						  gint steiner_max,
+						  GtsEncroachFunc encroaches,
+						  gpointer data);
+guint                gts_delaunay_refine         (GtsSurface * surface,
+						  gint steiner_max,
+						  GtsEncroachFunc encroaches,
+						  gpointer encroach_data,
+						  GtsKeyFunc cost,
+						  gpointer cost_data);
+
+/* Isosurfaces (marching cubes): iso.c */
+
+typedef struct _GtsGridPlane     GtsGridPlane;
+typedef struct _GtsIsoSlice      GtsIsoSlice;
+typedef struct _GtsCartesianGrid GtsCartesianGrid;
+
+struct _GtsGridPlane {
+  GtsPoint ** p;
+  guint nx, ny;
+};
+
+struct _GtsCartesianGrid {
+  guint nx, ny, nz;
+  gdouble x, dx, y, dy, z, dz;
+};
+
+typedef void (*GtsIsoCartesianFunc)         (gdouble ** a,
+					     GtsCartesianGrid g,
+					     guint i,
+					     gpointer data);
+
+GtsGridPlane * gts_grid_plane_new           (guint nx, 
+					     guint ny);
+void           gts_grid_plane_destroy       (GtsGridPlane * g);
+GtsIsoSlice *  gts_iso_slice_new            (guint nx, guint ny);
+void           gts_iso_slice_fill           (GtsIsoSlice * slice,
+					     GtsGridPlane * plane1,
+					     GtsGridPlane * plane2,
+					     gdouble ** f1,
+					     gdouble ** f2,
+					     gdouble iso,
+					     GtsVertexClass * klass);
+void           gts_iso_slice_fill_cartesian (GtsIsoSlice * slice,
+					     GtsCartesianGrid g,
+					     gdouble ** f1,
+					     gdouble ** f2,
+					     gdouble iso,
+					     GtsVertexClass * klass);
+void           gts_iso_slice_destroy        (GtsIsoSlice * slice);
+void           gts_isosurface_slice         (GtsIsoSlice * slice1,
+					     GtsIsoSlice * slice2,
+					     GtsSurface * surface);
+void           gts_isosurface_cartesian     (GtsSurface * surface,
+					     GtsCartesianGrid g,
+					     GtsIsoCartesianFunc f,
+					     gpointer data,
+					     gdouble iso);
+
+/* Isosurfaces (marching tetrahedra): isotetra.c */
+
+void           gts_isosurface_tetra         (GtsSurface * surface,
+					     GtsCartesianGrid g,
+					     GtsIsoCartesianFunc f,
+					     gpointer data,
+					     gdouble iso);
+void           gts_isosurface_tetra_bcl     (GtsSurface * surface,
+					     GtsCartesianGrid g,
+					     GtsIsoCartesianFunc f,
+					     gpointer data,
+					     gdouble iso);
+void           gts_isosurface_tetra_bounded (GtsSurface * surface,
+					     GtsCartesianGrid g,
+					     GtsIsoCartesianFunc f,
+					     gpointer data,
+					     gdouble iso);
+
+/* Named vertices, edges and triangles: named.c */
+
+#define GTS_NAME_LENGTH             40
+
+#define GTS_NVERTEX(obj)            GTS_OBJECT_CAST (obj,\
+						     GtsNVertex,\
+						     gts_nvertex_class ())
+#define GTS_NVERTEX_CLASS(klass)    GTS_OBJECT_CLASS_CAST (klass,\
+							   GtsNVertexClass,\
+							   gts_nvertex_class())
+#define GTS_IS_NVERTEX(obj)         (gts_object_is_from_class (obj,\
+				       gts_nvertex_class ()))
+
+typedef struct _GtsNVertex          GtsNVertex;
+typedef struct _GtsNVertexClass     GtsNVertexClass;
+
+struct _GtsNVertex {
+  GtsVertex parent;
+  char name[GTS_NAME_LENGTH];
+};
+
+struct _GtsNVertexClass {
+  GtsVertexClass parent_class;
+};
+
+GtsNVertexClass * gts_nvertex_class        (void);
+
+#define GTS_NEDGE(obj)            GTS_OBJECT_CAST (obj,\
+						   GtsNEdge,\
+						   gts_nedge_class ())
+#define GTS_NEDGE_CLASS(klass)    GTS_OBJECT_CLASS_CAST (klass,\
+							 GtsNEdgeClass,\
+							 gts_nedge_class())
+#define GTS_IS_NEDGE(obj)         (gts_object_is_from_class (obj,\
+				       gts_nedge_class ()))
+
+typedef struct _GtsNEdge          GtsNEdge;
+typedef struct _GtsNEdgeClass     GtsNEdgeClass;
+
+struct _GtsNEdge {
+  GtsEdge parent;
+  char name[GTS_NAME_LENGTH];
+};
+
+struct _GtsNEdgeClass {
+  GtsEdgeClass parent_class;
+};
+
+GtsNEdgeClass *   gts_nedge_class        (void);
+
+#define GTS_NFACE(obj)            GTS_OBJECT_CAST (obj,\
+						   GtsNFace,\
+						   gts_nface_class ())
+#define GTS_NFACE_CLASS(klass)    GTS_OBJECT_CLASS_CAST (klass,\
+							 GtsNFaceClass,\
+							 gts_nface_class())
+#define GTS_IS_NFACE(obj)         (gts_object_is_from_class (obj,\
+				       gts_nface_class ()))
+
+typedef struct _GtsNFace          GtsNFace;
+typedef struct _GtsNFaceClass     GtsNFaceClass;
+
+struct _GtsNFace {
+  GtsFace parent;
+  char name[GTS_NAME_LENGTH];
+};
+
+struct _GtsNFaceClass {
+  GtsFaceClass parent_class;
+};
+
+GtsNFaceClass *       gts_nface_class        (void);
+
+/* Cluster object for out-of-core simplification: oocs.c */
+
+#define GTS_CLUSTER(obj)            GTS_OBJECT_CAST (obj,\
+					           GtsCluster,\
+					           gts_cluster_class ())
+#define GTS_CLUSTER_CLASS(klass)    GTS_OBJECT_CLASS_CAST (klass,\
+						         GtsClusterClass,\
+						         gts_cluster_class())
+#define GTS_IS_CLUSTER(obj)         (gts_object_is_from_class (obj,\
+						   gts_cluster_class ()))
+     
+typedef struct _GtsCluster         GtsCluster;
+typedef struct _GtsClusterClass    GtsClusterClass;
+typedef struct _GtsClusterId       GtsClusterId;
+
+struct _GtsClusterId {
+  guint x, y, z;
+};
+
+struct _GtsCluster {
+  GtsObject parent;
+
+  GtsClusterId id;
+  GtsVertex * v;
+  guint n;
+};
+
+struct _GtsClusterClass {
+  GtsObjectClass parent_class;
+
+  void (* add) (GtsCluster * c, GtsPoint * p, gpointer data);
+  void (* update) (GtsCluster * c);
+};
+
+GtsClusterClass * gts_cluster_class                (void);
+GtsCluster *      gts_cluster_new                  (GtsClusterClass * klass,
+						    GtsClusterId id,
+						    GtsVertexClass * vklass);
+void              gts_cluster_add                  (GtsCluster * c, 
+						    GtsPoint * p,
+						    gpointer data);
+void              gts_cluster_update               (GtsCluster * c);
+
+/* Cluster group object for out-of-core simplification: oocs.c */
+
+#define GTS_CLUSTER_GRID(obj)            GTS_OBJECT_CAST (obj,\
+					           GtsClusterGrid,\
+					           gts_cluster_grid_class ())
+#define GTS_CLUSTER_GRID_CLASS(klass)    GTS_OBJECT_CLASS_CAST (klass,\
+						   GtsClusterGridClass,\
+						   gts_cluster_grid_class())
+#define GTS_IS_CLUSTER_GRID(obj)         (gts_object_is_from_class (obj,\
+						   gts_cluster_grid_class ()))
+     
+typedef struct _GtsClusterGrid         GtsClusterGrid;
+typedef struct _GtsClusterGridClass    GtsClusterGridClass;
+
+struct _GtsClusterGrid {
+  GtsObject parent;
+
+  GtsSurface * surface;
+  GtsBBox * bbox;
+  GtsVector size;
+
+  GtsClusterClass * cluster_class;
+  GHashTable * clusters;
+};
+
+struct _GtsClusterGridClass {
+  GtsObjectClass parent_class;
+};
+
+GtsClusterGridClass * gts_cluster_grid_class (void);
+GtsClusterGrid *      gts_cluster_grid_new   (GtsClusterGridClass * klass,
+					      GtsClusterClass * cluster_class,
+					      GtsSurface * s,
+					      GtsBBox * bbox,
+					      gdouble delta);
+void           gts_cluster_grid_add_triangle (GtsClusterGrid * cluster_grid,
+					      GtsPoint * p1,
+					      GtsPoint * p2,
+					      GtsPoint * p3,
+					      gpointer data);
+GtsRange       gts_cluster_grid_update       (GtsClusterGrid * cluster_grid);
+
+/* Triangle strip generation: stripe.c */
+GSList *       gts_surface_strip             (GtsSurface * s);
+
+/* GtsContainee: container.c */
+
+typedef struct _GtsContainee         GtsContainee;
+typedef struct _GtsContaineeClass    GtsContaineeClass;
+typedef struct _GtsContainer         GtsContainer;
+typedef struct _GtsContainerClass    GtsContainerClass;
+
+struct _GtsContainee {
+  GtsObject object;
+};
+
+struct _GtsContaineeClass {
+  GtsObjectClass parent_class;
+
+  void     (* add_container)    (GtsContainee *, GtsContainer *);
+  void     (* remove_container) (GtsContainee *, GtsContainer *);
+  void     (* foreach)          (GtsContainee *, GtsFunc, gpointer);
+  gboolean (* is_contained)     (GtsContainee *, GtsContainer *);
+  void     (* replace)          (GtsContainee *, GtsContainee *);
+};
+
+#define GTS_CONTAINEE(obj)            GTS_OBJECT_CAST (obj,\
+					           GtsContainee,\
+					           gts_containee_class ())
+#define GTS_CONTAINEE_CLASS(klass)    GTS_OBJECT_CLASS_CAST (klass,\
+						         GtsContaineeClass,\
+						         gts_containee_class())
+#define GTS_IS_CONTAINEE(obj)         (gts_object_is_from_class (obj,\
+						   gts_containee_class ()))
+     
+GtsContaineeClass * gts_containee_class        (void);
+GtsContainee *      gts_containee_new          (GtsContaineeClass * klass);
+gboolean            gts_containee_is_contained (GtsContainee * item, 
+						GtsContainer * c);
+void                gts_containee_replace      (GtsContainee * item,
+						GtsContainee * with);
+
+/* GtsSListContainee: container.c */
+
+typedef struct _GtsSListContainee         GtsSListContainee;
+typedef struct _GtsSListContaineeClass    GtsSListContaineeClass;
+
+struct _GtsSListContainee {
+  GtsContainee containee;
+
+  GSList * containers;
+};
+
+struct _GtsSListContaineeClass {
+  GtsContaineeClass parent_class;
+};
+
+#define GTS_SLIST_CONTAINEE(obj)            GTS_OBJECT_CAST (obj,\
+					           GtsSListContainee,\
+					           gts_slist_containee_class ())
+#define GTS_SLIST_CONTAINEE_CLASS(klass)    GTS_OBJECT_CLASS_CAST (klass,\
+						         GtsSListContaineeClass,\
+						         gts_slist_containee_class())
+#define GTS_IS_SLIST_CONTAINEE(obj)         (gts_object_is_from_class (obj,\
+						   gts_slist_containee_class ()))
+     
+GtsSListContaineeClass * gts_slist_containee_class   (void);
+
+/* GtsContainer: container.c */
+
+struct _GtsContainer {
+  GtsSListContainee object;
+};
+
+struct _GtsContainerClass {
+  GtsSListContaineeClass parent_class;
+
+  void  (* add)     (GtsContainer *, GtsContainee *);
+  void  (* remove)  (GtsContainer *, GtsContainee *);
+  void  (* foreach) (GtsContainer *, GtsFunc, gpointer);
+  guint (* size)    (GtsContainer *);
+};
+
+#define GTS_CONTAINER(obj)            GTS_OBJECT_CAST (obj,\
+					           GtsContainer,\
+					           gts_container_class ())
+#define GTS_CONTAINER_CLASS(klass)    GTS_OBJECT_CLASS_CAST (klass,\
+						         GtsContainerClass,\
+						         gts_container_class())
+#define GTS_IS_CONTAINER(obj)         (gts_object_is_from_class (obj,\
+						   gts_container_class ()))
+     
+GtsContainerClass * gts_container_class     (void);
+GtsContainer *      gts_container_new       (GtsContainerClass * klass);
+void                gts_container_add       (GtsContainer * c,
+					     GtsContainee * item);
+void                gts_container_remove    (GtsContainer * c,
+					     GtsContainee * item);
+void                gts_container_foreach   (GtsContainer * c,
+					     GtsFunc func,
+					     gpointer data);
+guint               gts_container_size      (GtsContainer * c);
+
+/* GtsHashContainer: container.c */
+
+typedef struct _GtsHashContainer         GtsHashContainer;
+typedef struct _GtsHashContainerClass    GtsHashContainerClass;
+
+struct _GtsHashContainer {
+  GtsContainer c;
+
+  GHashTable * items;
+  gboolean frozen;
+};
+
+struct _GtsHashContainerClass {
+  GtsContainerClass parent_class;
+};
+
+#define GTS_HASH_CONTAINER(obj)            GTS_OBJECT_CAST (obj,\
+					           GtsHashContainer,\
+					           gts_hash_container_class ())
+#define GTS_HASH_CONTAINER_CLASS(klass)    GTS_OBJECT_CLASS_CAST (klass,\
+						         GtsHashContainerClass,\
+						         gts_hash_container_class())
+#define GTS_IS_HASH_CONTAINER(obj)         (gts_object_is_from_class (obj,\
+						   gts_hash_container_class ()))
+     
+GtsHashContainerClass * gts_hash_container_class (void);
+
+/* GtsSListContainer: container.c */
+
+typedef struct _GtsSListContainer         GtsSListContainer;
+typedef struct _GtsSListContainerClass    GtsSListContainerClass;
+
+struct _GtsSListContainer {
+  GtsContainer c;
+
+  GSList * items;
+  gboolean frozen;
+};
+
+struct _GtsSListContainerClass {
+  GtsContainerClass parent_class;
+};
+
+#define GTS_SLIST_CONTAINER(obj)            GTS_OBJECT_CAST (obj,\
+					           GtsSListContainer,\
+					           gts_slist_container_class ())
+#define GTS_SLIST_CONTAINER_CLASS(klass)    GTS_OBJECT_CLASS_CAST (klass,\
+						         GtsSListContainerClass,\
+						         gts_slist_container_class())
+#define GTS_IS_SLIST_CONTAINER(obj)         (gts_object_is_from_class (obj,\
+						   gts_slist_container_class ()))
+     
+GtsSListContainerClass * gts_slist_container_class (void);
+
+/* GtsGNode: graph.c */
+
+typedef struct _GtsGNode         GtsGNode;
+typedef struct _GtsGNodeClass    GtsGNodeClass;
+typedef struct _GtsGraph         GtsGraph;
+typedef struct _GtsGraphClass    GtsGraphClass;
+
+struct _GtsGNode {
+  GtsSListContainer container;
+
+  guint level;
+};
+
+struct _GtsGNodeClass {
+  GtsSListContainerClass parent_class;
+
+  gfloat (* weight) (GtsGNode *);
+  void   (* write)  (GtsGNode *, FILE *);
+};
+
+#define GTS_GNODE(obj)            GTS_OBJECT_CAST (obj,\
+					           GtsGNode,\
+					           gts_gnode_class ())
+#define GTS_GNODE_CLASS(klass)    GTS_OBJECT_CLASS_CAST (klass,\
+						         GtsGNodeClass,\
+						         gts_gnode_class())
+#define GTS_IS_GNODE(obj)         (gts_object_is_from_class (obj,\
+						   gts_gnode_class ()))
+#define GTS_GNODE_NEIGHBOR(n,e)   (GTS_GEDGE (e)->n1 == n ? GTS_GEDGE (e)->n2 : GTS_GEDGE (e)->n2 == n ? GTS_GEDGE (e)->n1 : NULL)
+     
+GtsGNodeClass * gts_gnode_class                (void);
+GtsGNode *      gts_gnode_new                  (GtsGNodeClass * klass);
+void            gts_gnode_foreach_neighbor     (GtsGNode * n, 
+						GtsGraph * g,
+						GtsFunc func,
+						gpointer data);
+void            gts_gnode_foreach_edge         (GtsGNode * n,
+						GtsGraph * g,
+						GtsFunc func,
+						gpointer data);
+guint           gts_gnode_degree               (GtsGNode * n,
+						GtsGraph * g);
+gfloat          gts_gnode_move_cost            (GtsGNode * n,
+						GtsGraph * src,
+						GtsGraph * dst);
+gfloat          gts_gnode_weight               (GtsGNode * n);
+
+GTS_C_VAR
+gboolean        gts_allow_floating_gnodes;
+
+/* GtsNGNode: graph.c */
+
+typedef struct _GtsNGNode         GtsNGNode;
+typedef struct _GtsNGNodeClass    GtsNGNodeClass;
+
+struct _GtsNGNode {
+  GtsGNode node;
+
+  guint id;
+};
+
+struct _GtsNGNodeClass {
+  GtsGNodeClass parent_class;
+};
+
+#define GTS_NGNODE(obj)            GTS_OBJECT_CAST (obj,\
+					           GtsNGNode,\
+					           gts_ngnode_class ())
+#define GTS_NGNODE_CLASS(klass)    GTS_OBJECT_CLASS_CAST (klass,\
+						         GtsNGNodeClass,\
+						         gts_ngnode_class())
+#define GTS_IS_NGNODE(obj)         (gts_object_is_from_class (obj,\
+						   gts_ngnode_class ()))
+     
+GtsNGNodeClass * gts_ngnode_class                (void);
+GtsNGNode *      gts_ngnode_new                  (GtsNGNodeClass * klass,
+						  guint id);
+
+/* GtsWGNode: graph.c */
+
+typedef struct _GtsWGNode         GtsWGNode;
+typedef struct _GtsWGNodeClass    GtsWGNodeClass;
+
+struct _GtsWGNode {
+  GtsGNode node;
+  
+  gfloat weight;
+};
+
+struct _GtsWGNodeClass {
+  GtsGNodeClass parent_class;
+};
+
+#define GTS_WGNODE(obj)            GTS_OBJECT_CAST (obj,\
+					           GtsWGNode,\
+					           gts_wgnode_class ())
+#define GTS_WGNODE_CLASS(klass)    GTS_OBJECT_CLASS_CAST (klass,\
+						         GtsWGNodeClass,\
+						         gts_wgnode_class())
+#define GTS_IS_WGNODE(obj)         (gts_object_is_from_class (obj,\
+						   gts_wgnode_class ()))
+     
+GtsWGNodeClass * gts_wgnode_class                (void);
+GtsWGNode *      gts_wgnode_new                  (GtsWGNodeClass * klass,
+						  gfloat weight);
+
+/* GtsPNode */
+
+typedef struct _GtsPNode         GtsPNode;
+typedef struct _GtsPNodeClass    GtsPNodeClass;
+
+struct _GtsPNode {
+  GtsGNode node;
+
+  gpointer data;
+};
+
+struct _GtsPNodeClass {
+  GtsGNodeClass parent_class;
+};
+
+#define GTS_PNODE(obj)            GTS_OBJECT_CAST (obj,\
+					           GtsPNode,\
+					           gts_pnode_class ())
+#define GTS_PNODE_CLASS(klass)    GTS_OBJECT_CLASS_CAST (klass,\
+						         GtsPNodeClass,\
+						         gts_pnode_class())
+#define GTS_IS_PNODE(obj)         (gts_object_is_from_class (obj,\
+						   gts_pnode_class ()))
+     
+GtsPNodeClass * gts_pnode_class                (void);
+GtsPNode *      gts_pnode_new                  (GtsPNodeClass * klass,
+						gpointer data);
+
+/* GtsFNode */
+
+typedef struct _GtsFNode         GtsFNode;
+typedef struct _GtsFNodeClass    GtsFNodeClass;
+
+struct _GtsFNode {
+  GtsGNode node;
+
+  GtsFace * f;
+};
+
+struct _GtsFNodeClass {
+  GtsGNodeClass parent_class;
+};
+
+#define GTS_FNODE(obj)            GTS_OBJECT_CAST (obj,\
+					           GtsFNode,\
+					           gts_fnode_class ())
+#define GTS_FNODE_CLASS(klass)    GTS_OBJECT_CLASS_CAST (klass,\
+						         GtsFNodeClass,\
+						         gts_fnode_class())
+#define GTS_IS_FNODE(obj)         (gts_object_is_from_class (obj,\
+						   gts_fnode_class ()))
+     
+GtsFNodeClass * gts_fnode_class                (void);
+GtsFNode *      gts_fnode_new                  (GtsFNodeClass * klass,
+						GtsFace * f);
+
+/* GtsGEdge: graph.c */
+
+typedef struct _GtsGEdge         GtsGEdge;
+typedef struct _GtsGEdgeClass    GtsGEdgeClass;
+
+struct _GtsGEdge {
+  GtsContainee containee;
+
+  GtsGNode * n1;
+  GtsGNode * n2;
+};
+
+struct _GtsGEdgeClass {
+  GtsContaineeClass parent_class;
+
+  GtsGEdge * (* link)   (GtsGEdge * e, GtsGNode * n1, GtsGNode * n2);
+  gfloat     (* weight) (GtsGEdge * e);
+  void       (* write)  (GtsGEdge * e, FILE * fp);
+};
+
+#define GTS_GEDGE(obj)            GTS_OBJECT_CAST (obj,\
+					           GtsGEdge,\
+					           gts_gedge_class ())
+#define GTS_GEDGE_CLASS(klass)    GTS_OBJECT_CLASS_CAST (klass,\
+						         GtsGEdgeClass,\
+						         gts_gedge_class())
+#define GTS_IS_GEDGE(obj)         (gts_object_is_from_class (obj,\
+						   gts_gedge_class ()))
+     
+GtsGEdgeClass * gts_gedge_class                (void);
+GtsGEdge *      gts_gedge_new                  (GtsGEdgeClass * klass,
+						GtsGNode * n1,
+						GtsGNode * n2);
+gfloat          gts_gedge_weight               (GtsGEdge * e);
+#define         gts_gedge_connects(e, a1, a2)\
+   (((e)->n1 == a1 && (e)->n2 == a2) || ((e)->n1 == a2 && (e)->n2 == a1)) 
+
+/* GtsPGEdge: graph.c */
+
+typedef struct _GtsPGEdge         GtsPGEdge;
+typedef struct _GtsPGEdgeClass    GtsPGEdgeClass;
+
+struct _GtsPGEdge {
+  GtsGEdge gedge;
+
+  gpointer data;
+};
+
+struct _GtsPGEdgeClass {
+  GtsGEdgeClass parent_class;
+};
+
+#define GTS_PGEDGE(obj)            GTS_OBJECT_CAST (obj,\
+					           GtsPGEdge,\
+					           gts_pgedge_class ())
+#define GTS_PGEDGE_CLASS(klass)    GTS_OBJECT_CLASS_CAST (klass,\
+						         GtsPGEdgeClass,\
+						         gts_pgedge_class())
+#define GTS_IS_PGEDGE(obj)         (gts_object_is_from_class (obj,\
+						   gts_pgedge_class ()))
+     
+GtsPGEdgeClass * gts_pgedge_class                (void);
+GtsPGEdge *      gts_pgedge_new                  (GtsPGEdgeClass * klass,
+						  GtsGNode * n1,
+						  GtsGNode * n2,
+						  gpointer data);
+
+/* GtsWGEdge: graph.c */
+
+typedef struct _GtsWGEdge         GtsWGEdge;
+typedef struct _GtsWGEdgeClass    GtsWGEdgeClass;
+
+struct _GtsWGEdge {
+  GtsGEdge gedge;
+
+  gfloat weight;
+};
+
+struct _GtsWGEdgeClass {
+  GtsGEdgeClass parent_class;
+};
+
+#define GTS_WGEDGE(obj)            GTS_OBJECT_CAST (obj,\
+					           GtsWGEdge,\
+					           gts_wgedge_class ())
+#define GTS_WGEDGE_CLASS(klass)    GTS_OBJECT_CLASS_CAST (klass,\
+						         GtsWGEdgeClass,\
+						         gts_wgedge_class())
+#define GTS_IS_WGEDGE(obj)         (gts_object_is_from_class (obj,\
+						   gts_wgedge_class ()))
+     
+GtsWGEdgeClass * gts_wgedge_class                (void);
+GtsWGEdge *      gts_wgedge_new                  (GtsWGEdgeClass * klass,
+						  GtsGNode * n1,
+						  GtsGNode * n2,
+						  gfloat weight);
+
+/* GtsGraph: graph.c */
+
+struct _GtsGraph {
+  GtsHashContainer object;
+
+  GtsGraphClass * graph_class;
+  GtsGNodeClass * node_class;
+  GtsGEdgeClass * edge_class;
+};
+
+struct _GtsGraphClass {
+  GtsHashContainerClass parent_class;
+
+  gfloat (* weight) (GtsGraph *);
+};
+
+#define GTS_GRAPH(obj)            GTS_OBJECT_CAST (obj,\
+					           GtsGraph,\
+					           gts_graph_class ())
+#define GTS_GRAPH_CLASS(klass)    GTS_OBJECT_CLASS_CAST (klass,\
+						         GtsGraphClass,\
+						         gts_graph_class())
+#define GTS_IS_GRAPH(obj)         (gts_object_is_from_class (obj,\
+						   gts_graph_class ()))
+     
+GtsGraphClass * gts_graph_class                  (void);
+GtsGraph *      gts_graph_new                    (GtsGraphClass * klass,
+						  GtsGNodeClass * node_class,
+						  GtsGEdgeClass * edge_class);
+void            gts_graph_print_stats            (GtsGraph * g,
+						  FILE * fp);
+typedef struct _GtsGraphTraverse GtsGraphTraverse;
+typedef enum   { GTS_BREADTH_FIRST
+               }   GtsTraverseType;
+GtsGraphTraverse * gts_graph_traverse_new        (GtsGraph * g, 
+						  GtsGNode * n,
+						  GtsTraverseType type,
+						  gboolean reinit);
+GtsGNode *         gts_graph_traverse_next       (GtsGraphTraverse * t);
+GtsGNode *         gts_graph_traverse_what_next  (GtsGraphTraverse * t);
+void               gts_graph_traverse_destroy    (GtsGraphTraverse * t);
+void               gts_graph_foreach_edge        (GtsGraph * g,
+						  GtsFunc func,
+						  gpointer data);
+gfloat             gts_graph_weight              (GtsGraph * g);
+guint              gts_graph_distance_sum        (GtsGraph * g, 
+						  GtsGNode * center);
+GtsGNode *         gts_graph_farthest            (GtsGraph * g, 
+						  GSList * gnodes);
+guint              gts_graph_edges_cut           (GtsGraph * g);
+gfloat             gts_graph_edges_cut_weight    (GtsGraph * g);
+void               gts_graph_write               (GtsGraph * g, 
+						  FILE * fp);
+void               gts_graph_write_dot           (GtsGraph * g, 
+						  FILE * fp);
+GtsGraph *         gts_graph_read                (GtsFile * fp);
+guint              gts_graph_read_jostle         (GtsGraph * g, 
+						  GtsFile * fp);
+
+/* GtsWGraph: graph.c */
+
+typedef struct _GtsWGraph      GtsWGraph;
+typedef struct _GtsWGraphClass GtsWGraphClass;
+
+struct _GtsWGraph {
+  GtsGraph graph;
+
+  gfloat weight;
+};
+
+struct _GtsWGraphClass {
+  GtsGraphClass parent_class;
+};
+
+#define GTS_WGRAPH(obj)            GTS_OBJECT_CAST (obj,\
+					           GtsWGraph,\
+					           gts_wgraph_class ())
+#define GTS_WGRAPH_CLASS(klass)    GTS_OBJECT_CLASS_CAST (klass,\
+						         GtsWGraphClass,\
+						         gts_wgraph_class())
+#define GTS_IS_WGRAPH(obj)         (gts_object_is_from_class (obj,\
+						   gts_wgraph_class ()))
+     
+GtsWGraphClass * gts_wgraph_class                (void);
+gfloat           gts_wgraph_weight_max           (GtsWGraph * wg);
+
+/* Surface graph: graph.c */
+
+GtsGraph *       gts_surface_graph_new           (GtsGraphClass * klass,
+						  GtsSurface * s);
+GtsSurface *     gts_surface_graph_surface       (GtsGraph * surface_graph,
+						  GtsSurface * s);
+
+/* Segments graph: graph.c */
+
+GtsGraph *       gts_segments_graph_new          (GtsGraphClass * klass,
+						  GSList * segments);
+
+/* GtsGNodeSplit: pgraph.c */
+
+typedef struct _GtsGNodeSplit         GtsGNodeSplit;
+typedef struct _GtsGNodeSplitClass    GtsGNodeSplitClass;
+
+struct _GtsGNodeSplit {
+  GtsObject object;
+
+  GtsGNode * n;
+  GtsObject * n1;
+  GtsObject * n2;
+};
+
+struct _GtsGNodeSplitClass {
+  GtsObjectClass parent_class;
+};
+
+#define GTS_GNODE_SPLIT(obj)            GTS_OBJECT_CAST (obj,\
+					           GtsGNodeSplit,\
+					           gts_gnode_split_class ())
+#define GTS_GNODE_SPLIT_CLASS(klass)    GTS_OBJECT_CLASS_CAST (klass,\
+						         GtsGNodeSplitClass,\
+						         gts_gnode_split_class())
+#define GTS_IS_GNODE_SPLIT(obj)         (gts_object_is_from_class (obj,\
+						   gts_gnode_split_class ()))
+#define GTS_GNODE_SPLIT_N1(ns) (GTS_IS_GNODE_SPLIT ((ns)->n1) ? GTS_GNODE_SPLIT ((ns)->n1)->n : GTS_GNODE ((ns)->n1))
+#define GTS_GNODE_SPLIT_N2(ns) (GTS_IS_GNODE_SPLIT ((ns)->n2) ? GTS_GNODE_SPLIT ((ns)->n2)->n : GTS_GNODE ((ns)->n2))
+     
+GtsGNodeSplitClass * gts_gnode_split_class    (void);
+GtsGNodeSplit *      gts_gnode_split_new      (GtsGNodeSplitClass * klass,
+					       GtsGNode * n,
+					       GtsObject * n1,
+					       GtsObject * n2);
+void                 gts_gnode_split_collapse (GtsGNodeSplit * ns,
+					       GtsGraph * g,
+					       GtsWGEdgeClass * klass);
+void                 gts_gnode_split_expand   (GtsGNodeSplit * ns,
+					       GtsGraph * g);
+
+/* GtsPGraph: pgraph.c */
+
+typedef struct _GtsPGraph         GtsPGraph;
+typedef struct _GtsPGraphClass    GtsPGraphClass;
+
+struct _GtsPGraph {
+  GtsObject object;
+
+  GtsGraph * g;
+  GPtrArray * split;
+  GArray * levels;
+  GtsGNodeSplitClass * split_class;
+  GtsWGEdgeClass * edge_class;
+  guint pos, min, level;
+};
+
+struct _GtsPGraphClass {
+  GtsObjectClass parent_class;
+};
+
+#define GTS_PGRAPH(obj)            GTS_OBJECT_CAST (obj,\
+					           GtsPGraph,\
+					           gts_pgraph_class ())
+#define GTS_PGRAPH_CLASS(klass)    GTS_OBJECT_CLASS_CAST (klass,\
+						         GtsPGraphClass,\
+						         gts_pgraph_class())
+#define GTS_IS_PGRAPH(obj)         (gts_object_is_from_class (obj,\
+						   gts_pgraph_class ()))
+     
+GtsPGraphClass * gts_pgraph_class            (void);
+GtsPGraph *      gts_pgraph_new              (GtsPGraphClass * klass,
+					      GtsGraph * g,
+					      GtsGNodeSplitClass * split_class,
+					      GtsWGNodeClass * node_class,
+					      GtsWGEdgeClass * edge_class,
+					      guint min);
+GtsGNodeSplit *  gts_pgraph_add_node         (GtsPGraph * pg);
+GtsGNodeSplit *  gts_pgraph_remove_node      (GtsPGraph * pg);
+void             gts_pgraph_set_node_number  (GtsPGraph *pg,
+					      guint n);
+guint            gts_pgraph_get_node_number  (GtsPGraph *pg);
+guint            gts_pgraph_min_node_number  (GtsPGraph *pg);
+guint            gts_pgraph_max_node_number  (GtsPGraph *pg);
+void             gts_pgraph_foreach_node     (GtsPGraph *pg,
+					      GtsFunc func,
+					      gpointer data);
+gboolean         gts_pgraph_down             (GtsPGraph * pg,
+					      GtsFunc func,
+					      gpointer data);
+/* Graph partition: partition.c */
+
+GSList *         gts_graph_bubble_partition           (GtsGraph * g, 
+						       guint np, 
+						       guint niter,
+						       GtsFunc step_info,
+						       gpointer data);
+guint            gts_graph_partition_edges_cut        (GSList * partition);
+gfloat           gts_graph_partition_edges_cut_weight (GSList * partition);
+void             gts_graph_partition_print_stats      (GSList * partition,
+						       FILE * fp);
+gfloat           gts_graph_partition_balance          (GSList * partition);
+GSList *         gts_graph_partition_clone            (GSList * partition);
+GSList *         gts_graph_recursive_bisection        (GtsWGraph * wg,
+						       guint n,
+						       guint ntry,
+						       guint mmax,
+						       guint nmin,
+						       gfloat imbalance);
+void             gts_graph_partition_destroy          (GSList * partition);
+
+/* Graph bisection: partition.c */
+
+typedef struct _GtsGraphBisection GtsGraphBisection;
+
+struct _GtsGraphBisection {
+  GtsGraph * g;
+  GtsGraph * g1;
+  GtsGraph * g2;
+  GHashTable * bg1;
+  GHashTable * bg2;
+};
+
+gboolean            gts_graph_bisection_check      (GtsGraphBisection * bg);
+GtsGraphBisection * gts_graph_ggg_bisection        (GtsGraph * g, 
+						    guint ntry);
+GtsGraphBisection * gts_graph_bfgg_bisection       (GtsGraph * g, 
+						    guint ntry);
+gdouble             gts_graph_bisection_kl_refine  (GtsGraphBisection * bg,
+						    guint mmax);
+gdouble             gts_graph_bisection_bkl_refine (GtsGraphBisection * bg,
+						    guint mmax,
+						    gfloat imbalance);
+GtsGraphBisection * gts_graph_bisection_new        (GtsWGraph * wg,
+						    guint ntry,
+						    guint mmax,
+						    guint nmin,
+						    gfloat imbalance);
+void                gts_graph_bisection_destroy    (GtsGraphBisection * bg,
+						    gboolean destroy_graphs);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __GTS_H__ */
diff --git a/src_3rd/gts/heap.c b/src_3rd/gts/heap.c
new file mode 100644
index 0000000..4a37e58
--- /dev/null
+++ b/src_3rd/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/src_3rd/gts/hsurface.c b/src_3rd/gts/hsurface.c
new file mode 100644
index 0000000..80ac66a
--- /dev/null
+++ b/src_3rd/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/src_3rd/gts/iso.c b/src_3rd/gts/iso.c
new file mode 100644
index 0000000..5995a19
--- /dev/null
+++ b/src_3rd/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/src_3rd/gts/isotetra.c b/src_3rd/gts/isotetra.c
new file mode 100644
index 0000000..35fe2ba
--- /dev/null
+++ b/src_3rd/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/src_3rd/gts/kdtree.c b/src_3rd/gts/kdtree.c
new file mode 100644
index 0000000..ec5d422
--- /dev/null
+++ b/src_3rd/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/src_3rd/gts/matrix.c b/src_3rd/gts/matrix.c
new file mode 100644
index 0000000..7ada15d
--- /dev/null
+++ b/src_3rd/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. at H.x + @c^t.x + k, where @H is the 
+ * symmetric positive definite Hessian of f and k is a constant, find the
+ * minimum of f subject to the set of @n prior linear constraints, defined by
+ * the first @n rows of @A and @b (@A.x = @b). The new constraints given by
+ * the minimization are added to @A and @b only if they are linearly
+ * independent as determined by gts_matrix_compatible_row().
+ *
+ * Returns: the new number of constraints defined by @A and @b.
+ */
+guint gts_matrix_quadratic_optimization (GtsMatrix * A,
+					 GtsVector b,
+					 guint n,
+					 GtsMatrix * H,
+					 GtsVector c)
+{
+  g_return_val_if_fail (A != NULL, 0);
+  g_return_val_if_fail (b != NULL, 0);
+  g_return_val_if_fail (n < 3, 0);
+  g_return_val_if_fail (H != NULL, 0);
+
+  switch (n) {
+  case 0: {
+    n = gts_matrix_compatible_row (A, b, n, H[0], - c[0]);
+    n = gts_matrix_compatible_row (A, b, n, H[1], - c[1]);
+    n = gts_matrix_compatible_row (A, b, n, H[2], - c[2]);
+    return n;
+  }
+  case 1: {
+    GtsVector Q0 = {0., 0., 0.};
+    GtsVector Q1 = {0., 0., 0.};
+    GtsVector A1;
+    gdouble max = A[0][0]*A[0][0];
+    guint d = 0;
+
+    /* build a vector orthogonal to the constraint */
+    if (A[0][1]*A[0][1] > max) { max = A[0][1]*A[0][1]; d = 1; }
+    if (A[0][2]*A[0][2] > max) { max = A[0][2]*A[0][2]; d = 2; }
+    switch (d) {
+    case 0: Q0[0] = - A[0][2]/A[0][0]; Q0[2] = 1.0; break;
+    case 1: Q0[1] = - A[0][2]/A[0][1]; Q0[2] = 1.0; break;
+    case 2: Q0[2] = - A[0][0]/A[0][2]; Q0[0] = 1.0; break;
+    }
+
+    /* build a second vector orthogonal to the first and to the constraint */
+    gts_vector_cross (Q1, A[0], Q0);
+
+    A1[0] = gts_vector_scalar (Q0, H[0]);
+    A1[1] = gts_vector_scalar (Q0, H[1]);
+    A1[2] = gts_vector_scalar (Q0, H[2]);
+
+    n = gts_matrix_compatible_row (A, b, n, A1, - gts_vector_scalar (Q0, c));
+    
+    A1[0] = gts_vector_scalar (Q1, H[0]);
+    A1[1] = gts_vector_scalar (Q1, H[1]);
+    A1[2] = gts_vector_scalar (Q1, H[2]);
+
+    n = gts_matrix_compatible_row (A, b, n, A1, - gts_vector_scalar (Q1, c));
+
+    return n;
+  }
+  case 2: {
+    /* build a vector orthogonal to the two constraints */
+    GtsVector A1, Q;
+
+    gts_vector_cross (Q, A[0], A[1]);
+    A1[0] = gts_vector_scalar (Q, H[0]);
+    A1[1] = gts_vector_scalar (Q, H[1]);
+    A1[2] = gts_vector_scalar (Q, H[2]);
+    
+    n = gts_matrix_compatible_row (A, b, n, A1, - gts_vector_scalar (Q, c));
+
+    return n;
+  }
+  default:
+    g_assert_not_reached ();
+  }
+  return 0;
+}
+
+/**
+ * gts_matrix_destroy:
+ * @m: a #GtsMatrix.
+ *
+ * Free all the memory allocated for @m.
+ */
+void gts_matrix_destroy (GtsMatrix * m)
+{
+  g_free (m);
+}
+
+/**
+ * gts_matrix_product:
+ * @m1: a #GtsMatrix.
+ * @m2: another #GtsMatrix.
+ *
+ * Returns: a new #GtsMatrix, product of @m1 and @m2.
+ */
+GtsMatrix * gts_matrix_product (GtsMatrix * m1, GtsMatrix * m2)
+{
+  guint i, j;
+  GtsMatrix * m;
+
+  g_return_val_if_fail (m1 != NULL, NULL);
+  g_return_val_if_fail (m2 != NULL, NULL);
+  g_return_val_if_fail (m1 != m2, NULL);
+
+  m = g_malloc (4*sizeof (GtsVector4));
+
+  for (i = 0; i < 4; i++)
+    for (j = 0; j < 4; j++)
+      m[i][j] = m1[i][0]*m2[0][j] + m1[i][1]*m2[1][j] +
+        m1[i][2]*m2[2][j] + m1[i][3]*m2[3][j];
+  return m;
+}
+
+/**
+ * gts_matrix_zero:
+ * @m: a #GtsMatrix or $NULL.
+ *
+ * Initializes @m to zeros. Allocates a matrix if @m is %NULL.
+ *
+ * Returns: the zero'ed matrix.
+ */
+GtsMatrix * gts_matrix_zero (GtsMatrix * m)
+{
+  if (m == NULL)
+    m = g_malloc0 (4*sizeof (GtsVector4));
+  else {
+    m[0][0] = m[1][0] = m[2][0] = m[3][0] = 0.;
+    m[0][1] = m[1][1] = m[2][1] = m[3][1] = 0.;
+    m[0][2] = m[1][2] = m[2][2] = m[3][2] = 0.;
+    m[0][3] = m[1][3] = m[2][3] = m[3][3] = 0.;
+  }
+  return m;
+}
+
+/**
+ * gts_matrix_identity:
+ * @m: a #GtsMatrix or %NULL.
+ *
+ * Initializes @m to an identity matrix. Allocates a matrix if @m is %NULL.
+ *
+ * Returns: the identity matrix.
+ */
+GtsMatrix * gts_matrix_identity (GtsMatrix * m)
+{
+  m = gts_matrix_zero (m);
+  m[0][0] = m[1][1] = m[2][2] = m[3][3] = 1.;
+  return m;
+}
+
+/**
+ * gts_matrix_scale:
+ * @m: a #GtsMatrix or %NULL.
+ * @s: the scaling vector.
+ *
+ * Initializes @m to a scaling matrix for @s. Allocates a matrix if @m
+ * is %NULL.
+ *
+ * Returns: the scaling matrix.
+ */
+GtsMatrix * gts_matrix_scale (GtsMatrix * m, GtsVector s)
+{
+  m = gts_matrix_zero (m);
+  m[0][0] = s[0];
+  m[1][1] = s[1];
+  m[2][2] = s[2];
+  m[3][3] = 1.;
+  return m;
+}
+
+/**
+ * gts_matrix_translate:
+ * @m: a #GtsMatrix or %NULL.
+ * @t: the translation vector.
+ *
+ * Initializes @m to a translation matrix for @t.  Allocates a new
+ * matrix if @m is %NULL.
+ *
+ * Returns: the translation matix.
+ */
+GtsMatrix * gts_matrix_translate (GtsMatrix * m, GtsVector t)
+{
+  m = gts_matrix_zero (m);
+  m[0][3] = t[0];
+  m[1][3] = t[1];
+  m[2][3] = t[2];
+  m[3][3] = 1.;
+  m[0][0] = m[1][1] = m[2][2] = 1.;
+  return m;
+}
+
+/**
+ * gts_matrix_rotate:
+ * @m: a #GtsMatrix or %NULL.
+ * @r: the rotation axis.
+ * @angle: the angle (in radians) to rotate by.
+ *
+ * Initializes @m to a rotation matrix around @r by @angle.
+ * Allocates a new matrix if @m is %NULL.
+ *
+ * Returns: the rotation matrix.
+ */
+GtsMatrix * gts_matrix_rotate (GtsMatrix * m,
+			       GtsVector r,
+			       gdouble angle)
+{
+  gdouble c, c1, s;
+
+  gts_vector_normalize (r);
+
+  c = cos (angle);
+  c1 = 1. - c;
+  s = sin (angle);
+
+  if (m == NULL)
+    m = g_malloc (4*sizeof (GtsVector4));
+
+  m[0][0] = r[0]*r[0]*c1 + c;
+  m[0][1] = r[0]*r[1]*c1 - r[2]*s;
+  m[0][2] = r[0]*r[2]*c1 + r[1]*s;
+  m[0][3] = 0.;
+
+  m[1][0] = r[1]*r[0]*c1 + r[2]*s;
+  m[1][1] = r[1]*r[1]*c1 + c;
+  m[1][2] = r[1]*r[2]*c1 - r[0]*s;
+  m[1][3] = 0.;
+
+  m[2][0] = r[2]*r[0]*c1 - r[1]*s;
+  m[2][1] = r[2]*r[1]*c1 + r[0]*s;
+  m[2][2] = r[2]*r[2]*c1 + c;
+  m[2][3] = 0.;
+
+  m[3][0] = 0.;
+  m[3][1] = 0.;
+  m[3][2] = 0.;
+  m[3][3] = 1.;
+
+  return m;
+}
diff --git a/src_3rd/gts/misc.c b/src_3rd/gts/misc.c
new file mode 100644
index 0000000..393ba06
--- /dev/null
+++ b/src_3rd/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 = 1;
+const guint gts_binary_age = 1;
+
+static gboolean char_in_string (char c, const char * s)
+{
+  while (*s != '\0')
+    if (*(s++) == c)
+      return TRUE;
+  return FALSE;
+}
+
+static GtsFile * file_new (void)
+{
+  GtsFile * f;
+
+  f = g_malloc (sizeof (GtsFile));
+  f->fp = NULL;
+  f->s = f->s1 = NULL;
+  f->curline = 1;
+  f->curpos = 1;
+  f->token = g_string_new ("");
+  f->type = '\0';
+  f->error = NULL;
+  f->next_token = '\0';
+
+  f->scope = f->scope_max = 0;
+  f->delimiters = g_strdup (" \t");
+  f->comments = g_strdup (GTS_COMMENTS);
+  f->tokens = g_strdup ("\n{}()=");
+
+  return f;
+}
+
+/**
+ * gts_file_new:
+ * @fp: a file pointer.
+ *
+ * Returns: a new #GtsFile.
+ */
+GtsFile * gts_file_new (FILE * fp)
+{
+  GtsFile * f;
+
+  g_return_val_if_fail (fp != NULL, NULL);
+
+  f = file_new ();
+  f->fp = fp;
+  gts_file_next_token (f);
+
+  return f;
+}
+
+/**
+ * gts_file_new_from_string:
+ * @s: a string.
+ *
+ * Returns: a new #GtsFile.
+ */
+GtsFile * gts_file_new_from_string (const gchar * s)
+{
+  GtsFile * f;
+
+  g_return_val_if_fail (s != NULL, NULL);
+
+  f = file_new ();
+  f->s1 = f->s = g_strdup (s);
+  gts_file_next_token (f);
+
+  return f;
+}
+
+/**
+ * gts_file_destroy:
+ * @f: a #GtsFile.
+ *
+ * Frees all the memory allocated for @f.
+ */
+void gts_file_destroy (GtsFile * f)
+{
+  g_return_if_fail (f != NULL);
+
+  g_free (f->delimiters);
+  g_free (f->comments);
+  g_free (f->tokens);
+  if (f->error)
+    g_free (f->error);
+  if (f->s1)
+    g_free (f->s1);
+  g_string_free (f->token, TRUE);
+  g_free (f);
+}
+
+/**
+ * gts_file_verror:
+ * @f: a @GtsFile.
+ * @format: the standard sprintf() format string.
+ * @args: the list of parameters to insert into the format string.
+ *
+ * Sets the @error field of @f using g_strdup_vprintf().
+ *
+ * This function can be called only once and disables any other
+ * operation on @f (gts_file_close() excepted).
+ */
+void gts_file_verror (GtsFile * f,
+		      const gchar * format,
+		      va_list args)
+{
+  g_return_if_fail (f != NULL);
+  g_return_if_fail (format != NULL);
+
+  g_assert (f->type != GTS_ERROR);
+  f->error = g_strdup_vprintf (format, args);
+  f->type = GTS_ERROR;
+}
+
+/**
+ * gts_file_error:
+ * @f: a @GtsFile.
+ * @format: the standard sprintf() format string.
+ * @...: the parameters to insert into the format string.
+ *
+ * Sets the @error field of @f using gts_file_verror().
+ *
+ * This function can be called only once and disables any other
+ * operation on @f (gts_file_close() excepted).
+ */
+void gts_file_error (GtsFile * f,
+		     const gchar * format,
+		     ...)
+{
+  va_list args;
+
+  g_return_if_fail (f != NULL);
+  g_return_if_fail (format != NULL);
+
+  va_start (args, format);  
+  gts_file_verror (f, format, args);
+  va_end (args);
+}
+
+static gint next_char (GtsFile * f)
+{
+  if (f->fp)
+    return fgetc (f->fp);
+  else if (*f->s == '\0')
+    return EOF;
+  return *(f->s++);
+}
+
+/**
+ * gts_file_getc :
+ * @f: a #GtsFile.
+ *
+ * Returns: the next character in @f or EOF if the end of the file is
+ * reached or if an error occured.
+ */
+gint gts_file_getc (GtsFile * f)
+{
+  gint c;
+
+  g_return_val_if_fail (f != NULL, EOF);
+
+  if (f->type == GTS_ERROR)
+    return EOF;
+
+  c = next_char (f);
+  f->curpos++;
+  while (char_in_string (c, f->comments)) {
+    while (c != EOF && c != '\n')
+      c = next_char (f);
+    if (c == '\n') {
+      f->curline++;
+      f->curpos = 1;
+      c = next_char (f);
+    }
+  }
+  switch (c) {
+  case '\n': 
+    f->curline++;
+    f->curpos = 1; 
+    break;
+  case '{':
+    f->scope++; 
+    break;
+  case '}':
+    if (f->scope == 0) {
+      f->line = f->curline;
+      f->pos = f->curpos - 1;
+      gts_file_error (f, "no matching opening brace");
+      c = EOF;
+    }
+    else
+      f->scope--;
+  }
+  return c;
+}
+
+/**
+ * gts_file_read:
+ * @f: a #GtsFile.
+ * @ptr: a pointer.
+ * @size: size of an element.
+ * @nmemb: number of elements.
+ *
+ * Reads @nmemb elements of data, each @size bytes long, from @f,
+ * storing them at the location given by @ptr.
+ *
+ * Returns: the number of elements read.
+ */
+guint gts_file_read (GtsFile * f, gpointer ptr, guint size, guint nmemb)
+{
+  guint i, n;
+  gchar * p;
+
+  g_return_val_if_fail (f != NULL, 0);
+  g_return_val_if_fail (ptr != NULL, 0);
+  g_return_val_if_fail (f->fp != NULL, 0);
+
+  if (f->type == GTS_ERROR)
+    return 0;
+
+  n = fread (ptr, size, nmemb, f->fp);
+  for (i = 0, p = ptr; i < n*size; i++, p++) {
+    f->curpos++;
+    if (*p == '\n') {
+      f->curline++;
+      f->curpos = 1;
+    }
+  }
+  return n;
+}
+
+/**
+ * gts_file_getc_scope :
+ * @f: a #GtsFile.
+ *
+ * Returns: the next character in @f in the scope defined by
+ * @f->scope_max or EOF if the end of the file is reached or if an
+ * error occured.
+ */
+gint gts_file_getc_scope (GtsFile * f)
+{
+  gint c;
+
+  g_return_val_if_fail (f != NULL, EOF);
+
+  if (f->type == GTS_ERROR)
+    return EOF;
+  
+  if (f->scope <= f->scope_max)
+    c = gts_file_getc (f);
+  else {
+    c = gts_file_getc (f);
+    while (c != EOF && f->scope > f->scope_max)
+      c = gts_file_getc (f);    
+  }
+  return c;
+}
+
+/**
+ * gts_file_next_token:
+ * @f: a #GtsFile.
+ *
+ * Reads next token from @f and updates its @token and @delim fields.
+ */
+void gts_file_next_token (GtsFile * f)
+{
+  gint c;
+  gboolean in_string = FALSE;
+
+  g_return_if_fail (f != NULL);
+
+  if (f->type == GTS_ERROR)
+    return;
+  f->token->str[0] = '\0';
+  f->token->len = 0;
+  if (f->next_token != '\0') {
+    if (char_in_string (f->next_token, f->tokens)) {
+      f->line = f->curline;
+      f->pos = f->curpos - 1;
+      g_string_append_c (f->token, f->next_token);
+      f->type = f->next_token;
+      f->next_token = '\0';
+      return;
+    }
+    else {
+      c = f->next_token;
+      f->next_token = '\0';
+    }
+  }
+  else
+    c = gts_file_getc_scope (f);
+  f->type = GTS_NONE;
+  while (c != EOF && (!in_string || !char_in_string (c, f->delimiters))) {
+    if (in_string) {
+      if (char_in_string (c, f->tokens)) {
+	f->next_token = c;
+	break;
+      }
+      g_string_append_c (f->token, c);
+    }
+    else if (!char_in_string (c, f->delimiters)) {
+      in_string = TRUE;
+      f->line = f->curline;
+      f->pos = f->curpos - 1;
+      g_string_append_c (f->token, c);
+      if (char_in_string (c, f->tokens)) {
+	f->type = c;
+	break;
+      }
+    }
+    c = gts_file_getc_scope (f);
+  }
+  if (f->type == GTS_NONE && f->token->len > 0) {
+    gchar * a;
+
+    a = f->token->str;
+    while (*a != '\0' && char_in_string (*a, "+-")) a++;
+    if (*a == '\0') {
+      f->type = GTS_STRING;
+      return;
+    }
+    a = f->token->str;
+    while (*a != '\0' && char_in_string (*a, "+-0123456789")) a++;
+    if (*a == '\0') {
+      f->type = GTS_INT;
+      return;
+    }
+    a = f->token->str;
+    while (*a != '\0' && char_in_string (*a, "+-eE.")) a++;
+    if (*a == '\0') {
+      f->type = GTS_STRING;
+      return;
+    }
+    a = f->token->str;
+    while (*a != '\0' && char_in_string (*a, "+-0123456789eE.")) a++;
+    if (*a == '\0') {
+      f->type = GTS_FLOAT;
+      return;
+    }
+    a = f->token->str;
+    if (!strncmp (a, "0x", 2) || 
+	!strncmp (a, "-0x", 3) || 
+	!strncmp (a, "+0x", 3)) {
+      while (*a != '\0' && char_in_string (*a, "+-0123456789abcdefx")) a++;
+      if (*a == '\0') {
+	f->type = GTS_INT;
+	return;
+      }
+      a = f->token->str;
+      while (*a != '\0' && char_in_string (*a, "+-0123456789abcdefx.p")) a++;
+      if (*a == '\0') {
+	f->type = GTS_FLOAT;
+	return;
+      }
+    }
+    f->type = GTS_STRING;
+  }
+}
+
+/**
+ * gts_file_first_token_after:
+ * @f: a #GtsFile.
+ * @type: a #GtsTokenType.
+ *
+ * Finds and sets the first token of a type different from @type 
+ * occuring after a token of type @type.
+ */
+void gts_file_first_token_after (GtsFile * f, GtsTokenType type)
+{
+  g_return_if_fail (f != NULL);
+
+  while (f->type != GTS_ERROR && 
+	 f->type != GTS_NONE &&
+	 f->type != type)
+    gts_file_next_token (f);
+  while (f->type == type)
+    gts_file_next_token (f);
+}
+
+/**
+ * gts_file_assign_start:
+ * @f: a #GtsFile.
+ * @vars: a %GTS_NONE terminated array of #GtsFileVariable.
+ *
+ * Opens a block delimited by braces to read a list of optional
+ * arguments specified by @vars.  
+ *
+ * If an error is encountered the @error field of @f is set.
+ */
+void gts_file_assign_start (GtsFile * f, GtsFileVariable * vars)
+{
+  GtsFileVariable * var;
+
+  g_return_if_fail (f != NULL);
+  g_return_if_fail (vars != NULL);
+
+  var = vars;
+  while (var->type != GTS_NONE)
+    (var++)->set = FALSE;
+
+  if (f->type != '{') {
+    gts_file_error (f, "expecting an opening brace");
+    return;
+  }
+
+  f->scope_max++;
+  gts_file_next_token (f);
+}
+
+/**
+ * gts_file_assign_next:
+ * @f: a #GtsFile.
+ * @vars: a %GTS_NONE terminated array of #GtsFileVariable.
+ *
+ * Assigns the next optional argument of @vars read from @f.
+ *
+ * Returns: the variable of @vars which has been assigned or %NULL if
+ * no variable has been assigned (if an error has been encountered the
+ * @error field of @f is set).  
+ */
+GtsFileVariable * gts_file_assign_next (GtsFile * f, GtsFileVariable * vars)
+{
+  GtsFileVariable * var;
+  gboolean found = FALSE;
+
+  g_return_val_if_fail (f != NULL, NULL);
+  g_return_val_if_fail (vars != NULL, NULL);
+
+  while (f->type == '\n')
+    gts_file_next_token (f);
+  if (f->type == '}') {
+    f->scope_max--;
+    gts_file_next_token (f);
+    return NULL;
+  }
+  if (f->type == GTS_ERROR)
+    return NULL;
+
+  var = vars;
+  while (f->type != GTS_ERROR && var->type != GTS_NONE && !found) {
+    if (!strcmp (var->name, f->token->str)) {
+      found = TRUE;
+      if (var->unique && var->set)
+	gts_file_error (f, "variable `%s' was already set at line %d:%d", 
+			var->name, var->line, var->pos);
+      else {
+	var->line = f->line;
+	var->pos = f->pos;
+	gts_file_next_token (f);
+	if (f->type != '=')
+	  gts_file_error (f, "expecting `='");
+	else {
+	  var->set = TRUE;
+	  switch (var->type) {
+	  case GTS_FILE:
+	    break;
+	  case GTS_INT:
+	    gts_file_next_token (f);
+	    if (f->type != GTS_INT) {
+	      gts_file_error (f, "expecting an integer");
+	      var->set = FALSE;
+	    }
+	    else if (var->data)
+	      *((gint *) var->data) = atoi (f->token->str); 
+	    break;
+	  case GTS_UINT:
+	    gts_file_next_token (f);
+	    if (f->type != GTS_INT) {
+	      gts_file_error (f, "expecting an integer");
+	      var->set = FALSE;
+	    }
+	    else if (var->data)
+	      *((guint *) var->data) = atoi (f->token->str); 
+	    break;
+	  case GTS_FLOAT:
+	    gts_file_next_token (f);
+	    if (f->type != GTS_INT && f->type != GTS_FLOAT) {
+	      gts_file_error (f, "expecting a number");
+	      var->set = FALSE;
+	    }
+	    else if (var->data)
+	      *((gfloat *) var->data) = atof (f->token->str); 
+	    break;
+	  case GTS_DOUBLE:
+	    gts_file_next_token (f);
+	    if (f->type != GTS_INT && f->type != GTS_FLOAT) {
+	      gts_file_error (f, "expecting a number");
+	      var->set = FALSE;
+	    }
+	    else if (var->data)
+	      *((gdouble *) var->data) = atof (f->token->str); 
+	    break;
+	  case GTS_STRING:
+	    gts_file_next_token (f);
+	    if (f->type != GTS_INT && 
+		f->type != GTS_FLOAT && 
+		f->type != GTS_STRING) {
+	      gts_file_error (f, "expecting a string");
+	      var->set = FALSE;
+	    }
+	    else if (var->data)
+	      *((gchar **) var->data) = g_strdup (f->token->str); 
+	    break;
+	  default:
+	    g_assert_not_reached ();
+	  }
+	}
+      }
+    }
+    else
+      var++;
+  }
+  if (!found)
+    gts_file_error (f, "unknown identifier `%s'", f->token->str);
+  else if (f->type != GTS_ERROR) {
+    g_assert (var->set);
+    gts_file_next_token (f);
+    return var;
+  }
+  return NULL;
+}
+
+/**
+ * gts_file_assign_variables:
+ * @f: a #GtsFile.
+ * @vars: an array of #GtsFileVariable.
+ *
+ * Assigns all the variables belonging to @vars found in @f.
+ *
+ * If an error is encountered the @error field of @f is set.
+ */
+void gts_file_assign_variables (GtsFile * f, GtsFileVariable * vars)
+{
+  g_return_if_fail (f != NULL);
+  g_return_if_fail (vars != NULL);
+
+  gts_file_assign_start (f, vars);
+  while (gts_file_assign_next (f, vars))
+    ;
+}
+
+/**
+ * gts_file_variable_error:
+ * @f: a #GtsFile.
+ * @vars: an array of #GtsFileVariable.
+ * @name: the name of a variable in @vars.
+ * @format: the standard sprintf() format string.
+ * @...: the parameters to insert into the format string.
+ *
+ * Sets the @error field of @f using gts_file_verror().
+ *
+ * String @name must match one of the variable names in @vars.
+ *
+ * If variable @name has been assigned (using gts_file_assign_variables())
+ * sets the @line and @pos fields of @f to the line and position where
+ * it has been assigned.
+ */
+void gts_file_variable_error (GtsFile * f, 
+			      GtsFileVariable * vars,
+			      const gchar * name,
+			      const gchar * format,
+			      ...)
+{
+  va_list args;
+  GtsFileVariable * var;
+
+  g_return_if_fail (f != NULL);
+  g_return_if_fail (vars != NULL);
+  g_return_if_fail (name != NULL);
+  g_return_if_fail (format != NULL);
+
+  var = vars;
+  while (var->type != GTS_NONE && strcmp (var->name, name))
+    var++;
+
+  g_return_if_fail (var->type != GTS_NONE); /* @name not found in @vars */
+
+  if (var->set) {
+    f->line = var->line;
+    f->pos = var->pos;
+  }
+
+  va_start (args, format);  
+  gts_file_verror (f, format, args);
+  va_end (args);
+}
+
+#ifdef DEBUG_FUNCTIONS
+static GHashTable * ids = NULL;
+static guint next_id = 1;
+
+guint id (gpointer p)
+{
+  g_return_val_if_fail (p != NULL, 0);
+  g_return_val_if_fail (ids != NULL, 0);
+  g_assert (g_hash_table_lookup (ids, p));
+  return GPOINTER_TO_UINT (g_hash_table_lookup (ids, p));
+}
+
+void id_insert (gpointer p)
+{
+  g_return_if_fail (p != NULL);
+  if (ids == NULL) ids = g_hash_table_new (NULL, NULL);
+  g_assert (g_hash_table_lookup (ids, p) == NULL);
+  g_hash_table_insert (ids, p, GUINT_TO_POINTER (next_id++));
+}
+
+void id_remove (gpointer p)
+{
+  g_assert (g_hash_table_lookup (ids, p));  
+  g_hash_table_remove (ids, p);
+}
+
+void gts_write_triangle (GtsTriangle * t, 
+			 GtsPoint * o,
+			 FILE * fptr)
+{
+  gdouble xo = o ? o->x : 0.0;
+  gdouble yo = o ? o->y : 0.0;
+  gdouble zo = o ? o->z : 0.0;
+
+  g_return_if_fail (t != NULL && fptr != NULL);
+
+  fprintf (fptr, "(hdefine geometry \"t%d\" { =\n", id (t));
+  fprintf (fptr, "OFF 3 1 0\n"
+	   "%g %g %g\n%g %g %g\n%g %g %g\n3 0 1 2\n})\n"
+	   "(geometry \"t%d\" { : \"t%d\"})\n"
+	   "(normalization \"t%d\" none)\n",
+	   GTS_POINT (GTS_SEGMENT (t->e1)->v1)->x - xo, 
+	   GTS_POINT (GTS_SEGMENT (t->e1)->v1)->y - yo,
+	   GTS_POINT (GTS_SEGMENT (t->e1)->v1)->z - zo,
+	   GTS_POINT (GTS_SEGMENT (t->e1)->v2)->x - xo, 
+	   GTS_POINT (GTS_SEGMENT (t->e1)->v2)->y - yo, 
+	   GTS_POINT (GTS_SEGMENT (t->e1)->v2)->z - zo,
+	   GTS_POINT (gts_triangle_vertex (t))->x - xo,
+	   GTS_POINT (gts_triangle_vertex (t))->y - yo,
+	   GTS_POINT (gts_triangle_vertex (t))->z - zo,
+	   id (t), id (t), id (t));
+}
+
+void gts_write_segment (GtsSegment * s, 
+			GtsPoint * o,
+			FILE * fptr)
+{
+  gdouble xo = o ? o->x : 0.0;
+  gdouble yo = o ? o->y : 0.0;
+  gdouble zo = o ? o->z : 0.0;
+
+  g_return_if_fail (s != NULL && fptr != NULL);
+
+  fprintf (fptr, "(geometry \"s%d\" { =\n", id (s));
+  fprintf (fptr, "VECT 1 2 0 2 0 %g %g %g %g %g %g })\n"
+	   "(normalization \"s%d\" none)\n",
+	   GTS_POINT (s->v1)->x - xo, 
+	   GTS_POINT (s->v1)->y - yo, 
+	   GTS_POINT (s->v1)->z - zo,
+	   GTS_POINT (s->v2)->x - xo, 
+	   GTS_POINT (s->v2)->y - yo, 
+	   GTS_POINT (s->v2)->z - zo,
+	   id (s));
+}
+#endif /* DEBUG_FUNCTIONS */
diff --git a/src_3rd/gts/named.c b/src_3rd/gts/named.c
new file mode 100644
index 0000000..379f9f6
--- /dev/null
+++ b/src_3rd/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/src_3rd/gts/object.c b/src_3rd/gts/object.c
new file mode 100644
index 0000000..5970e50
--- /dev/null
+++ b/src_3rd/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/src_3rd/gts/oocs.c b/src_3rd/gts/oocs.c
new file mode 100644
index 0000000..f0d76bf
--- /dev/null
+++ b/src_3rd/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/src_3rd/gts/partition.c b/src_3rd/gts/partition.c
new file mode 100644
index 0000000..3b73e68
--- /dev/null
+++ b/src_3rd/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/src_3rd/gts/pgraph.c b/src_3rd/gts/pgraph.c
new file mode 100644
index 0000000..6d1f619
--- /dev/null
+++ b/src_3rd/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/src_3rd/gts/point.c b/src_3rd/gts/point.c
new file mode 100644
index 0000000..42fce69
--- /dev/null
+++ b/src_3rd/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/src_3rd/gts/predicates.c b/src_3rd/gts/predicates.c
new file mode 100644
index 0000000..7b7fcf2
--- /dev/null
+++ b/src_3rd/gts/predicates.c
@@ -0,0 +1,2742 @@
+/*****************************************************************************/
+/*                                                                           */
+/*  Routines for Arbitrary Precision Floating-point Arithmetic               */
+/*  and Fast Robust Geometric Predicates                                     */
+/*  (predicates.c)                                                           */
+/*                                                                           */
+/*  May 18, 1996                                                             */
+/*                                                                           */
+/*  Placed in the public domain by                                           */
+/*  Jonathan Richard Shewchuk                                                */
+/*  School of Computer Science                                               */
+/*  Carnegie Mellon University                                               */
+/*  5000 Forbes Avenue                                                       */
+/*  Pittsburgh, Pennsylvania  15213-3891                                     */
+/*  jrs at cs.cmu.edu                                                           */
+/*                                                                           */
+/*  This file contains C implementation of algorithms for exact addition     */
+/*    and multiplication of floating-point numbers, and predicates for       */
+/*    robustly performing the orientation and incircle tests used in         */
+/*    computational geometry.  The algorithms and underlying theory are      */
+/*    described in Jonathan Richard Shewchuk.  "Adaptive Precision Floating- */
+/*    Point Arithmetic and Fast Robust Geometric Predicates."  Technical     */
+/*    Report CMU-CS-96-140, School of Computer Science, Carnegie Mellon      */
+/*    University, Pittsburgh, Pennsylvania, May 1996.  (Submitted to         */
+/*    Discrete & Computational Geometry.)                                    */
+/*                                                                           */
+/*  This file, the paper listed above, and other information are available   */
+/*    from the Web page http://www.cs.cmu.edu/~quake/robust.html .           */
+/*                                                                           */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  Using this code:                                                         */
+/*                                                                           */
+/*  First, read the short or long version of the paper (from the Web page    */
+/*    above).                                                                */
+/*                                                                           */
+/*  Be sure to call exactinit() once, before calling any of the arithmetic   */
+/*    functions or geometric predicates.  Also be sure to turn on the        */
+/*    optimizer when compiling this file.                                    */
+/*                                                                           */
+/*                                                                           */
+/*  Several geometric predicates are defined.  Their parameters are all      */
+/*    points.  Each point is an array of two or three floating-point         */
+/*    numbers.  The geometric predicates, described in the papers, are       */
+/*                                                                           */
+/*    orient2d(pa, pb, pc)                                                   */
+/*    orient2dfast(pa, pb, pc)                                               */
+/*    orient3d(pa, pb, pc, pd)                                               */
+/*    orient3dfast(pa, pb, pc, pd)                                           */
+/*    incircle(pa, pb, pc, pd)                                               */
+/*    incirclefast(pa, pb, pc, pd)                                           */
+/*    insphere(pa, pb, pc, pd, pe)                                           */
+/*    inspherefast(pa, pb, pc, pd, pe)                                       */
+/*                                                                           */
+/*  Those with suffix "fast" are approximate, non-robust versions.  Those    */
+/*    without the suffix are adaptive precision, robust versions.  There     */
+/*    are also versions with the suffices "exact" and "slow", which are      */
+/*    non-adaptive, exact arithmetic versions, which I use only for timings  */
+/*    in my arithmetic papers.                                               */
+/*                                                                           */
+/*                                                                           */
+/*  An expansion is represented by an array of floating-point numbers,       */
+/*    sorted from smallest to largest magnitude (possibly with interspersed  */
+/*    zeros).  The length of each expansion is stored as a separate integer, */
+/*    and each arithmetic function returns an integer which is the length    */
+/*    of the expansion it created.                                           */
+/*                                                                           */
+/*  Several arithmetic functions are defined.  Their parameters are          */
+/*                                                                           */
+/*    e, f           Input expansions                                        */
+/*    elen, flen     Lengths of input expansions (must be >= 1)              */
+/*    h              Output expansion                                        */
+/*    b              Input scalar                                            */
+/*                                                                           */
+/*  The arithmetic functions are                                             */
+/*                                                                           */
+/*    grow_expansion(elen, e, b, h)                                          */
+/*    grow_expansion_zeroelim(elen, e, b, h)                                 */
+/*    expansion_sum(elen, e, flen, f, h)                                     */
+/*    expansion_sum_zeroelim1(elen, e, flen, f, h)                           */
+/*    expansion_sum_zeroelim2(elen, e, flen, f, h)                           */
+/*    fast_expansion_sum(elen, e, flen, f, h)                                */
+/*    fast_expansion_sum_zeroelim(elen, e, flen, f, h)                       */
+/*    linear_expansion_sum(elen, e, flen, f, h)                              */
+/*    linear_expansion_sum_zeroelim(elen, e, flen, f, h)                     */
+/*    scale_expansion(elen, e, b, h)                                         */
+/*    scale_expansion_zeroelim(elen, e, b, h)                                */
+/*    compress(elen, e, h)                                                   */
+/*                                                                           */
+/*  All of these are described in the long version of the paper; some are    */
+/*    described in the short version.  All return an integer that is the     */
+/*    length of h.  Those with suffix _zeroelim perform zero elimination,    */
+/*    and are recommended over their counterparts.  The procedure            */
+/*    fast_expansion_sum_zeroelim() (or linear_expansion_sum_zeroelim() on   */
+/*    processors that do not use the round-to-even tiebreaking rule) is      */
+/*    recommended over expansion_sum_zeroelim().  Each procedure has a       */
+/*    little note next to it (in the code below) that tells you whether or   */
+/*    not the output expansion may be the same array as one of the input     */
+/*    expansions.                                                            */
+/*                                                                           */
+/*                                                                           */
+/*  If you look around below, you'll also find macros for a bunch of         */
+/*    simple unrolled arithmetic operations, and procedures for printing     */
+/*    expansions (commented out because they don't work with all C           */
+/*    compilers) and for generating random floating-point numbers whose      */
+/*    significand bits are all random.  Most of the macros have undocumented */
+/*    requirements that certain of their parameters should not be the same   */
+/*    variable; for safety, better to make sure all the parameters are       */
+/*    distinct variables.  Feel free to send email to jrs at cs.cmu.edu 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;
+
+void 
+gts_predicates_init()
+{
+  double half = 0.5;
+  double check = 1.0, lastcheck;
+  int every_other = 1;
+  /* epsilon = 2^(-p).  Used to estimate roundoff errors. */
+  double epsilon = 1.0;   
+
+  FPU_ROUND_DOUBLE;
+
+  splitter = 1.;
+
+  /* Repeatedly divide `epsilon' by two until it is too small to add to   */
+  /* one without causing roundoff.  (Also check if the sum is equal to    */
+  /* the previous sum, for machines that round up instead of using exact  */
+  /* rounding.  Not that this library will work on such machines anyway). */
+  do {
+    lastcheck = check;
+    epsilon *= half;
+    if (every_other) {
+      splitter *= 2.0;
+    }
+    every_other = !every_other;
+    check = 1.0 + epsilon;
+  } while ((check != 1.0) && (check != lastcheck));
+  splitter += 1.0;
+  /* Error bounds for orientation and incircle tests. */
+  resulterrbound = (3.0 + 8.0 * epsilon) * epsilon;
+  ccwerrboundA = (3.0 + 16.0 * epsilon) * epsilon;
+  ccwerrboundB = (2.0 + 12.0 * epsilon) * epsilon;
+  ccwerrboundC = (9.0 + 64.0 * epsilon) * epsilon * epsilon;
+  o3derrboundA = (7.0 + 56.0 * epsilon) * epsilon;
+  o3derrboundB = (3.0 + 28.0 * epsilon) * epsilon;
+  o3derrboundC = (26.0 + 288.0 * epsilon) * epsilon * epsilon;
+  iccerrboundA = (10.0 + 96.0 * epsilon) * epsilon;
+  iccerrboundB = (4.0 + 48.0 * epsilon) * epsilon;
+  iccerrboundC = (44.0 + 576.0 * epsilon) * epsilon * epsilon;
+  isperrboundA = (16.0 + 224.0 * epsilon) * epsilon;
+  isperrboundB = (5.0 + 72.0 * epsilon) * epsilon;
+  isperrboundC = (71.0 + 1408.0 * epsilon) * epsilon * epsilon;
+
+
+  FPU_RESTORE;
+}
+
+#endif /* USE_PREDICATES_INIT */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  doubleprint()   Print the bit representation of a double.                */
+/*                                                                           */
+/*  Useful for debugging exact arithmetic routines.                          */
+/*                                                                           */
+/*****************************************************************************/
+
+/*
+void doubleprint(number)
+double number;
+{
+  unsigned long long no;
+  unsigned long long sign, expo;
+  int exponent;
+  int i, bottomi;
+
+  no = *(unsigned long long *) &number;
+  sign = no & 0x8000000000000000ll;
+  expo = (no >> 52) & 0x7ffll;
+  exponent = (int) expo;
+  exponent = exponent - 1023;
+  if (sign) {
+    printf("-");
+  } else {
+    printf(" ");
+  }
+  if (exponent == -1023) {
+    printf(
+      "0.0000000000000000000000000000000000000000000000000000_     (   )");
+  } else {
+    printf("1.");
+    bottomi = -1;
+    for (i = 0; i < 52; i++) {
+      if (no & 0x0008000000000000ll) {
+        printf("1");
+        bottomi = i;
+      } else {
+        printf("0");
+      }
+      no <<= 1;
+    }
+    printf("_%d  (%d)", exponent, exponent - 1 - bottomi);
+  }
+}
+*/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  floatprint()   Print the bit representation of a float.                  */
+/*                                                                           */
+/*  Useful for debugging exact arithmetic routines.                          */
+/*                                                                           */
+/*****************************************************************************/
+
+/*
+void floatprint(number)
+float number;
+{
+  unsigned no;
+  unsigned sign, expo;
+  int exponent;
+  int i, bottomi;
+
+  no = *(unsigned *) &number;
+  sign = no & 0x80000000;
+  expo = (no >> 23) & 0xff;
+  exponent = (int) expo;
+  exponent = exponent - 127;
+  if (sign) {
+    printf("-");
+  } else {
+    printf(" ");
+  }
+  if (exponent == -127) {
+    printf("0.00000000000000000000000_     (   )");
+  } else {
+    printf("1.");
+    bottomi = -1;
+    for (i = 0; i < 23; i++) {
+      if (no & 0x00400000) {
+        printf("1");
+        bottomi = i;
+      } else {
+        printf("0");
+      }
+      no <<= 1;
+    }
+    printf("_%3d  (%3d)", exponent, exponent - 1 - bottomi);
+  }
+}
+*/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  expansion_print()   Print the bit representation of an expansion.        */
+/*                                                                           */
+/*  Useful for debugging exact arithmetic routines.                          */
+/*                                                                           */
+/*****************************************************************************/
+
+/*
+void expansion_print(elen, e)
+int elen;
+REAL *e;
+{
+  int i;
+
+  for (i = elen - 1; i >= 0; i--) {
+    REALPRINT(e[i]);
+    if (i > 0) {
+      printf(" +\n");
+    } else {
+      printf("\n");
+    }
+  }
+}
+*/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  doublerand()   Generate a double with random 53-bit significand and a    */
+/*                 random exponent in [0, 511].                              */
+/*                                                                           */
+/*****************************************************************************/
+
+/*
+static double doublerand()
+{
+  double result;
+  double expo;
+  long a, b, c;
+  long i;
+
+  a = random();
+  b = random();
+  c = random();
+  result = (double) (a - 1073741824) * 8388608.0 + (double) (b >> 8);
+  for (i = 512, expo = 2; i <= 131072; i *= 2, expo = expo * expo) {
+    if (c & i) {
+      result *= expo;
+    }
+  }
+  return result;
+}
+*/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  narrowdoublerand()   Generate a double with random 53-bit significand    */
+/*                       and a random exponent in [0, 7].                    */
+/*                                                                           */
+/*****************************************************************************/
+
+/*
+static double narrowdoublerand()
+{
+  double result;
+  double expo;
+  long a, b, c;
+  long i;
+
+  a = random();
+  b = random();
+  c = random();
+  result = (double) (a - 1073741824) * 8388608.0 + (double) (b >> 8);
+  for (i = 512, expo = 2; i <= 2048; i *= 2, expo = expo * expo) {
+    if (c & i) {
+      result *= expo;
+    }
+  }
+  return result;
+}
+*/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  uniformdoublerand()   Generate a double with random 53-bit significand.  */
+/*                                                                           */
+/*****************************************************************************/
+
+/*
+static double uniformdoublerand()
+{
+  double result;
+  long a, b;
+
+  a = random();
+  b = random();
+  result = (double) (a - 1073741824) * 8388608.0 + (double) (b >> 8);
+  return result;
+}
+*/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  floatrand()   Generate a float with random 24-bit significand and a      */
+/*                random exponent in [0, 63].                                */
+/*                                                                           */
+/*****************************************************************************/
+
+/*
+static float floatrand()
+{
+  float result;
+  float expo;
+  long a, c;
+  long i;
+
+  a = random();
+  c = random();
+  result = (float) ((a - 1073741824) >> 6);
+  for (i = 512, expo = 2; i <= 16384; i *= 2, expo = expo * expo) {
+    if (c & i) {
+      result *= expo;
+    }
+  }
+  return result;
+}
+*/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  narrowfloatrand()   Generate a float with random 24-bit significand and  */
+/*                      a random exponent in [0, 7].                         */
+/*                                                                           */
+/*****************************************************************************/
+
+/*
+static float narrowfloatrand()
+{
+  float result;
+  float expo;
+  long a, c;
+  long i;
+
+  a = random();
+  c = random();
+  result = (float) ((a - 1073741824) >> 6);
+  for (i = 512, expo = 2; i <= 2048; i *= 2, expo = expo * expo) {
+    if (c & i) {
+      result *= expo;
+    }
+  }
+  return result;
+}
+*/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  uniformfloatrand()   Generate a float with random 24-bit significand.    */
+/*                                                                           */
+/*****************************************************************************/
+
+/*
+static float uniformfloatrand()
+{
+  float result;
+  long a;
+
+  a = random();
+  result = (float) ((a - 1073741824) >> 6);
+  return result;
+}
+*/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  fast_expansion_sum_zeroelim()   Sum two expansions, eliminating zero     */
+/*                                  components from the output expansion.    */
+/*                                                                           */
+/*  Sets h = e + f.  See the long version of my paper for details.           */
+/*                                                                           */
+/*  If round-to-even is used (as with IEEE 754), maintains the strongly      */
+/*  nonoverlapping property.  (That is, if e is strongly nonoverlapping, h   */
+/*  will be also.)  Does NOT maintain the nonoverlapping or nonadjacent      */
+/*  properties.                                                              */
+/*                                                                           */
+/*****************************************************************************/
+
+static int fast_expansion_sum_zeroelim(int elen, REAL *e, 
+				       int flen, REAL *f, REAL *h)
+     /* h cannot be e or f. */
+{
+  REAL Q;
+  INEXACT REAL Qnew;
+  INEXACT REAL hh;
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  int eindex, findex, hindex;
+  REAL enow, fnow;
+
+  enow = e[0];
+  fnow = f[0];
+  eindex = findex = 0;
+  if ((fnow > enow) == (fnow > -enow)) {
+    Q = enow;
+    enow = e[++eindex];
+  } else {
+    Q = fnow;
+    fnow = f[++findex];
+  }
+  hindex = 0;
+  if ((eindex < elen) && (findex < flen)) {
+    if ((fnow > enow) == (fnow > -enow)) {
+      Fast_Two_Sum(enow, Q, Qnew, hh);
+      enow = e[++eindex];
+    } else {
+      Fast_Two_Sum(fnow, Q, Qnew, hh);
+      fnow = f[++findex];
+    }
+    Q = Qnew;
+    if (hh != 0.0) {
+      h[hindex++] = hh;
+    }
+    while ((eindex < elen) && (findex < flen)) {
+      if ((fnow > enow) == (fnow > -enow)) {
+        Two_Sum(Q, enow, Qnew, hh);
+        enow = e[++eindex];
+      } else {
+        Two_Sum(Q, fnow, Qnew, hh);
+        fnow = f[++findex];
+      }
+      Q = Qnew;
+      if (hh != 0.0) {
+        h[hindex++] = hh;
+      }
+    }
+  }
+  while (eindex < elen) {
+    Two_Sum(Q, enow, Qnew, hh);
+    enow = e[++eindex];
+    Q = Qnew;
+    if (hh != 0.0) {
+      h[hindex++] = hh;
+    }
+  }
+  while (findex < flen) {
+    Two_Sum(Q, fnow, Qnew, hh);
+    fnow = f[++findex];
+    Q = Qnew;
+    if (hh != 0.0) {
+      h[hindex++] = hh;
+    }
+  }
+  if ((Q != 0.0) || (hindex == 0)) {
+    h[hindex++] = Q;
+  }
+  return hindex;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  scale_expansion_zeroelim()   Multiply an expansion by a scalar,          */
+/*                               eliminating zero components from the        */
+/*                               output expansion.                           */
+/*                                                                           */
+/*  Sets h = be.  See either version of my paper for details.                */
+/*                                                                           */
+/*  Maintains the nonoverlapping property.  If round-to-even is used (as     */
+/*  with IEEE 754), maintains the strongly nonoverlapping and nonadjacent    */
+/*  properties as well.  (That is, if e has one of these properties, so      */
+/*  will h.)                                                                 */
+/*                                                                           */
+/*****************************************************************************/
+
+static int scale_expansion_zeroelim(int elen, REAL *e, REAL b, REAL *h)
+     /* e and h cannot be the same. */
+{
+  INEXACT REAL Q, sum;
+  REAL hh;
+  INEXACT REAL product1;
+  REAL product0;
+  int eindex, hindex;
+  REAL enow;
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  INEXACT REAL c;
+  INEXACT REAL abig;
+  REAL ahi, alo, bhi, blo;
+  REAL err1, err2, err3;
+
+  Split(b, bhi, blo);
+  Two_Product_Presplit(e[0], b, bhi, blo, Q, hh);
+  hindex = 0;
+  if (hh != 0) {
+    h[hindex++] = hh;
+  }
+  for (eindex = 1; eindex < elen; eindex++) {
+    enow = e[eindex];
+    Two_Product_Presplit(enow, b, bhi, blo, product1, product0);
+    Two_Sum(Q, product0, sum, hh);
+    if (hh != 0) {
+      h[hindex++] = hh;
+    }
+    Fast_Two_Sum(product1, sum, Q, hh);
+    if (hh != 0) {
+      h[hindex++] = hh;
+    }
+  }
+  if ((Q != 0.0) || (hindex == 0)) {
+    h[hindex++] = Q;
+  }
+  return hindex;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  estimate()   Produce a one-word estimate of an expansion's value.        */
+/*                                                                           */
+/*  See either version of my paper for details.                              */
+/*                                                                           */
+/*****************************************************************************/
+
+static REAL estimate(int elen, REAL *e)
+{
+  REAL Q;
+  int eindex;
+
+  Q = e[0];
+  for (eindex = 1; eindex < elen; eindex++) {
+    Q += e[eindex];
+  }
+  return Q;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  orient2dfast()   Approximate 2D orientation test.  Nonrobust.            */
+/*  orient2dexact()   Exact 2D orientation test.  Robust.                    */
+/*  orient2dslow()   Another exact 2D orientation test.  Robust.             */
+/*  orient2d()   Adaptive exact 2D orientation test.  Robust.                */
+/*                                                                           */
+/*               Return a positive value if the points pa, pb, and pc occur  */
+/*               in counterclockwise order; a negative value if they occur   */
+/*               in clockwise order; and zero if they are collinear.  The    */
+/*               result is also a rough approximation of twice the signed    */
+/*               area of the triangle defined by the three points.           */
+/*                                                                           */
+/*  Only the first and last routine should be used; the middle two are for   */
+/*  timings.                                                                 */
+/*                                                                           */
+/*  The last three use exact arithmetic to ensure a correct answer.  The     */
+/*  result returned is the determinant of a matrix.  In orient2d() only,     */
+/*  this determinant is computed adaptively, in the sense that exact         */
+/*  arithmetic is used only to the degree it is needed to ensure that the    */
+/*  returned value has the correct sign.  Hence, orient2d() is usually quite */
+/*  fast, but will run more slowly when the input points are collinear or    */
+/*  nearly so.                                                               */
+/*                                                                           */
+/*****************************************************************************/
+
+static REAL orient2dadapt(REAL *pa, REAL *pb, REAL *pc, REAL detsum)
+{
+  INEXACT REAL acx, acy, bcx, bcy;
+  REAL acxtail, acytail, bcxtail, bcytail;
+  INEXACT REAL detleft, detright;
+  REAL detlefttail, detrighttail;
+  REAL det, errbound;
+  REAL B[4], C1[8], C2[12], D[16];
+  INEXACT REAL B3;
+  int C1length, C2length, Dlength;
+  REAL u[4];
+  INEXACT REAL u3;
+  INEXACT REAL s1, t1;
+  REAL s0, t0;
+
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  INEXACT REAL c;
+  INEXACT REAL abig;
+  REAL ahi, alo, bhi, blo;
+  REAL err1, err2, err3;
+  INEXACT REAL _i, _j;
+  REAL _0;
+
+  acx = (REAL) (pa[0] - pc[0]);
+  bcx = (REAL) (pb[0] - pc[0]);
+  acy = (REAL) (pa[1] - pc[1]);
+  bcy = (REAL) (pb[1] - pc[1]);
+
+  Two_Product(acx, bcy, detleft, detlefttail);
+  Two_Product(acy, bcx, detright, detrighttail);
+
+  Two_Two_Diff(detleft, detlefttail, detright, detrighttail,
+               B3, B[2], B[1], B[0]);
+  B[3] = B3;
+
+  det = estimate(4, B);
+  errbound = ccwerrboundB * detsum;
+  if ((det >= errbound) || (-det >= errbound)) {
+    return det;
+  }
+
+  Two_Diff_Tail(pa[0], pc[0], acx, acxtail);
+  Two_Diff_Tail(pb[0], pc[0], bcx, bcxtail);
+  Two_Diff_Tail(pa[1], pc[1], acy, acytail);
+  Two_Diff_Tail(pb[1], pc[1], bcy, bcytail);
+
+  if ((acxtail == 0.0) && (acytail == 0.0)
+      && (bcxtail == 0.0) && (bcytail == 0.0)) {
+    return det;
+  }
+
+  errbound = ccwerrboundC * detsum + resulterrbound * Absolute(det);
+  det += (acx * bcytail + bcy * acxtail)
+       - (acy * bcxtail + bcx * acytail);
+  if ((det >= errbound) || (-det >= errbound)) {
+    return det;
+  }
+
+  Two_Product(acxtail, bcy, s1, s0);
+  Two_Product(acytail, bcx, t1, t0);
+  Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]);
+  u[3] = u3;
+  C1length = fast_expansion_sum_zeroelim(4, B, 4, u, C1);
+
+  Two_Product(acx, bcytail, s1, s0);
+  Two_Product(acy, bcxtail, t1, t0);
+  Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]);
+  u[3] = u3;
+  C2length = fast_expansion_sum_zeroelim(C1length, C1, 4, u, C2);
+
+  Two_Product(acxtail, bcytail, s1, s0);
+  Two_Product(acytail, bcxtail, t1, t0);
+  Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]);
+  u[3] = u3;
+  Dlength = fast_expansion_sum_zeroelim(C2length, C2, 4, u, D);
+
+  return(D[Dlength - 1]);
+}
+
+REAL orient2d(pa, pb, pc)
+REAL *pa;
+REAL *pb;
+REAL *pc;
+{
+  REAL detleft, detright, det;
+  REAL detsum, errbound;
+  REAL orient;
+
+  FPU_ROUND_DOUBLE;
+
+  detleft = (pa[0] - pc[0]) * (pb[1] - pc[1]);
+  detright = (pa[1] - pc[1]) * (pb[0] - pc[0]);
+  det = detleft - detright;
+
+  if (detleft > 0.0) {
+    if (detright <= 0.0) {
+      FPU_RESTORE;
+      return det;
+    } else {
+      detsum = detleft + detright;
+    }
+  } else if (detleft < 0.0) {
+    if (detright >= 0.0) {
+      FPU_RESTORE;
+      return det;
+    } else {
+      detsum = -detleft - detright;
+    }
+  } else {
+    FPU_RESTORE;
+    return det;
+  }
+
+  errbound = ccwerrboundA * detsum;
+  if ((det >= errbound) || (-det >= errbound)) {
+    FPU_RESTORE;
+    return det;
+  }
+
+  orient = orient2dadapt(pa, pb, pc, detsum);
+  FPU_RESTORE;
+  return orient;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  orient3dfast()   Approximate 3D orientation test.  Nonrobust.            */
+/*  orient3dexact()   Exact 3D orientation test.  Robust.                    */
+/*  orient3dslow()   Another exact 3D orientation test.  Robust.             */
+/*  orient3d()   Adaptive exact 3D orientation test.  Robust.                */
+/*                                                                           */
+/*               Return a positive value if the point pd lies below the      */
+/*               plane passing through pa, pb, and pc; "below" is defined so */
+/*               that pa, pb, and pc appear in counterclockwise order when   */
+/*               viewed from above the plane.  Returns a negative value if   */
+/*               pd lies above the plane.  Returns zero if the points are    */
+/*               coplanar.  The result is also a rough approximation of six  */
+/*               times the signed volume of the tetrahedron defined by the   */
+/*               four points.                                                */
+/*                                                                           */
+/*  Only the first and last routine should be used; the middle two are for   */
+/*  timings.                                                                 */
+/*                                                                           */
+/*  The last three use exact arithmetic to ensure a correct answer.  The     */
+/*  result returned is the determinant of a matrix.  In orient3d() only,     */
+/*  this determinant is computed adaptively, in the sense that exact         */
+/*  arithmetic is used only to the degree it is needed to ensure that the    */
+/*  returned value has the correct sign.  Hence, orient3d() is usually quite */
+/*  fast, but will run more slowly when the input points are coplanar or     */
+/*  nearly so.                                                               */
+/*                                                                           */
+/*****************************************************************************/
+
+static REAL orient3dadapt(REAL *pa, REAL *pb, REAL *pc, REAL *pd, 
+			  REAL permanent)
+{
+  INEXACT REAL adx, bdx, cdx, ady, bdy, cdy, adz, bdz, cdz;
+  REAL det, errbound;
+
+  INEXACT REAL bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1;
+  REAL bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0;
+  REAL bc[4], ca[4], ab[4];
+  INEXACT REAL bc3, ca3, ab3;
+  REAL adet[8], bdet[8], cdet[8];
+  int alen, blen, clen;
+  REAL abdet[16];
+  int ablen;
+  REAL *finnow, *finother, *finswap;
+  REAL fin1[192], fin2[192];
+  int finlength;
+
+  REAL adxtail, bdxtail, cdxtail;
+  REAL adytail, bdytail, cdytail;
+  REAL adztail, bdztail, cdztail;
+  INEXACT REAL at_blarge, at_clarge;
+  INEXACT REAL bt_clarge, bt_alarge;
+  INEXACT REAL ct_alarge, ct_blarge;
+  REAL at_b[4], at_c[4], bt_c[4], bt_a[4], ct_a[4], ct_b[4];
+  int at_blen, at_clen, bt_clen, bt_alen, ct_alen, ct_blen;
+  INEXACT REAL bdxt_cdy1, cdxt_bdy1, cdxt_ady1;
+  INEXACT REAL adxt_cdy1, adxt_bdy1, bdxt_ady1;
+  REAL bdxt_cdy0, cdxt_bdy0, cdxt_ady0;
+  REAL adxt_cdy0, adxt_bdy0, bdxt_ady0;
+  INEXACT REAL bdyt_cdx1, cdyt_bdx1, cdyt_adx1;
+  INEXACT REAL adyt_cdx1, adyt_bdx1, bdyt_adx1;
+  REAL bdyt_cdx0, cdyt_bdx0, cdyt_adx0;
+  REAL adyt_cdx0, adyt_bdx0, bdyt_adx0;
+  REAL bct[8], cat[8], abt[8];
+  int bctlen, catlen, abtlen;
+  INEXACT REAL bdxt_cdyt1, cdxt_bdyt1, cdxt_adyt1;
+  INEXACT REAL adxt_cdyt1, adxt_bdyt1, bdxt_adyt1;
+  REAL bdxt_cdyt0, cdxt_bdyt0, cdxt_adyt0;
+  REAL adxt_cdyt0, adxt_bdyt0, bdxt_adyt0;
+  REAL u[4], v[12], w[16];
+  INEXACT REAL u3;
+  int vlength, wlength;
+  REAL negate;
+
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  INEXACT REAL c;
+  INEXACT REAL abig;
+  REAL ahi, alo, bhi, blo;
+  REAL err1, err2, err3;
+  INEXACT REAL _i, _j, _k;
+  REAL _0;
+
+  adx = (REAL) (pa[0] - pd[0]);
+  bdx = (REAL) (pb[0] - pd[0]);
+  cdx = (REAL) (pc[0] - pd[0]);
+  ady = (REAL) (pa[1] - pd[1]);
+  bdy = (REAL) (pb[1] - pd[1]);
+  cdy = (REAL) (pc[1] - pd[1]);
+  adz = (REAL) (pa[2] - pd[2]);
+  bdz = (REAL) (pb[2] - pd[2]);
+  cdz = (REAL) (pc[2] - pd[2]);
+
+  Two_Product(bdx, cdy, bdxcdy1, bdxcdy0);
+  Two_Product(cdx, bdy, cdxbdy1, cdxbdy0);
+  Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]);
+  bc[3] = bc3;
+  alen = scale_expansion_zeroelim(4, bc, adz, adet);
+
+  Two_Product(cdx, ady, cdxady1, cdxady0);
+  Two_Product(adx, cdy, adxcdy1, adxcdy0);
+  Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]);
+  ca[3] = ca3;
+  blen = scale_expansion_zeroelim(4, ca, bdz, bdet);
+
+  Two_Product(adx, bdy, adxbdy1, adxbdy0);
+  Two_Product(bdx, ady, bdxady1, bdxady0);
+  Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]);
+  ab[3] = ab3;
+  clen = scale_expansion_zeroelim(4, ab, cdz, cdet);
+
+  ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
+  finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1);
+
+  det = estimate(finlength, fin1);
+  errbound = o3derrboundB * permanent;
+  if ((det >= errbound) || (-det >= errbound)) {
+    return det;
+  }
+
+  Two_Diff_Tail(pa[0], pd[0], adx, adxtail);
+  Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail);
+  Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail);
+  Two_Diff_Tail(pa[1], pd[1], ady, adytail);
+  Two_Diff_Tail(pb[1], pd[1], bdy, bdytail);
+  Two_Diff_Tail(pc[1], pd[1], cdy, cdytail);
+  Two_Diff_Tail(pa[2], pd[2], adz, adztail);
+  Two_Diff_Tail(pb[2], pd[2], bdz, bdztail);
+  Two_Diff_Tail(pc[2], pd[2], cdz, cdztail);
+
+  if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0)
+      && (adytail == 0.0) && (bdytail == 0.0) && (cdytail == 0.0)
+      && (adztail == 0.0) && (bdztail == 0.0) && (cdztail == 0.0)) {
+    return det;
+  }
+
+  errbound = o3derrboundC * permanent + resulterrbound * Absolute(det);
+  det += (adz * ((bdx * cdytail + cdy * bdxtail)
+                 - (bdy * cdxtail + cdx * bdytail))
+          + adztail * (bdx * cdy - bdy * cdx))
+       + (bdz * ((cdx * adytail + ady * cdxtail)
+                 - (cdy * adxtail + adx * cdytail))
+          + bdztail * (cdx * ady - cdy * adx))
+       + (cdz * ((adx * bdytail + bdy * adxtail)
+                 - (ady * bdxtail + bdx * adytail))
+          + cdztail * (adx * bdy - ady * bdx));
+  if ((det >= errbound) || (-det >= errbound)) {
+    return det;
+  }
+
+  finnow = fin1;
+  finother = fin2;
+
+  if (adxtail == 0.0) {
+    if (adytail == 0.0) {
+      at_b[0] = 0.0;
+      at_blen = 1;
+      at_c[0] = 0.0;
+      at_clen = 1;
+    } else {
+      negate = -adytail;
+      Two_Product(negate, bdx, at_blarge, at_b[0]);
+      at_b[1] = at_blarge;
+      at_blen = 2;
+      Two_Product(adytail, cdx, at_clarge, at_c[0]);
+      at_c[1] = at_clarge;
+      at_clen = 2;
+    }
+  } else {
+    if (adytail == 0.0) {
+      Two_Product(adxtail, bdy, at_blarge, at_b[0]);
+      at_b[1] = at_blarge;
+      at_blen = 2;
+      negate = -adxtail;
+      Two_Product(negate, cdy, at_clarge, at_c[0]);
+      at_c[1] = at_clarge;
+      at_clen = 2;
+    } else {
+      Two_Product(adxtail, bdy, adxt_bdy1, adxt_bdy0);
+      Two_Product(adytail, bdx, adyt_bdx1, adyt_bdx0);
+      Two_Two_Diff(adxt_bdy1, adxt_bdy0, adyt_bdx1, adyt_bdx0,
+                   at_blarge, at_b[2], at_b[1], at_b[0]);
+      at_b[3] = at_blarge;
+      at_blen = 4;
+      Two_Product(adytail, cdx, adyt_cdx1, adyt_cdx0);
+      Two_Product(adxtail, cdy, adxt_cdy1, adxt_cdy0);
+      Two_Two_Diff(adyt_cdx1, adyt_cdx0, adxt_cdy1, adxt_cdy0,
+                   at_clarge, at_c[2], at_c[1], at_c[0]);
+      at_c[3] = at_clarge;
+      at_clen = 4;
+    }
+  }
+  if (bdxtail == 0.0) {
+    if (bdytail == 0.0) {
+      bt_c[0] = 0.0;
+      bt_clen = 1;
+      bt_a[0] = 0.0;
+      bt_alen = 1;
+    } else {
+      negate = -bdytail;
+      Two_Product(negate, cdx, bt_clarge, bt_c[0]);
+      bt_c[1] = bt_clarge;
+      bt_clen = 2;
+      Two_Product(bdytail, adx, bt_alarge, bt_a[0]);
+      bt_a[1] = bt_alarge;
+      bt_alen = 2;
+    }
+  } else {
+    if (bdytail == 0.0) {
+      Two_Product(bdxtail, cdy, bt_clarge, bt_c[0]);
+      bt_c[1] = bt_clarge;
+      bt_clen = 2;
+      negate = -bdxtail;
+      Two_Product(negate, ady, bt_alarge, bt_a[0]);
+      bt_a[1] = bt_alarge;
+      bt_alen = 2;
+    } else {
+      Two_Product(bdxtail, cdy, bdxt_cdy1, bdxt_cdy0);
+      Two_Product(bdytail, cdx, bdyt_cdx1, bdyt_cdx0);
+      Two_Two_Diff(bdxt_cdy1, bdxt_cdy0, bdyt_cdx1, bdyt_cdx0,
+                   bt_clarge, bt_c[2], bt_c[1], bt_c[0]);
+      bt_c[3] = bt_clarge;
+      bt_clen = 4;
+      Two_Product(bdytail, adx, bdyt_adx1, bdyt_adx0);
+      Two_Product(bdxtail, ady, bdxt_ady1, bdxt_ady0);
+      Two_Two_Diff(bdyt_adx1, bdyt_adx0, bdxt_ady1, bdxt_ady0,
+                  bt_alarge, bt_a[2], bt_a[1], bt_a[0]);
+      bt_a[3] = bt_alarge;
+      bt_alen = 4;
+    }
+  }
+  if (cdxtail == 0.0) {
+    if (cdytail == 0.0) {
+      ct_a[0] = 0.0;
+      ct_alen = 1;
+      ct_b[0] = 0.0;
+      ct_blen = 1;
+    } else {
+      negate = -cdytail;
+      Two_Product(negate, adx, ct_alarge, ct_a[0]);
+      ct_a[1] = ct_alarge;
+      ct_alen = 2;
+      Two_Product(cdytail, bdx, ct_blarge, ct_b[0]);
+      ct_b[1] = ct_blarge;
+      ct_blen = 2;
+    }
+  } else {
+    if (cdytail == 0.0) {
+      Two_Product(cdxtail, ady, ct_alarge, ct_a[0]);
+      ct_a[1] = ct_alarge;
+      ct_alen = 2;
+      negate = -cdxtail;
+      Two_Product(negate, bdy, ct_blarge, ct_b[0]);
+      ct_b[1] = ct_blarge;
+      ct_blen = 2;
+    } else {
+      Two_Product(cdxtail, ady, cdxt_ady1, cdxt_ady0);
+      Two_Product(cdytail, adx, cdyt_adx1, cdyt_adx0);
+      Two_Two_Diff(cdxt_ady1, cdxt_ady0, cdyt_adx1, cdyt_adx0,
+                   ct_alarge, ct_a[2], ct_a[1], ct_a[0]);
+      ct_a[3] = ct_alarge;
+      ct_alen = 4;
+      Two_Product(cdytail, bdx, cdyt_bdx1, cdyt_bdx0);
+      Two_Product(cdxtail, bdy, cdxt_bdy1, cdxt_bdy0);
+      Two_Two_Diff(cdyt_bdx1, cdyt_bdx0, cdxt_bdy1, cdxt_bdy0,
+                   ct_blarge, ct_b[2], ct_b[1], ct_b[0]);
+      ct_b[3] = ct_blarge;
+      ct_blen = 4;
+    }
+  }
+
+  bctlen = fast_expansion_sum_zeroelim(bt_clen, bt_c, ct_blen, ct_b, bct);
+  wlength = scale_expansion_zeroelim(bctlen, bct, adz, w);
+  finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+                                          finother);
+  finswap = finnow; finnow = finother; finother = finswap;
+
+  catlen = fast_expansion_sum_zeroelim(ct_alen, ct_a, at_clen, at_c, cat);
+  wlength = scale_expansion_zeroelim(catlen, cat, bdz, w);
+  finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+                                          finother);
+  finswap = finnow; finnow = finother; finother = finswap;
+
+  abtlen = fast_expansion_sum_zeroelim(at_blen, at_b, bt_alen, bt_a, abt);
+  wlength = scale_expansion_zeroelim(abtlen, abt, cdz, w);
+  finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+                                          finother);
+  finswap = finnow; finnow = finother; finother = finswap;
+
+  if (adztail != 0.0) {
+    vlength = scale_expansion_zeroelim(4, bc, adztail, v);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v,
+                                            finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (bdztail != 0.0) {
+    vlength = scale_expansion_zeroelim(4, ca, bdztail, v);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v,
+                                            finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (cdztail != 0.0) {
+    vlength = scale_expansion_zeroelim(4, ab, cdztail, v);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v,
+                                            finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+
+  if (adxtail != 0.0) {
+    if (bdytail != 0.0) {
+      Two_Product(adxtail, bdytail, adxt_bdyt1, adxt_bdyt0);
+      Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdz, u3, u[2], u[1], u[0]);
+      u[3] = u3;
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                              finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+      if (cdztail != 0.0) {
+        Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdztail, u3, u[2], u[1], u[0]);
+        u[3] = u3;
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                                finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+    }
+    if (cdytail != 0.0) {
+      negate = -adxtail;
+      Two_Product(negate, cdytail, adxt_cdyt1, adxt_cdyt0);
+      Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdz, u3, u[2], u[1], u[0]);
+      u[3] = u3;
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                              finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+      if (bdztail != 0.0) {
+        Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdztail, u3, u[2], u[1], u[0]);
+        u[3] = u3;
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                                finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+    }
+  }
+  if (bdxtail != 0.0) {
+    if (cdytail != 0.0) {
+      Two_Product(bdxtail, cdytail, bdxt_cdyt1, bdxt_cdyt0);
+      Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adz, u3, u[2], u[1], u[0]);
+      u[3] = u3;
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                              finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+      if (adztail != 0.0) {
+        Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adztail, u3, u[2], u[1], u[0]);
+        u[3] = u3;
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                                finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+    }
+    if (adytail != 0.0) {
+      negate = -bdxtail;
+      Two_Product(negate, adytail, bdxt_adyt1, bdxt_adyt0);
+      Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdz, u3, u[2], u[1], u[0]);
+      u[3] = u3;
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                              finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+      if (cdztail != 0.0) {
+        Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdztail, u3, u[2], u[1], u[0]);
+        u[3] = u3;
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                                finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+    }
+  }
+  if (cdxtail != 0.0) {
+    if (adytail != 0.0) {
+      Two_Product(cdxtail, adytail, cdxt_adyt1, cdxt_adyt0);
+      Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdz, u3, u[2], u[1], u[0]);
+      u[3] = u3;
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                              finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+      if (bdztail != 0.0) {
+        Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdztail, u3, u[2], u[1], u[0]);
+        u[3] = u3;
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                                finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+    }
+    if (bdytail != 0.0) {
+      negate = -cdxtail;
+      Two_Product(negate, bdytail, cdxt_bdyt1, cdxt_bdyt0);
+      Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adz, u3, u[2], u[1], u[0]);
+      u[3] = u3;
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                              finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+      if (adztail != 0.0) {
+        Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adztail, u3, u[2], u[1], u[0]);
+        u[3] = u3;
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                                finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+    }
+  }
+
+  if (adztail != 0.0) {
+    wlength = scale_expansion_zeroelim(bctlen, bct, adztail, w);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+                                            finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (bdztail != 0.0) {
+    wlength = scale_expansion_zeroelim(catlen, cat, bdztail, w);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+                                            finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (cdztail != 0.0) {
+    wlength = scale_expansion_zeroelim(abtlen, abt, cdztail, w);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+                                            finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+
+  return finnow[finlength - 1];
+}
+
+REAL orient3d(pa, pb, pc, pd)
+REAL *pa;
+REAL *pb;
+REAL *pc;
+REAL *pd;
+{
+  REAL adx, bdx, cdx, ady, bdy, cdy, adz, bdz, cdz;
+  REAL bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady;
+  REAL det;
+  REAL permanent, errbound;
+  REAL orient;
+
+  FPU_ROUND_DOUBLE;
+
+  adx = pa[0] - pd[0];
+  bdx = pb[0] - pd[0];
+  cdx = pc[0] - pd[0];
+  ady = pa[1] - pd[1];
+  bdy = pb[1] - pd[1];
+  cdy = pc[1] - pd[1];
+  adz = pa[2] - pd[2];
+  bdz = pb[2] - pd[2];
+  cdz = pc[2] - pd[2];
+
+  bdxcdy = bdx * cdy;
+  cdxbdy = cdx * bdy;
+
+  cdxady = cdx * ady;
+  adxcdy = adx * cdy;
+
+  adxbdy = adx * bdy;
+  bdxady = bdx * ady;
+
+  det = adz * (bdxcdy - cdxbdy) 
+      + bdz * (cdxady - adxcdy)
+      + cdz * (adxbdy - bdxady);
+
+  permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * Absolute(adz)
+            + (Absolute(cdxady) + Absolute(adxcdy)) * Absolute(bdz)
+            + (Absolute(adxbdy) + Absolute(bdxady)) * Absolute(cdz);
+  errbound = o3derrboundA * permanent;
+  if ((det > errbound) || (-det > errbound)) {
+    FPU_RESTORE;
+    return det;
+  }
+
+  orient = orient3dadapt(pa, pb, pc, pd, permanent);
+  FPU_RESTORE;
+  return orient;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  incirclefast()   Approximate 2D incircle test.  Nonrobust.               */
+/*  incircleexact()   Exact 2D incircle test.  Robust.                       */
+/*  incircleslow()   Another exact 2D incircle test.  Robust.                */
+/*  incircle()   Adaptive exact 2D incircle test.  Robust.                   */
+/*                                                                           */
+/*               Return a positive value if the point pd lies inside the     */
+/*               circle passing through pa, pb, and pc; a negative value if  */
+/*               it lies outside; and zero if the four points are cocircular.*/
+/*               The points pa, pb, and pc must be in counterclockwise       */
+/*               order, or the sign of the result will be reversed.          */
+/*                                                                           */
+/*  Only the first and last routine should be used; the middle two are for   */
+/*  timings.                                                                 */
+/*                                                                           */
+/*  The last three use exact arithmetic to ensure a correct answer.  The     */
+/*  result returned is the determinant of a matrix.  In incircle() only,     */
+/*  this determinant is computed adaptively, in the sense that exact         */
+/*  arithmetic is used only to the degree it is needed to ensure that the    */
+/*  returned value has the correct sign.  Hence, incircle() is usually quite */
+/*  fast, but will run more slowly when the input points are cocircular or   */
+/*  nearly so.                                                               */
+/*                                                                           */
+/*****************************************************************************/
+
+static REAL incircleadapt(REAL *pa, REAL *pb, REAL *pc, REAL *pd, 
+			  REAL permanent)
+{
+  INEXACT REAL adx, bdx, cdx, ady, bdy, cdy;
+  REAL det, errbound;
+
+  INEXACT REAL bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1;
+  REAL bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0;
+  REAL bc[4], ca[4], ab[4];
+  INEXACT REAL bc3, ca3, ab3;
+  REAL axbc[8], axxbc[16], aybc[8], ayybc[16], adet[32];
+  int axbclen, axxbclen, aybclen, ayybclen, alen;
+  REAL bxca[8], bxxca[16], byca[8], byyca[16], bdet[32];
+  int bxcalen, bxxcalen, bycalen, byycalen, blen;
+  REAL cxab[8], cxxab[16], cyab[8], cyyab[16], cdet[32];
+  int cxablen, cxxablen, cyablen, cyyablen, clen;
+  REAL abdet[64];
+  int ablen;
+  REAL fin1[1152], fin2[1152];
+  REAL *finnow, *finother, *finswap;
+  int finlength;
+
+  REAL adxtail, bdxtail, cdxtail, adytail, bdytail, cdytail;
+  INEXACT REAL adxadx1, adyady1, bdxbdx1, bdybdy1, cdxcdx1, cdycdy1;
+  REAL adxadx0, adyady0, bdxbdx0, bdybdy0, cdxcdx0, cdycdy0;
+  REAL aa[4], bb[4], cc[4];
+  INEXACT REAL aa3, bb3, cc3;
+  INEXACT REAL ti1, tj1;
+  REAL ti0, tj0;
+  REAL u[4], v[4];
+  INEXACT REAL u3, v3;
+  REAL temp8[8], temp16a[16], temp16b[16], temp16c[16];
+  REAL temp32a[32], temp32b[32], temp48[48], temp64[64];
+  int temp8len, temp16alen, temp16blen, temp16clen;
+  int temp32alen, temp32blen, temp48len, temp64len;
+  REAL axtbb[8], axtcc[8], aytbb[8], aytcc[8];
+  int axtbblen, axtcclen, aytbblen, aytcclen;
+  REAL bxtaa[8], bxtcc[8], bytaa[8], bytcc[8];
+  int bxtaalen, bxtcclen, bytaalen, bytcclen;
+  REAL cxtaa[8], cxtbb[8], cytaa[8], cytbb[8];
+  int cxtaalen, cxtbblen, cytaalen, cytbblen;
+  REAL axtbc[8], aytbc[8], bxtca[8], bytca[8], cxtab[8], cytab[8];
+  int axtbclen = 0, aytbclen = 0;
+  int bxtcalen = 0, bytcalen = 0;
+  int cxtablen = 0, cytablen = 0;
+  REAL axtbct[16], aytbct[16], bxtcat[16], bytcat[16], cxtabt[16], cytabt[16];
+  int axtbctlen, aytbctlen, bxtcatlen, bytcatlen, cxtabtlen, cytabtlen;
+  REAL axtbctt[8], aytbctt[8], bxtcatt[8];
+  REAL bytcatt[8], cxtabtt[8], cytabtt[8];
+  int axtbcttlen, aytbcttlen, bxtcattlen, bytcattlen, cxtabttlen, cytabttlen;
+  REAL abt[8], bct[8], cat[8];
+  int abtlen, bctlen, catlen;
+  REAL abtt[4], bctt[4], catt[4];
+  int abttlen, bcttlen, cattlen;
+  INEXACT REAL abtt3, bctt3, catt3;
+  REAL negate;
+
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  INEXACT REAL c;
+  INEXACT REAL abig;
+  REAL ahi, alo, bhi, blo;
+  REAL err1, err2, err3;
+  INEXACT REAL _i, _j;
+  REAL _0;
+
+  adx = (REAL) (pa[0] - pd[0]);
+  bdx = (REAL) (pb[0] - pd[0]);
+  cdx = (REAL) (pc[0] - pd[0]);
+  ady = (REAL) (pa[1] - pd[1]);
+  bdy = (REAL) (pb[1] - pd[1]);
+  cdy = (REAL) (pc[1] - pd[1]);
+
+  Two_Product(bdx, cdy, bdxcdy1, bdxcdy0);
+  Two_Product(cdx, bdy, cdxbdy1, cdxbdy0);
+  Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]);
+  bc[3] = bc3;
+  axbclen = scale_expansion_zeroelim(4, bc, adx, axbc);
+  axxbclen = scale_expansion_zeroelim(axbclen, axbc, adx, axxbc);
+  aybclen = scale_expansion_zeroelim(4, bc, ady, aybc);
+  ayybclen = scale_expansion_zeroelim(aybclen, aybc, ady, ayybc);
+  alen = fast_expansion_sum_zeroelim(axxbclen, axxbc, ayybclen, ayybc, adet);
+
+  Two_Product(cdx, ady, cdxady1, cdxady0);
+  Two_Product(adx, cdy, adxcdy1, adxcdy0);
+  Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]);
+  ca[3] = ca3;
+  bxcalen = scale_expansion_zeroelim(4, ca, bdx, bxca);
+  bxxcalen = scale_expansion_zeroelim(bxcalen, bxca, bdx, bxxca);
+  bycalen = scale_expansion_zeroelim(4, ca, bdy, byca);
+  byycalen = scale_expansion_zeroelim(bycalen, byca, bdy, byyca);
+  blen = fast_expansion_sum_zeroelim(bxxcalen, bxxca, byycalen, byyca, bdet);
+
+  Two_Product(adx, bdy, adxbdy1, adxbdy0);
+  Two_Product(bdx, ady, bdxady1, bdxady0);
+  Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]);
+  ab[3] = ab3;
+  cxablen = scale_expansion_zeroelim(4, ab, cdx, cxab);
+  cxxablen = scale_expansion_zeroelim(cxablen, cxab, cdx, cxxab);
+  cyablen = scale_expansion_zeroelim(4, ab, cdy, cyab);
+  cyyablen = scale_expansion_zeroelim(cyablen, cyab, cdy, cyyab);
+  clen = fast_expansion_sum_zeroelim(cxxablen, cxxab, cyyablen, cyyab, cdet);
+
+  ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
+  finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1);
+
+  det = estimate(finlength, fin1);
+  errbound = iccerrboundB * permanent;
+  if ((det >= errbound) || (-det >= errbound)) {
+    return det;
+  }
+
+  Two_Diff_Tail(pa[0], pd[0], adx, adxtail);
+  Two_Diff_Tail(pa[1], pd[1], ady, adytail);
+  Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail);
+  Two_Diff_Tail(pb[1], pd[1], bdy, bdytail);
+  Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail);
+  Two_Diff_Tail(pc[1], pd[1], cdy, cdytail);
+  if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0)
+      && (adytail == 0.0) && (bdytail == 0.0) && (cdytail == 0.0)) {
+    return det;
+  }
+
+  errbound = iccerrboundC * permanent + resulterrbound * Absolute(det);
+  det += ((adx * adx + ady * ady) * ((bdx * cdytail + cdy * bdxtail)
+                                     - (bdy * cdxtail + cdx * bdytail))
+          + 2.0 * (adx * adxtail + ady * adytail) * (bdx * cdy - bdy * cdx))
+       + ((bdx * bdx + bdy * bdy) * ((cdx * adytail + ady * cdxtail)
+                                     - (cdy * adxtail + adx * cdytail))
+          + 2.0 * (bdx * bdxtail + bdy * bdytail) * (cdx * ady - cdy * adx))
+       + ((cdx * cdx + cdy * cdy) * ((adx * bdytail + bdy * adxtail)
+                                     - (ady * bdxtail + bdx * adytail))
+          + 2.0 * (cdx * cdxtail + cdy * cdytail) * (adx * bdy - ady * bdx));
+  if ((det >= errbound) || (-det >= errbound)) {
+    return det;
+  }
+
+  finnow = fin1;
+  finother = fin2;
+
+  if ((bdxtail != 0.0) || (bdytail != 0.0)
+      || (cdxtail != 0.0) || (cdytail != 0.0)) {
+    Square(adx, adxadx1, adxadx0);
+    Square(ady, adyady1, adyady0);
+    Two_Two_Sum(adxadx1, adxadx0, adyady1, adyady0, aa3, aa[2], aa[1], aa[0]);
+    aa[3] = aa3;
+  }
+  if ((cdxtail != 0.0) || (cdytail != 0.0)
+      || (adxtail != 0.0) || (adytail != 0.0)) {
+    Square(bdx, bdxbdx1, bdxbdx0);
+    Square(bdy, bdybdy1, bdybdy0);
+    Two_Two_Sum(bdxbdx1, bdxbdx0, bdybdy1, bdybdy0, bb3, bb[2], bb[1], bb[0]);
+    bb[3] = bb3;
+  }
+  if ((adxtail != 0.0) || (adytail != 0.0)
+      || (bdxtail != 0.0) || (bdytail != 0.0)) {
+    Square(cdx, cdxcdx1, cdxcdx0);
+    Square(cdy, cdycdy1, cdycdy0);
+    Two_Two_Sum(cdxcdx1, cdxcdx0, cdycdy1, cdycdy0, cc3, cc[2], cc[1], cc[0]);
+    cc[3] = cc3;
+  }
+
+  if (adxtail != 0.0) {
+    axtbclen = scale_expansion_zeroelim(4, bc, adxtail, axtbc);
+    temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, 2.0 * adx,
+                                          temp16a);
+
+    axtcclen = scale_expansion_zeroelim(4, cc, adxtail, axtcc);
+    temp16blen = scale_expansion_zeroelim(axtcclen, axtcc, bdy, temp16b);
+
+    axtbblen = scale_expansion_zeroelim(4, bb, adxtail, axtbb);
+    temp16clen = scale_expansion_zeroelim(axtbblen, axtbb, -cdy, temp16c);
+
+    temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                            temp16blen, temp16b, temp32a);
+    temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
+                                            temp32alen, temp32a, temp48);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                            temp48, finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (adytail != 0.0) {
+    aytbclen = scale_expansion_zeroelim(4, bc, adytail, aytbc);
+    temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, 2.0 * ady,
+                                          temp16a);
+
+    aytbblen = scale_expansion_zeroelim(4, bb, adytail, aytbb);
+    temp16blen = scale_expansion_zeroelim(aytbblen, aytbb, cdx, temp16b);
+
+    aytcclen = scale_expansion_zeroelim(4, cc, adytail, aytcc);
+    temp16clen = scale_expansion_zeroelim(aytcclen, aytcc, -bdx, temp16c);
+
+    temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                            temp16blen, temp16b, temp32a);
+    temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
+                                            temp32alen, temp32a, temp48);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                            temp48, finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (bdxtail != 0.0) {
+    bxtcalen = scale_expansion_zeroelim(4, ca, bdxtail, bxtca);
+    temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, 2.0 * bdx,
+                                          temp16a);
+
+    bxtaalen = scale_expansion_zeroelim(4, aa, bdxtail, bxtaa);
+    temp16blen = scale_expansion_zeroelim(bxtaalen, bxtaa, cdy, temp16b);
+
+    bxtcclen = scale_expansion_zeroelim(4, cc, bdxtail, bxtcc);
+    temp16clen = scale_expansion_zeroelim(bxtcclen, bxtcc, -ady, temp16c);
+
+    temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                            temp16blen, temp16b, temp32a);
+    temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
+                                            temp32alen, temp32a, temp48);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                            temp48, finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (bdytail != 0.0) {
+    bytcalen = scale_expansion_zeroelim(4, ca, bdytail, bytca);
+    temp16alen = scale_expansion_zeroelim(bytcalen, bytca, 2.0 * bdy,
+                                          temp16a);
+
+    bytcclen = scale_expansion_zeroelim(4, cc, bdytail, bytcc);
+    temp16blen = scale_expansion_zeroelim(bytcclen, bytcc, adx, temp16b);
+
+    bytaalen = scale_expansion_zeroelim(4, aa, bdytail, bytaa);
+    temp16clen = scale_expansion_zeroelim(bytaalen, bytaa, -cdx, temp16c);
+
+    temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                            temp16blen, temp16b, temp32a);
+    temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
+                                            temp32alen, temp32a, temp48);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                            temp48, finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (cdxtail != 0.0) {
+    cxtablen = scale_expansion_zeroelim(4, ab, cdxtail, cxtab);
+    temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, 2.0 * cdx,
+                                          temp16a);
+
+    cxtbblen = scale_expansion_zeroelim(4, bb, cdxtail, cxtbb);
+    temp16blen = scale_expansion_zeroelim(cxtbblen, cxtbb, ady, temp16b);
+
+    cxtaalen = scale_expansion_zeroelim(4, aa, cdxtail, cxtaa);
+    temp16clen = scale_expansion_zeroelim(cxtaalen, cxtaa, -bdy, temp16c);
+
+    temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                            temp16blen, temp16b, temp32a);
+    temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
+                                            temp32alen, temp32a, temp48);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                            temp48, finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (cdytail != 0.0) {
+    cytablen = scale_expansion_zeroelim(4, ab, cdytail, cytab);
+    temp16alen = scale_expansion_zeroelim(cytablen, cytab, 2.0 * cdy,
+                                          temp16a);
+
+    cytaalen = scale_expansion_zeroelim(4, aa, cdytail, cytaa);
+    temp16blen = scale_expansion_zeroelim(cytaalen, cytaa, bdx, temp16b);
+
+    cytbblen = scale_expansion_zeroelim(4, bb, cdytail, cytbb);
+    temp16clen = scale_expansion_zeroelim(cytbblen, cytbb, -adx, temp16c);
+
+    temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                            temp16blen, temp16b, temp32a);
+    temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
+                                            temp32alen, temp32a, temp48);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                            temp48, finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+
+  if ((adxtail != 0.0) || (adytail != 0.0)) {
+    if ((bdxtail != 0.0) || (bdytail != 0.0)
+        || (cdxtail != 0.0) || (cdytail != 0.0)) {
+      Two_Product(bdxtail, cdy, ti1, ti0);
+      Two_Product(bdx, cdytail, tj1, tj0);
+      Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]);
+      u[3] = u3;
+      negate = -bdy;
+      Two_Product(cdxtail, negate, ti1, ti0);
+      negate = -bdytail;
+      Two_Product(cdx, negate, tj1, tj0);
+      Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]);
+      v[3] = v3;
+      bctlen = fast_expansion_sum_zeroelim(4, u, 4, v, bct);
+
+      Two_Product(bdxtail, cdytail, ti1, ti0);
+      Two_Product(cdxtail, bdytail, tj1, tj0);
+      Two_Two_Diff(ti1, ti0, tj1, tj0, bctt3, bctt[2], bctt[1], bctt[0]);
+      bctt[3] = bctt3;
+      bcttlen = 4;
+    } else {
+      bct[0] = 0.0;
+      bctlen = 1;
+      bctt[0] = 0.0;
+      bcttlen = 1;
+    }
+
+    if (adxtail != 0.0) {
+      temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, adxtail, temp16a);
+      axtbctlen = scale_expansion_zeroelim(bctlen, bct, adxtail, axtbct);
+      temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, 2.0 * adx,
+                                            temp32a);
+      temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp32alen, temp32a, temp48);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                              temp48, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+      if (bdytail != 0.0) {
+        temp8len = scale_expansion_zeroelim(4, cc, adxtail, temp8);
+        temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail,
+                                              temp16a);
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
+                                                temp16a, finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+      if (cdytail != 0.0) {
+        temp8len = scale_expansion_zeroelim(4, bb, -adxtail, temp8);
+        temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail,
+                                              temp16a);
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
+                                                temp16a, finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+
+      temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, adxtail,
+                                            temp32a);
+      axtbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adxtail, axtbctt);
+      temp16alen = scale_expansion_zeroelim(axtbcttlen, axtbctt, 2.0 * adx,
+                                            temp16a);
+      temp16blen = scale_expansion_zeroelim(axtbcttlen, axtbctt, adxtail,
+                                            temp16b);
+      temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp16blen, temp16b, temp32b);
+      temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                              temp32blen, temp32b, temp64);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
+                                              temp64, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+    }
+    if (adytail != 0.0) {
+      temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, adytail, temp16a);
+      aytbctlen = scale_expansion_zeroelim(bctlen, bct, adytail, aytbct);
+      temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, 2.0 * ady,
+                                            temp32a);
+      temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp32alen, temp32a, temp48);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                              temp48, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+
+
+      temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, adytail,
+                                            temp32a);
+      aytbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adytail, aytbctt);
+      temp16alen = scale_expansion_zeroelim(aytbcttlen, aytbctt, 2.0 * ady,
+                                            temp16a);
+      temp16blen = scale_expansion_zeroelim(aytbcttlen, aytbctt, adytail,
+                                            temp16b);
+      temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp16blen, temp16b, temp32b);
+      temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                              temp32blen, temp32b, temp64);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
+                                              temp64, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+    }
+  }
+  if ((bdxtail != 0.0) || (bdytail != 0.0)) {
+    if ((cdxtail != 0.0) || (cdytail != 0.0)
+        || (adxtail != 0.0) || (adytail != 0.0)) {
+      Two_Product(cdxtail, ady, ti1, ti0);
+      Two_Product(cdx, adytail, tj1, tj0);
+      Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]);
+      u[3] = u3;
+      negate = -cdy;
+      Two_Product(adxtail, negate, ti1, ti0);
+      negate = -cdytail;
+      Two_Product(adx, negate, tj1, tj0);
+      Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]);
+      v[3] = v3;
+      catlen = fast_expansion_sum_zeroelim(4, u, 4, v, cat);
+
+      Two_Product(cdxtail, adytail, ti1, ti0);
+      Two_Product(adxtail, cdytail, tj1, tj0);
+      Two_Two_Diff(ti1, ti0, tj1, tj0, catt3, catt[2], catt[1], catt[0]);
+      catt[3] = catt3;
+      cattlen = 4;
+    } else {
+      cat[0] = 0.0;
+      catlen = 1;
+      catt[0] = 0.0;
+      cattlen = 1;
+    }
+
+    if (bdxtail != 0.0) {
+      temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, bdxtail, temp16a);
+      bxtcatlen = scale_expansion_zeroelim(catlen, cat, bdxtail, bxtcat);
+      temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, 2.0 * bdx,
+                                            temp32a);
+      temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp32alen, temp32a, temp48);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                              temp48, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+      if (cdytail != 0.0) {
+        temp8len = scale_expansion_zeroelim(4, aa, bdxtail, temp8);
+        temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail,
+                                              temp16a);
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
+                                                temp16a, finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+      if (adytail != 0.0) {
+        temp8len = scale_expansion_zeroelim(4, cc, -bdxtail, temp8);
+        temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail,
+                                              temp16a);
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
+                                                temp16a, finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+
+      temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, bdxtail,
+                                            temp32a);
+      bxtcattlen = scale_expansion_zeroelim(cattlen, catt, bdxtail, bxtcatt);
+      temp16alen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, 2.0 * bdx,
+                                            temp16a);
+      temp16blen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, bdxtail,
+                                            temp16b);
+      temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp16blen, temp16b, temp32b);
+      temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                              temp32blen, temp32b, temp64);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
+                                              temp64, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+    }
+    if (bdytail != 0.0) {
+      temp16alen = scale_expansion_zeroelim(bytcalen, bytca, bdytail, temp16a);
+      bytcatlen = scale_expansion_zeroelim(catlen, cat, bdytail, bytcat);
+      temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, 2.0 * bdy,
+                                            temp32a);
+      temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp32alen, temp32a, temp48);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                              temp48, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+
+
+      temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, bdytail,
+                                            temp32a);
+      bytcattlen = scale_expansion_zeroelim(cattlen, catt, bdytail, bytcatt);
+      temp16alen = scale_expansion_zeroelim(bytcattlen, bytcatt, 2.0 * bdy,
+                                            temp16a);
+      temp16blen = scale_expansion_zeroelim(bytcattlen, bytcatt, bdytail,
+                                            temp16b);
+      temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp16blen, temp16b, temp32b);
+      temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                              temp32blen, temp32b, temp64);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
+                                              temp64, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+    }
+  }
+  if ((cdxtail != 0.0) || (cdytail != 0.0)) {
+    if ((adxtail != 0.0) || (adytail != 0.0)
+        || (bdxtail != 0.0) || (bdytail != 0.0)) {
+      Two_Product(adxtail, bdy, ti1, ti0);
+      Two_Product(adx, bdytail, tj1, tj0);
+      Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]);
+      u[3] = u3;
+      negate = -ady;
+      Two_Product(bdxtail, negate, ti1, ti0);
+      negate = -adytail;
+      Two_Product(bdx, negate, tj1, tj0);
+      Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]);
+      v[3] = v3;
+      abtlen = fast_expansion_sum_zeroelim(4, u, 4, v, abt);
+
+      Two_Product(adxtail, bdytail, ti1, ti0);
+      Two_Product(bdxtail, adytail, tj1, tj0);
+      Two_Two_Diff(ti1, ti0, tj1, tj0, abtt3, abtt[2], abtt[1], abtt[0]);
+      abtt[3] = abtt3;
+      abttlen = 4;
+    } else {
+      abt[0] = 0.0;
+      abtlen = 1;
+      abtt[0] = 0.0;
+      abttlen = 1;
+    }
+
+    if (cdxtail != 0.0) {
+      temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, cdxtail, temp16a);
+      cxtabtlen = scale_expansion_zeroelim(abtlen, abt, cdxtail, cxtabt);
+      temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, 2.0 * cdx,
+                                            temp32a);
+      temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp32alen, temp32a, temp48);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                              temp48, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+      if (adytail != 0.0) {
+        temp8len = scale_expansion_zeroelim(4, bb, cdxtail, temp8);
+        temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail,
+                                              temp16a);
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
+                                                temp16a, finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+      if (bdytail != 0.0) {
+        temp8len = scale_expansion_zeroelim(4, aa, -cdxtail, temp8);
+        temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail,
+                                              temp16a);
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
+                                                temp16a, finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+
+      temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, cdxtail,
+                                            temp32a);
+      cxtabttlen = scale_expansion_zeroelim(abttlen, abtt, cdxtail, cxtabtt);
+      temp16alen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, 2.0 * cdx,
+                                            temp16a);
+      temp16blen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, cdxtail,
+                                            temp16b);
+      temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp16blen, temp16b, temp32b);
+      temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                              temp32blen, temp32b, temp64);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
+                                              temp64, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+    }
+    if (cdytail != 0.0) {
+      temp16alen = scale_expansion_zeroelim(cytablen, cytab, cdytail, temp16a);
+      cytabtlen = scale_expansion_zeroelim(abtlen, abt, cdytail, cytabt);
+      temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, 2.0 * cdy,
+                                            temp32a);
+      temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp32alen, temp32a, temp48);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                              temp48, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+
+
+      temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, cdytail,
+                                            temp32a);
+      cytabttlen = scale_expansion_zeroelim(abttlen, abtt, cdytail, cytabtt);
+      temp16alen = scale_expansion_zeroelim(cytabttlen, cytabtt, 2.0 * cdy,
+                                            temp16a);
+      temp16blen = scale_expansion_zeroelim(cytabttlen, cytabtt, cdytail,
+                                            temp16b);
+      temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp16blen, temp16b, temp32b);
+      temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                              temp32blen, temp32b, temp64);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
+                                              temp64, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+    }
+  }
+
+  return finnow[finlength - 1];
+}
+
+REAL incircle(pa, pb, pc, pd)
+REAL *pa;
+REAL *pb;
+REAL *pc;
+REAL *pd;
+{
+  REAL adx, bdx, cdx, ady, bdy, cdy;
+  REAL bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady;
+  REAL alift, blift, clift;
+  REAL det;
+  REAL permanent, errbound;
+  REAL inc;
+
+  FPU_ROUND_DOUBLE;
+  
+  adx = pa[0] - pd[0];
+  bdx = pb[0] - pd[0];
+  cdx = pc[0] - pd[0];
+  ady = pa[1] - pd[1];
+  bdy = pb[1] - pd[1];
+  cdy = pc[1] - pd[1];
+
+  bdxcdy = bdx * cdy;
+  cdxbdy = cdx * bdy;
+  alift = adx * adx + ady * ady;
+
+  cdxady = cdx * ady;
+  adxcdy = adx * cdy;
+  blift = bdx * bdx + bdy * bdy;
+
+  adxbdy = adx * bdy;
+  bdxady = bdx * ady;
+  clift = cdx * cdx + cdy * cdy;
+
+  det = alift * (bdxcdy - cdxbdy)
+      + blift * (cdxady - adxcdy)
+      + clift * (adxbdy - bdxady);
+
+  permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * alift
+            + (Absolute(cdxady) + Absolute(adxcdy)) * blift
+            + (Absolute(adxbdy) + Absolute(bdxady)) * clift;
+  errbound = iccerrboundA * permanent;
+  if ((det > errbound) || (-det > errbound)) {
+    FPU_RESTORE;
+    return det;
+  }
+
+  inc = incircleadapt(pa, pb, pc, pd, permanent);
+  FPU_RESTORE;
+  return inc;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  inspherefast()   Approximate 3D insphere test.  Nonrobust.               */
+/*  insphereexact()   Exact 3D insphere test.  Robust.                       */
+/*  insphereslow()   Another exact 3D insphere test.  Robust.                */
+/*  insphere()   Adaptive exact 3D insphere test.  Robust.                   */
+/*                                                                           */
+/*               Return a positive value if the point pe lies inside the     */
+/*               sphere passing through pa, pb, pc, and pd; a negative value */
+/*               if it lies outside; and zero if the five points are         */
+/*               cospherical.  The points pa, pb, pc, and pd must be ordered */
+/*               so that they have a positive orientation (as defined by     */
+/*               orient3d()), or the sign of the result will be reversed.    */
+/*                                                                           */
+/*  Only the first and last routine should be used; the middle two are for   */
+/*  timings.                                                                 */
+/*                                                                           */
+/*  The last three use exact arithmetic to ensure a correct answer.  The     */
+/*  result returned is the determinant of a matrix.  In insphere() only,     */
+/*  this determinant is computed adaptively, in the sense that exact         */
+/*  arithmetic is used only to the degree it is needed to ensure that the    */
+/*  returned value has the correct sign.  Hence, insphere() is usually quite */
+/*  fast, but will run more slowly when the input points are cospherical or  */
+/*  nearly so.                                                               */
+/*                                                                           */
+/*****************************************************************************/
+
+static REAL insphereexact(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe)
+{
+  INEXACT REAL axby1, bxcy1, cxdy1, dxey1, exay1;
+  INEXACT REAL bxay1, cxby1, dxcy1, exdy1, axey1;
+  INEXACT REAL axcy1, bxdy1, cxey1, dxay1, exby1;
+  INEXACT REAL cxay1, dxby1, excy1, axdy1, bxey1;
+  REAL axby0, bxcy0, cxdy0, dxey0, exay0;
+  REAL bxay0, cxby0, dxcy0, exdy0, axey0;
+  REAL axcy0, bxdy0, cxey0, dxay0, exby0;
+  REAL cxay0, dxby0, excy0, axdy0, bxey0;
+  REAL ab[4], bc[4], cd[4], de[4], ea[4];
+  REAL ac[4], bd[4], ce[4], da[4], eb[4];
+  REAL temp8a[8], temp8b[8], temp16[16];
+  int temp8alen, temp8blen, temp16len;
+  REAL abc[24], bcd[24], cde[24], dea[24], eab[24];
+  REAL abd[24], bce[24], cda[24], deb[24], eac[24];
+  int abclen, bcdlen, cdelen, dealen, eablen;
+  int abdlen, bcelen, cdalen, deblen, eaclen;
+  REAL temp48a[48], temp48b[48];
+  int temp48alen, temp48blen;
+  REAL abcd[96], bcde[96], cdea[96], deab[96], eabc[96];
+  int abcdlen, bcdelen, cdealen, deablen, eabclen;
+  REAL temp192[192];
+  REAL det384x[384], det384y[384], det384z[384];
+  int xlen, ylen, zlen;
+  REAL detxy[768];
+  int xylen;
+  REAL adet[1152], bdet[1152], cdet[1152], ddet[1152], edet[1152];
+  int alen, blen, clen, dlen, elen;
+  REAL abdet[2304], cddet[2304], cdedet[3456];
+  int ablen, cdlen;
+  REAL deter[5760];
+  int deterlen;
+  int i;
+
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  INEXACT REAL c;
+  INEXACT REAL abig;
+  REAL ahi, alo, bhi, blo;
+  REAL err1, err2, err3;
+  INEXACT REAL _i, _j;
+  REAL _0;
+
+  Two_Product(pa[0], pb[1], axby1, axby0);
+  Two_Product(pb[0], pa[1], bxay1, bxay0);
+  Two_Two_Diff(axby1, axby0, bxay1, bxay0, ab[3], ab[2], ab[1], ab[0]);
+
+  Two_Product(pb[0], pc[1], bxcy1, bxcy0);
+  Two_Product(pc[0], pb[1], cxby1, cxby0);
+  Two_Two_Diff(bxcy1, bxcy0, cxby1, cxby0, bc[3], bc[2], bc[1], bc[0]);
+
+  Two_Product(pc[0], pd[1], cxdy1, cxdy0);
+  Two_Product(pd[0], pc[1], dxcy1, dxcy0);
+  Two_Two_Diff(cxdy1, cxdy0, dxcy1, dxcy0, cd[3], cd[2], cd[1], cd[0]);
+
+  Two_Product(pd[0], pe[1], dxey1, dxey0);
+  Two_Product(pe[0], pd[1], exdy1, exdy0);
+  Two_Two_Diff(dxey1, dxey0, exdy1, exdy0, de[3], de[2], de[1], de[0]);
+
+  Two_Product(pe[0], pa[1], exay1, exay0);
+  Two_Product(pa[0], pe[1], axey1, axey0);
+  Two_Two_Diff(exay1, exay0, axey1, axey0, ea[3], ea[2], ea[1], ea[0]);
+
+  Two_Product(pa[0], pc[1], axcy1, axcy0);
+  Two_Product(pc[0], pa[1], cxay1, cxay0);
+  Two_Two_Diff(axcy1, axcy0, cxay1, cxay0, ac[3], ac[2], ac[1], ac[0]);
+
+  Two_Product(pb[0], pd[1], bxdy1, bxdy0);
+  Two_Product(pd[0], pb[1], dxby1, dxby0);
+  Two_Two_Diff(bxdy1, bxdy0, dxby1, dxby0, bd[3], bd[2], bd[1], bd[0]);
+
+  Two_Product(pc[0], pe[1], cxey1, cxey0);
+  Two_Product(pe[0], pc[1], excy1, excy0);
+  Two_Two_Diff(cxey1, cxey0, excy1, excy0, ce[3], ce[2], ce[1], ce[0]);
+
+  Two_Product(pd[0], pa[1], dxay1, dxay0);
+  Two_Product(pa[0], pd[1], axdy1, axdy0);
+  Two_Two_Diff(dxay1, dxay0, axdy1, axdy0, da[3], da[2], da[1], da[0]);
+
+  Two_Product(pe[0], pb[1], exby1, exby0);
+  Two_Product(pb[0], pe[1], bxey1, bxey0);
+  Two_Two_Diff(exby1, exby0, bxey1, bxey0, eb[3], eb[2], eb[1], eb[0]);
+
+  temp8alen = scale_expansion_zeroelim(4, bc, pa[2], temp8a);
+  temp8blen = scale_expansion_zeroelim(4, ac, -pb[2], temp8b);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
+                                          temp16);
+  temp8alen = scale_expansion_zeroelim(4, ab, pc[2], temp8a);
+  abclen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
+                                       abc);
+
+  temp8alen = scale_expansion_zeroelim(4, cd, pb[2], temp8a);
+  temp8blen = scale_expansion_zeroelim(4, bd, -pc[2], temp8b);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
+                                          temp16);
+  temp8alen = scale_expansion_zeroelim(4, bc, pd[2], temp8a);
+  bcdlen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
+                                       bcd);
+
+  temp8alen = scale_expansion_zeroelim(4, de, pc[2], temp8a);
+  temp8blen = scale_expansion_zeroelim(4, ce, -pd[2], temp8b);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
+                                          temp16);
+  temp8alen = scale_expansion_zeroelim(4, cd, pe[2], temp8a);
+  cdelen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
+                                       cde);
+
+  temp8alen = scale_expansion_zeroelim(4, ea, pd[2], temp8a);
+  temp8blen = scale_expansion_zeroelim(4, da, -pe[2], temp8b);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
+                                          temp16);
+  temp8alen = scale_expansion_zeroelim(4, de, pa[2], temp8a);
+  dealen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
+                                       dea);
+
+  temp8alen = scale_expansion_zeroelim(4, ab, pe[2], temp8a);
+  temp8blen = scale_expansion_zeroelim(4, eb, -pa[2], temp8b);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
+                                          temp16);
+  temp8alen = scale_expansion_zeroelim(4, ea, pb[2], temp8a);
+  eablen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
+                                       eab);
+
+  temp8alen = scale_expansion_zeroelim(4, bd, pa[2], temp8a);
+  temp8blen = scale_expansion_zeroelim(4, da, pb[2], temp8b);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
+                                          temp16);
+  temp8alen = scale_expansion_zeroelim(4, ab, pd[2], temp8a);
+  abdlen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
+                                       abd);
+
+  temp8alen = scale_expansion_zeroelim(4, ce, pb[2], temp8a);
+  temp8blen = scale_expansion_zeroelim(4, eb, pc[2], temp8b);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
+                                          temp16);
+  temp8alen = scale_expansion_zeroelim(4, bc, pe[2], temp8a);
+  bcelen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
+                                       bce);
+
+  temp8alen = scale_expansion_zeroelim(4, da, pc[2], temp8a);
+  temp8blen = scale_expansion_zeroelim(4, ac, pd[2], temp8b);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
+                                          temp16);
+  temp8alen = scale_expansion_zeroelim(4, cd, pa[2], temp8a);
+  cdalen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
+                                       cda);
+
+  temp8alen = scale_expansion_zeroelim(4, eb, pd[2], temp8a);
+  temp8blen = scale_expansion_zeroelim(4, bd, pe[2], temp8b);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
+                                          temp16);
+  temp8alen = scale_expansion_zeroelim(4, de, pb[2], temp8a);
+  deblen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
+                                       deb);
+
+  temp8alen = scale_expansion_zeroelim(4, ac, pe[2], temp8a);
+  temp8blen = scale_expansion_zeroelim(4, ce, pa[2], temp8b);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
+                                          temp16);
+  temp8alen = scale_expansion_zeroelim(4, ea, pc[2], temp8a);
+  eaclen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
+                                       eac);
+
+  temp48alen = fast_expansion_sum_zeroelim(cdelen, cde, bcelen, bce, temp48a);
+  temp48blen = fast_expansion_sum_zeroelim(deblen, deb, bcdlen, bcd, temp48b);
+  for (i = 0; i < temp48blen; i++) {
+    temp48b[i] = -temp48b[i];
+  }
+  bcdelen = fast_expansion_sum_zeroelim(temp48alen, temp48a,
+                                        temp48blen, temp48b, bcde);
+  xlen = scale_expansion_zeroelim(bcdelen, bcde, pa[0], temp192);
+  xlen = scale_expansion_zeroelim(xlen, temp192, pa[0], det384x);
+  ylen = scale_expansion_zeroelim(bcdelen, bcde, pa[1], temp192);
+  ylen = scale_expansion_zeroelim(ylen, temp192, pa[1], det384y);
+  zlen = scale_expansion_zeroelim(bcdelen, bcde, pa[2], temp192);
+  zlen = scale_expansion_zeroelim(zlen, temp192, pa[2], det384z);
+  xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy);
+  alen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, adet);
+
+  temp48alen = fast_expansion_sum_zeroelim(dealen, dea, cdalen, cda, temp48a);
+  temp48blen = fast_expansion_sum_zeroelim(eaclen, eac, cdelen, cde, temp48b);
+  for (i = 0; i < temp48blen; i++) {
+    temp48b[i] = -temp48b[i];
+  }
+  cdealen = fast_expansion_sum_zeroelim(temp48alen, temp48a,
+                                        temp48blen, temp48b, cdea);
+  xlen = scale_expansion_zeroelim(cdealen, cdea, pb[0], temp192);
+  xlen = scale_expansion_zeroelim(xlen, temp192, pb[0], det384x);
+  ylen = scale_expansion_zeroelim(cdealen, cdea, pb[1], temp192);
+  ylen = scale_expansion_zeroelim(ylen, temp192, pb[1], det384y);
+  zlen = scale_expansion_zeroelim(cdealen, cdea, pb[2], temp192);
+  zlen = scale_expansion_zeroelim(zlen, temp192, pb[2], det384z);
+  xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy);
+  blen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, bdet);
+
+  temp48alen = fast_expansion_sum_zeroelim(eablen, eab, deblen, deb, temp48a);
+  temp48blen = fast_expansion_sum_zeroelim(abdlen, abd, dealen, dea, temp48b);
+  for (i = 0; i < temp48blen; i++) {
+    temp48b[i] = -temp48b[i];
+  }
+  deablen = fast_expansion_sum_zeroelim(temp48alen, temp48a,
+                                        temp48blen, temp48b, deab);
+  xlen = scale_expansion_zeroelim(deablen, deab, pc[0], temp192);
+  xlen = scale_expansion_zeroelim(xlen, temp192, pc[0], det384x);
+  ylen = scale_expansion_zeroelim(deablen, deab, pc[1], temp192);
+  ylen = scale_expansion_zeroelim(ylen, temp192, pc[1], det384y);
+  zlen = scale_expansion_zeroelim(deablen, deab, pc[2], temp192);
+  zlen = scale_expansion_zeroelim(zlen, temp192, pc[2], det384z);
+  xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy);
+  clen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, cdet);
+
+  temp48alen = fast_expansion_sum_zeroelim(abclen, abc, eaclen, eac, temp48a);
+  temp48blen = fast_expansion_sum_zeroelim(bcelen, bce, eablen, eab, temp48b);
+  for (i = 0; i < temp48blen; i++) {
+    temp48b[i] = -temp48b[i];
+  }
+  eabclen = fast_expansion_sum_zeroelim(temp48alen, temp48a,
+                                        temp48blen, temp48b, eabc);
+  xlen = scale_expansion_zeroelim(eabclen, eabc, pd[0], temp192);
+  xlen = scale_expansion_zeroelim(xlen, temp192, pd[0], det384x);
+  ylen = scale_expansion_zeroelim(eabclen, eabc, pd[1], temp192);
+  ylen = scale_expansion_zeroelim(ylen, temp192, pd[1], det384y);
+  zlen = scale_expansion_zeroelim(eabclen, eabc, pd[2], temp192);
+  zlen = scale_expansion_zeroelim(zlen, temp192, pd[2], det384z);
+  xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy);
+  dlen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, ddet);
+
+  temp48alen = fast_expansion_sum_zeroelim(bcdlen, bcd, abdlen, abd, temp48a);
+  temp48blen = fast_expansion_sum_zeroelim(cdalen, cda, abclen, abc, temp48b);
+  for (i = 0; i < temp48blen; i++) {
+    temp48b[i] = -temp48b[i];
+  }
+  abcdlen = fast_expansion_sum_zeroelim(temp48alen, temp48a,
+                                        temp48blen, temp48b, abcd);
+  xlen = scale_expansion_zeroelim(abcdlen, abcd, pe[0], temp192);
+  xlen = scale_expansion_zeroelim(xlen, temp192, pe[0], det384x);
+  ylen = scale_expansion_zeroelim(abcdlen, abcd, pe[1], temp192);
+  ylen = scale_expansion_zeroelim(ylen, temp192, pe[1], det384y);
+  zlen = scale_expansion_zeroelim(abcdlen, abcd, pe[2], temp192);
+  zlen = scale_expansion_zeroelim(zlen, temp192, pe[2], det384z);
+  xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy);
+  elen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, edet);
+
+  ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
+  cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet);
+  cdelen = fast_expansion_sum_zeroelim(cdlen, cddet, elen, edet, cdedet);
+  deterlen = fast_expansion_sum_zeroelim(ablen, abdet, cdelen, cdedet, deter);
+
+  return deter[deterlen - 1];
+}
+
+static REAL insphereadapt(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe, 
+			  REAL permanent)
+{
+  INEXACT REAL aex, bex, cex, dex, aey, bey, cey, dey, aez, bez, cez, dez;
+  REAL det, errbound;
+
+  INEXACT REAL aexbey1, bexaey1, bexcey1, cexbey1;
+  INEXACT REAL cexdey1, dexcey1, dexaey1, aexdey1;
+  INEXACT REAL aexcey1, cexaey1, bexdey1, dexbey1;
+  REAL aexbey0, bexaey0, bexcey0, cexbey0;
+  REAL cexdey0, dexcey0, dexaey0, aexdey0;
+  REAL aexcey0, cexaey0, bexdey0, dexbey0;
+  REAL ab[4], bc[4], cd[4], da[4], ac[4], bd[4];
+  INEXACT REAL ab3, bc3, cd3, da3, ac3, bd3;
+  REAL abeps, bceps, cdeps, daeps, aceps, bdeps;
+  REAL temp8a[8], temp8b[8], temp8c[8], temp16[16], temp24[24], temp48[48];
+  int temp8alen, temp8blen, temp8clen, temp16len, temp24len, temp48len;
+  REAL xdet[96], ydet[96], zdet[96], xydet[192];
+  int xlen, ylen, zlen, xylen;
+  REAL adet[288], bdet[288], cdet[288], ddet[288];
+  int alen, blen, clen, dlen;
+  REAL abdet[576], cddet[576];
+  int ablen, cdlen;
+  REAL fin1[1152];
+  int finlength;
+
+  REAL aextail, bextail, cextail, dextail;
+  REAL aeytail, beytail, ceytail, deytail;
+  REAL aeztail, beztail, ceztail, deztail;
+
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  INEXACT REAL c;
+  INEXACT REAL abig;
+  REAL ahi, alo, bhi, blo;
+  REAL err1, err2, err3;
+  INEXACT REAL _i, _j;
+  REAL _0;
+
+  aex = (REAL) (pa[0] - pe[0]);
+  bex = (REAL) (pb[0] - pe[0]);
+  cex = (REAL) (pc[0] - pe[0]);
+  dex = (REAL) (pd[0] - pe[0]);
+  aey = (REAL) (pa[1] - pe[1]);
+  bey = (REAL) (pb[1] - pe[1]);
+  cey = (REAL) (pc[1] - pe[1]);
+  dey = (REAL) (pd[1] - pe[1]);
+  aez = (REAL) (pa[2] - pe[2]);
+  bez = (REAL) (pb[2] - pe[2]);
+  cez = (REAL) (pc[2] - pe[2]);
+  dez = (REAL) (pd[2] - pe[2]);
+
+  Two_Product(aex, bey, aexbey1, aexbey0);
+  Two_Product(bex, aey, bexaey1, bexaey0);
+  Two_Two_Diff(aexbey1, aexbey0, bexaey1, bexaey0, ab3, ab[2], ab[1], ab[0]);
+  ab[3] = ab3;
+
+  Two_Product(bex, cey, bexcey1, bexcey0);
+  Two_Product(cex, bey, cexbey1, cexbey0);
+  Two_Two_Diff(bexcey1, bexcey0, cexbey1, cexbey0, bc3, bc[2], bc[1], bc[0]);
+  bc[3] = bc3;
+
+  Two_Product(cex, dey, cexdey1, cexdey0);
+  Two_Product(dex, cey, dexcey1, dexcey0);
+  Two_Two_Diff(cexdey1, cexdey0, dexcey1, dexcey0, cd3, cd[2], cd[1], cd[0]);
+  cd[3] = cd3;
+
+  Two_Product(dex, aey, dexaey1, dexaey0);
+  Two_Product(aex, dey, aexdey1, aexdey0);
+  Two_Two_Diff(dexaey1, dexaey0, aexdey1, aexdey0, da3, da[2], da[1], da[0]);
+  da[3] = da3;
+
+  Two_Product(aex, cey, aexcey1, aexcey0);
+  Two_Product(cex, aey, cexaey1, cexaey0);
+  Two_Two_Diff(aexcey1, aexcey0, cexaey1, cexaey0, ac3, ac[2], ac[1], ac[0]);
+  ac[3] = ac3;
+
+  Two_Product(bex, dey, bexdey1, bexdey0);
+  Two_Product(dex, bey, dexbey1, dexbey0);
+  Two_Two_Diff(bexdey1, bexdey0, dexbey1, dexbey0, bd3, bd[2], bd[1], bd[0]);
+  bd[3] = bd3;
+
+  temp8alen = scale_expansion_zeroelim(4, cd, bez, temp8a);
+  temp8blen = scale_expansion_zeroelim(4, bd, -cez, temp8b);
+  temp8clen = scale_expansion_zeroelim(4, bc, dez, temp8c);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a,
+                                          temp8blen, temp8b, temp16);
+  temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c,
+                                          temp16len, temp16, temp24);
+  temp48len = scale_expansion_zeroelim(temp24len, temp24, aex, temp48);
+  xlen = scale_expansion_zeroelim(temp48len, temp48, -aex, xdet);
+  temp48len = scale_expansion_zeroelim(temp24len, temp24, aey, temp48);
+  ylen = scale_expansion_zeroelim(temp48len, temp48, -aey, ydet);
+  temp48len = scale_expansion_zeroelim(temp24len, temp24, aez, temp48);
+  zlen = scale_expansion_zeroelim(temp48len, temp48, -aez, zdet);
+  xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet);
+  alen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, adet);
+
+  temp8alen = scale_expansion_zeroelim(4, da, cez, temp8a);
+  temp8blen = scale_expansion_zeroelim(4, ac, dez, temp8b);
+  temp8clen = scale_expansion_zeroelim(4, cd, aez, temp8c);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a,
+                                          temp8blen, temp8b, temp16);
+  temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c,
+                                          temp16len, temp16, temp24);
+  temp48len = scale_expansion_zeroelim(temp24len, temp24, bex, temp48);
+  xlen = scale_expansion_zeroelim(temp48len, temp48, bex, xdet);
+  temp48len = scale_expansion_zeroelim(temp24len, temp24, bey, temp48);
+  ylen = scale_expansion_zeroelim(temp48len, temp48, bey, ydet);
+  temp48len = scale_expansion_zeroelim(temp24len, temp24, bez, temp48);
+  zlen = scale_expansion_zeroelim(temp48len, temp48, bez, zdet);
+  xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet);
+  blen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, bdet);
+
+  temp8alen = scale_expansion_zeroelim(4, ab, dez, temp8a);
+  temp8blen = scale_expansion_zeroelim(4, bd, aez, temp8b);
+  temp8clen = scale_expansion_zeroelim(4, da, bez, temp8c);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a,
+                                          temp8blen, temp8b, temp16);
+  temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c,
+                                          temp16len, temp16, temp24);
+  temp48len = scale_expansion_zeroelim(temp24len, temp24, cex, temp48);
+  xlen = scale_expansion_zeroelim(temp48len, temp48, -cex, xdet);
+  temp48len = scale_expansion_zeroelim(temp24len, temp24, cey, temp48);
+  ylen = scale_expansion_zeroelim(temp48len, temp48, -cey, ydet);
+  temp48len = scale_expansion_zeroelim(temp24len, temp24, cez, temp48);
+  zlen = scale_expansion_zeroelim(temp48len, temp48, -cez, zdet);
+  xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet);
+  clen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, cdet);
+
+  temp8alen = scale_expansion_zeroelim(4, bc, aez, temp8a);
+  temp8blen = scale_expansion_zeroelim(4, ac, -bez, temp8b);
+  temp8clen = scale_expansion_zeroelim(4, ab, cez, temp8c);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a,
+                                          temp8blen, temp8b, temp16);
+  temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c,
+                                          temp16len, temp16, temp24);
+  temp48len = scale_expansion_zeroelim(temp24len, temp24, dex, temp48);
+  xlen = scale_expansion_zeroelim(temp48len, temp48, dex, xdet);
+  temp48len = scale_expansion_zeroelim(temp24len, temp24, dey, temp48);
+  ylen = scale_expansion_zeroelim(temp48len, temp48, dey, ydet);
+  temp48len = scale_expansion_zeroelim(temp24len, temp24, dez, temp48);
+  zlen = scale_expansion_zeroelim(temp48len, temp48, dez, zdet);
+  xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet);
+  dlen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, ddet);
+
+  ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
+  cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet);
+  finlength = fast_expansion_sum_zeroelim(ablen, abdet, cdlen, cddet, fin1);
+
+  det = estimate(finlength, fin1);
+  errbound = isperrboundB * permanent;
+  if ((det >= errbound) || (-det >= errbound)) {
+    return det;
+  }
+
+  Two_Diff_Tail(pa[0], pe[0], aex, aextail);
+  Two_Diff_Tail(pa[1], pe[1], aey, aeytail);
+  Two_Diff_Tail(pa[2], pe[2], aez, aeztail);
+  Two_Diff_Tail(pb[0], pe[0], bex, bextail);
+  Two_Diff_Tail(pb[1], pe[1], bey, beytail);
+  Two_Diff_Tail(pb[2], pe[2], bez, beztail);
+  Two_Diff_Tail(pc[0], pe[0], cex, cextail);
+  Two_Diff_Tail(pc[1], pe[1], cey, ceytail);
+  Two_Diff_Tail(pc[2], pe[2], cez, ceztail);
+  Two_Diff_Tail(pd[0], pe[0], dex, dextail);
+  Two_Diff_Tail(pd[1], pe[1], dey, deytail);
+  Two_Diff_Tail(pd[2], pe[2], dez, deztail);
+  if ((aextail == 0.0) && (aeytail == 0.0) && (aeztail == 0.0)
+      && (bextail == 0.0) && (beytail == 0.0) && (beztail == 0.0)
+      && (cextail == 0.0) && (ceytail == 0.0) && (ceztail == 0.0)
+      && (dextail == 0.0) && (deytail == 0.0) && (deztail == 0.0)) {
+    return det;
+  }
+
+  errbound = isperrboundC * permanent + resulterrbound * Absolute(det);
+  abeps = (aex * beytail + bey * aextail)
+        - (aey * bextail + bex * aeytail);
+  bceps = (bex * ceytail + cey * bextail)
+        - (bey * cextail + cex * beytail);
+  cdeps = (cex * deytail + dey * cextail)
+        - (cey * dextail + dex * ceytail);
+  daeps = (dex * aeytail + aey * dextail)
+        - (dey * aextail + aex * deytail);
+  aceps = (aex * ceytail + cey * aextail)
+        - (aey * cextail + cex * aeytail);
+  bdeps = (bex * deytail + dey * bextail)
+        - (bey * dextail + dex * beytail);
+  det += (((bex * bex + bey * bey + bez * bez)
+           * ((cez * daeps + dez * aceps + aez * cdeps)
+              + (ceztail * da3 + deztail * ac3 + aeztail * cd3))
+           + (dex * dex + dey * dey + dez * dez)
+           * ((aez * bceps - bez * aceps + cez * abeps)
+              + (aeztail * bc3 - beztail * ac3 + ceztail * ab3)))
+          - ((aex * aex + aey * aey + aez * aez)
+           * ((bez * cdeps - cez * bdeps + dez * bceps)
+              + (beztail * cd3 - ceztail * bd3 + deztail * bc3))
+           + (cex * cex + cey * cey + cez * cez)
+           * ((dez * abeps + aez * bdeps + bez * daeps)
+              + (deztail * ab3 + aeztail * bd3 + beztail * da3))))
+       + 2.0 * (((bex * bextail + bey * beytail + bez * beztail)
+                 * (cez * da3 + dez * ac3 + aez * cd3)
+                 + (dex * dextail + dey * deytail + dez * deztail)
+                 * (aez * bc3 - bez * ac3 + cez * ab3))
+                - ((aex * aextail + aey * aeytail + aez * aeztail)
+                 * (bez * cd3 - cez * bd3 + dez * bc3)
+                 + (cex * cextail + cey * ceytail + cez * ceztail)
+                 * (dez * ab3 + aez * bd3 + bez * da3)));
+  if ((det >= errbound) || (-det >= errbound)) {
+    return det;
+  }
+
+  return insphereexact(pa, pb, pc, pd, pe);
+}
+
+REAL insphere(pa, pb, pc, pd, pe)
+REAL *pa;
+REAL *pb;
+REAL *pc;
+REAL *pd;
+REAL *pe;
+{
+  REAL aex, bex, cex, dex;
+  REAL aey, bey, cey, dey;
+  REAL aez, bez, cez, dez;
+  REAL aexbey, bexaey, bexcey, cexbey, cexdey, dexcey, dexaey, aexdey;
+  REAL aexcey, cexaey, bexdey, dexbey;
+  REAL alift, blift, clift, dlift;
+  REAL ab, bc, cd, da, ac, bd;
+  REAL abc, bcd, cda, dab;
+  REAL aezplus, bezplus, cezplus, dezplus;
+  REAL aexbeyplus, bexaeyplus, bexceyplus, cexbeyplus;
+  REAL cexdeyplus, dexceyplus, dexaeyplus, aexdeyplus;
+  REAL aexceyplus, cexaeyplus, bexdeyplus, dexbeyplus;
+  REAL det;
+  REAL permanent, errbound;
+  REAL ins;
+
+  FPU_ROUND_DOUBLE;
+
+  aex = pa[0] - pe[0];
+  bex = pb[0] - pe[0];
+  cex = pc[0] - pe[0];
+  dex = pd[0] - pe[0];
+  aey = pa[1] - pe[1];
+  bey = pb[1] - pe[1];
+  cey = pc[1] - pe[1];
+  dey = pd[1] - pe[1];
+  aez = pa[2] - pe[2];
+  bez = pb[2] - pe[2];
+  cez = pc[2] - pe[2];
+  dez = pd[2] - pe[2];
+
+  aexbey = aex * bey;
+  bexaey = bex * aey;
+  ab = aexbey - bexaey;
+  bexcey = bex * cey;
+  cexbey = cex * bey;
+  bc = bexcey - cexbey;
+  cexdey = cex * dey;
+  dexcey = dex * cey;
+  cd = cexdey - dexcey;
+  dexaey = dex * aey;
+  aexdey = aex * dey;
+  da = dexaey - aexdey;
+
+  aexcey = aex * cey;
+  cexaey = cex * aey;
+  ac = aexcey - cexaey;
+  bexdey = bex * dey;
+  dexbey = dex * bey;
+  bd = bexdey - dexbey;
+
+  abc = aez * bc - bez * ac + cez * ab;
+  bcd = bez * cd - cez * bd + dez * bc;
+  cda = cez * da + dez * ac + aez * cd;
+  dab = dez * ab + aez * bd + bez * da;
+
+  alift = aex * aex + aey * aey + aez * aez;
+  blift = bex * bex + bey * bey + bez * bez;
+  clift = cex * cex + cey * cey + cez * cez;
+  dlift = dex * dex + dey * dey + dez * dez;
+
+  det = (dlift * abc - clift * dab) + (blift * cda - alift * bcd);
+
+  aezplus = Absolute(aez);
+  bezplus = Absolute(bez);
+  cezplus = Absolute(cez);
+  dezplus = Absolute(dez);
+  aexbeyplus = Absolute(aexbey);
+  bexaeyplus = Absolute(bexaey);
+  bexceyplus = Absolute(bexcey);
+  cexbeyplus = Absolute(cexbey);
+  cexdeyplus = Absolute(cexdey);
+  dexceyplus = Absolute(dexcey);
+  dexaeyplus = Absolute(dexaey);
+  aexdeyplus = Absolute(aexdey);
+  aexceyplus = Absolute(aexcey);
+  cexaeyplus = Absolute(cexaey);
+  bexdeyplus = Absolute(bexdey);
+  dexbeyplus = Absolute(dexbey);
+  permanent = ((cexdeyplus + dexceyplus) * bezplus
+               + (dexbeyplus + bexdeyplus) * cezplus
+               + (bexceyplus + cexbeyplus) * dezplus)
+            * alift
+            + ((dexaeyplus + aexdeyplus) * cezplus
+               + (aexceyplus + cexaeyplus) * dezplus
+               + (cexdeyplus + dexceyplus) * aezplus)
+            * blift
+            + ((aexbeyplus + bexaeyplus) * dezplus
+               + (bexdeyplus + dexbeyplus) * aezplus
+               + (dexaeyplus + aexdeyplus) * bezplus)
+            * clift
+            + ((bexceyplus + cexbeyplus) * aezplus
+               + (cexaeyplus + aexceyplus) * bezplus
+               + (aexbeyplus + bexaeyplus) * cezplus)
+            * dlift;
+  errbound = isperrboundA * permanent;
+  if ((det > errbound) || (-det > errbound)) {
+    FPU_RESTORE;
+    return det;
+  }
+
+  ins = insphereadapt(pa, pb, pc, pd, pe, permanent);
+  FPU_RESTORE;
+  return ins;
+}
diff --git a/src_3rd/gts/predicates.h b/src_3rd/gts/predicates.h
new file mode 100644
index 0000000..8b026ed
--- /dev/null
+++ b/src_3rd/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/src_3rd/gts/psurface.c b/src_3rd/gts/psurface.c
new file mode 100644
index 0000000..6db3ae2
--- /dev/null
+++ b/src_3rd/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/src_3rd/gts/refine.c b/src_3rd/gts/refine.c
new file mode 100644
index 0000000..293eb11
--- /dev/null
+++ b/src_3rd/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/src_3rd/gts/rounding.h b/src_3rd/gts/rounding.h
new file mode 100644
index 0000000..053b32f
--- /dev/null
+++ b/src_3rd/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/src_3rd/gts/segment.c b/src_3rd/gts/segment.c
new file mode 100644
index 0000000..58a0540
--- /dev/null
+++ b/src_3rd/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/src_3rd/gts/split.c b/src_3rd/gts/split.c
new file mode 100644
index 0000000..43fea3a
--- /dev/null
+++ b/src_3rd/gts/split.c
@@ -0,0 +1,1840 @@
+/* GTS - Library for the manipulation of triangulated surfaces
+ * Copyright (C) 1999 St�phane Popinet
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <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 */
+
+/* Not currently used.  May be useful for some debug code */
+#ifdef DEBUG
+static void print_split (GtsSplit * vs, FILE * fptr)
+{
+  guint j;
+  GtsSplitCFace * cf;
+
+  g_return_if_fail (vs != NULL);
+  g_return_if_fail (fptr != NULL);
+
+  fprintf (fptr, "%p: v: %p v1: %p v2: %p ncf: %u cfaces: %p\n",
+	   vs, vs->v, vs->v1, vs->v2, vs->ncf, vs->cfaces);
+  cf = vs->cfaces;
+  j = vs->ncf;
+  while (j--) {
+    fprintf (stderr, "  f: %p a1: %p a2: %p\n",
+	     cf->f, cf->a1, cf->a2);
+    cf++;
+  }
+}
+#endif
+
+/**
+ * gts_split_collapse:
+ * @vs: a #GtsSplit.
+ * @klass: a #GtsEdgeClass.
+ * @heap: a #GtsEHeap or %NULL.
+ *
+ * Collapses the vertex split @vs. Any new edge created during the process will
+ * be of class @klass. If heap is not %NULL, the new edges will be inserted
+ * into it and the destroyed edges will be removed from it.
+ */
+void gts_split_collapse (GtsSplit * vs, 
+			 GtsEdgeClass * klass,
+			 GtsEHeap * heap)
+{
+  GtsEdge * e;
+  GtsVertex * v, * v1, * v2;
+  GSList * i, * end;
+#ifdef DYNAMIC_SPLIT
+  GtsSplitCFace * cf;
+  guint j;
+#endif
+#ifdef DEBUG
+  gboolean invalid = FALSE;
+  static guint ninvalid = 0;
+#endif
+
+  g_return_if_fail (vs != NULL);
+  g_return_if_fail (klass != NULL);
+
+  v = vs->v;
+
+  g_return_if_fail (v->segments == NULL);
+  
+  /* we don't want to destroy vertices */
+  gts_allow_floating_vertices = TRUE;
+
+  v1 = GTS_SPLIT_V1 (vs);
+  v2 = GTS_SPLIT_V2 (vs);
+  g_assert ((e = GTS_EDGE (gts_vertices_are_connected (v1, v2))));
+
+#ifdef DEBUG
+  fprintf (stderr, "collapsing %p: v1: %p v2: %p v: %p\n", vs, v1, v2, v);
+  if (!edge_collapse_is_valid (e)) {
+    char fname[80];
+    FILE * fptr;
+    GSList * triangles, * i;
+
+    g_warning ("invalid edge collapse");
+    invalid = TRUE;
+    sprintf (fname, "invalid.%d", ninvalid);
+    fptr = fopen (fname, "wt");
+    gts_write_segment (GTS_SEGMENT (e), GTS_POINT (v), fptr);
+    triangles = gts_vertex_triangles (v1, NULL);
+    triangles = gts_vertex_triangles (v2, triangles);
+    i = triangles;
+    while (i) {
+      gts_write_triangle (i->data, GTS_POINT (v), fptr);
+      i = i->next;
+    }
+    g_slist_free (triangles);
+    fclose (fptr);
+  }
+#endif
+
+  i = e->triangles;
+#ifdef DYNAMIC_SPLIT
+  cf = vs->cfaces;
+  j = vs->ncf;
+  while (j--) {
+    g_free (cf->a1);
+    g_free (cf->a2);
+    cf++;
+  }
+  g_free (vs->cfaces);
+
+  vs->ncf = g_slist_length (i);
+  g_assert (vs->ncf > 0);
+  cf = vs->cfaces = g_malloc (vs->ncf*sizeof (GtsSplitCFace));
+#endif /* DYNAMIC_SPLIT */
+#ifdef NEW
+  while (i) {
+    cf->f = i->data;
+    g_assert (GTS_IS_FACE (cf->f));
+    GTS_OBJECT (cf->f)->klass = GTS_OBJECT_CLASS (cface_class ());
+    cf++;
+    i = i->next;
+  }
+  i = e->triangles;
+  cf = vs->cfaces;
+  while (i) {
+    cface_new (i->data, e, v1, v2, vs, heap, klass, cf);
+#ifdef DEBUG
+    fprintf (stderr, "cface: %p->%d t: %p->%d a1: ", 
+	     cf->f, id (cf->f), CFACE (cf->f)->t, id (CFACE (cf->f)->t));
+    {
+      GtsTriangle * t, ** a;
+      a = cf->a1;
+      while ((t = *(a++)))
+	fprintf (stderr, "%p->%d ", t, id (t));
+      fprintf (stderr, "a2: ");
+      a = cf->a2;
+      while ((t = *(a++)))
+	fprintf (stderr, "%p->%d ", t, id (t));
+      fprintf (stderr, "\n");
+    }
+#endif
+    cf++;
+    i = i->next;
+  }
+#else /* not NEW */
+  while (i) {
+    cface_new (i->data, e, v1, v2, vs, heap
+#ifdef DYNAMIC_SPLIT
+	       , cf
+#endif /* DYNAMIC_SPLIT */
+	       );
+#ifdef DYNAMIC_SPLIT
+    cf->f = i->data;
+    cf++;
+#endif /* DYNAMIC_SPLIT */
+    i = i->next;
+  }
+#endif /* NEW */
+  g_slist_free (e->triangles);
+  e->triangles = NULL;
+  gts_object_destroy (GTS_OBJECT (e));
+
+  gts_allow_floating_vertices = FALSE;
+
+  end = NULL;
+  i = v1->segments;
+  while (i) {
+    GtsSegment * s = i->data;
+    if (s->v1 == v1)
+      s->v1 = v;
+    else
+      s->v2 = v;
+    end = i;
+    i = i->next;
+  }
+  if (end) {
+    end->next = v->segments;
+    v->segments = v1->segments;
+    v1->segments = NULL;
+  }
+
+  end = NULL;
+  i = v2->segments;
+  while (i) {
+    GtsSegment * s = i->data;
+    if (s->v1 == v2)
+      s->v1 = v;
+    else
+      s->v2 = v;
+    end = i;
+    i = i->next;
+  }
+  if (end) {
+    end->next = v->segments;
+    v->segments = v2->segments;
+    v2->segments = NULL;
+  }
+
+#ifdef DEBUG
+  if (invalid) {
+    char fname[80];
+    FILE * fptr;
+    GSList * triangles, * i;
+    GtsSurface * surface = NULL;
+
+    sprintf (fname, "invalid_after.%d", ninvalid);
+    fptr = fopen (fname, "wt");
+    triangles = gts_vertex_triangles (v, NULL);
+    i = triangles;
+    while (i) {
+      GtsTriangle * t = i->data;
+      fprintf (stderr, "checking %p->%d\n", t, id (t));
+      g_assert (GTS_IS_FACE (t));
+      gts_write_triangle (t, GTS_POINT (v), fptr);
+      surface = GTS_FACE (t)->surfaces->data;
+      if (gts_triangle_is_duplicate (t))
+	fprintf (stderr, "%p->%d is duplicate\n", t, id (t));
+      if (gts_segment_is_duplicate (GTS_SEGMENT (t->e1)))
+	fprintf (stderr, "e1 of %p->%d is duplicate\n", t, id (t));
+      if (gts_segment_is_duplicate (GTS_SEGMENT (t->e2)))
+	fprintf (stderr, "e2 of %p->%d is duplicate\n", t, id (t));
+      if (gts_segment_is_duplicate (GTS_SEGMENT (t->e3)))
+	fprintf (stderr, "e3 of %p->%d is duplicate\n", t, id (t));
+      i = i->next;
+    }
+    fclose (fptr);
+    g_slist_free (triangles);
+#if 0
+    gts_split_expand (vs, surface);
+
+    sprintf (fname, "invalid_after_after.%d", ninvalid);
+    fptr = fopen (fname, "wt");
+    triangles = gts_vertex_triangles (v1, NULL);
+    triangles = gts_vertex_triangles (v2, triangles);
+    i = triangles;
+    while (i) {
+      GtsTriangle * t = i->data;
+      gts_write_triangle (t, GTS_POINT (v), fptr);
+      surface = GTS_FACE (t)->surfaces->data;
+      if (gts_triangle_is_duplicate (t))
+	fprintf (stderr, "%p->%d is duplicate\n", t, id (t));
+      if (gts_segment_is_duplicate (GTS_SEGMENT (t->e1)))
+	fprintf (stderr, "e1 of %p->%d is duplicate\n", t, id (t));
+      if (gts_segment_is_duplicate (GTS_SEGMENT (t->e2)))
+	fprintf (stderr, "e2 of %p->%d is duplicate\n", t, id (t));
+      if (gts_segment_is_duplicate (GTS_SEGMENT (t->e3)))
+	fprintf (stderr, "e3 of %p->%d is duplicate\n", t, id (t));
+      i = i->next;
+    }
+    fclose (fptr);
+    g_slist_free (triangles);
+
+    exit (1);
+#endif
+    ninvalid++;
+  }
+#endif
+}
+
+/**
+ * gts_split_expand:
+ * @vs: a #GtsSplit.
+ * @s: a #GtsSurface.
+ * @klass: a #GtsEdgeClass.
+ *
+ * Expands the vertex split @vs adding the newly created faces to @s. Any 
+ * new edge will be of class @klass.
+ */
+void gts_split_expand (GtsSplit * vs, 
+		       GtsSurface * s,
+		       GtsEdgeClass * klass)
+{
+  GSList * i;
+  GtsEdge * e;
+  GtsVertex * v, * v1, * v2;  
+  gboolean changed = FALSE;
+  GtsSplitCFace * cf;
+  guint j;
+  
+  g_return_if_fail (vs != NULL);
+  g_return_if_fail (s != NULL);
+  g_return_if_fail (klass != NULL);
+
+  /* we don't want to destroy vertices */
+  gts_allow_floating_vertices = TRUE;
+
+  v1 = GTS_SPLIT_V1 (vs);
+  v2 = GTS_SPLIT_V2 (vs);
+  v = vs->v;
+#ifdef DEBUG_EXPAND
+  fprintf (stderr, "expanding %p->%d: v1: %p->%d v2: %p->%d v: %p->%d\n",
+	   vs, id (vs), v1, id (v1), v2, id (v2), v, id (v));
+#endif
+  e = gts_edge_new (klass, v1, v2);
+  cf = vs->cfaces;
+  j = vs->ncf;
+  while (j--) {
+    cface_expand (CFACE (cf->f), cf->a1, cf->a2, e, v1, v2, v, klass);
+    gts_surface_add_face (s, cf->f);
+    cf++;
+  }
+
+  gts_allow_floating_vertices = FALSE;
+
+  /* this part is described by figure "expand.fig" */
+  i = v->segments;
+  while (i) {
+    GtsEdge * e1 = i->data;
+    GtsVertex * with = NULL;
+    GSList * j = e1->triangles, * next = i->next;
+    // fprintf (stderr, "e1: %p->%d\n", e1, id (e1));
+    while (j && !with) {
+      with = GTS_OBJECT (j->data)->reserved;
+      j = j->next;
+    }
+    if (with) {
+      j = e1->triangles;
+      while (j) {
+	GtsTriangle * t = j->data;
+	if (GTS_OBJECT (t)->reserved) {
+	  g_assert (GTS_OBJECT (t)->reserved == with);
+	  GTS_OBJECT (t)->reserved = NULL;
+	}
+	else
+	  GTS_OBJECT (t)->reserved = with;
+	j = j->next;
+      }
+      if (GTS_SEGMENT (e1)->v1 == v)
+	GTS_SEGMENT (e1)->v1 = with;
+      else
+	GTS_SEGMENT (e1)->v2 = with;
+
+      v->segments = g_slist_remove_link (v->segments, i);
+      i->next = with->segments;
+      with->segments = i;
+      changed = TRUE;
+    }
+    if (next)
+      i = next;
+    else {
+      /* check for infinite loop (the crossed out case in 
+	 figure "expand.fig") */
+      g_assert (changed);
+      changed = FALSE;
+      i = v->segments;
+    }
+  }
+}
+
+#ifndef DYNAMIC_SPLIT
+static void cface_neighbors (GtsSplitCFace * cf,
+			     GtsEdge * e,
+			     GtsVertex * v1,
+			     GtsVertex * v2)
+{
+  GtsTriangle * t = GTS_TRIANGLE (cf->f), ** a;
+  GtsEdge * e1 = t->e1, * e2 = t->e2, * e3 = t->e3;
+  GSList * i;
+  guint size;
+
+  ROTATE_ORIENT (e, e1, e2, e3);
+  if (SEGMENT_USE_VERTEX (GTS_SEGMENT (e1), v2)) {
+    e3 = e1; e1 = e2; e2 = e3;
+  }
+  
+  i = e1->triangles;
+  size = g_slist_length (i)*sizeof (GtsTriangle *);
+  a = cf->a1 = g_malloc (size > 0 ? size : sizeof (GtsTriangle *));
+  while (i) {
+    if (i->data != t)
+      *(a++) = i->data;
+    i = i->next;
+  }
+  *a = NULL;
+
+  i = e2->triangles;
+  size = g_slist_length (i)*sizeof (GtsTriangle *);
+  a = cf->a2 = g_malloc (size > 0 ? size : sizeof (GtsTriangle *));
+  while (i) {
+    if (i->data != t)
+      *(a++) = i->data;
+    i = i->next;
+  }
+  *a = NULL;
+}
+#endif /*ifndef DYNAMIC_SPLIT */
+
+/**
+ * gts_split_new:
+ * @klass: a #GtsSplitClass.
+ * @v: a #GtsVertex.
+ * @o1: either a #GtsVertex or a #GtsSplit.
+ * @o2: either a #GtsVertex or a #GtsSplit.
+ *
+ * Creates a new #GtsSplit which would collapse @o1 and @o2 into @v. The 
+ * collapse itself is not performed.
+ *
+ * Returns: the new #GtsSplit.
+ */
+GtsSplit * gts_split_new (GtsSplitClass * klass,
+			  GtsVertex * v,
+			  GtsObject * o1,
+			  GtsObject * o2)
+{
+  GtsSplit * vs;
+#ifndef DYNAMIC_SPLIT
+  GtsVertex * v1, * v2;
+  GtsEdge * e;
+  GSList * i;
+  GtsSplitCFace * cf;
+#endif
+
+  g_return_val_if_fail (klass != NULL, NULL);
+  g_return_val_if_fail (v != NULL, NULL);
+  g_return_val_if_fail (GTS_IS_SPLIT (o1) || GTS_IS_VERTEX (o1), NULL);
+  g_return_val_if_fail (GTS_IS_SPLIT (o2) || GTS_IS_VERTEX (o2), NULL);
+
+  vs = GTS_SPLIT (gts_object_new (GTS_OBJECT_CLASS (klass)));
+  vs->v = v;
+  vs->v1 = o1;
+  vs->v2 = o2;
+#ifdef DYNAMIC_SPLIT
+  vs->ncf = 0;
+  vs->cfaces = NULL;
+#else
+  v1 = GTS_SPLIT_V1 (vs);
+  v2 = GTS_SPLIT_V2 (vs);
+  g_assert ((e = GTS_EDGE (gts_vertices_are_connected (v1, v2))));
+  i = e->triangles;
+  vs->ncf = g_slist_length (i);
+  g_assert (vs->ncf > 0);
+  cf = vs->cfaces = g_malloc (vs->ncf*sizeof (GtsSplitCFace));
+  while (i) {
+    cf->f = i->data;
+    cface_neighbors (cf, e, v1, v2);
+    i = i->next;
+    cf++;
+  }
+#endif
+  
+  return vs;
+}
+
+static gboolean 
+split_traverse_pre_order (GtsSplit *           vs,
+			  GtsSplitTraverseFunc func,
+			  gpointer	       data)
+{
+  if (func (vs, data))
+    return TRUE;
+  if (GTS_IS_SPLIT (vs->v1) &&
+      split_traverse_pre_order (GTS_SPLIT (vs->v1), func, data))
+    return TRUE;
+  if (GTS_IS_SPLIT (vs->v2) &&
+      split_traverse_pre_order (GTS_SPLIT (vs->v2), func, data))
+    return TRUE;
+  return FALSE;
+}
+
+static gboolean 
+split_depth_traverse_pre_order (GtsSplit *             vs,
+				guint                  depth,
+				GtsSplitTraverseFunc   func,
+				gpointer	       data)
+{
+  if (func (vs, data))
+      return TRUE;
+    
+  depth--;
+  if (!depth)
+    return FALSE;
+
+  if (GTS_IS_SPLIT (vs->v1) &&
+      split_depth_traverse_pre_order (GTS_SPLIT (vs->v1), depth, func, data))
+    return TRUE;
+  if (GTS_IS_SPLIT (vs->v2) &&
+      split_depth_traverse_pre_order (GTS_SPLIT (vs->v2), depth, func, data))
+    return TRUE;
+  return FALSE;
+}
+
+static gboolean 
+split_traverse_post_order (GtsSplit *           vs,
+			   GtsSplitTraverseFunc func,
+			   gpointer	        data)
+{
+  if (GTS_IS_SPLIT (vs->v1) &&
+      split_traverse_post_order (GTS_SPLIT (vs->v1), func, data))
+    return TRUE;
+  if (GTS_IS_SPLIT (vs->v2) &&
+      split_traverse_post_order (GTS_SPLIT (vs->v2), func, data))
+    return TRUE;
+  if (func (vs, data))
+    return TRUE;
+  return FALSE;
+}
+
+static gboolean
+split_depth_traverse_post_order (GtsSplit *           vs,
+				 guint                depth,
+				 GtsSplitTraverseFunc func,
+				 gpointer	      data)
+{
+  depth--;
+  if (depth) {
+    if (GTS_IS_SPLIT (vs->v1) &&
+	split_depth_traverse_post_order (GTS_SPLIT (vs->v1), 
+					 depth, func, data))
+      return TRUE;
+    if (GTS_IS_SPLIT (vs->v2) &&
+	split_depth_traverse_post_order (GTS_SPLIT (vs->v2),
+					 depth, func, data))
+      return TRUE;
+  }
+  if (func (vs, data))
+    return TRUE;
+  return FALSE;
+}
+
+/**
+ * gts_split_traverse:
+ * @root: the #GtsSplit to start the traversal from.
+ * @order: the order in which nodes are visited - G_PRE_ORDER or G_POST_ORDER.
+ * @depth: the maximum depth of the traversal. Nodes below this depth
+ * will not be visited. If depth is -1 all nodes in the tree are
+ * visited. If depth is 1, only the root is visited. If depth is 2,
+ * the root and its children are visited. And so on.
+ * @func: the function to call for each visited #GtsHSplit.
+ * @data: user data to pass to the function.
+ *
+ * Traverses the #GtsSplit tree having @root as root. Calls @func for each
+ * #GtsSplit of the tree in the order specified by @order. If order is set
+ * to G_PRE_ORDER @func is called for the #GtsSplit then its children, if order
+ * is set to G_POST_ORDER @func is called for the children and then for the
+ * #GtsSplit.
+ */
+void gts_split_traverse (GtsSplit *           root,
+			 GTraverseType        order,
+			 gint                 depth,
+			 GtsSplitTraverseFunc func,
+			 gpointer             data)
+{
+  g_return_if_fail (root != NULL);
+  g_return_if_fail (func != NULL);
+  g_return_if_fail (order < G_LEVEL_ORDER);
+  g_return_if_fail (depth == -1 || depth > 0);
+
+  switch (order) {
+  case G_PRE_ORDER:
+    if (depth < 0)
+      split_traverse_pre_order (root, func, data);
+    else
+      split_depth_traverse_pre_order (root, depth, func, data);
+    break;
+  case G_POST_ORDER:
+    if (depth < 0)
+      split_traverse_post_order (root, func, data);
+    else
+      split_depth_traverse_post_order (root, depth, func, data);
+    break;
+  default:
+    g_assert_not_reached ();
+  }
+}
+
+/**
+ * gts_split_height:
+ * @root: a #GtsSplit.
+ *
+ * Returns: the maximum height of the vertex split tree having @root as root.
+ */
+guint gts_split_height (GtsSplit * root)
+{
+  guint height = 0, tmp_height;
+
+  g_return_val_if_fail (root != NULL, 0);
+
+  if (GTS_IS_SPLIT (root->v1)) {
+    tmp_height = gts_split_height (GTS_SPLIT (root->v1));
+    if (tmp_height > height)
+      height = tmp_height;
+  }
+  if (GTS_IS_SPLIT (root->v2)) {
+    tmp_height = gts_split_height (GTS_SPLIT (root->v2));
+    if (tmp_height > height)
+      height = tmp_height;
+  }
+
+  return height + 1;
+}
+
+#ifndef DYNAMIC_SPLIT
+static gboolean list_array_are_identical (GSList * list, 
+					  gpointer * array,
+					  gpointer excluded)
+{
+  while (list) {
+    gpointer data = list->data;
+    if (data != excluded) {
+      gboolean found = FALSE;
+      gpointer * a = array;
+      
+      while (!found && *a)
+	if (*(a++) == data)
+	  found = TRUE;
+      if (!found)
+	return FALSE;
+    }
+    list = list->next;
+  }
+  return TRUE;
+}
+#endif /* ifndef DYNAMIC_SPLIT */
+
+#ifndef NEW
+gboolean gts_split_is_collapsable (GtsSplit * vs)
+{
+  guint i;
+  GtsSplitCFace * cf;
+  GtsVertex * v1, * v2;
+  GtsEdge * e;
+
+  g_return_val_if_fail (vs != NULL, FALSE);
+
+  v1 = GTS_SPLIT_V1 (vs);
+  v2 = GTS_SPLIT_V2 (vs);
+  g_return_val_if_fail ((e = GTS_EDGE (gts_vertices_are_connected (v1, v2))),
+			FALSE);
+
+#ifdef DYNAMIC_SPLIT
+  if (!gts_edge_collapse_is_valid (e))
+    return FALSE;
+#else 
+  i = vs->ncf;
+  cf = vs->cfaces;
+  while (i--) {
+    GtsTriangle * t = GTS_TRIANGLE (cf->f);
+    GtsEdge * e1 = t->e1, * e2 = t->e2, * e3 = t->e3;
+
+    ROTATE_ORIENT (e, e1, e2, e3);
+    if (SEGMENT_USE_VERTEX (GTS_SEGMENT (e1), v2)) {
+      e3 = e1; e1 = e2; e2 = e3;
+    }
+
+    if (!list_array_are_identical (e1->triangles, (gpointer *) cf->a1, t))
+      return FALSE;
+    if (!list_array_are_identical (e2->triangles, (gpointer *) cf->a2, t))
+      return FALSE;
+    
+    cf++;
+  }
+#endif
+  return TRUE;
+}
+#endif /* not NEW */
+
+#ifdef DEBUG_HEXPAND
+static guint expand_level = 0;
+
+static void expand_indent (FILE * fptr)
+{
+  guint i = expand_level;
+  while (i--)
+    fputc (' ', fptr);
+}
+#endif
+
+/**
+ * gts_hsplit_force_expand:
+ * @hs: a #GtsHSplit.
+ * @hsurface: a #GtsHSurface.
+ *
+ * Forces the expansion of @hs by first expanding all its dependencies not
+ * already expanded.
+ */
+void gts_hsplit_force_expand (GtsHSplit * hs,
+			      GtsHSurface * hsurface)
+{
+  guint i;
+  GtsSplitCFace * cf;
+
+  g_return_if_fail (hs != NULL);
+  g_return_if_fail (hsurface != NULL);
+  g_return_if_fail (hs->nchild == 0);
+
+#ifdef DEBUG_HEXPAND
+  expand_level += 2;
+#endif
+
+  if (hs->parent && hs->parent->nchild == 0) {
+#ifdef DEBUG_HEXPAND
+    expand_indent (stderr); 
+    fprintf (stderr, "expand parent %p\n", hs->parent);
+#endif
+    gts_hsplit_force_expand (hs->parent, hsurface);
+  }
+
+  i = GTS_SPLIT (hs)->ncf;
+  cf = GTS_SPLIT (hs)->cfaces;
+  while (i--) {
+    GtsTriangle ** j, * t;
+
+    j = cf->a1;
+    while ((t = *(j++)))
+      if (IS_CFACE (t)) {
+#ifdef DEBUG_HEXPAND
+	expand_indent (stderr); 
+	fprintf (stderr, "expand a1: cf->f: %p t: %p parent_split: %p\n", 
+		 cf->f,
+		 t,
+		 GTS_HSPLIT (CFACE (t)->parent_split));
+#endif
+	gts_hsplit_force_expand (GTS_HSPLIT (CFACE (t)->parent_split),
+				 hsurface);
+#ifdef DEBUG_HEXPAND
+	g_assert (!IS_CFACE (t));
+#endif
+      }
+    j = cf->a2;
+    while ((t = *(j++)))
+      if (IS_CFACE (t)) {
+#ifdef DEBUG_HEXPAND
+	expand_indent (stderr); 
+	fprintf (stderr, "expand a2: cf->f: %p t: %p parent_split: %p\n", 
+		 cf->f,
+		 t,
+		 GTS_HSPLIT (CFACE (t)->parent_split));
+#endif
+	gts_hsplit_force_expand (GTS_HSPLIT (CFACE (t)->parent_split),
+				 hsurface);
+      }
+    cf++;
+  }
+
+  gts_hsplit_expand (hs, hsurface);
+
+#ifdef DEBUG_HEXPAND
+  expand_level -= 2; 
+  expand_indent (stderr); 
+  fprintf (stderr, "%p expanded\n", hs);
+#endif
+}
+
+static void index_object (GtsObject * o, guint * n)
+{
+  o->reserved = GUINT_TO_POINTER ((*n)++);
+}
+
+static void index_face (GtsFace * f, gpointer * data)
+{
+  guint * nf = data[1];
+
+  g_hash_table_insert (data[0], f, GUINT_TO_POINTER ((*nf)++));
+}
+
+/**
+ * gts_psurface_write:
+ * @ps: a #GtsPSurface.
+ * @fptr: a file pointer.
+ *
+ * Writes to @fptr a GTS progressive surface description.
+ */
+void gts_psurface_write (GtsPSurface * ps, FILE * fptr)
+{
+  guint nv = 1;
+  guint nf = 1;
+  GHashTable * hash;
+  gpointer data[2];
+
+  g_return_if_fail (ps != NULL);
+  g_return_if_fail (fptr != NULL);
+  g_return_if_fail (GTS_PSURFACE_IS_CLOSED (ps));
+
+  while (gts_psurface_remove_vertex (ps))
+    ;
+
+  GTS_POINT_CLASS (ps->s->vertex_class)->binary = FALSE;
+  gts_surface_write (ps->s, fptr);
+  
+  gts_surface_foreach_vertex (ps->s, (GtsFunc) index_object, &nv);
+  hash = g_hash_table_new (NULL, NULL);
+  data[0] = hash;
+  data[1] = &nf;
+  gts_surface_foreach_face (ps->s, (GtsFunc) index_face, data);
+
+  fprintf (fptr, "%u\n", ps->split->len);
+  while (ps->pos) {
+    GtsSplit * vs = g_ptr_array_index (ps->split, --ps->pos);
+    GtsSplitCFace * scf = vs->cfaces;
+    GtsVertex * v1, * v2;
+    guint i = vs->ncf;
+
+    fprintf (fptr, "%u %u",
+	     GPOINTER_TO_UINT (GTS_OBJECT (vs->v)->reserved),
+	     vs->ncf);
+    if (GTS_OBJECT (vs)->klass->write)
+      (*GTS_OBJECT (vs)->klass->write) (GTS_OBJECT (vs), fptr);
+    fputc ('\n', fptr);
+
+    v1 = GTS_IS_SPLIT (vs->v1) ? GTS_SPLIT (vs->v1)->v : GTS_VERTEX (vs->v1);
+    GTS_OBJECT (v1)->reserved = GUINT_TO_POINTER (nv++);
+    v2 = GTS_IS_SPLIT (vs->v2) ? GTS_SPLIT (vs->v2)->v : GTS_VERTEX (vs->v2);
+    GTS_OBJECT (v2)->reserved = GUINT_TO_POINTER (nv++);
+
+    (*GTS_OBJECT (v1)->klass->write) (GTS_OBJECT (v1), fptr);
+    fputc ('\n', fptr);
+
+    (*GTS_OBJECT (v2)->klass->write) (GTS_OBJECT (v2), fptr);
+    fputc ('\n', fptr);
+    
+    while (i--) {
+      CFace * cf = CFACE (scf->f);
+      GtsTriangle ** a, * t;
+
+      fprintf (fptr, "%u %u",
+	       GPOINTER_TO_UINT (g_hash_table_lookup (hash, cf->t)),
+	       cf->flags);
+      if (GTS_OBJECT_CLASS (ps->s->face_class)->write)
+	(*GTS_OBJECT_CLASS (ps->s->face_class)->write) (GTS_OBJECT (cf), fptr);
+      fputc ('\n', fptr);
+
+      a = scf->a1;
+      while ((t = *(a++)))
+	fprintf (fptr, "%u ",
+		 GPOINTER_TO_UINT (g_hash_table_lookup (hash, t)));
+      fprintf (fptr, "\n");
+
+      a = scf->a2;
+      while ((t = *(a++)))
+	fprintf (fptr, "%u ",
+		 GPOINTER_TO_UINT (g_hash_table_lookup (hash, t)));
+      fprintf (fptr, "\n");
+
+      g_hash_table_insert (hash, cf, GUINT_TO_POINTER (nf++));
+
+      scf++;
+    }
+
+    gts_split_expand (vs, ps->s, ps->s->edge_class);
+  }
+
+  gts_surface_foreach_vertex (ps->s, 
+			      (GtsFunc) gts_object_reset_reserved, NULL);
+  g_hash_table_destroy (hash);
+}
+
+static guint surface_read (GtsSurface * surface, 
+			   GtsFile * f,
+			   GPtrArray * vertices,
+			   GPtrArray * faces)
+{
+  GtsEdge ** edges;
+  guint n, nv, ne, nf;
+
+  g_return_val_if_fail (surface != NULL, 1);
+  g_return_val_if_fail (f != NULL, 1);
+  
+  if (f->type != GTS_INT) {
+    gts_file_error (f, "expecting an integer (number of vertices)");
+    return f->line;
+  }
+  nv = atoi (f->token->str);
+
+  gts_file_next_token (f);
+  if (f->type != GTS_INT) {
+    gts_file_error (f, "expecting an integer (number of edges)");
+    return f->line;
+  }
+  ne = atoi (f->token->str);
+
+  gts_file_next_token (f);
+  if (f->type != GTS_INT) {
+    gts_file_error (f, "expecting an integer (number of faces)");
+    return f->line;
+  }
+  nf = atoi (f->token->str);
+
+  gts_file_next_token (f);
+  if (f->type == GTS_STRING) {
+    if (f->type != GTS_STRING) {
+      gts_file_error (f, "expecting a string (GtsSurfaceClass)");
+      return f->line;
+    }
+    gts_file_next_token (f);
+    if (f->type != GTS_STRING) {
+      gts_file_error (f, "expecting a string (GtsFaceClass)");
+      return f->line;
+    }
+    gts_file_next_token (f);
+    if (f->type != GTS_STRING) {
+      gts_file_error (f, "expecting a string (GtsEdgeClass)");
+      return f->line;
+    }
+    gts_file_next_token (f);
+    if (f->type != GTS_STRING) {
+      gts_file_error (f, "expecting a string (GtsVertexClass)");
+      return f->line;
+    }
+    if (!strcmp (f->token->str, "GtsVertexBinary"))
+      GTS_POINT_CLASS (surface->vertex_class)->binary = TRUE;
+    else
+      gts_file_first_token_after (f, '\n');
+  }
+  else
+    gts_file_first_token_after (f, '\n');
+
+  g_ptr_array_set_size (vertices, nv);
+  g_ptr_array_set_size (faces, nf);
+  /* allocate nv + 1 just in case nv == 0 */
+  edges = g_malloc ((ne + 1)*sizeof (GtsEdge *));
+  
+  n = 0;
+  while (n < nv && f->type != GTS_ERROR) {
+    GtsObject * new_vertex =
+      gts_object_new (GTS_OBJECT_CLASS (surface->vertex_class));
+
+    (* GTS_OBJECT_CLASS (surface->vertex_class)->read) (&new_vertex, f);
+    if (f->type != GTS_ERROR) {
+      if (!GTS_POINT_CLASS (surface->vertex_class)->binary)
+	gts_file_first_token_after (f, '\n');
+      g_ptr_array_index (vertices, n++) = new_vertex;
+    }
+    else
+      gts_object_destroy (new_vertex);
+  }
+  if (f->type == GTS_ERROR)
+    nv = n;
+  if (GTS_POINT_CLASS (surface->vertex_class)->binary)
+    gts_file_first_token_after (f, '\n');
+
+  n = 0;
+  while (n < ne && f->type != GTS_ERROR) {
+    guint p1, p2;
+
+    if (f->type != GTS_INT)
+      gts_file_error (f, "expecting an integer (first vertex index)");
+    else {
+      p1 = atoi (f->token->str);
+      if (p1 == 0 || p1 > nv)
+	gts_file_error (f, "vertex index `%d' is out of range `[1,%d]'", 
+			p1, nv);
+      else {
+	gts_file_next_token (f);
+	if (f->type != GTS_INT)
+	  gts_file_error (f, "expecting an integer (second vertex index)");
+	else {
+	  p2 = atoi (f->token->str);
+	  if (p2 == 0 || p2 > nv)
+	    gts_file_error (f, "vertex index `%d' is out of range `[1,%d]'", 
+			    p2, nv);
+	  else {
+	    GtsEdge * new_edge =
+	      gts_edge_new (surface->edge_class,
+			    g_ptr_array_index (vertices, p1 - 1),
+			    g_ptr_array_index (vertices, p2 - 1));
+
+	    gts_file_next_token (f);
+	    if (f->type != '\n')
+	      if (GTS_OBJECT_CLASS (surface->edge_class)->read)
+		(*GTS_OBJECT_CLASS (surface->edge_class)->read)
+		  ((GtsObject **) &new_edge, f);
+	    gts_file_first_token_after (f, '\n');
+	    edges[n++] = new_edge;
+	  }
+	}
+      }
+    }
+  }
+  if (f->type == GTS_ERROR)
+    ne = n;
+
+  n = 0;
+  while (n < nf && f->type != GTS_ERROR) {
+    guint s1, s2, s3;
+
+    if (f->type != GTS_INT)
+      gts_file_error (f, "expecting an integer (first edge index)");
+    else {
+      s1 = atoi (f->token->str);
+      if (s1 == 0 || s1 > ne)
+	gts_file_error (f, "edge index `%d' is out of range `[1,%d]'", 
+			s1, ne);
+      else {
+	gts_file_next_token (f);
+	if (f->type != GTS_INT)
+	  gts_file_error (f, "expecting an integer (second edge index)");
+	else {
+	  s2 = atoi (f->token->str);
+	  if (s2 == 0 || s2 > ne)
+	    gts_file_error (f, "edge index `%d' is out of range `[1,%d]'", 
+			    s2, ne);
+	  else {
+	    gts_file_next_token (f);
+	    if (f->type != GTS_INT)
+	      gts_file_error (f, "expecting an integer (third edge index)");
+	    else {
+	      s3 = atoi (f->token->str);
+	      if (s3 == 0 || s3 > ne)
+		gts_file_error (f, "edge index `%d' is out of range `[1,%d]'", 
+				s3, ne);
+	      else {
+		GtsFace * new_face = gts_face_new (surface->face_class,
+						   edges[s1 - 1],
+						   edges[s2 - 1],
+						   edges[s3 - 1]);
+
+		gts_file_next_token (f);
+		if (f->type != '\n')
+		  if (GTS_OBJECT_CLASS (surface->face_class)->read)
+		    (*GTS_OBJECT_CLASS (surface->face_class)->read)
+		      ((GtsObject **) &new_face, f);
+		gts_file_first_token_after (f, '\n');
+		gts_surface_add_face (surface, new_face);
+		g_ptr_array_index (faces, n++) = new_face;
+	      }
+	    }
+	  }
+	}
+      }
+    }
+  }
+
+  g_free (edges);
+
+  if (f->type == GTS_ERROR) {
+    gts_allow_floating_vertices = TRUE;
+    while (nv)
+      gts_object_destroy (GTS_OBJECT (g_ptr_array_index (vertices, nv-- - 1)));
+    gts_allow_floating_vertices = FALSE;    
+    return f->line;
+  }
+
+  return 0;
+}
+
+/**
+ * gts_psurface_open:
+ * @klass: a #GtsPSurfaceClass.
+ * @s: a #GtsSurface.
+ * @split_class: a #GtsSplitClass to use for the #GtsSplit.
+ * @f: a #GtsFile.
+ *
+ * Creates a new #GtsPSurface prepared for input from the file @f 
+ * containing a valid GTS representation of a progressive surface. The initial
+ * shape of the progressive surface is loaded into @s.
+ * 
+ * Before being usable as such this progressive surface must be closed using
+ * gts_psurface_close(). While open however, the functions
+ * gts_psurface_get_vertex_number(), gts_psurface_min_vertex_number() and
+ * gts_psurface_max_vertex_number() can still be used.
+ *
+ * Returns: a new #GtsPSurface or %NULL if there was a format error while
+ * reading the file, in which case @f contains information about the error.
+ */
+GtsPSurface * gts_psurface_open (GtsPSurfaceClass * klass,
+				 GtsSurface * s,
+				 GtsSplitClass * split_class,
+				 GtsFile * f)
+{
+  GtsPSurface * ps;
+
+  g_return_val_if_fail (klass != NULL, NULL);
+  g_return_val_if_fail (s != NULL, NULL);
+  g_return_val_if_fail (split_class != NULL, NULL);
+  g_return_val_if_fail (f != NULL, NULL);
+
+  ps = GTS_PSURFACE (gts_object_new (GTS_OBJECT_CLASS (klass)));
+  ps->s = s;
+  ps->split_class = split_class;
+
+  ps->vertices = g_ptr_array_new ();
+  ps->faces = g_ptr_array_new ();
+
+  if (surface_read (s, f, ps->vertices, ps->faces)) {
+    ps->s = NULL;
+    gts_object_destroy (GTS_OBJECT (ps));
+    return NULL;
+  }
+
+  ps->min = gts_surface_vertex_number (ps->s);
+  ps->pos = 0;
+
+  if (f->type == GTS_INT) {
+    gint ns = atoi (f->token->str);
+    
+    if (ns > 0) {
+      g_ptr_array_set_size (ps->split, ns);
+      gts_file_first_token_after (f, '\n');
+    }
+  }
+
+  return ps;
+}
+
+/**
+ * gts_psurface_read_vertex:
+ * @ps: a #GtsPSurface prealably created with gts_psurface_open().
+ * @fp: a #GtsFile.
+ *
+ * Reads in one vertex split operation from @fp and performs the expansion.
+ *
+ * If an error occurs while reading the file, the @error field of @fp is set.
+ *
+ * Returns: the newly created #GtsSplit or %NULL if no vertex split could be
+ * read from @fp.
+ */
+GtsSplit * gts_psurface_read_vertex (GtsPSurface * ps, GtsFile * fp)
+{
+  guint nv, ncf;
+  GtsSplit * vs, * parent;
+  GtsSplitCFace * scf;
+
+  g_return_val_if_fail (ps != NULL, NULL);
+  g_return_val_if_fail (fp != NULL, NULL);
+  g_return_val_if_fail (!GTS_PSURFACE_IS_CLOSED (ps), NULL);
+  
+  if (ps->pos >= ps->split->len)
+    return NULL;
+
+  if (fp->type == GTS_NONE)
+    return NULL;
+  if (fp->type != GTS_INT) {
+    gts_file_error (fp, "expecting an integer (vertex index)");
+    return NULL;
+  }
+  nv = atoi (fp->token->str);
+  if (nv == 0 || nv > ps->vertices->len) {
+    gts_file_error (fp, "vertex index `%d' is out of range `[1,%d]'",
+		    nv, ps->vertices->len);
+    return NULL;
+  }
+
+  gts_file_next_token (fp);
+  if (fp->type != GTS_INT) {
+    gts_file_error (fp, "expecting an integer (ncf)");
+    return NULL;
+  }
+  ncf = atoi (fp->token->str);
+  
+  vs = GTS_SPLIT (gts_object_new (GTS_OBJECT_CLASS (ps->split_class)));
+
+  vs->v = g_ptr_array_index (ps->vertices, nv - 1);
+  vs->v1 = vs->v2 = NULL;
+  vs->cfaces = NULL;
+  vs->ncf = 0;
+
+  gts_file_next_token (fp);
+  if (fp->type != '\n')
+    if (GTS_OBJECT (vs)->klass->read)
+      (* GTS_OBJECT (vs)->klass->read) ((GtsObject **) &vs, fp);
+  gts_file_first_token_after (fp, '\n');
+
+  if (fp->type != GTS_ERROR) {
+    vs->v1 = gts_object_new (GTS_OBJECT_CLASS (ps->s->vertex_class));
+    (* GTS_OBJECT_CLASS (ps->s->vertex_class)->read) (&(vs->v1), fp);
+    if (fp->type != GTS_ERROR) {
+      vs->v1->reserved = vs;
+      g_ptr_array_add (ps->vertices, vs->v1);
+
+      gts_file_first_token_after (fp, '\n');
+      
+      vs->v2 = gts_object_new (GTS_OBJECT_CLASS (ps->s->vertex_class));
+      (*GTS_OBJECT_CLASS (ps->s->vertex_class)->read) (&(vs->v2), fp);
+      if (fp->type != GTS_ERROR) {
+	vs->v2->reserved = vs;
+	g_ptr_array_add (ps->vertices, vs->v2);
+	gts_file_first_token_after (fp, '\n');
+      }
+    }
+  }
+
+  if (fp->type != GTS_ERROR) {
+    scf = vs->cfaces = g_malloc (sizeof (GtsSplitCFace)*ncf);
+    while (fp->type != GTS_ERROR && ncf--) {
+      guint it, flags;
+      GtsFace * f;
+      CFace * cf;
+      GPtrArray * a;
+
+      if (fp->type != GTS_INT)
+	gts_file_error (fp, "expecting an integer (face index)");
+      else {
+	it = atoi (fp->token->str);
+	if (it == 0 || it > ps->faces->len)
+	  gts_file_error (fp, "face index `%d' is out of range `[1,%d]'",
+			  it, ps->faces->len);
+	else {
+	  gts_file_next_token (fp);
+	  if (fp->type != GTS_INT)
+	    gts_file_error (fp, "expecting an integer (flags)");
+	  else {
+	    flags = atoi (fp->token->str);
+	    f = 
+	      GTS_FACE (gts_object_new (GTS_OBJECT_CLASS (ps->s->face_class)));
+
+	    gts_file_next_token (fp);
+	    if (fp->type != '\n')
+	      if (GTS_OBJECT (f)->klass->read)
+		(*GTS_OBJECT (f)->klass->read) ((GtsObject **) &f, fp);
+	    gts_file_first_token_after (fp, '\n');
+	    if (fp->type != GTS_ERROR) {
+	      scf->f = f;
+
+	      cf = (CFace *) f;
+	      GTS_OBJECT (cf)->klass = GTS_OBJECT_CLASS (cface_class ());
+	      cf->parent_split = vs;
+	      cf->t = g_ptr_array_index (ps->faces, it - 1);
+	      cf->flags = flags;
+	  
+	      a = g_ptr_array_new ();
+	      do {
+		if (fp->type != GTS_INT)
+		  gts_file_error (fp, "expecting an integer (face index)");
+		else {
+		  it = atoi (fp->token->str);
+		  if (it > ps->faces->len)
+		    gts_file_error (fp, 
+				    "face index `%d' is out of range `[1,%d]'",
+				    it, ps->faces->len);
+		  else {
+		    g_ptr_array_add (a, g_ptr_array_index (ps->faces, 
+							   it - 1));
+		    gts_file_next_token (fp);
+		  }
+		}
+	      } while (fp->type != GTS_ERROR && fp->type != '\n');
+	      gts_file_first_token_after (fp, '\n');
+	      g_ptr_array_add (a, NULL);
+	      scf->a1 = (GtsTriangle **) a->pdata;
+	      g_ptr_array_free (a, FALSE);
+
+	      if (fp->type != GTS_ERROR) {
+		a = g_ptr_array_new ();
+		do {
+		  if (fp->type != GTS_INT)
+		    gts_file_error (fp, "expecting an integer (face index)");
+		  else {
+		    it = atoi (fp->token->str);
+		    if (it > ps->faces->len)
+		      gts_file_error (fp, 
+				   "face index `%d' is out of range `[1,%d]'",
+				      it, ps->faces->len);
+		    else {
+		      g_ptr_array_add (a, g_ptr_array_index (ps->faces, 
+							     it - 1));
+		      gts_file_next_token (fp);
+		    }
+		  }
+		} while (fp->type != GTS_ERROR && fp->type != '\n');
+		gts_file_first_token_after (fp, '\n');
+		g_ptr_array_add (a, NULL);
+		scf->a2 = (GtsTriangle **) a->pdata;
+		g_ptr_array_free (a, FALSE);
+		
+		g_ptr_array_add (ps->faces, f);
+	      
+		vs->ncf++;
+		scf++;
+	      }
+	    }
+	  }
+	}
+      }
+    }
+  }
+
+  if (fp->type != GTS_ERROR) {
+    if ((parent = GTS_OBJECT (vs->v)->reserved)) {
+      GTS_OBJECT (vs->v)->reserved = NULL;
+      if (parent->v1 == GTS_OBJECT (vs->v))
+	parent->v1 = GTS_OBJECT (vs);
+      else {
+	g_assert (parent->v2 == GTS_OBJECT (vs->v));
+	parent->v2 = GTS_OBJECT (vs);
+      }
+    }
+    g_ptr_array_index (ps->split, ps->pos++) = vs;
+    gts_split_expand (vs, ps->s, ps->s->edge_class);
+
+    return vs;
+  }
+
+  if (vs->v1) gts_object_destroy (vs->v1);
+  if (vs->v2) gts_object_destroy (vs->v2);
+  gts_object_destroy (GTS_OBJECT (vs));
+  
+  return NULL;
+}
+
+/**
+ * gts_psurface_close:
+ * @ps: a #GtsPSurface prealably created with gts_psurface_open().
+ *
+ * Closes a progressive surface.
+ */
+void gts_psurface_close (GtsPSurface * ps)
+{
+  g_return_if_fail (ps != NULL);
+  g_return_if_fail (!GTS_PSURFACE_IS_CLOSED (ps));
+
+  g_ptr_array_free (ps->vertices, TRUE);
+  g_ptr_array_free (ps->faces, TRUE);
+  ps->faces = ps->vertices = NULL;
+  
+  gts_surface_foreach_vertex (ps->s, 
+			      (GtsFunc) gts_object_reset_reserved, NULL);
+  if (ps->pos > 0)
+    g_ptr_array_set_size (ps->split, ps->pos);
+  if (ps->split->len > 1) {
+    guint i, half = ps->split->len/2, n = ps->split->len - 1;
+    
+    for (i = 0; i < half; i++) {
+      gpointer p1 = g_ptr_array_index (ps->split, i);
+      gpointer p2 = g_ptr_array_index (ps->split, n - i);
+      g_ptr_array_index (ps->split, n - i) = p1;
+      g_ptr_array_index (ps->split, i) = p2;
+    }
+  }
+  ps->pos = 0;
+}
diff --git a/src_3rd/gts/stripe.c b/src_3rd/gts/stripe.c
new file mode 100644
index 0000000..7e98a9c
--- /dev/null
+++ b/src_3rd/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/src_3rd/gts/surface.c b/src_3rd/gts/surface.c
new file mode 100644
index 0000000..34c5cbe
--- /dev/null
+++ b/src_3rd/gts/surface.c
@@ -0,0 +1,2743 @@
+/* GTS - Library for the manipulation of triangulated surfaces
+ * Copyright (C) 1999 St�phane Popinet
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <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;
+}
+
+/*
+ * I don't see where this code is ever used, but keep it for a bit 
+ * in case it is needed for debugging
+ */
+#ifdef GTS_NEED_UPDATE_CLOSEST_NEIGHBORS
+static void update_closest_neighbors (GtsVertex * v, GtsEHeap * heap)
+{
+  GSList * i = v->segments;
+  
+  while (i) {
+    GtsSegment * s = i->data;
+    if (GTS_IS_EDGE (s)) {
+      HEAP_REMOVE_EDGE (heap, GTS_EDGE (s));
+      HEAP_INSERT_EDGE (heap, GTS_EDGE (s));
+    }
+    i = i->next;
+  }
+}
+#endif
+
+static void update_2nd_closest_neighbors (GtsVertex * v, GtsEHeap * heap)
+{
+  GSList * i = v->segments;
+  GSList * list = NULL;
+  
+  while (i) {
+    GtsSegment * s = i->data;
+    if (GTS_IS_EDGE (s)) {
+      GtsVertex * v1 = s->v1 == v ? s->v2 : s->v1;
+      GSList * j = v1->segments;
+      while (j) {
+	GtsSegment * s1 = j->data;
+	if (GTS_IS_EDGE (s1) && !g_slist_find (list, s1))
+	  list = g_slist_prepend (list, s1);
+	j = j->next;
+      }
+    }
+    i = i->next;
+  }
+
+  i = list;
+  while (i) {
+    GtsEdge * e = i->data;
+    HEAP_REMOVE_EDGE (heap, e);
+    HEAP_INSERT_EDGE (heap, e);
+    i = i->next;
+  }
+
+  g_slist_free (list);
+}
+
+static gdouble edge_length2 (GtsEdge * e)
+{
+  return gts_point_distance2 (GTS_POINT (GTS_SEGMENT (e)->v1), 
+			      GTS_POINT (GTS_SEGMENT (e)->v2));
+}
+
+static void create_heap_coarsen (GtsEdge * e, GtsEHeap * heap)
+{
+  HEAP_INSERT_EDGE (heap, e);
+}
+
+/**
+ * gts_surface_coarsen:
+ * @surface: a #GtsSurface.
+ * @cost_func: a function returning the cost for a given edge.
+ * @cost_data: user data to be passed to @cost_func.
+ * @coarsen_func: a #GtsCoarsenVertexFunc.
+ * @coarsen_data: user data to be passed to @coarsen_func.
+ * @stop_func: a #GtsStopFunc.
+ * @stop_data: user data to be passed to @stop_func.
+ * @minangle: minimum angle between two neighboring triangles.
+ *
+ * The edges of @surface are sorted according to @cost_func to 
+ * create a priority heap (a #GtsEHeap). The edges are extracted in
+ * turn from the top of the heap and collapsed (i.e. the vertices are
+ * replaced by the vertex returned by the @coarsen_func function)
+ * until the @stop_func functions returns %TRUE.
+ *
+ * If @cost_func is set to %NULL, the edges are sorted according 
+ * to their length squared (the shortest is on top).
+ *
+ * If @coarsen_func is set to %NULL gts_segment_midvertex() is used.
+ *
+ * The minimum angle is used to avoid introducing faces which would be folded.
+ */
+void gts_surface_coarsen (GtsSurface * surface,
+			  GtsKeyFunc cost_func,
+			  gpointer cost_data,
+			  GtsCoarsenFunc coarsen_func,
+			  gpointer coarsen_data,
+			  GtsStopFunc stop_func,
+			  gpointer stop_data,
+			  gdouble minangle)
+{
+  GtsEHeap * heap;
+  GtsEdge * e;
+  gdouble top_cost;
+  gdouble maxcosine2;
+
+  g_return_if_fail (surface != NULL);
+  g_return_if_fail (stop_func != NULL);
+
+  if (cost_func == NULL)
+    cost_func = (GtsKeyFunc) edge_length2;
+  if (coarsen_func == NULL)
+    coarsen_func = (GtsCoarsenFunc) gts_segment_midvertex;
+
+  heap = gts_eheap_new (cost_func, cost_data);
+  maxcosine2 = cos (minangle); maxcosine2 *= maxcosine2;
+
+  gts_eheap_freeze (heap);
+  gts_surface_foreach_edge (surface, (GtsFunc) create_heap_coarsen, heap);
+  gts_eheap_thaw (heap);
+  /* we want to control edge destruction manually */
+  gts_allow_floating_edges = TRUE;
+  while ((e = gts_eheap_remove_top (heap, &top_cost)) &&
+	 (top_cost < G_MAXDOUBLE) &&
+	 !(*stop_func) (top_cost, gts_eheap_size (heap) - 
+			gts_edge_face_number (e, surface), stop_data))
+    {
+      GtsVertex * v = edge_collapse (e, heap, coarsen_func, coarsen_data,
+				     surface->vertex_class, maxcosine2);
+      if (v != NULL)
+	update_2nd_closest_neighbors (v, heap);
+    }
+  gts_allow_floating_edges = FALSE;
+
+  /* set reserved field of remaining edges back to NULL */
+  if (e) GTS_OBJECT (e)->reserved = NULL;
+  gts_eheap_foreach (heap, (GFunc) gts_object_reset_reserved, NULL);
+
+  gts_eheap_destroy (heap);
+}
+
+/**
+ * gts_coarsen_stop_number:
+ * @cost: the cost of the edge collapse considered.
+ * @nedge: the current number of edges of the surface being simplified.
+ * @min_number: a pointer to the minimum number of edges desired for the 
+ * surface being simplified.
+ *
+ * This function is to be used as the @stop_func argument of 
+ * gts_surface_coarsen() or gts_psurface_new().
+ *
+ * Returns: %TRUE if the edge collapse would create a surface with a smaller 
+ * number of edges than given by @min_number, %FALSE otherwise.
+ */
+gboolean gts_coarsen_stop_number (gdouble cost, 
+				  guint nedge, 
+				  guint * min_number)
+{
+  g_return_val_if_fail (min_number != NULL, TRUE);
+
+  if (nedge < *min_number)
+    return TRUE;
+  return FALSE;
+}
+
+/**
+ * gts_coarsen_stop_cost:
+ * @cost: the cost of the edge collapse considered.
+ * @nedge: the current number of edges of the surface being simplified.
+ * @max_cost: a pointer to the maximum cost allowed for an edge collapse.
+ *
+ * This function is to be used as the @stop_func argument of 
+ * gts_surface_coarsen() or gts_psurface_new().
+ *
+ * Returns: %TRUE if the cost of the edge collapse considered is larger than
+ * given by @max_cost, %FALSE otherwise.
+ */
+gboolean gts_coarsen_stop_cost (gdouble cost, 
+				guint nedge, 
+				gdouble * max_cost)
+{
+  g_return_val_if_fail (max_cost != NULL, TRUE);
+
+  if (cost > *max_cost)
+    return TRUE;
+  return FALSE;
+}
+
+#define GTS_M_ICOSAHEDRON_X /* sqrt(sqrt(5)+1)/sqrt(2*sqrt(5)) */ \
+  0.850650808352039932181540497063011072240401406
+#define GTS_M_ICOSAHEDRON_Y /* sqrt(2)/sqrt(5+sqrt(5))         */ \
+  0.525731112119133606025669084847876607285497935
+#define GTS_M_ICOSAHEDRON_Z 0.0
+
+static guint generate_icosahedron (GtsSurface * s)
+{
+  GtsVertex * v01 = gts_vertex_new (s->vertex_class,
+      +GTS_M_ICOSAHEDRON_Z, +GTS_M_ICOSAHEDRON_X, -GTS_M_ICOSAHEDRON_Y);
+  GtsVertex * v02 = gts_vertex_new (s->vertex_class,
+      +GTS_M_ICOSAHEDRON_X, +GTS_M_ICOSAHEDRON_Y, +GTS_M_ICOSAHEDRON_Z);
+  GtsVertex * v03 = gts_vertex_new (s->vertex_class,
+      +GTS_M_ICOSAHEDRON_Y, +GTS_M_ICOSAHEDRON_Z, -GTS_M_ICOSAHEDRON_X);
+  GtsVertex * v04 = gts_vertex_new (s->vertex_class,
+      +GTS_M_ICOSAHEDRON_Y, +GTS_M_ICOSAHEDRON_Z, +GTS_M_ICOSAHEDRON_X);
+  GtsVertex * v05 = gts_vertex_new (s->vertex_class,
+      +GTS_M_ICOSAHEDRON_X, -GTS_M_ICOSAHEDRON_Y, +GTS_M_ICOSAHEDRON_Z);
+  GtsVertex * v06 = gts_vertex_new (s->vertex_class,
+      +GTS_M_ICOSAHEDRON_Z, +GTS_M_ICOSAHEDRON_X, +GTS_M_ICOSAHEDRON_Y);
+  GtsVertex * v07 = gts_vertex_new (s->vertex_class,
+      -GTS_M_ICOSAHEDRON_Y, +GTS_M_ICOSAHEDRON_Z, +GTS_M_ICOSAHEDRON_X);
+  GtsVertex * v08 = gts_vertex_new (s->vertex_class,
+      +GTS_M_ICOSAHEDRON_Z, -GTS_M_ICOSAHEDRON_X, -GTS_M_ICOSAHEDRON_Y);
+  GtsVertex * v09 = gts_vertex_new (s->vertex_class,
+      -GTS_M_ICOSAHEDRON_X, +GTS_M_ICOSAHEDRON_Y, +GTS_M_ICOSAHEDRON_Z);
+  GtsVertex * v10 = gts_vertex_new (s->vertex_class,
+      -GTS_M_ICOSAHEDRON_Y, +GTS_M_ICOSAHEDRON_Z, -GTS_M_ICOSAHEDRON_X);
+  GtsVertex * v11 = gts_vertex_new (s->vertex_class,
+      -GTS_M_ICOSAHEDRON_X, -GTS_M_ICOSAHEDRON_Y, +GTS_M_ICOSAHEDRON_Z);
+  GtsVertex * v12 = gts_vertex_new (s->vertex_class,
+      +GTS_M_ICOSAHEDRON_Z, -GTS_M_ICOSAHEDRON_X, +GTS_M_ICOSAHEDRON_Y);
+
+  GtsEdge * e01 = gts_edge_new (s->edge_class, v01, v02);
+  GtsEdge * e02 = gts_edge_new (s->edge_class, v03, v02);
+  GtsEdge * e03 = gts_edge_new (s->edge_class, v01, v03);
+  GtsEdge * e04 = gts_edge_new (s->edge_class, v04, v05);
+  GtsEdge * e05 = gts_edge_new (s->edge_class, v02, v05);
+  GtsEdge * e06 = gts_edge_new (s->edge_class, v04, v02);
+  GtsEdge * e07 = gts_edge_new (s->edge_class, v06, v07);
+  GtsEdge * e08 = gts_edge_new (s->edge_class, v04, v07);
+  GtsEdge * e09 = gts_edge_new (s->edge_class, v06, v04);
+  GtsEdge * e10 = gts_edge_new (s->edge_class, v08, v03);
+  GtsEdge * e11 = gts_edge_new (s->edge_class, v03, v05);
+  GtsEdge * e12 = gts_edge_new (s->edge_class, v08, v05);
+  GtsEdge * e13 = gts_edge_new (s->edge_class, v06, v09);
+  GtsEdge * e14 = gts_edge_new (s->edge_class, v07, v09);
+  GtsEdge * e15 = gts_edge_new (s->edge_class, v08, v10);
+  GtsEdge * e16 = gts_edge_new (s->edge_class, v03, v10);
+  GtsEdge * e17 = gts_edge_new (s->edge_class, v06, v01);
+  GtsEdge * e18 = gts_edge_new (s->edge_class, v01, v09);
+  GtsEdge * e19 = gts_edge_new (s->edge_class, v08, v11);
+  GtsEdge * e20 = gts_edge_new (s->edge_class, v10, v11);
+  GtsEdge * e21 = gts_edge_new (s->edge_class, v06, v02);
+  GtsEdge * e22 = gts_edge_new (s->edge_class, v12, v11);
+  GtsEdge * e23 = gts_edge_new (s->edge_class, v12, v08);
+  GtsEdge * e24 = gts_edge_new (s->edge_class, v12, v07);
+  GtsEdge * e25 = gts_edge_new (s->edge_class, v07, v11);
+  GtsEdge * e26 = gts_edge_new (s->edge_class, v12, v04);
+  GtsEdge * e27 = gts_edge_new (s->edge_class, v09, v11);
+  GtsEdge * e28 = gts_edge_new (s->edge_class, v10, v09);
+  GtsEdge * e29 = gts_edge_new (s->edge_class, v12, v05);
+  GtsEdge * e30 = gts_edge_new (s->edge_class, v01, v10);
+  
+  gts_surface_add_face (s, gts_face_new (s->face_class, e01, e02, e03));
+  gts_surface_add_face (s, gts_face_new (s->face_class, e04, e05, e06));
+  gts_surface_add_face (s, gts_face_new (s->face_class, e07, e08, e09));
+  gts_surface_add_face (s, gts_face_new (s->face_class, e10, e11, e12));
+  gts_surface_add_face (s, gts_face_new (s->face_class, e13, e14, e07));
+  gts_surface_add_face (s, gts_face_new (s->face_class, e15, e16, e10));
+  gts_surface_add_face (s, gts_face_new (s->face_class, e17, e18, e13));
+  gts_surface_add_face (s, gts_face_new (s->face_class, e19, e20, e15));
+  gts_surface_add_face (s, gts_face_new (s->face_class, e21, e01, e17));
+  gts_surface_add_face (s, gts_face_new (s->face_class, e22, e19, e23));
+  gts_surface_add_face (s, gts_face_new (s->face_class, e09, e06, e21));
+  gts_surface_add_face (s, gts_face_new (s->face_class, e24, e25, e22));
+  gts_surface_add_face (s, gts_face_new (s->face_class, e26, e08, e24));
+  gts_surface_add_face (s, gts_face_new (s->face_class, e20, e27, e28));
+  gts_surface_add_face (s, gts_face_new (s->face_class, e29, e04, e26));
+  gts_surface_add_face (s, gts_face_new (s->face_class, e14, e27, e25));
+  gts_surface_add_face (s, gts_face_new (s->face_class, e23, e12, e29));
+  gts_surface_add_face (s, gts_face_new (s->face_class, e02, e05, e11));
+  gts_surface_add_face (s, gts_face_new (s->face_class, e30, e28, e18));
+  gts_surface_add_face (s, gts_face_new (s->face_class, e03, e16, e30));
+
+  return 0;
+}
+
+static GtsVertex * unit_sphere_arc_midvertex (GtsSegment * s, 
+					      GtsVertexClass * vertex_class)
+{
+  GtsPoint * p1, * p2;
+  gdouble x, y, z, norm;
+
+  p1 = GTS_POINT (s->v1); p2 = GTS_POINT (s->v2);
+
+  x = 0.5*(p1->x + p2->x);
+  y = 0.5*(p1->y + p2->y);
+  z = 0.5*(p1->z + p2->z);
+
+  norm = x*x + y*y + z*z;
+  norm = sqrt (norm);
+
+  x /= norm; y /= norm; z /= norm;
+
+  return gts_vertex_new (vertex_class, x, y, z);
+}
+
+static void tessellate_face (GtsFace * f,
+			     GtsSurface * s,
+			     GtsRefineFunc refine_func,
+			     gpointer refine_data,
+			     GtsVertexClass * vertex_class,
+			     GtsEdgeClass * edge_class)
+{
+  GtsTriangle * t;
+  GtsEdge * e1, * e2, * e3;                          /* former edges     */
+  GtsVertex * v1, * v2, * v3;                        /* initial vertices */
+  GtsVertex * v4, * v5, * v6;                        /* new vertices     */ 
+  GtsEdge * e56, * e64, * e45;                       /* new inside edges */
+  GtsEdge * e24, * e34, * e35, * e15, * e16, * e26;  /* new border edges */
+  GSList * dum;
+  GtsEdge * edum;
+  
+  t = GTS_TRIANGLE (f);
+  e1 = t->e1; e2 = t->e2; e3 = t->e3;
+
+  if (GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e2)->v1) {
+    v1 = GTS_SEGMENT (e2)->v2;
+    v2 = GTS_SEGMENT (e1)->v1;
+    v3 = GTS_SEGMENT (e1)->v2;
+  }
+  else if (GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e2)->v2) {
+    v1 = GTS_SEGMENT (e2)->v1;
+    v2 = GTS_SEGMENT (e1)->v1;
+    v3 = GTS_SEGMENT (e1)->v2;
+  }
+  else if (GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e2)->v1) {
+    v1 = GTS_SEGMENT (e2)->v2;
+    v2 = GTS_SEGMENT (e1)->v2;
+    v3 = GTS_SEGMENT (e1)->v1;
+  }
+  else if (GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e2)->v2) {
+    v1 = GTS_SEGMENT (e2)->v1;
+    v2 = GTS_SEGMENT (e1)->v2;
+    v3 = GTS_SEGMENT (e1)->v1;
+  }
+  else {
+    v1 = v2 = v3 = NULL;
+    g_assert_not_reached ();
+  }
+
+  e1->triangles = g_slist_remove (e1->triangles, t);
+  e2->triangles = g_slist_remove (e2->triangles, t);
+  e3->triangles = g_slist_remove (e3->triangles, t);
+  
+  if (GTS_OBJECT (e1)->reserved) {
+    dum = (GTS_OBJECT (e1)->reserved);
+    e24 = dum->data;
+    e34 = dum->next->data;
+    v4 = GTS_SEGMENT (e24)->v2;
+    if (GTS_SEGMENT (e24)->v1 == v3) {
+      edum = e34; e34 = e24; e24 = edum;
+    }
+  }
+  else {
+    v4 = (*refine_func) (e1, vertex_class, refine_data);
+    e24 = gts_edge_new (edge_class, v2, v4);
+    e34 = gts_edge_new (edge_class, v3, v4);
+    dum = g_slist_append (NULL, e24);
+    dum = g_slist_append (dum,  e34);
+    GTS_OBJECT (e1)->reserved = dum;
+  }
+  if (GTS_OBJECT (e2)->reserved) {
+    dum = (GTS_OBJECT (e2)->reserved);
+    e35 = dum->data;
+    e15 = dum->next->data;
+    v5 = GTS_SEGMENT (e35)->v2;
+    if (GTS_SEGMENT (e35)->v1 == v1) {
+      edum = e15; e15 = e35; e35 = edum;
+    }
+  }
+  else {
+    v5 = (*refine_func) (e2, vertex_class, refine_data);
+    e35 = gts_edge_new (edge_class, v3, v5);
+    e15 = gts_edge_new (edge_class, v1, v5);
+    dum = g_slist_append (NULL, e35);
+    dum = g_slist_append (dum,  e15);
+    GTS_OBJECT (e2)->reserved = dum;
+  }
+  if (GTS_OBJECT (e3)->reserved) {
+    dum = (GTS_OBJECT (e3)->reserved);
+    e16 = dum->data;
+    e26 = dum->next->data;
+    v6 = GTS_SEGMENT (e16)->v2;
+    if (GTS_SEGMENT (e16)->v1 == v2) {
+      edum = e16; e16 = e26; e26 = edum;
+    }
+  }
+  else {
+    v6 = (*refine_func) (e3, vertex_class, refine_data);
+    e16 = gts_edge_new (edge_class, v1, v6);
+    e26 = gts_edge_new (edge_class, v2, v6);
+    dum = g_slist_append (NULL, e16);
+    dum = g_slist_append (dum,  e26);
+    GTS_OBJECT (e3)->reserved = dum;
+  }
+  
+  if (e1->triangles == NULL) {
+    g_slist_free (GTS_OBJECT (e1)->reserved);
+    GTS_OBJECT (e1)->reserved = NULL;
+    gts_object_destroy (GTS_OBJECT (e1));
+    e1 = NULL;
+  }
+  if (e2->triangles == NULL) {
+    g_slist_free (GTS_OBJECT (e2)->reserved);
+    GTS_OBJECT (e2)->reserved = NULL;
+    gts_object_destroy (GTS_OBJECT (e2));
+    e2 = NULL;
+  }
+  if (e3->triangles == NULL) {
+    g_slist_free (GTS_OBJECT (e3)->reserved);
+    GTS_OBJECT (e3)->reserved = NULL;
+    gts_object_destroy (GTS_OBJECT (e3));
+    e3 = NULL;
+  }
+
+  e56 = gts_edge_new (edge_class, v5, v6);
+  e64 = gts_edge_new (edge_class, v6, v4);
+  e45 = gts_edge_new (edge_class, v4, v5);
+  t->e1 = e56; e56->triangles = g_slist_prepend (e56->triangles, t);
+  t->e2 = e64; e64->triangles = g_slist_prepend (e64->triangles, t);
+  t->e3 = e45; e45->triangles = g_slist_prepend (e45->triangles, t);
+  
+  gts_surface_add_face (s, gts_face_new (s->face_class, e16, e56, e15));
+  gts_surface_add_face (s, gts_face_new (s->face_class, e26, e24, e64));
+  gts_surface_add_face (s, gts_face_new (s->face_class, e45, e34, e35)); 
+}
+
+static void create_array_tessellate (GtsFace * f, GPtrArray * array)
+{
+  g_ptr_array_add (array, f);
+}
+
+/**
+ * gts_surface_tessellate:
+ * @s: a #GtsSurface.
+ * @refine_func: a #GtsRefineFunc.
+ * @refine_data: user data to be passed to @refine_func.
+ *
+ * Tessellate each triangle of @s with 4 triangles:   
+ * the number of triangles is increased by a factor of 4.
+ * http://mathworld.wolfram.com/GeodesicDome.html
+ *
+ * If @refine_func is set to %NULL a mid arc function is used: if
+ * the surface is a polyhedron with the unit sphere as circum sphere,
+ * then gts_surface_tessellate() corresponds to a geodesation step
+ * (see gts_surface_generate_sphere()).
+ * 
+ */
+void gts_surface_tessellate (GtsSurface * s,
+			     GtsRefineFunc refine_func,
+			     gpointer refine_data)
+{
+  GPtrArray * array;
+  guint i;
+
+  g_return_if_fail (s != NULL);
+  
+  if (refine_func == NULL) /* tessellate_surface == geodesate_surface */
+    refine_func = (GtsRefineFunc) unit_sphere_arc_midvertex;
+
+  array = g_ptr_array_new ();
+  gts_surface_foreach_face (s, (GtsFunc) create_array_tessellate, array);
+  for(i = 0; i < array->len; i++)
+    tessellate_face (g_ptr_array_index (array, i),
+		     s, refine_func, refine_data, 
+		     s->vertex_class, s->edge_class);
+  g_ptr_array_free (array, TRUE);
+}
+
+/**
+ * gts_surface_generate_sphere:
+ * @s: a #GtsSurface.
+ * @geodesation_order: a #guint.
+ *
+ * Add a triangulated unit sphere generated by recursive subdivision to @s.
+ * First approximation is an isocahedron; each level of refinement
+ * (@geodesation_order) increases the number of triangles by a factor of 4.
+ * http://mathworld.wolfram.com/GeodesicDome.html
+ *
+ * Returns: @s.
+ */
+GtsSurface * gts_surface_generate_sphere (GtsSurface * s, 
+					  guint geodesation_order)
+{
+  guint cgo; 
+
+  g_return_val_if_fail (s != NULL, NULL);
+  g_return_val_if_fail (geodesation_order != 0, NULL);
+
+  generate_icosahedron (s);
+
+  for (cgo = 1; cgo < geodesation_order; cgo++)
+    gts_surface_tessellate (s, NULL, NULL);
+  
+  return s;
+}
+
+static void foreach_vertex_copy (GtsPoint * p, GtsVertexClass * klass)
+{
+  GTS_OBJECT (p)->reserved = gts_vertex_new (klass, p->x, p->y, p->z);
+}
+
+static void foreach_edge_copy (GtsSegment * s, GtsEdgeClass * klass)
+{
+  GTS_OBJECT (s)->reserved = gts_edge_new (klass,
+					   GTS_OBJECT (s->v1)->reserved, 
+					   GTS_OBJECT (s->v2)->reserved);
+}
+
+static void foreach_face_copy (GtsTriangle * t,
+			       GtsSurface * s)
+{
+  gts_surface_add_face (s, gts_face_new (s->face_class,
+					 GTS_OBJECT (t->e1)->reserved,
+					 GTS_OBJECT (t->e2)->reserved,
+					 GTS_OBJECT (t->e3)->reserved));
+}
+
+/**
+ * gts_surface_copy:
+ * @s1: a #GtsSurface.
+ * @s2: a #GtsSurface.
+ *
+ * Add a copy of all the faces, edges and vertices of @s2 to @s1.
+ *
+ * Returns: @s1.
+ */
+GtsSurface * gts_surface_copy (GtsSurface * s1, GtsSurface * s2)
+{
+  g_return_val_if_fail (s1 != NULL, NULL);
+  g_return_val_if_fail (s2 != NULL, NULL);
+  
+  gts_surface_foreach_vertex (s2, (GtsFunc) foreach_vertex_copy, 
+			      s1->vertex_class);
+  gts_surface_foreach_edge (s2, (GtsFunc) foreach_edge_copy, s1->edge_class);
+  gts_surface_foreach_face (s2, (GtsFunc) foreach_face_copy, s1);
+
+  gts_surface_foreach_vertex (s2, (GtsFunc) gts_object_reset_reserved, NULL);
+  gts_surface_foreach_edge (s2, (GtsFunc) gts_object_reset_reserved, NULL);
+  
+  return s1;
+}
+
+static void merge_foreach_face (GtsFace * f, 
+				GtsSurface * s)
+{
+  gts_surface_add_face (s, f);
+}
+
+/**
+ * gts_surface_merge:
+ * @s: a #GtsSurface.
+ * @with: another #GtsSurface.
+ *
+ * Adds all the faces of @with which do not already belong to @s
+ * to @s.
+ */
+void gts_surface_merge (GtsSurface * s, GtsSurface * with)
+{
+  g_return_if_fail (s != NULL);
+  g_return_if_fail (with != NULL);
+  
+  gts_surface_foreach_face (with, (GtsFunc) merge_foreach_face, s);
+}
+
+static void manifold_foreach_edge (GtsEdge * e, gpointer * data)
+{
+  gboolean * is_manifold = data[0];
+
+  if (*is_manifold) {
+    if (gts_edge_face_number (e, data[1]) > 2)
+      *is_manifold = FALSE;
+  }
+}
+
+/**
+ * gts_surface_is_manifold:
+ * @s: a #GtsSurface.
+ *
+ * Returns: %TRUE if the surface is a manifold, %FALSE otherwise.
+ */
+gboolean gts_surface_is_manifold (GtsSurface * s)
+{
+  gboolean is_manifold = TRUE;
+  gpointer data[2];
+
+  g_return_val_if_fail (s != NULL, FALSE);
+
+  data[0] = &is_manifold;
+  data[1] = s;
+  gts_surface_foreach_edge (s, (GtsFunc) manifold_foreach_edge, data);
+  return is_manifold;
+}
+
+static void closed_foreach_edge (GtsEdge * e, gpointer * data)
+{
+  gboolean * is_closed = data[0];
+
+  if (*is_closed) {
+    if (gts_edge_face_number (e, data[1]) != 2)
+      *is_closed = FALSE;
+  }
+}
+
+/**
+ * gts_surface_is_closed:
+ * @s: a #GtsSurface.
+ *
+ * Returns: %TRUE if @s is a closed surface, %FALSE otherwise. Note that a
+ * closed surface is also a manifold.
+ */
+gboolean gts_surface_is_closed (GtsSurface * s)
+{
+  gboolean is_closed = TRUE;
+  gpointer data[2];
+
+  g_return_val_if_fail (s != NULL, FALSE);
+
+  data[0] = &is_closed;
+  data[1] = s;
+  gts_surface_foreach_edge (s, (GtsFunc) closed_foreach_edge, data);
+  return is_closed;
+}
+
+static void orientable_foreach_edge (GtsEdge * e, gpointer * data)
+{
+  gboolean * is_orientable = data[0];
+
+  if (*is_orientable) {
+    GtsSurface * surface = data[1];
+    GtsFace * f1 = NULL, * f2 = NULL;
+    GSList * i = e->triangles;
+    while (i && *is_orientable) {
+      GtsFace * f = i->data;
+      if (GTS_IS_FACE (f) && gts_face_has_parent_surface (f, surface)) {
+	if (!f1) f1 = f;
+	else if (!f2) f2 = f;
+	else *is_orientable = FALSE;
+      }
+      i = i->next;
+    }
+    if (f1 && f2 && !gts_triangles_are_compatible (GTS_TRIANGLE (f1), 
+						   GTS_TRIANGLE (f2), e))
+      *is_orientable = FALSE;
+  }
+}
+
+/**
+ * gts_surface_is_orientable:
+ * @s: a #GtsSurface.
+ *
+ * Returns: %TRUE if all the faces of @s have compatible orientation
+ * as checked by gts_faces_are_compatible(), %FALSE otherwise. Note that
+ * an orientable surface is also a manifold.
+ */
+gboolean gts_surface_is_orientable (GtsSurface * s)
+{
+  gboolean is_orientable = TRUE;
+  gpointer data[2];
+
+  g_return_val_if_fail (s != NULL, FALSE);
+
+  data[0] = &is_orientable;
+  data[1] = s;
+  gts_surface_foreach_edge (s, (GtsFunc) orientable_foreach_edge, data);
+  return is_orientable;
+}
+
+static void volume_foreach_face (GtsTriangle * t,
+				 gdouble * volume)
+{
+  GtsVertex * va, * vb, * vc;
+  GtsPoint * pa, * pb, * pc;
+
+  gts_triangle_vertices (t, &va, &vb, &vc);
+  pa = GTS_POINT (va);
+  pb = GTS_POINT (vb);
+  pc = GTS_POINT (vc);
+  
+  *volume += (pa->x * (pb->y * pc->z - pb->z * pc->y) +
+	      pb->x * (pc->y * pa->z - pc->z * pa->y) +
+	      pc->x * (pa->y * pb->z - pa->z * pb->y));
+}
+
+/**
+ * gts_surface_volume:
+ * @s: a #GtsSurface.
+ *
+ * Returns: the signed volume of the domain bounded by the surface @s. It
+ * makes sense only if @s is a closed and orientable manifold.
+ */
+gdouble gts_surface_volume (GtsSurface * s)
+{
+  gdouble volume = 0.0;
+
+  g_return_val_if_fail (s != NULL, 0.0);
+
+  gts_surface_foreach_face (s, (GtsFunc) volume_foreach_face, &volume);
+
+  return volume/6.;
+}
+
+static void center_of_mass_foreach_face (GtsTriangle * t,
+					 gpointer * data)
+{
+  GtsVertex * v1, * v2, * v3;
+  GtsPoint * p1, * p2, * p3;
+  gdouble x1, y1, z1, x2, y2, z2, nx, ny, nz;
+  gdouble * volume = data[0];
+  gdouble * cm = data[1];
+
+  gts_triangle_vertices (t, &v1, &v2, &v3);
+  p1 = GTS_POINT (v1);
+  p2 = GTS_POINT (v2);
+  p3 = GTS_POINT (v3);
+
+  x1 = p2->x - p1->x;
+  y1 = p2->y - p1->y;
+  z1 = p2->z - p1->z;
+
+  x2 = p3->x - p1->x;
+  y2 = p3->y - p1->y;
+  z2 = p3->z - p1->z;
+  
+  nx = y1*z2 - z1*y2;
+  ny = z1*x2 - x1*z2;
+  nz = x1*y2 - y1*x2;
+
+  cm[0] += nx*(p1->x*p1->x + p2->x*p2->x + p3->x*p3->x + 
+	       p1->x*p2->x + p1->x*p3->x + p2->x*p3->x);
+  cm[1] += ny*(p1->y*p1->y + p2->y*p2->y + p3->y*p3->y + 
+	       p1->y*p2->y + p1->y*p3->y + p2->y*p3->y);
+  cm[2] += nz*(p1->z*p1->z + p2->z*p2->z + p3->z*p3->z + 
+	       p1->z*p2->z + p1->z*p3->z + p2->z*p3->z);
+
+  *volume += nx*(p1->x + p2->x + p3->x);
+}
+
+
+/**
+ * gts_surface_center_of_mass:
+ * @s: a #GtsSurface.
+ * @cm: a #GtsVector.
+ *
+ * Fills @cm with the coordinates of the center of mass of @s.
+ *
+ * Returns: the signed volume of the domain bounded by the surface @s.
+ */
+gdouble gts_surface_center_of_mass (GtsSurface * s,
+				    GtsVector cm)
+{
+  gdouble volume = 0.;
+  gpointer data[2];
+
+  g_return_val_if_fail (s != NULL, 0.0);
+
+  data[0] = &volume;
+  data[1] = &(cm[0]);
+  cm[0] = cm[1] = cm[2] = 0.;
+  gts_surface_foreach_face (s, (GtsFunc) center_of_mass_foreach_face, data);
+  
+  if (volume != 0.) {
+    cm[0] /= 4.*volume;
+    cm[1] /= 4.*volume;
+    cm[2] /= 4.*volume;
+  }
+
+  return volume/6.;
+}
+
+static void center_of_area_foreach_face (GtsTriangle * t,
+					 gpointer * data)
+{
+  GtsVertex * v1, * v2, * v3;
+  GtsPoint * p1, * p2, * p3;
+  gdouble a;
+  gdouble * area = data[0];
+  gdouble * cm = data[1];
+
+  gts_triangle_vertices (t, &v1, &v2, &v3);
+  p1 = GTS_POINT (v1);
+  p2 = GTS_POINT (v2);
+  p3 = GTS_POINT (v3);
+
+  a = gts_triangle_area (t);
+  cm[0] += a*(p1->x + p2->x + p3->x);
+  cm[1] += a*(p1->y + p2->y + p3->y);
+  cm[2] += a*(p1->z + p2->z + p3->z);
+  *area += a;
+}
+
+
+/**
+ * gts_surface_center_of_area:
+ * @s: a #GtsSurface.
+ * @cm: a #GtsVector.
+ *
+ * Fills @cm with the coordinates of the center of area of @s.
+ *
+ * Returns: the area of surface @s.
+ */
+gdouble gts_surface_center_of_area (GtsSurface * s,
+				    GtsVector cm)
+{
+  gdouble area = 0.;
+  gpointer data[2];
+
+  g_return_val_if_fail (s != NULL, 0.0);
+
+  data[0] = &area;
+  data[1] = &(cm[0]);
+  cm[0] = cm[1] = cm[2] = 0.;
+  gts_surface_foreach_face (s, (GtsFunc) center_of_area_foreach_face, data);
+  
+  if (area != 0.) {
+    cm[0] /= 3.*area;
+    cm[1] /= 3.*area;
+    cm[2] /= 3.*area;
+  }
+
+  return area;
+}
+
+static void number_foreach (gpointer data, guint * n)
+{
+  (*n)++;
+}
+
+/**
+ * gts_surface_vertex_number:
+ * @s: a #GtsSurface.
+ *
+ * Returns: the number of vertices of @s.
+ */
+guint gts_surface_vertex_number (GtsSurface * s)
+{
+  guint n = 0;
+
+  g_return_val_if_fail (s != NULL, 0);
+
+  gts_surface_foreach_vertex (s, (GtsFunc) number_foreach, &n);
+
+  return n;
+}
+
+/**
+ * gts_surface_edge_number:
+ * @s: a #GtsSurface.
+ *
+ * Returns: the number of edges of @s.
+ */
+guint gts_surface_edge_number (GtsSurface * s)
+{
+  guint n = 0;
+
+  g_return_val_if_fail (s != NULL, 0);
+
+  gts_surface_foreach_edge (s, (GtsFunc) number_foreach, &n);
+
+  return n;
+}
+
+/**
+ * gts_surface_face_number:
+ * @s: a #GtsSurface.
+ *
+ * Returns: the number of faces of @s
+ */
+guint gts_surface_face_number (GtsSurface * s)
+{
+  g_return_val_if_fail (s != NULL, 0);
+
+#ifdef USE_SURFACE_BTREE
+  return g_tree_nnodes (s->faces);
+#else /* not USE_SURFACE_BTREE */
+  return g_hash_table_size (s->faces);
+#endif /* not USE_SURFACE_BTREE */
+}
+
+static void build_list_face (GtsTriangle * t, GSList ** list)
+{
+  *list = g_slist_prepend (*list, gts_bbox_triangle (gts_bbox_class (), t));
+}
+
+static void build_list_boundary (GtsEdge * e, GSList ** list)
+{
+  if (gts_edge_is_boundary (e, NULL))
+    *list = g_slist_prepend (*list, gts_bbox_segment (gts_bbox_class (),
+						      GTS_SEGMENT (e)));
+}
+
+/**
+ * gts_surface_distance:
+ * @s1: a #GtsSurface.
+ * @s2: a #GtsSurface.
+ * @delta: a spatial increment defined as the percentage of the diagonal
+ * of the bounding box of @s2.
+ * @face_range: a #GtsRange.
+ * @boundary_range: a #GtsRange.
+ *
+ * Using the gts_bb_tree_surface_distance() and
+ * gts_bb_tree_surface_boundary_distance() functions fills @face_range
+ * and @boundary_range with the min, max and average Euclidean
+ * (minimum) distances between the faces of @s1 and the faces of @s2
+ * and between the boundary edges of @s1 and @s2.  
+ */
+void gts_surface_distance (GtsSurface * s1, GtsSurface * s2, gdouble delta,
+			   GtsRange * face_range, GtsRange * boundary_range)
+{
+  GNode * face_tree, * boundary_tree;
+  GSList * bboxes;
+
+  g_return_if_fail (s1 != NULL);
+  g_return_if_fail (s2 != NULL);
+  g_return_if_fail (delta > 0. && delta < 1.);
+  g_return_if_fail (face_range != NULL);
+  g_return_if_fail (boundary_range != NULL);
+
+  bboxes = NULL;
+  gts_surface_foreach_face (s2, (GtsFunc) build_list_face, &bboxes);
+  if (bboxes != NULL) {
+    face_tree = gts_bb_tree_new (bboxes);
+    g_slist_free (bboxes);
+    
+    gts_bb_tree_surface_distance (face_tree, s1, 
+			       (GtsBBoxDistFunc) gts_point_triangle_distance,
+				  delta, face_range);
+    gts_bb_tree_destroy (face_tree, TRUE);
+    
+    bboxes = NULL;
+    gts_surface_foreach_edge (s2, (GtsFunc) build_list_boundary, &bboxes);
+    if (bboxes != NULL) {
+      boundary_tree = gts_bb_tree_new (bboxes);
+      g_slist_free (bboxes);
+
+      gts_bb_tree_surface_boundary_distance (boundary_tree,
+	       s1, 
+	       (GtsBBoxDistFunc) gts_point_segment_distance,
+	       delta, boundary_range);
+      gts_bb_tree_destroy (boundary_tree, TRUE);
+    }
+    else
+      gts_range_reset (boundary_range);
+  }
+  else {
+    gts_range_reset (face_range);
+    gts_range_reset (boundary_range);
+  }
+}
+
+static void surface_boundary (GtsEdge * e, gpointer * data)
+{
+  GSList ** list = data[0];
+
+  if (gts_edge_is_boundary (e, data[1]))
+    *list = g_slist_prepend (*list, e);
+}
+
+/**
+ * gts_surface_boundary:
+ * @surface: a #GtsSurface.
+ *
+ * Returns: a list of #GtsEdge boundary of @surface.
+ */
+GSList * gts_surface_boundary (GtsSurface * surface)
+{
+  GSList * list = NULL;
+  gpointer data[2];
+
+  g_return_val_if_fail (surface != NULL, NULL);
+
+  data[0] = &list;
+  data[1] = surface;
+  gts_surface_foreach_edge (surface, (GtsFunc) surface_boundary, data);
+  
+  return list;
+}
+
+struct _GtsSurfaceTraverse {
+  GtsFifo * q;
+  GtsSurface * s;
+};
+
+/**
+ * gts_surface_traverse_new:
+ * @s: a #GtsSurface.
+ * @f: a #GtsFace belonging to @s.
+ *
+ * Returns: a new #GtsSurfaceTraverse, initialized to start traversing
+ * from face @f of surface @s.  
+ */
+GtsSurfaceTraverse * gts_surface_traverse_new (GtsSurface * s,
+					       GtsFace * f)
+{
+  GtsSurfaceTraverse * t;
+
+  g_return_val_if_fail (s != NULL, NULL);
+  g_return_val_if_fail (f != NULL, NULL);
+  g_return_val_if_fail (gts_face_has_parent_surface (f, s), NULL);
+  
+  t = g_malloc (sizeof (GtsSurfaceTraverse));
+  t->q = gts_fifo_new ();
+  t->s = s;
+  GTS_OBJECT (f)->reserved = GUINT_TO_POINTER (1);
+  gts_fifo_push (t->q, f);
+  return t;
+}
+
+static void push_neighbor (GtsFace * v, gpointer * data)
+{
+  if (!GTS_OBJECT (v)->reserved) {
+    GTS_OBJECT (v)->reserved = 
+      GUINT_TO_POINTER (GPOINTER_TO_UINT (GTS_OBJECT (data[1])->reserved) + 1);
+    gts_fifo_push (data[0], v);
+  }
+}
+
+/**
+ * gts_surface_traverse_next:
+ * @t: a #GtsSurfaceTraverse.
+ * @level: a pointer to a guint or %NULL.
+ *
+ * Returns: the next face of the traversal in breadth-first order or
+ * %NULL if no faces are left. If @level if not %NULL, it is filled
+ * with the level of the returned face (0 for the initial face, 1 for
+ * its neighbors and so on).  
+ */
+GtsFace * gts_surface_traverse_next (GtsSurfaceTraverse * t,
+				     guint * level)
+{
+  GtsFace * u;
+
+  g_return_val_if_fail (t != NULL, NULL);
+
+  u = gts_fifo_pop (t->q);
+  if (u) {
+    gpointer data[2];
+
+    if (level)
+      *level = GPOINTER_TO_UINT (GTS_OBJECT (u)->reserved);
+    data[0] = t->q;
+    data[1] = u;
+    gts_face_foreach_neighbor (u, t->s, (GtsFunc) push_neighbor, data);
+  }
+  return u;
+}
+
+/**
+ * gts_surface_traverse_destroy:
+ * @t: a #GtsSurfaceTraverse.
+ *
+ * Frees all the memory allocated for @t.
+ */
+void gts_surface_traverse_destroy (GtsSurfaceTraverse * t)
+{
+  g_return_if_fail (t != NULL);
+
+  gts_surface_foreach_face (t->s, (GtsFunc) gts_object_reset_reserved, NULL);
+  gts_fifo_destroy (t->q);
+  g_free (t);
+}
+
+static void traverse_manifold (GtsTriangle * t, GtsSurface * s)
+{
+  if (g_slist_length (GTS_FACE (t)->surfaces) > 1)
+    return;
+
+  gts_surface_add_face (s, GTS_FACE (t));
+  if (g_slist_length (t->e1->triangles) == 2) {
+    if (t->e1->triangles->data != t)
+      traverse_manifold (t->e1->triangles->data, s);
+    else
+      traverse_manifold (t->e1->triangles->next->data, s);
+  }
+  if (g_slist_length (t->e2->triangles) == 2) {
+    if (t->e2->triangles->data != t)
+      traverse_manifold (t->e2->triangles->data, s);
+    else
+      traverse_manifold (t->e2->triangles->next->data, s);
+  }
+  if (g_slist_length (t->e3->triangles) == 2) {
+    if (t->e3->triangles->data != t)
+      traverse_manifold (t->e3->triangles->data, s);
+    else
+      traverse_manifold (t->e3->triangles->next->data, s);
+  }
+}
+
+static void non_manifold_edges (GtsEdge * e, gpointer * data)
+{
+  GtsSurface * s = data[0];
+  GSList ** non_manifold = data[1];
+
+  if (gts_edge_face_number (e, s) > 2) {
+    GSList * i = e->triangles;
+
+    while (i) {
+      if (gts_face_has_parent_surface (i->data, s) &&
+	  !g_slist_find (*non_manifold, i->data))
+	*non_manifold = g_slist_prepend (*non_manifold, i->data);
+      i = i->next;
+    }
+  }
+}
+
+static void traverse_boundary (GtsEdge * e, gpointer * data)
+{
+  GtsSurface * orig = data[0];
+  GSList ** components = data[1];
+  GtsFace * f = gts_edge_is_boundary (e, orig);
+
+  if (f != NULL && g_slist_length (f->surfaces) == 1) {
+    GtsSurface * s = 
+      gts_surface_new (GTS_SURFACE_CLASS (GTS_OBJECT (orig)->klass),
+		       orig->face_class,
+		       orig->edge_class,
+		       orig->vertex_class);
+    GSList * non_manifold = NULL, * i;
+    gpointer data[2];
+
+    *components = g_slist_prepend (*components, s);
+    data[0] = s;
+    data[1] = &non_manifold;
+    traverse_manifold (GTS_TRIANGLE (f), s);
+
+    gts_surface_foreach_edge (s, (GtsFunc) non_manifold_edges, data);
+    i = non_manifold;
+    while (i) {
+      gts_surface_remove_face (s, i->data);
+      i = i->next;
+    }
+    g_slist_free (non_manifold);
+  }
+}
+
+static void traverse_remaining (GtsFace * f, gpointer * data)
+{
+  GtsSurface * orig = data[0];
+  GSList ** components = data[1];
+
+  if (g_slist_length (f->surfaces) == 1) {
+    GtsSurface * s = 
+      gts_surface_new (GTS_SURFACE_CLASS (GTS_OBJECT (orig)->klass),
+		       orig->face_class,
+		       orig->edge_class,
+		       orig->vertex_class);
+    GSList * non_manifold = NULL, * i;
+    gpointer data[2];
+
+    *components = g_slist_prepend (*components, s);
+    data[0] = s;
+    data[1] = &non_manifold;
+    traverse_manifold (GTS_TRIANGLE (f), s);
+
+    gts_surface_foreach_edge (s, (GtsFunc) non_manifold_edges, data);
+    i = non_manifold;
+    while (i) {
+      gts_surface_remove_face (s, i->data);
+      i = i->next;
+    }
+    g_slist_free (non_manifold);
+  }
+}
+
+/**
+ * gts_surface_split:
+ * @s: a #GtsSurface.
+ *
+ * Splits a surface into connected and manifold components.
+ * 
+ * Returns: a list of new #GtsSurface.
+ */
+GSList * gts_surface_split (GtsSurface * s)
+{
+  gpointer data[2];
+  GSList * components = NULL;
+
+  g_return_val_if_fail (s != NULL, NULL);
+
+  data[0] = s;
+  data[1] = &components;
+
+  /* boundary components */
+  gts_surface_foreach_edge (s, (GtsFunc) traverse_boundary, data);
+
+  /* remaining components */
+  gts_surface_foreach_face (s, (GtsFunc) traverse_remaining, data);
+
+  return components;
+}
diff --git a/src_3rd/gts/triangle.c b/src_3rd/gts/triangle.c
new file mode 100644
index 0000000..5213a51
--- /dev/null
+++ b/src_3rd/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/src_3rd/gts/tribox3.c b/src_3rd/gts/tribox3.c
new file mode 100644
index 0000000..c0ea778
--- /dev/null
+++ b/src_3rd/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/src_3rd/gts/vertex.c b/src_3rd/gts/vertex.c
new file mode 100644
index 0000000..d312869
--- /dev/null
+++ b/src_3rd/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 ()),
+				  &gts_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 ()),
+				  &gts_color_vertex_info);
+  }
+
+  return klass;
+}
+
diff --git a/src_3rd/gts/vopt.c b/src_3rd/gts/vopt.c
new file mode 100644
index 0000000..d772af9
--- /dev/null
+++ b/src_3rd/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_3rd/liblhtpers/lhtpers.c b/src_3rd/liblhtpers/lhtpers.c
index d99eca8..088df91 100644
--- a/src_3rd/liblhtpers/lhtpers.c
+++ b/src_3rd/liblhtpers/lhtpers.c
@@ -1,3 +1,28 @@
+/*
+    lhtpers - persistent lihata formatting
+    Copyright (C) 2016  Tibor 'Igor2' Palinkas
+
+    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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+    Project URLs: http://repo.hu/projects/lihata
+                  svn://repo.hu/lihata
+
+
+   This file is the main logic for learning and applying local style.
+*/
+
 #include <assert.h>
 #include <stdlib.h>
 #include <stdio.h>
diff --git a/src_3rd/liblhtpers/output.c b/src_3rd/liblhtpers/output.c
index 602ae7d..6005d36 100644
--- a/src_3rd/liblhtpers/output.c
+++ b/src_3rd/liblhtpers/output.c
@@ -1,3 +1,28 @@
+/*
+    lhtpers - persistent lihata formatting
+    Copyright (C) 2016  Tibor 'Igor2' Palinkas
+
+    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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+    Project URLs: http://repo.hu/projects/lihata
+                  svn://repo.hu/lihata
+
+
+   Generate styled output without existing input for a subtree
+*/
+
 static void insert_subtree_by_rules(lht_pers_t *p, const char *ind, lht_node_t *subtree);
 static void export_subtree_by_rule(lht_pers_t *p, const char *ind, lht_node_t *subtree, lhtpers_rule_t *rule);
 
diff --git a/src_3rd/liblhtpers/pers_hash.c b/src_3rd/liblhtpers/pers_hash.c
index ed45786..91803ea 100644
--- a/src_3rd/liblhtpers/pers_hash.c
+++ b/src_3rd/liblhtpers/pers_hash.c
@@ -1,3 +1,28 @@
+/*
+    lhtpers - persistent lihata formatting
+    Copyright (C) 2016  Tibor 'Igor2' Palinkas
+
+    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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+    Project URLs: http://repo.hu/projects/lihata
+                  svn://repo.hu/lihata
+
+
+   Handle styled output for hashes.
+*/
+
 /* use htsp because lihata already uses that */
 
 /* called for each on-disk node whose parent is a hash, before printing the node */
diff --git a/src_3rd/liblhtpers/pers_list.c b/src_3rd/liblhtpers/pers_list.c
index b35090d..c079a3c 100644
--- a/src_3rd/liblhtpers/pers_list.c
+++ b/src_3rd/liblhtpers/pers_list.c
@@ -1,3 +1,29 @@
+/*
+    lhtpers - persistent lihata formatting
+    Copyright (C) 2016  Tibor 'Igor2' Palinkas
+
+    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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+    Project URLs: http://repo.hu/projects/lihata
+                  svn://repo.hu/lihata
+
+
+   Handle styled output for lists.
+*/
+
+
 /* called for each on-disk node whose parent is a list, before printing the node */
 static pre_fin_ctrl_t pers_event_li_pre(lht_pers_t *p, lht_perstyle_t *sp, lht_node_t *indoc_parent, inst_t *parent_st)
 {
diff --git a/src_3rd/liblhtpers/pers_table.c b/src_3rd/liblhtpers/pers_table.c
index 22fb749..3b7d811 100644
--- a/src_3rd/liblhtpers/pers_table.c
+++ b/src_3rd/liblhtpers/pers_table.c
@@ -1,3 +1,29 @@
+/*
+    lhtpers - persistent lihata formatting
+    Copyright (C) 2016  Tibor 'Igor2' Palinkas
+
+    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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+    Project URLs: http://repo.hu/projects/lihata
+                  svn://repo.hu/lihata
+
+
+   Handle styled output for tables.
+*/
+
+
 /********** ROW **********/
 
 void insert_trow(lht_pers_t *p, lht_perstyle_t *sp, lht_perstyle_t *alt, lht_node_t *indoc_parent, int row)
diff --git a/src_3rd/liblhtpers/pers_text.c b/src_3rd/liblhtpers/pers_text.c
index ef55d90..884ebbf 100644
--- a/src_3rd/liblhtpers/pers_text.c
+++ b/src_3rd/liblhtpers/pers_text.c
@@ -1,3 +1,29 @@
+/*
+    lhtpers - persistent lihata formatting
+    Copyright (C) 2016  Tibor 'Igor2' Palinkas
+
+    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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+    Project URLs: http://repo.hu/projects/lihata
+                  svn://repo.hu/lihata
+
+
+   Handle styled output for text nodes.
+*/
+
+
 /* called for each on-disk node that is a text or symlink, before printing the node */
 static pre_fin_ctrl_t pers_event_te_pre(lht_pers_t *p, lht_perstyle_t *sp, lht_node_t *indoc_node, inst_t *parent_st)
 {
diff --git a/src_3rd/liblhtpers/tests/perstest.c b/src_3rd/liblhtpers/tests/perstest.c
index 11ce20a..7bce291 100644
--- a/src_3rd/liblhtpers/tests/perstest.c
+++ b/src_3rd/liblhtpers/tests/perstest.c
@@ -1,3 +1,29 @@
+/*
+    lhtpers - persistent lihata formatting
+    Copyright (C) 2016  Tibor 'Igor2' Palinkas
+
+    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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+    Project URLs: http://repo.hu/projects/lihata
+                  svn://repo.hu/lihata
+
+
+    Persistent style save test program.
+*/
+
+
 #include <stdlib.h>
 #include <string.h>
 #include <liblhtpers/lhtpers.h>
diff --git a/src_3rd/sphash/sphash.c b/src_3rd/sphash/sphash.c
index 9635241..bb9d552 100644
--- a/src_3rd/sphash/sphash.c
+++ b/src_3rd/sphash/sphash.c
@@ -346,7 +346,7 @@ void dump_file(char *file_name_, char *prefix)
 	fprintf(f, "	unsigned int n, a;\n");
 	fprintf(f, "	const char *s;\n");
 	fprintf(f, "\n");
-	fprintf(f, "	for(n = 0, s = str; (*s != '\\0') && (n<SPHASH_SEED1) ; n++,s++);");
+	fprintf(f, "	for(n = 0, s = str; (*s != '\\0') && (n<SPHASH_SEED1) ; n++,s++);\n");
 	fprintf(f, "	a = 0;\n");
 	fprintf(f, "	for(; (*s != '\\0') && (n<SPHASH_SEED0) ; n++,s++) {\n");
 	fprintf(f, "		sphash_function(a, *s, n);\n");
diff --git a/src_plugins/import_sch/import_sch.c b/src_plugins/import_sch/import_sch.c
index a595bcc..6b4f590 100644
--- a/src_plugins/import_sch/import_sch.c
+++ b/src_plugins/import_sch/import_sch.c
@@ -319,20 +319,22 @@ static int ActionImport(int argc, const char **argv, Coord x, Coord y)
 			return 1;
 		}
 
-		cmd = (const char **) malloc((7 + nsources) * sizeof(char *));
+		cmd = (const char **) malloc((9 + nsources) * sizeof(char *));
 		cmd[0] = conf_import_sch.plugins.import_sch.gnetlist_program;
-		cmd[1] = "-g";
-		cmd[2] = "pcbrndfwd";
-		cmd[3] = "-o";
-		cmd[4] = tmpfile;
-		cmd[5] = "--";
+		cmd[1] = "-L";
+		cmd[2] = PCBLIBDIR;
+		cmd[3] = "-g";
+		cmd[4] = "pcbrndfwd";
+		cmd[5] = "-o";
+		cmd[6] = tmpfile;
+		cmd[7] = "--";
 		for (i = 0; i < nsources; i++)
-			cmd[6 + i] = sources[i];
-		cmd[6 + nsources] = NULL;
+			cmd[8 + i] = sources[i];
+		cmd[8 + nsources] = NULL;
 
 #ifdef DEBUG
 		printf("ActionImport:  ===========  About to run gnetlist  ============\n");
-		printf("%s %s %s %s %s %s %s ...\n", cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6]);
+		printf("%s %s %s %s %s %s %s %s %s ...\n", cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6], cmd[7], cmd[8]);
 #endif
 
 		if (pcb_spawnvp(cmd)) {
diff --git a/src_plugins/toporouter/Plug.tmpasm b/src_plugins/toporouter/Plug.tmpasm
index 2afd67e..5031aa9 100644
--- a/src_plugins/toporouter/Plug.tmpasm
+++ b/src_plugins/toporouter/Plug.tmpasm
@@ -1,7 +1,6 @@
 put /local/pcb/mod {toporouter}
 put /local/pcb/mod/OBJS [@ $(PLUGDIR)/toporouter/toporouter.o @]
 
-append /local/pcb/CFLAGS  {-I../src_3rd/gts}
 
 put /local/pcb/toporouter_rules [@
 ../src_3rd/gts/libgts.a:
@@ -10,12 +9,14 @@ put /local/pcb/toporouter_rules [@
 
 switch /local/pcb/toporouter/controls
 	case {buildin}
+		append /local/pcb/CFLAGS          {-I../src_3rd/gts}
 		append /local/pcb/RULES           /local/pcb/toporouter_rules
 		append /local/pcb/LIBS            { ../src_3rd/gts/libgts.a }
 		append /local/pcb/EXEDEPS         { ../src_3rd/gts/libgts.a }
 		include /local/pcb/tmpasm/buildin
 		end;
 	case {plugin}
+		append /local/mod/CFLAGS          {-I../src_3rd/gts}
 		append /local/pcb/RULES           /local/pcb/toporouter_rules
 		append /local/pcb/toporouter/OBJS { ../src_3rd/gts/libgts.a }
 		include /local/pcb/tmpasm/plugin
diff --git a/util/Makefile b/util/Makefile
index 772f210..9a22c8f 100644
--- a/util/Makefile
+++ b/util/Makefile
@@ -1,7 +1,5 @@
 # plain old hand crafted Makefile
 
-SCMDIR=$(DATADIR)/../gEDA/scheme
-
 all:
 	cd gsch2pcb-rnd && make all
 
@@ -11,10 +9,11 @@ clean:
 	cd gsch2pcb-rnd && make clean
 
 install_:
-	$(MKDIR) "$(BINDIR)" "$(LIBDIR)" "$(SCMDIR)"
+	$(MKDIR) "$(BINDIR)" "$(LIBDIR)"
 	$(CPC) "`pwd`/fp2anim" "$(BINDIR)/fp2anim"
 	$(CPC) "`pwd`/pcb-strip" "$(BINDIR)/pcb-strip"
-	$(CPC) "`pwd`/gnet-pcbrndfwd.scm" "$(SCMDIR)/gnet-pcbrndfwd.scm"
+	$(CPC) "`pwd`/gnet-pcbrndfwd.scm" "$(LIBDIR)/gnet-pcbrndfwd.scm"
+	$(CPC) "`pwd`/gnet-pcbrndfwd_elem.scm" "$(LIBDIR)/gnet-pcbrndfwd_elem.scm"
 	cd gsch2pcb-rnd && make install_ CPC="$(CPC)"
 
 install:
@@ -26,5 +25,6 @@ linstall:
 uninstall:
 	$(RM) "$(BINDIR)/fp2anim"
 	$(RM) "$(BINDIR)/pcb-strip"
-	$(RM) "$(SCMDIR)/gnet-pcbrndfwd.scm"
+	$(RM) "$(LIBDIR)/gnet-pcbrndfwd.scm"
+	$(RM) "$(LIBDIR)/gnet-pcbrndfwd_elem.scm"
 	cd gsch2pcb-rnd && make uninstall
diff --git a/util/gnet-pcbrndfwd_elem.scm b/util/gnet-pcbrndfwd_elem.scm
new file mode 100644
index 0000000..dfa362d
--- /dev/null
+++ b/util/gnet-pcbrndfwd_elem.scm
@@ -0,0 +1,107 @@
+;;; gEDA - GPL Electronic Design Automation
+;;; gnetlist - gEDA Netlist
+;;; Copyright (C) 1998-2008 Ales Hvezda
+;;; Copyright (C) 1998-2008 gEDA Contributors (see ChangeLog for details)
+;;;
+;;; 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.
+
+;; PCB forward annotation script;
+;; modified version of pcbrndfwd: do not do the net list, only elements,
+;; this way nets can be imported in the traditional netlist-way
+
+(use-modules (ice-9 regex))
+(use-modules (ice-9 format))
+
+;; This is a list of attributes which are propogated to the pcb
+;; elements.  Note that refdes, value, and footprint need not be
+;; listed here.
+(define pcbrndfwd:element-attrs
+  '("device"
+    "manufacturer"
+    "manufacturer_part_number"
+    "vendor"
+    "vendor_part_number"
+    ))
+
+(define (pcbrndfwd:quote-string s)
+  (string-append "\""
+		 (regexp-substitute/global #f "\"" s 'pre "\\\"" 'post)
+		 "\"")
+  )
+
+
+(define (pcbrndfwd:each-attr refdes attrs port)
+  (if (not (null? attrs))
+      (let ((attr (car attrs)))
+	(format port "ElementSetAttr(~a,~a,~a)~%"
+		(pcbrndfwd:quote-string refdes)
+		(pcbrndfwd:quote-string attr)
+		(pcbrndfwd:quote-string (gnetlist:get-package-attribute refdes attr)))
+	(pcbrndfwd:each-attr refdes (cdr attrs) port))))
+
+;; write out the pins for a particular component
+(define pcbrndfwd:component_pins
+  (lambda (port package pins)
+    (if (and (not (null? package)) (not (null? pins)))
+	(begin
+	  (let (
+		(pin (car pins))
+		(label #f)
+		(pinnum #f)
+		)
+	    (display "ChangePinName(" port)
+	    (display (pcbrndfwd:quote-string package) port)
+	    (display ", " port)
+
+	    (set! pinnum (gnetlist:get-attribute-by-pinnumber package pin "pinnumber"))
+
+	    (display pinnum port)
+	    (display ", " port)
+
+	    (set! label (gnetlist:get-attribute-by-pinnumber package pin "pinlabel"))
+	    (if (string=? label "unknown") 
+		(set! label pinnum)
+		)
+	    (display (pcbrndfwd:quote-string label) port)
+	    (display ")\n" port)
+	    )
+	  (pcbrndfwd:component_pins port package (cdr pins))
+	  )
+	)
+    )
+  )
+
+(define (pcbrndfwd:each-element elements port)
+  (if (not (null? elements))
+      (let* ((refdes (car elements))
+	     (value (gnetlist:get-package-attribute refdes "value"))
+	     (footprint (gnetlist:get-package-attribute refdes "footprint"))
+	     )
+
+	(format port "ElementList(Need,~a,~a,~a)~%"
+		(pcbrndfwd:quote-string refdes)
+		(pcbrndfwd:quote-string footprint)
+		(pcbrndfwd:quote-string value))
+	(pcbrndfwd:each-attr refdes pcbrndfwd:element-attrs port)
+	(pcbrndfwd:component_pins port refdes (gnetlist:get-pins refdes))
+
+	(pcbrndfwd:each-element (cdr elements) port))))
+
+(define (pcbrndfwd_elem output-filename)
+  (let ((port (open-output-file output-filename)))
+    (format port "ElementList(Start)\n")
+    (pcbrndfwd:each-element packages port)
+    (format port "ElementList(Done)\n")
+    (close-output-port port)))
diff --git a/util/gsch2pcb-rnd/Makefile.in b/util/gsch2pcb-rnd/Makefile.in
index cd240bc..35bf4fc 100644
--- a/util/gsch2pcb-rnd/Makefile.in
+++ b/util/gsch2pcb-rnd/Makefile.in
@@ -47,7 +47,14 @@ FP_OBJS    = @/local/pcb/OBJS@ ../../src/plug_footprint.o
 CONF_OBJS  = ../../src/vtlibrary.o ../../src/compat_fs.o ../../src/paths.o ../../src/conf.o ../../src/conf_core.o ../../src/hid_cfg.o ../../src/misc_util.o ../../src/unit.o ../../src/conf_internal.o ../../src/list_conf.o ../../src/conf_hid.o ../../src/pcb-printf.o ../../src/compat_misc.o
 FP_LDFLAGS = @/local/pcb/LDFLAGS@
 FP_CFLAGS  = @/local/pcb/CFLAGS@
-OBJS=gsch2pcb.o help.o
+OBJS = \
+ gsch2pcb.o \
+ help.o \
+ netlister.o \
+ run.o \
+ glue.o \
+ method_pcb.o \
+ method_import.o
 
 all:
 	make revcheck
diff --git a/util/gsch2pcb-rnd/glue.c b/util/gsch2pcb-rnd/glue.c
new file mode 100644
index 0000000..3d1d437
--- /dev/null
+++ b/util/gsch2pcb-rnd/glue.c
@@ -0,0 +1,61 @@
+/* gsch2pcb-rnd
+ *  (C) 2015..2016, Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software which I release under the GNU General Public
+ *  License. You may redistribute and/or modify this program under the terms
+ *  of that 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.  Version 2 is in the
+ *  COPYRIGHT file in the top level directory of this distribution.
+ *
+ *  To get a copy of the GNU General Puplic License, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include <stdio.h>
+#include <stdarg.h>
+#include "../src/error.h"
+
+/* glue for pcb-rnd core */
+
+void ChdirErrorMessage(const char *DirName)
+{
+	fprintf(stderr, "gsch2pcb-rnd: warning: can't cd to %s\n", DirName);
+}
+
+void OpendirErrorMessage(const char *DirName)
+{
+	fprintf(stderr, "gsch2pcb-rnd: warning: can't opendir %s\n", DirName);
+}
+
+void PopenErrorMessage(const char *cmd)
+{
+	fprintf(stderr, "gsch2pcb-rnd: warning: can't popen %s\n", cmd);
+}
+
+void Message(enum pcb_message_level level, const char *fmt, ...)
+{
+	va_list args;
+	fprintf(stderr, "gsch2pcb-rnd: ");
+	va_start(args, fmt);
+	vfprintf(stderr, fmt, args);
+	va_end(args);
+	fprintf(stderr, "\n");
+}
+
+void pcb_trace(const char *Format, ...)
+{
+#ifndef NDEBUG
+	va_list args;
+	va_start(args, Format);
+	vfprintf(stderr, Format, args);
+	va_end(args);
+#endif
+}
+
+const char *pcb_board_get_filename(void) { return NULL; }
+const char *pcb_board_get_name(void) { return NULL; }
diff --git a/util/gsch2pcb-rnd/gsch2pcb.c b/util/gsch2pcb-rnd/gsch2pcb.c
index bbe70a2..3bdf73f 100644
--- a/util/gsch2pcb-rnd/gsch2pcb.c
+++ b/util/gsch2pcb-rnd/gsch2pcb.c
@@ -25,97 +25,53 @@
   - use pcb-rnd's conf system
   - use popen() instead of glib's spawn (stderr is always printed to stderr)
  */
-/* for popen() */
-#define _DEFAULT_SOURCE
-#define _BSD_SOURCE
-
 #include "config.h"
 
-#include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <time.h>
-#include <unistd.h>
-#include <sys/stat.h>
 #include "../src/plug_footprint.h"
-#include "../src/paths.h"
 #include "../src/conf.h"
 #include "../src/conf_core.h"
-#include "../src_3rd/genvector/vts0.h"
-#include "../src_3rd/genlist/gendlist.h"
-#include "../src_3rd/genlist/genadlist.h"
 #include "../src_3rd/qparse/qparse.h"
 #include "../config.h"
 #include "../src/error.h"
 #include "../src/plugins.h"
-#include "../src/plug_footprint.h"
 #include "../src/compat_misc.h"
+#include "method.h"
 #include "help.h"
 #include "gsch2pcb_rnd_conf.h"
+#include "gsch2pcb.h"
+#include "method_pcb.h"
+#include "method_import.h"
 
-#define TRUE 1
-#define FALSE 0
-
-#define GSCH2PCB_RND_VERSION "1.0.1"
-
-#define DEFAULT_PCB_INC "pcb.inc"
-
-#define SEP_STRING "--------\n"
-
-/* from scconfig str lib: */
-char *str_concat(const char *sep, ...);
-
-typedef struct {
-	char *refdes, *value, *description, *changed_description, *changed_value;
-	char *flags, *tail;
-	char *x, *y;
-	char *pkg_name_fix;
-	char res_char;
-
-	gdl_elem_t all_elems;
-
-	unsigned still_exists:1;
-	unsigned new_format:1;
-	unsigned hi_res_format:1;
-	unsigned quoted_flags:1;
-	unsigned omit_PKG:1;
-	unsigned nonetlist:1;
-
-} PcbElement;
-
-typedef struct {
-	char *part_number, *element_name;
-} ElementMap;
+static const char *want_method_default = "pcb";
 
 gdl_list_t pcb_element_list; /* initialized to 0 */
 gadl_list_t schematics, extra_gnetlist_arg_list, extra_gnetlist_list;
 
-static int n_deleted, n_added_ef, n_fixed, n_PKG_removed_new,
-           n_PKG_removed_old, n_preserved, n_changed_value, n_not_found,
-           n_unknown, n_none, n_empty;
+int n_deleted, n_added_ef, n_fixed, n_PKG_removed_new,
+    n_PKG_removed_old, n_preserved, n_changed_value, n_not_found,
+    n_unknown, n_none, n_empty;
 
-static int bak_done, need_PKG_purge;
+int bak_done, need_PKG_purge;
 
 conf_gsch2pcb_rnd_t conf_g2pr;
 
-static const char *element_search_path = NULL; /* queried once from the config, when the config is already stable */
+method_t *methods = NULL, *current_method;
 
-static char *loc_strndup(const char *str, size_t len)
+void method_register(method_t *method)
 {
-	char *s;
-	int l;
+	method->next = methods;
+	methods = method;
+}
 
-	if (str == NULL)
-		return NULL;
-	l = strlen(str);
-	if (l < len)
-		len = l;
-	s = malloc(len+1);
-	memcpy(s, str, len);
-	s[len] = '\0';
-	return s;
+method_t *method_find(const char *name)
+{
+	method_t *m;
+	for(m = methods; m != NULL; m = m->next)
+		if (strcmp(m->name, name) == 0)
+			return m;
+	return NULL;
 }
 
 /* Return a pointer to the suffix if inp ends in that suffix */
@@ -127,283 +83,7 @@ static char *loc_str_has_suffix(char *inp, const char *suffix, int suff_len)
 	return NULL;
 }
 
-/* Checks if a file exists and is readable */
-static int file_exists(const char *fn)
-{
-	FILE *f;
-	f = fopen(fn, "r");
-	if (f == NULL)
-		return 0;
-	fclose(f);
-	return 1;
-}
-
-void ChdirErrorMessage(const char *DirName)
-{
-	fprintf(stderr, "gsch2pcb-rnd: warning: can't cd to %s\n", DirName);
-}
-
-void OpendirErrorMessage(const char *DirName)
-{
-	fprintf(stderr, "gsch2pcb-rnd: warning: can't opendir %s\n", DirName);
-}
-
-void PopenErrorMessage(const char *cmd)
-{
-	fprintf(stderr, "gsch2pcb-rnd: warning: can't popen %s\n", cmd);
-}
-
-void Message(enum pcb_message_level level, const char *fmt, ...)
-{
-	va_list args;
-	fprintf(stderr, "gsch2pcb-rnd: ");
-	va_start(args, fmt);
-	vfprintf(stderr, fmt, args);
-	va_end(args);
-	fprintf(stderr, "\n");
-}
-
-void pcb_trace(const char *Format, ...)
-{
-#ifndef NDEBUG
-	va_list args;
-	va_start(args, Format);
-	vfprintf(stderr, Format, args);
-	va_end(args);
-#endif
-}
-
-
-/**
- * Build and run a command. No redirection or error handling is
- * done.  Format string is split on whitespace. Specifiers %l and %s
- * are replaced with contents of positional args. To be recognized,
- * specifiers must be separated from other arguments in the format by
- * whitespace.
- *  - %L expects a gadl_list_t vith char * payload, contents used as separate arguments
- *  - %s expects a char*, contents used as a single argument (omitted if NULL)
- * @param[in] format  used to specify command to be executed
- * @param[in] ...     positional parameters
- */
-static int build_and_run_command(const char * format_, ...)
-{
-	va_list vargs;
-	int result = FALSE;
-	vts0_t args;
-	char *format, *s, *start;
-
-	/* Translate the format string; args elements point to const char *'s
-	   within a copy of the format string. The format string is copied so
-	   that these parts can be terminated by overwriting whitepsace with \0 */
-	va_start(vargs, format_);
-	format = pcb_strdup(format_);
-	vts0_init(&args);
-	for(s = start = format; *s != '\0'; s++) {
-		/* if word separator is reached, save the previous word */
-		if (isspace(s[0])) {
-			if (start == s) { /* empty word - skip */
-				start++;
-				continue;
-			}
-			*s = '\0';
-			vts0_append(&args, start);
-			start = s+1;
-			continue;
-		}
-
-		/* check if current word is a format */
-		if ((s == start) && (s[0] == '%') && (s[1] != '\0') && ((s[2] == '\0') || isspace(s[2]))) {
-			switch(s[1]) {
-				case 'L': /* append contents of char * gadl_list_t */
-					{
-						gadl_list_t *list = va_arg(vargs, gadl_list_t *);
-						gadl_iterator_t it;
-						char **s;
-						gadl_foreach(list, &it, s) {
-							vts0_append(&args, *s);
-						}
-					}
-					start = s+2;
-					s++;
-					continue;
-				case 's':
-					{
-						char *arg = va_arg(vargs, char *);
-						if (arg != NULL)
-							vts0_append(&args, arg);
-						start = s+2;
-						s++;
-					}
-					continue;
-			}
-		}
-	}
-	va_end(vargs);
-
-	if (args.used > 0) {
-		int i, l;
-		char *cmd, *end, line[1024];
-		FILE *f;
-
-		l = 0;
-		for (i = 0; i < args.used; i++)
-			l += strlen(args.array[i]) + 3;
-
-		end = cmd = malloc(l+1);
-		for (i = 0; i < args.used; i++) {
-			l = strlen(args.array[i]);
-			*end = '"'; end++;
-			memcpy(end, args.array[i], l);
-			end += l;
-			*end = '"'; end++;
-			*end = ' '; end++;
-		}
-		end--;
-		*end = '\0';
-
-		/* we have something in the list, build & call command */
-		if (conf_g2pr.utils.gsch2pcb_rnd.verbose) {
-			printf("Running command:\n\t%s\n", cmd);
-			printf("%s", SEP_STRING);
-		}
-
-		f = popen(cmd, "r");
-		while(fgets(line, sizeof(line), f) != NULL) {
-			if (conf_g2pr.utils.gsch2pcb_rnd.verbose)
-				fputs(line, stdout);
-		}
-
-		if (pclose(f) == 0)
-			result = TRUE;
-		else
-			fprintf(stderr, "Failed to execute external program\n");
-		free(cmd);
-
-		if (conf_g2pr.utils.gsch2pcb_rnd.verbose)
-			printf("\n%s", SEP_STRING);
-	}
-
-	free(format);
-	vts0_uninit(&args);
-	return result;
-}
-
-/* Run gnetlist to generate a netlist and a PCB board file.  gnetlist
- * has exit status of 0 even if it's given an invalid arg, so do some
- * stat() hoops to decide if gnetlist successfully generated the PCB
- * board file (only gnetlist >= 20030901 recognizes -m).
- */
-static int run_gnetlist(const char * pins_file, const char * net_file, const char * pcb_file, const char * basename, const gadl_list_t *largs)
-{
-	struct stat st;
-	time_t mtime;
-	static const char *gnetlist = NULL;
-	gadl_iterator_t it;
-	char **sp;
-	char *verbose_str = NULL;
-
-	/* Allow the user to specify a full path or a different name for
-	 * the gnetlist command.  Especially useful if multiple copies
-	 * are installed at once.
-	 */
-	if (gnetlist == NULL)
-		gnetlist = getenv("GNETLIST");
-	if (gnetlist == NULL)
-		gnetlist = "gnetlist";
-
-	if (!conf_g2pr.utils.gsch2pcb_rnd.verbose)
-		verbose_str = "-q";
-
-	if (!build_and_run_command("%s %s -g pcbpins -o %s %L %L", gnetlist, verbose_str, pins_file, &extra_gnetlist_arg_list, largs))
-		return FALSE;
-
-	if (!build_and_run_command("%s %s -g PCB -o %s %L %L", gnetlist, verbose_str, net_file, &extra_gnetlist_arg_list, largs))
-		return FALSE;
-
-	mtime = (stat(pcb_file, &st) == 0) ? st.st_mtime : 0;
-
-	if (!build_and_run_command("%s %s -L " SCMDIR " -g gsch2pcb-rnd -o %s %L %L",
-														 gnetlist, verbose_str, pcb_file, &extra_gnetlist_arg_list, largs)) {
-		if (stat(pcb_file, &st) != 0 || mtime == st.st_mtime) {
-			fprintf(stderr, "gsch2pcb: gnetlist command failed, `%s' not updated\n", pcb_file);
-			return FALSE;
-		}
-		return FALSE;
-	}
-
-	gadl_foreach(&extra_gnetlist_list, &it, sp) {
-		const char *s = *sp;
-		const char *s2 = strstr(s, " -o ");
-		char *out_file;
-		char *backend;
-		if (!s2) {
-			out_file = str_concat(NULL, basename, ".", s, NULL);
-			backend = pcb_strdup(s);
-		}
-		else {
-			out_file = pcb_strdup(s2 + 4);
-			backend = loc_strndup(s, s2 - s);
-		}
-
-		if (!build_and_run_command("%s %s -g %s -o %s %L %L",
-															 gnetlist, verbose_str, backend, out_file, &extra_gnetlist_arg_list, largs))
-			return FALSE;
-		free(out_file);
-		free(backend);
-	}
-
-	return TRUE;
-}
-
-static char *token(char * string, char ** next, int * quoted_ret, int parenth)
-{
-	static char *str;
-	char *s, *ret;
-	int quoted = FALSE;
-
-	if (string)
-		str = string;
-	if (!str || !*str) {
-		if (next)
-			*next = str;
-		return pcb_strdup("");
-	}
-	while (*str == ' ' || *str == '\t' || *str == ',' || *str == '\n')
-		++str;
-
-	if (*str == '"') {
-		quoted = TRUE;
-		if (quoted_ret)
-			*quoted_ret = TRUE;
-		++str;
-		for (s = str; *s && *s != '"' && *s != '\n'; ++s);
-	}
-	else {
-		if (quoted_ret)
-			*quoted_ret = FALSE;
-		for (s = str; *s != '\0'; ++s) {
-			if ((parenth) && (*s == '(')) {
-				quoted = TRUE;
-				if (quoted_ret)
-					*quoted_ret = TRUE;
-				for (; *s && *s != ')' && *s != '\n'; ++s);
-				/* preserve closing ')' */
-				if (*s == ')')
-					s++;
-				break;
-			}
-			if (*s == ' ' || *s == '\t' || *s == ',' || *s == '\n')
-				break;
-		}
-	}
-	ret = loc_strndup(str, s - str);
-	str = (quoted && *s) ? s + 1 : s;
-	if (next)
-		*next = str;
-	return ret;
-}
-
-static char *fix_spaces(char * str)
+char *fix_spaces(char * str)
 {
 	char *s;
 
@@ -415,556 +95,6 @@ static char *fix_spaces(char * str)
 	return str;
 }
 
-	/* As of 1/9/2004 CVS hi_res Element[] line format:
-	 *   Element[element_flags, description, pcb-name, value, mark_x, mark_y,
-	 *       text_x, text_y, text_direction, text_scale, text_flags]
-	 *   New PCB 1.7 / 1.99 Element() line format:
-	 *   Element(element_flags, description, pcb-name, value, mark_x, mark_y,
-	 *       text_x, text_y, text_direction, text_scale, text_flags)
-	 *   Old PCB 1.6 Element() line format:
-	 *   Element(element_flags, description, pcb-name, value,
-	 *       text_x, text_y, text_direction, text_scale, text_flags)
-	 *
-	 *   (mark_x, mark_y) is the element position (mark) and (text_x,text_y)
-	 *   is the description text position which is absolute in pre 1.7 and
-	 *   is now relative.  The hi_res mark_x,mark_y and text_x,text_y resolutions
-	 *   are 100x the other formats.
-	 */
-PcbElement *pcb_element_line_parse(char * line)
-{
-	PcbElement *el = NULL;
-	char *s, *t, close_char;
-	int state = 0, elcount = 0, tmp;
-
-	if (strncmp(line, "Element", 7))
-		return NULL;
-
-	el = calloc(sizeof(PcbElement), 1);
-
-	s = line + 7;
-	while (*s == ' ' || *s == '\t')
-		++s;
-
-	if (*s == '[')
-		el->hi_res_format = TRUE;
-	else if (*s != '(') {
-		free(el);
-		return NULL;
-	}
-
-	el->res_char = el->hi_res_format ? '[' : '(';
-	close_char = el->hi_res_format ? ']' : ')';
-
-	el->flags = token(s + 1, NULL, &tmp, 0); el->quoted_flags = tmp;
-	el->description = token(NULL, NULL, NULL, 0);
-	el->refdes = token(NULL, NULL, NULL, 0);
-	el->value = token(NULL, NULL, NULL, 0);
-
-	el->x = token(NULL, NULL, NULL, 0);
-	el->y = token(NULL, &t, NULL, 0);
-
-	el->tail = pcb_strdup(t ? t : "");
-	if ((s = strrchr(el->tail, (int) '\n')) != NULL)
-		*s = '\0';
-
-	/* Count the tokens in tail to decide if it's new or old format.
-	 * Old format will have 3 tokens, new format will have 5 tokens.
-	 */
-	for (s = el->tail; *s && *s != close_char; ++s) {
-		if (*s != ' ') {
-			if (state == 0)
-				++elcount;
-			state = 1;
-		}
-		else
-			state = 0;
-	}
-	el->nonetlist = 0;
-	if (elcount > 4) {
-		el->new_format = TRUE;
-		if (strstr(el->tail, "nonetlist") != NULL)
-			el->nonetlist = 1;
-	}
-
-	fix_spaces(el->description);
-	fix_spaces(el->refdes);
-	fix_spaces(el->value);
-
-	/* Don't allow elements with no refdes to ever be deleted because
-	 * they may be desired pc board elements not in schematics.  So
-	 * initialize still_exists to TRUE if empty or non-alphanumeric
-	 * refdes.
-	 */
-	if (!*el->refdes || !isalnum((int) (*el->refdes)))
-		el->still_exists = TRUE;
-
-	return el;
-}
-
-static void pcb_element_free(PcbElement * el)
-{
-	if (!el)
-		return;
-	free(el->flags);
-	free(el->description);
-	free(el->changed_description);
-	free(el->changed_value);
-	free(el->refdes);
-	free(el->value);
-	free(el->x);
-	free(el->y);
-	free(el->tail);
-	free(el->pkg_name_fix);
-	free(el);
-}
-
-static void get_pcb_element_list(char * pcb_file)
-{
-	FILE *f;
-	PcbElement *el;
-	char *s, buf[1024];
-
-	if ((f = fopen(pcb_file, "r")) == NULL)
-		return;
-	while ((fgets(buf, sizeof(buf), f)) != NULL) {
-		for (s = buf; *s == ' ' || *s == '\t'; ++s);
-		if (!strncmp(s, "PKG_", 4)) {
-			need_PKG_purge = TRUE;
-			continue;
-		}
-		if ((el = pcb_element_line_parse(s)) == NULL)
-			continue;
-		gdl_append(&pcb_element_list, el, all_elems);
-	}
-	fclose(f);
-}
-
-static PcbElement *pcb_element_exists(PcbElement * el_test, int record)
-{
-	PcbElement *el;
-	gdl_iterator_t it;
-
-	gdl_foreach(&pcb_element_list, &it, el) {
-		if (strcmp(el_test->refdes, el->refdes))
-			continue;
-		if (strcmp(el_test->description, el->description)) {	/* footprint */
-			if (record)
-				el->changed_description = pcb_strdup(el_test->description);
-		}
-		else {
-			if (record) {
-				if (strcmp(el_test->value, el->value))
-					el->changed_value = pcb_strdup(el_test->value);
-				el->still_exists = TRUE;
-			}
-			return el;
-		}
-	}
-	return NULL;
-}
-
-/* A problem is that new PCB 1.7 file elements have the
- * (mark_x,mark_y) value set to wherever the element was created and
- * no equivalent of a gschem translate symbol was done.
- *
- * So, file elements inserted can be scattered over a big area and
- * this is bad when loading a file.new.pcb into an existing PC
- * board.  So, do a simple translate if (mark_x,mark_y) is
- * (arbitrarily) over 1000.  I'll assume that for values < 1000 the
- * element creator was concerned with a sane initial element
- * placement.  Unless someone has a better idea?  Don't bother with
- * pre PCB 1.7 formats as that would require parsing the mark().
- */
-static void simple_translate(PcbElement * el)
-{
-	if (el->x != NULL)
-		free(el->x);
-	if (el->y != NULL)
-		free(el->y);
-	el->x = pcb_strdup("0");
-	el->y = pcb_strdup("0");
-}
-
-static int insert_element(FILE * f_out, FILE * f_elem, char * footprint, char * refdes, char * value)
-{
-	PcbElement *el;
-	char *fmt, *s, buf[1024];
-	int retval = FALSE;
-
-	/* Copy the file element lines.  Substitute new parameters into the
-	 * Element() or Element[] line and strip comments.
-	 */
-	while ((fgets(buf, sizeof(buf), f_elem)) != NULL) {
-		for (s = buf; *s == ' ' || *s == '\t'; ++s);
-		if ((el = pcb_element_line_parse(s)) != NULL) {
-			simple_translate(el);
-			fmt = el->quoted_flags ? "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %s %s%s\n" : "Element%c%s \"%s\" \"%s\" \"%s\" %s %s%s\n";
-
-			fprintf(f_out, fmt, el->res_char, el->flags, footprint, refdes, value, el->x, el->y, el->tail);
-			retval = TRUE;
-		}
-		else if (*s != '#')
-			fputs(buf, f_out);
-		pcb_element_free(el);
-	}
-	return retval;
-}
-
-
-char *search_element(PcbElement * el)
-{
-	char *elname = NULL, *path = NULL;
-
-	if (!elname)
-		elname = pcb_strdup(el->description);
-
-	if (!strcmp(elname, "unknown")) {
-		free(elname);
-		return NULL;
-	}
-	if (conf_g2pr.utils.gsch2pcb_rnd.verbose > 1)
-		printf("\tSearching directories looking for file element: %s\n", elname);
-	free(elname);
-	return path;
-}
-
-/* The gnetlist backend gnet-gsch2pcb-rnd.scm generates PKG lines:
- *
- *        PKG(footprint,refdes,value)
- *
- */
-static PcbElement *pkg_to_element(FILE * f, char * pkg_line)
-{
-	PcbElement *el;
-	char *s, *end, *refdes, *fp, *value;
-
-/*fprintf(stderr, "--- %s\n", pkg_line);*/
-
-	if (strncmp(pkg_line, "PKG", 3)
-			|| (s = strchr(pkg_line, (int) '(')) == NULL)
-		return NULL;
-
-/* remove trailing ")" */
-	end = s + strlen(s) - 2;
-	if (*end == ')')
-		*end = '\0';
-
-/* tokenize the line keeping () */
-	fp = token(s + 1, NULL, NULL, 1);
-	refdes = token(NULL, NULL, NULL, 1);
-	value = token(NULL, NULL, NULL, 1);
-
-
-/*fprintf(stderr, "refdes: %s\n", refdes);
-fprintf(stderr, "    fp: %s\n", fp);
-fprintf(stderr, "   val: %s\n", value);*/
-
-
-	if (!refdes || !fp || !value) {
-		if (refdes != NULL)
-			free(refdes);
-		if (fp != NULL)
-			free(fp);
-		if (value != NULL)
-			free(value);
-		fprintf(stderr, "Bad package line: %s\n", pkg_line);
-		return NULL;
-	}
-
-	fix_spaces(refdes);
-	fix_spaces(value);
-
-	el = calloc(sizeof(PcbElement), 1);
-	el->description = fp;
-	el->refdes = refdes;
-	el->value = value;
-
-/*
-// wtf?
-//  if ((s = strchr (el->value, (int) ')')) != NULL)
-//    *s = '\0';
-*/
-
-	if (conf_g2pr.utils.gsch2pcb_rnd.empty_footprint_name && !strcmp(el->description, conf_g2pr.utils.gsch2pcb_rnd.empty_footprint_name)) {
-		if (conf_g2pr.utils.gsch2pcb_rnd.verbose)
-			printf("%s: has the empty footprint attribute \"%s\" so won't be in the layout.\n", el->refdes, el->description);
-		n_empty += 1;
-		el->omit_PKG = TRUE;
-	}
-	else if (!strcmp(el->description, "none")) {
-		fprintf(stderr, "WARNING: %s has a footprint attribute \"%s\" so won't be in the layout.\n", el->refdes, el->description);
-		n_none += 1;
-		el->omit_PKG = TRUE;
-	}
-	else if (!strcmp(el->description, "unknown")) {
-		fprintf(stderr, "WARNING: %s has no footprint attribute so won't be in the layout.\n", el->refdes);
-		n_unknown += 1;
-		el->omit_PKG = TRUE;
-	}
-	return el;
-}
-
-/* Copies the content of fn to fout and returns 0 on success. */
-static int CatPCB(FILE * fout, const char *fn)
-{
-	FILE *fin;
-	fin = fopen(fn, "r");
-	if (fin == NULL)
-		return -1;
-
-	for (;;) {
-		char buff[1024];
-		int len;
-
-		len = fread(buff, 1, sizeof(buff), fin);
-		if (len <= 0)
-			break;
-
-		fwrite(buff, len, 1, fout);
-	}
-
-	fclose(fin);
-	return 0;
-}
-
-/* Process the newly created pcb file which is the output from
- *     gnetlist -g gsch2pcb-rnd ...
- *
- * Insert elements for PKG_ lines if they be found by external element query.
- * If there was an existing pcb file, strip out any elements if they are
- * already present so that the new pcb file will only have new elements.
- */
-static int add_elements(char * pcb_file)
-{
-	FILE *f_in, *f_out, *fp;
-	PcbElement *el = NULL;
-	char *tmp_file, *s, buf[1024];
-	int total, paren_level = 0;
-	int skipping = FALSE;
-	int dpcb;
-	fp_fopen_ctx_t fctx;
-
-	if ((f_in = fopen(pcb_file, "r")) == NULL)
-		return 0;
-	tmp_file = str_concat(NULL, pcb_file, ".tmp", NULL);
-	if ((f_out = fopen(tmp_file, "wb")) == NULL) {
-		fclose(f_in);
-		free(tmp_file);
-		return 0;
-	}
-
-	if (conf_g2pr.utils.gsch2pcb_rnd.default_pcb == NULL) {
-		dpcb = -1;
-		conf_list_foreach_path_first(dpcb, &conf_core.rc.default_pcb_file, CatPCB(f_out, __path__));
-		if (dpcb != 0) {
-			fprintf(stderr, "ERROR: can't load default pcb (using the configured search paths)\n");
-			exit(1);
-		}
-	}
-	else {
-		if (CatPCB(f_out, conf_g2pr.utils.gsch2pcb_rnd.default_pcb) != 0) {
-			fprintf(stderr, "ERROR: can't load default pcb (using user defined %s)\n", conf_g2pr.utils.gsch2pcb_rnd.default_pcb);
-			exit(1);
-		}
-	}
-
-	while ((fgets(buf, sizeof(buf), f_in)) != NULL) {
-		for (s = buf; *s == ' ' || *s == '\t'; ++s);
-		if (skipping) {
-			if (*s == '(')
-				++paren_level;
-			else if (*s == ')' && --paren_level <= 0)
-				skipping = FALSE;
-			continue;
-		}
-		el = pkg_to_element(f_out, s);
-		if (el && pcb_element_exists(el, TRUE)) {
-			pcb_element_free(el);
-			continue;
-		}
-		if (!el || el->omit_PKG) {
-			if (el) {
-
-			}
-			else
-				fputs(buf, f_out);
-			continue;
-		}
-
-		{
-			if (conf_g2pr.utils.gsch2pcb_rnd.verbose)
-				printf("%s: need new element for footprint  %s (value=%s)\n", el->refdes, el->description, el->value);
-
-			fp = fp_fopen(element_search_path, el->description, &fctx);
-
-			if (fp == NULL && conf_g2pr.utils.gsch2pcb_rnd.verbose)
-				printf("\tNo file element found.\n");
-
-			if ((fp != NULL) && insert_element(f_out, fp, el->description, el->refdes, el->value)) {
-				++n_added_ef;
-				if (conf_g2pr.utils.gsch2pcb_rnd.verbose)
-					printf("%s: added new element for footprint %s (value=%s)\n", el->refdes, el->description, el->value);
-			}
-			else {
-				fprintf(stderr, "%s: can't find PCB element for footprint %s (value=%s)\n", el->refdes, el->description, el->value);
-				if (conf_g2pr.utils.gsch2pcb_rnd.remove_unfound_elements && !conf_g2pr.utils.gsch2pcb_rnd.fix_elements) {
-					fprintf(stderr, "So device %s will not be in the layout.\n", el->refdes);
-					++n_PKG_removed_new;
-				}
-				else {
-					++n_not_found;
-					fputs(buf, f_out);		/* Copy PKG_ line */
-				}
-			}
-			if (fp != NULL)
-				fp_fclose(fp, &fctx);
-		}
-
-		pcb_element_free(el);
-		if (conf_g2pr.utils.gsch2pcb_rnd.verbose)
-			printf("----\n");
-	}
-	fclose(f_in);
-	fclose(f_out);
-
-	total = n_added_ef + n_not_found;
-	if (total == 0)
-		build_and_run_command("rm %s", tmp_file);
-	else
-		build_and_run_command("mv %s %s", tmp_file, pcb_file);
-	free(tmp_file);
-	return total;
-}
-
-static void update_element_descriptions(char * pcb_file, char * bak)
-{
-	FILE *f_in, *f_out;
-	PcbElement *el, *el_exists;
-	char *fmt, *tmp, *s, buf[1024];
-	gdl_iterator_t it;
-
-	gdl_foreach(&pcb_element_list, &it, el) {
-		if (el->changed_description)
-			++n_fixed;
-	}
-	if (!pcb_element_list.length || n_fixed == 0) {
-		fprintf(stderr, "Could not find any elements to fix.\n");
-		return;
-	}
-	if ((f_in = fopen(pcb_file, "r")) == NULL)
-		return;
-	tmp = str_concat(NULL, pcb_file, ".tmp", NULL);
-	if ((f_out = fopen(tmp, "wb")) == NULL) {
-		fclose(f_in);
-		return;
-	}
-	while ((fgets(buf, sizeof(buf), f_in)) != NULL) {
-		for (s = buf; *s == ' ' || *s == '\t'; ++s);
-		if ((el = pcb_element_line_parse(s)) != NULL
-				&& (el_exists = pcb_element_exists(el, FALSE)) != NULL && el_exists->changed_description) {
-			fmt = el->quoted_flags ? "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %s %s%s\n" : "Element%c%s \"%s\" \"%s\" \"%s\" %s %s%s\n";
-			fprintf(f_out, fmt,
-							el->res_char, el->flags, el_exists->changed_description, el->refdes, el->value, el->x, el->y, el->tail);
-			printf("%s: updating element Description: %s -> %s\n", el->refdes, el->description, el_exists->changed_description);
-			el_exists->still_exists = TRUE;
-		}
-		else
-			fputs(buf, f_out);
-		pcb_element_free(el);
-	}
-	fclose(f_in);
-	fclose(f_out);
-
-	if (!bak_done) {
-		build_and_run_command("mv %s %s", pcb_file, bak);
-		bak_done = TRUE;
-	}
-
-	build_and_run_command("mv %s %s", tmp, pcb_file);
-	free(tmp);
-}
-
-static void prune_elements(char * pcb_file, char * bak)
-{
-	FILE *f_in, *f_out;
-	PcbElement *el, *el_exists;
-	char *fmt, *tmp, *s, buf[1024];
-	int paren_level = 0;
-	int skipping = FALSE;
-	gdl_iterator_t it;
-
-	gdl_foreach(&pcb_element_list, &it, el) {
-		if (!el->still_exists) {
-			if ((conf_g2pr.utils.gsch2pcb_rnd.preserve) || (el->nonetlist)) {
-				++n_preserved;
-				if (conf_g2pr.utils.gsch2pcb_rnd.verbose > 1)
-					fprintf(stderr,
-									"Preserving PCB element not in the schematic:    %s (element   %s) %s\n",
-									el->refdes, el->description, el->nonetlist ? "nonetlist" : "");
-			}
-			else
-				++n_deleted;
-		}
-		else if (el->changed_value)
-			++n_changed_value;
-	}
-	if ((pcb_element_list.length == 0) || (n_deleted == 0 && !need_PKG_purge && n_changed_value == 0)) {
-		return;
-	}
-	if ((f_in = fopen(pcb_file, "r")) == NULL) {
-		fprintf(stderr, "error: can not read %s\n", pcb_file);
-		return;
-	}
-	tmp = str_concat(NULL, pcb_file, ".tmp", NULL);
-	if ((f_out = fopen(tmp, "wb")) == NULL) {
-		fprintf(stderr, "error: can not write %s\n", tmp);
-		fclose(f_in);
-		return;
-	}
-
-	while ((fgets(buf, sizeof(buf), f_in)) != NULL) {
-		for (s = buf; *s == ' ' || *s == '\t'; ++s);
-		if (skipping) {
-			if (*s == '(')
-				++paren_level;
-			else if (*s == ')' && --paren_level <= 0)
-				skipping = FALSE;
-			continue;
-		}
-		el_exists = NULL;
-		if ((el = pcb_element_line_parse(s)) != NULL
-				&& (el_exists = pcb_element_exists(el, FALSE)) != NULL && !el_exists->still_exists && !conf_g2pr.utils.gsch2pcb_rnd.preserve && !el->nonetlist) {
-			skipping = TRUE;
-			if (conf_g2pr.utils.gsch2pcb_rnd.verbose)
-				printf("%s: deleted element %s (value=%s)\n", el->refdes, el->description, el->value);
-			pcb_element_free(el);
-			continue;
-		}
-		if (el_exists && el_exists->changed_value) {
-			fmt = el->quoted_flags ? "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %s %s%s\n" : "Element%c%s \"%s\" \"%s\" \"%s\" %s %s%s\n";
-			fprintf(f_out, fmt,
-							el->res_char, el->flags, el->description, el->refdes, el_exists->changed_value, el->x, el->y, el->tail);
-			if (conf_g2pr.utils.gsch2pcb_rnd.verbose)
-				printf("%s: changed element %s value: %s -> %s\n", el->refdes, el->description, el->value, el_exists->changed_value);
-		}
-		else if (!strncmp(s, "PKG_", 4))
-			++n_PKG_removed_old;
-		else
-			fputs(buf, f_out);
-		pcb_element_free(el);
-	}
-	fclose(f_in);
-	fclose(f_out);
-
-	if (!bak_done) {
-		build_and_run_command("mv %s %s", pcb_file, bak);
-		bak_done = TRUE;
-	}
-
-	build_and_run_command("mv %s %s", tmp, pcb_file);
-	free(tmp);
-}
-
 static void add_schematic(char * sch)
 {
 	char **n;
@@ -974,7 +104,7 @@ static void add_schematic(char * sch)
 	if (!conf_g2pr.utils.gsch2pcb_rnd.sch_basename) {
 		char *suff = loc_str_has_suffix(sch, ".sch", 4);
 		if (suff != NULL) {
-			char *tmp = loc_strndup(sch, suff - sch);
+			char *tmp = pcb_strndup(sch, suff - sch);
 			conf_set(CFR_CLI, "utils/gsch2pcb_rnd/sch_basename", -1, tmp, POL_OVERWRITE);
 			free(tmp);
 		}
@@ -1103,6 +233,7 @@ static void load_extra_project_files(void)
 	done = TRUE;
 }
 
+int have_project_file = 0;
 static void get_args(int argc, char ** argv)
 {
 	char *opt, *arg;
@@ -1125,6 +256,15 @@ static void get_args(int argc, char ** argv)
 				conf_set(CFR_CLI, "utils/gsch2pcb_rnd/verbose", -1, tmp, POL_OVERWRITE);
 				continue;
 			}
+			else if (!strcmp(opt, "m") || !strcmp(opt, "method")) {
+				if (method_find(arg) == NULL) {
+					Message(PCB_MSG_ERROR, "Error: can't use unknown method '%s'; try --help\n", arg);
+					exit(1);
+				}
+				conf_set(CFR_CLI, "utils/gsch2pcb_rnd/method", -1, arg, POL_OVERWRITE);
+				i++;
+				continue;
+			}
 			else if (!strcmp(opt, "c") || !strcmp(opt, "conf")) {
 				const char *stmp;
 				if (conf_set_from_cli(NULL, arg, NULL, &stmp) != 0) {
@@ -1161,6 +301,7 @@ static void get_args(int argc, char ** argv)
 			if (loc_str_has_suffix(argv[i], ".sch", 4) == NULL) {
 				load_extra_project_files();
 				load_project(argv[i]);
+				have_project_file = 1;
 			}
 			else
 				add_schematic(argv[i]);
@@ -1187,22 +328,22 @@ void free_strlist(gadl_list_t *lst)
 
 #include "fp_init.h"
 
+const char *local_project_pcb_name = NULL;
+
 /************************ main ***********************/
-char *pcb_file_name, *pcb_new_file_name, *bak_file_name, *pins_file_name, *net_file_name;
 int main(int argc, char ** argv)
 {
-	int i;
-	int initial_pcb = TRUE;
-	int created_pcb_file = TRUE;
+	const char *want_method;
+
+	method_pcb_register();
+	method_import_register();
 
 	if (argc < 2)
 		usage();
 
-
 	conf_init();
 	conf_core_init();
 
-
 	gadl_list_init(&schematics, sizeof(char *), NULL, NULL);
 	gadl_list_init(&extra_gnetlist_arg_list, sizeof(char *), NULL, NULL);
 	gadl_list_init(&extra_gnetlist_list, sizeof(char *), NULL, NULL);
@@ -1223,120 +364,45 @@ int main(int argc, char ** argv)
 
 	load_extra_project_files();
 
-	conf_update(NULL); /* because of CLI changes */
-
-	fp_init();
-
-	element_search_path = fp_default_search_path();
-
-	if (gadl_length(&schematics) == 0)
-		usage();
-
-	pins_file_name = str_concat(NULL, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, ".cmd", NULL);
-	net_file_name = str_concat(NULL, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, ".net", NULL);
-	pcb_file_name = str_concat(NULL, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, ".pcb", NULL);
-
-	conf_load_project(NULL, pcb_file_name);
-	conf_update(NULL); /* because of the project file */
 
-	{ /* set bak_file_name, finding the first number that results in a non-existing bak */
-		int len;
-		char *end;
-
-		len = strlen(conf_g2pr.utils.gsch2pcb_rnd.sch_basename);
-		bak_file_name = malloc(len+8+64); /* make room for ".pcb.bak" and an integer */
-		memcpy(bak_file_name, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, len);
-		end = bak_file_name + len;
-		strcpy(end, ".pcb.bak");
-		end += 8;
-
-		for (i = 0; file_exists(bak_file_name); ++i)
-			sprintf(end, "%d", i);
-	}
-
-	if (file_exists(pcb_file_name)) {
-		initial_pcb = FALSE;
-		pcb_new_file_name = str_concat(NULL, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, ".new.pcb", NULL);
-		get_pcb_element_list(pcb_file_name);
-	}
-	else
-		pcb_new_file_name = pcb_strdup(pcb_file_name);
+	conf_update(NULL); /* because of CLI changes */
 
-	if (!run_gnetlist(pins_file_name, net_file_name, pcb_new_file_name, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, &schematics)) {
-		fprintf(stderr, "Failed to run gnetlist\n");
-		exit(1);
+	want_method = conf_g2pr.utils.gsch2pcb_rnd.method;
+	if (want_method == NULL) {
+		method_t *m;
+		for(m = methods; m != NULL; m = m->next) {
+			if (m->guess_out_name()) {
+				current_method = m;
+				break;
+			}
+		}
+		if (current_method == NULL) {
+			want_method = want_method_default;
+			Message(PCB_MSG_WARNING, "Warning: method not specified for a project without a board; defaulting to %s. This warning is harmless if you are running gsch2pcb-rnd for the first time on this project and you are fine with this default method.", want_method);
+		}
 	}
 
-	if (add_elements(pcb_new_file_name) == 0) {
-		build_and_run_command("rm %s", pcb_new_file_name);
-		if (initial_pcb) {
-			printf("No elements found, so nothing to do.\n");
-			exit(0);
+	if (current_method == NULL) {
+		current_method = method_find(want_method);
+		if (current_method == NULL) {
+			Message(PCB_MSG_ERROR, "Error: can't find method %s\n", want_method);
+			exit(1);
 		}
 	}
 
-	if (conf_g2pr.utils.gsch2pcb_rnd.fix_elements)
-		update_element_descriptions(pcb_file_name, bak_file_name);
-	prune_elements(pcb_file_name, bak_file_name);
-
-	/* Report work done during processing */
-	if (conf_g2pr.utils.gsch2pcb_rnd.verbose)
-		printf("\n");
-	printf("\n----------------------------------\n");
-	printf("Done processing.  Work performed:\n");
-	if (n_deleted > 0 || n_fixed > 0 || need_PKG_purge || n_changed_value > 0)
-		printf("%s is backed up as %s.\n", pcb_file_name, bak_file_name);
-	if (pcb_element_list.length && n_deleted > 0)
-		printf("%d elements deleted from %s.\n", n_deleted, pcb_file_name);
-
-	if (n_added_ef > 0)
-		printf("%d file elements added to %s.\n", n_added_ef, pcb_new_file_name);
-	else if (n_not_found == 0) {
-		printf("No elements to add so not creating %s\n", pcb_new_file_name);
-		created_pcb_file = FALSE;
-	}
+	current_method->init();
+	conf_update(NULL);
 
-	if (n_not_found > 0) {
-		printf("%d not found elements added to %s.\n", n_not_found, pcb_new_file_name);
-	}
-	if (n_unknown > 0)
-		printf("%d components had no footprint attribute and are omitted.\n", n_unknown);
-	if (n_none > 0)
-		printf("%d components with footprint \"none\" omitted from %s.\n", n_none, pcb_new_file_name);
-	if (n_empty > 0)
-		printf("%d components with empty footprint \"%s\" omitted from %s.\n", n_empty, conf_g2pr.utils.gsch2pcb_rnd.empty_footprint_name, pcb_new_file_name);
-	if (n_changed_value > 0)
-		printf("%d elements had a value change in %s.\n", n_changed_value, pcb_file_name);
-	if (n_fixed > 0)
-		printf("%d elements fixed in %s.\n", n_fixed, pcb_file_name);
-	if (n_PKG_removed_old > 0) {
-		printf("%d elements could not be found.", n_PKG_removed_old);
-		if (created_pcb_file)
-			printf("  So %s is incomplete.\n", pcb_file_name);
-		else
-			printf("\n");
-	}
-	if (n_PKG_removed_new > 0) {
-		printf("%d elements could not be found.", n_PKG_removed_new);
-		if (created_pcb_file)
-			printf("  So %s is incomplete.\n", pcb_new_file_name);
-		else
-			printf("\n");
-	}
-	if (n_preserved > 0)
-		printf("%d elements not in the schematic preserved in %s.\n", n_preserved, pcb_file_name);
+	if (gadl_length(&schematics) == 0)
+		usage();
 
-	/* Tell user what to do next */
-	if (conf_g2pr.utils.gsch2pcb_rnd.verbose)
-		printf("\n");
+	if ((local_project_pcb_name != NULL) && (!have_project_file))
+		conf_load_project(NULL, local_project_pcb_name);
+	conf_update(NULL); /* because of the project file */
 
-	if (n_added_ef > 0)
-		next_steps(initial_pcb, conf_g2pr.utils.gsch2pcb_rnd.quiet_mode);
+	current_method->go(); /* the traditional, "parse element and edit the pcb file" approach */
 
-	free(net_file_name);
-	free(pins_file_name);
-	free(pcb_file_name);
-	free(bak_file_name);
+	current_method->uninit();
 
 	conf_uninit();
 
@@ -1344,8 +410,5 @@ int main(int argc, char ** argv)
 	free_strlist(&extra_gnetlist_arg_list);
 	free_strlist(&extra_gnetlist_list);
 
-	if (pcb_new_file_name != NULL)
-		free(pcb_new_file_name);
-
 	return 0;
 }
diff --git a/util/gsch2pcb-rnd/gsch2pcb.h b/util/gsch2pcb-rnd/gsch2pcb.h
new file mode 100644
index 0000000..def40f7
--- /dev/null
+++ b/util/gsch2pcb-rnd/gsch2pcb.h
@@ -0,0 +1,48 @@
+#include "../src_3rd/genlist/gendlist.h"
+#include "../src_3rd/genlist/genadlist.h"
+
+#define TRUE 1
+#define FALSE 0
+
+#define GSCH2PCB_RND_VERSION "2.0.0"
+
+#define DEFAULT_PCB_INC "pcb.inc"
+
+#define SEP_STRING "--------\n"
+
+/* from scconfig str lib: */
+char *str_concat(const char *sep, ...);
+
+typedef struct {
+	char *refdes, *value, *description, *changed_description, *changed_value;
+	char *flags, *tail;
+	char *x, *y;
+	char *pkg_name_fix;
+	char res_char;
+
+	gdl_elem_t all_elems;
+
+	unsigned still_exists:1;
+	unsigned new_format:1;
+	unsigned hi_res_format:1;
+	unsigned quoted_flags:1;
+	unsigned omit_PKG:1;
+	unsigned nonetlist:1;
+} PcbElement;
+
+typedef struct {
+	char *part_number, *element_name;
+} ElementMap;
+
+extern gdl_list_t pcb_element_list;
+extern gadl_list_t schematics, extra_gnetlist_arg_list, extra_gnetlist_list;
+
+extern int n_deleted, n_added_ef, n_fixed, n_PKG_removed_new,
+           n_PKG_removed_old, n_preserved, n_changed_value, n_not_found,
+           n_unknown, n_none, n_empty;
+
+extern int bak_done, need_PKG_purge;
+
+const char *local_project_pcb_name; /* file name of the design from which the local project file name shall be derived */
+
+char *fix_spaces(char * str);
diff --git a/util/gsch2pcb-rnd/gsch2pcb_rnd_conf.h b/util/gsch2pcb-rnd/gsch2pcb_rnd_conf.h
index 3854424..2070a59 100644
--- a/util/gsch2pcb-rnd/gsch2pcb_rnd_conf.h
+++ b/util/gsch2pcb-rnd/gsch2pcb_rnd_conf.h
@@ -14,6 +14,7 @@ typedef struct {
 			CFT_STRING sch_basename;
 			CFT_STRING default_pcb;            /* override default pcb with a given file */
 			CFT_STRING empty_footprint_name;
+			CFT_STRING method;
 		} gsch2pcb_rnd;
 	} utils;
 } conf_gsch2pcb_rnd_t;
diff --git a/util/gsch2pcb-rnd/help.c b/util/gsch2pcb-rnd/help.c
index 97c1092..9c4dc7e 100644
--- a/util/gsch2pcb-rnd/help.c
+++ b/util/gsch2pcb-rnd/help.c
@@ -25,29 +25,31 @@
   - use popen() instead of glib's spawn (stderr is always printed to stderr)
  */
 #include "config.h"
-
+#include "method.h"
 #include <stdlib.h>
 #include <stdio.h>
 
 
 extern char *pcb_file_name, *pcb_new_file_name, *bak_file_name, *pins_file_name, *net_file_name;
 
-static char *usage_string0 =
+static char *usage_string0a =
 	"usage: gsch2pcb [options] {project | foo.sch [foo1.sch ...]}\n"
 	"\n"
 	"Generate a PCB layout file from a set of gschem schematics.\n"
-	"   gnetlist -g PCB is run to generate foo.net from the schematics.\n" "\n"
+	"   gnetlist -g PCB is run to generate foo.net from the schematics.\n" "\n";
 /* TODO */
 /*  "   gnetlist -g gsch2pcb is run to get PCB elements which\n"
   "   match schematic footprints.  For schematic footprints which don't match\n"
   "   any PCB layout elements, search a set of file element directories in\n"
   "   an attempt to find matching PCB file elements.\n"*/
+static char *usage_string0b =
 	"   Output to foo.pcb if it doesn't exist.  If there is a current foo.pcb,\n"
 	"   output only new elements to foo.new.pcb.\n"
 	"   If any elements with a non-empty element name in the current foo.pcb\n"
 	"   have no matching schematic component, then remove those elements from\n"
 	"   foo.pcb and rename foo.pcb to a foo.pcb.bak sequence.\n"
-	"\n"
+	"\n";
+static char *usage_string0c =
 	"   gnetlist -g pcbpins is run to get a PCB actions file which will rename all\n"
 	"   of the pins in a .pcb file to match pin names from the schematic.\n"
 	"\n"
@@ -57,56 +59,68 @@ static char *usage_string0 =
 	"   Options in a project file are like command line args without the \"-\":\n"
 	"       output-name myproject\n"
 	"\n"
-	"options (may be included in a project file):\n"
+	"options (may be included in a project file):\n";
+static char *usage_string0d =
 	"   -d, --elements-dir D    Search D for PCB file elements.  These defaults\n"
 	"                           are searched if they exist. See the default\n"
 	"                           search paths at the end of this text."
 	"   -c, --elements-dir-clr  Clear the elements dir. Useful before a series\n"
 	"                           if -d's to flush defaults.\n"
+	"   -m, --method M          Use method M for the import. See available\n"
+	"                           methods below.\n";
+static char *usage_string0e =
 	"   -s, --elements-shell S  Use S as a prefix for running parametric footrint\n"
 	"                           generators. It is useful on systems where popen()\n"
 	"                           doesn't do the right thing or the process should\n"
 	"                           be wrapped. Example -s \"/bin/sh -c\"\n"
 	"   -P, --default-pcb       Change the default PCB file's name; this file is\n"
 	"                           inserted on top of the *.new.pcb generated, for\n"
-	"                           PCB default settings\n"
+	"                           PCB default settings\n";
+static char *usage_string0f =
 	"   -o, --output-name N     Use output file names N.net, N.pcb, and N.new.pcb\n"
 	"                           instead of foo.net, ... where foo is the basename\n"
 	"                           of the first command line .sch file.\n"
 	"   -r, --remove-unfound    Don't include references to unfound elements in\n"
 	"                           the generated .pcb files.  Use if you want PCB to\n"
 	"                           be able to load the (incomplete) .pcb file.\n"
-	"                           This is the default behavior.\n"
+	"                           This is the default behavior.\n";
+static char *usage_string0g =
 	"   -k, --keep-unfound      Keep include references to unfound elements in\n"
 	"                           the generated .pcb files.  Use if you want to hand\n"
 	"                           edit or otherwise preprocess the generated .pcb file\n"
-	"                           before running pcb.\n"
+	"                           before running pcb.\n";
+static char *usage_string0h =
 	"   -p, --preserve          Preserve elements in PCB files which are not found\n"
 	"                           in the schematics.  Note that elements with an empty\n"
 	"                           element name (schematic refdes) are never deleted,\n"
-	"                           so you really shouldn't need this option.\n"
+	"                           so you really shouldn't need this option.\n";
+static char *usage_string0i =
 	"   -q, --quiet             Don't tell the user what to do next after running\n"
 	"                           gsch2pcb-rnd.\n" "\n";
 
-static char *usage_string1 =
+static char *usage_string1a =
 	"   --gnetlist backend    A convenience run of extra gnetlist -g commands.\n"
 	"                         Example:  gnetlist partslist3\n"
 	"                         Creates:  myproject.partslist3\n"
 	" --empty-footprint name  See the project.sample file.\n"
-	"\n"
+	"\n";
+static char *usage_string1b =
 	"options (not recognized in a project file):\n"
 	"   --gnetlist-arg arg    Allows additional arguments to be passed to gnetlist.\n"
 	"       --fix-elements    If a schematic component footprint is not equal\n"
 	"                         to its PCB element Description, update the\n"
 	"                         Description instead of replacing the element.\n"
 	"                         Do this the first time gsch2pcb is used with\n"
-	"                         PCB files originally created with gschem2pcb.\n"
+	"                         PCB files originally created with gschem2pcb.\n";
+static char *usage_string1c =
 	"   -v, --verbose         Use -v -v for additional file element debugging.\n"
 	"   -V, --version\n\n"
 	"environment variables:\n"
 	"   GNETLIST              If set, this specifies the name of the gnetlist program\n"
 	"                         to execute.\n"
-	"\n"
+	"\n";
+
+static char *usage_string_foot =
 	"Additional Resources:\n"
 	"\n"
 	"  gnetlist user guide:  http://wiki.geda-project.org/geda:gnetlist_ug\n"
@@ -116,33 +130,24 @@ static char *usage_string1 =
 
 void usage(void)
 {
-	puts(usage_string0);
-	puts(usage_string1);
-	exit(0);
-}
+	method_t *m;
+	printf("%s", usage_string0a);
+	printf("%s", usage_string0b);
+	printf("%s", usage_string0c);
+	printf("%s", usage_string0d);
+	printf("%s", usage_string0e);
+	printf("%s", usage_string0f);
+	printf("%s", usage_string0g);
+	printf("%s", usage_string0h);
+	printf("%s", usage_string0i);
+	printf("%s", usage_string1a);
+	printf("%s", usage_string1b);
+	printf("%s", usage_string1c);
 
-void next_steps(int initial_pcb, int quiet_mode)
-{
-	if (initial_pcb) {
-		printf("\nNext step:\n");
-		printf("1.  Run pcb on your file %s.\n", pcb_file_name);
-		printf("    You will find all your footprints in a bundle ready for you to place\n");
-		printf("    or disperse with \"Select -> Disperse all elements\" in PCB.\n\n");
-		printf("2.  From within PCB, select \"File -> Load netlist file\" and select \n");
-		printf("    %s to load the netlist.\n\n", net_file_name);
-		printf("3.  From within PCB, enter\n\n");
-		printf("           :ExecuteFile(%s)\n\n", pins_file_name);
-		printf("    to propagate the pin names of all footprints to the layout.\n\n");
-	}
-	else if (!quiet_mode) {
-		printf("\nNext steps:\n");
-		printf("1.  Run pcb on your file %s.\n", pcb_file_name);
-		printf("2.  From within PCB, select \"File -> Load layout data to paste buffer\"\n");
-		printf("    and select %s to load the new footprints into your existing layout.\n", pcb_new_file_name);
-		printf("3.  From within PCB, select \"File -> Load netlist file\" and select \n");
-		printf("    %s to load the updated netlist.\n\n", net_file_name);
-		printf("4.  From within PCB, enter\n\n");
-		printf("           :ExecuteFile(%s)\n\n", pins_file_name);
-		printf("    to update the pin names of all footprints.\n\n");
-	}
+	printf("\nMethods available:\n");
+	for(m = methods; m != NULL; m = m->next)
+		printf("  %-12s %s\n", m->name, m->desc);
+	printf("\n");
+	printf("%s", usage_string_foot);
+	exit(0);
 }
diff --git a/util/gsch2pcb-rnd/help.h b/util/gsch2pcb-rnd/help.h
index d9039c4..f06d9c2 100644
--- a/util/gsch2pcb-rnd/help.h
+++ b/util/gsch2pcb-rnd/help.h
@@ -1,2 +1 @@
 void usage(void);
-void next_steps(int initial_pcb, int quiet_mode);
diff --git a/util/gsch2pcb-rnd/method.h b/util/gsch2pcb-rnd/method.h
new file mode 100644
index 0000000..3663e36
--- /dev/null
+++ b/util/gsch2pcb-rnd/method.h
@@ -0,0 +1,15 @@
+typedef struct method_s  method_t;
+
+struct method_s {
+	const char *name;
+	const char *desc;
+	void (*init)(void);
+	void (*go)(void);
+	void (*uninit)(void);
+	int (*guess_out_name)(void); /* returns 1 if the output file of the format exists for the current project */
+	method_t *next;
+};
+
+extern method_t *methods;
+
+void method_register(method_t *fmt);
diff --git a/util/gsch2pcb-rnd/method_import.c b/util/gsch2pcb-rnd/method_import.c
new file mode 100644
index 0000000..0c7a3b3
--- /dev/null
+++ b/util/gsch2pcb-rnd/method_import.c
@@ -0,0 +1,141 @@
+/* gsch2pcb-rnd
+ *
+ *  Original version: Bill Wilson    billw at wt.net
+ *  rnd-version: (C) 2015..2016, Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software which I release under the GNU General Public
+ *  License. You may redistribute and/or modify this program under the terms
+ *  of that 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.  Version 2 is in the
+ *  COPYRIGHT file in the top level directory of this distribution.
+ *
+ *  To get a copy of the GNU General Puplic License, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include "gsch2pcb.h"
+#include "gsch2pcb_rnd_conf.h"
+#include "method_pcb.h"
+#include "run.h"
+#include "netlister.h"
+#include "method.h"
+#include "../src/conf.h"
+#include "../src/conf_core.h"
+#include "../src/compat_misc.h"
+#include "../src/compat_fs.h"
+
+char *cmd_file_name;
+char *pcb_file_name;
+char *net_file_name;
+
+static void method_import_init(void)
+{
+	pcb_file_name = str_concat(NULL, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, ".pcb", NULL);
+	cmd_file_name = str_concat(NULL, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, ".cmd", NULL);
+	net_file_name = str_concat(NULL, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, ".net", NULL);
+	local_project_pcb_name = pcb_file_name;
+}
+
+static void import_go(int sep_net)
+{
+	char *verbose_str = NULL;
+	const char *gnetlist, *backend;
+
+	gnetlist = gnetlist_name();
+	if (!conf_g2pr.utils.gsch2pcb_rnd.verbose)
+		verbose_str = "-q";
+
+	backend = sep_net ? "pcbrndfwd_elem" : "pcbrndfwd";
+	if (!build_and_run_command("%s %s -L %s -g %s -o %s %L %L", gnetlist, verbose_str, PCBLIBDIR, backend, cmd_file_name, &extra_gnetlist_arg_list, &schematics)) {
+		fprintf(stderr, "Failed to run gnetlist with backend %s to generate the elements\n", backend);
+		exit(1);
+	}
+
+	if (sep_net) {
+		if (!build_and_run_command("%s %s -L %s -g PCB -o %s %L %L", gnetlist, verbose_str, PCBLIBDIR, net_file_name, &extra_gnetlist_arg_list, &schematics)) {
+			fprintf(stderr, "Failed to run gnetlist net file\n");
+			exit(1);
+		}
+	}
+
+	/* Tell user what to do next */
+	printf("\nNext step:\n");
+	printf("1.  Run pcb-rnd on your board file (or on an empty board if it's the first time).\n");
+	printf("2.  From within pcb-rnd, enter\n\n");
+	printf("    :ExecuteFile(%s)\n\n", cmd_file_name);
+
+	if (sep_net) {
+		printf("    (this will update the elements)\n\n");
+		printf("3.  From within pcb-rnd, select \"File -> Load netlist file\" and select \n");
+		printf("    %s to load the updated netlist.\n\n", net_file_name);
+	}
+	else
+		printf("    (this will update the elements and the netlist)\n\n");
+}
+
+static void method_import_go()
+{
+	import_go(0);
+}
+
+static void method_import_sep_go()
+{
+	import_go(1);
+}
+
+
+static int method_import_guess_out_name(void)
+{
+	int res;
+	char *name;
+	
+	name = str_concat(NULL, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, ".lht", NULL);
+	res = pcb_file_readable(name);
+	free(name);
+	if (!res) {
+		name = str_concat(NULL, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, ".pcb.lht", NULL);
+		res = pcb_file_readable(name);
+		free(name);
+	}
+	return res;
+}
+
+static void method_import_uninit(void)
+{
+	if (pcb_file_name != NULL)
+		free(pcb_file_name);
+	if (cmd_file_name != NULL)
+		free(cmd_file_name);
+	if (net_file_name != NULL)
+		free(net_file_name);
+}
+
+static method_t method_import;
+static method_t method_import_sep;
+
+void method_import_register(void)
+{
+	method_import.name = "import";
+	method_import.desc = "import schematics (pure action script)";
+	method_import.init = method_import_init;
+	method_import.go = method_import_go;
+	method_import.uninit = method_import_uninit;
+	method_import.guess_out_name = method_import_guess_out_name;
+	method_register(&method_import);
+
+	method_import_sep.name = "importsep";
+	method_import_sep.desc = "import schematics (separate action script and netlist)";
+	method_import_sep.init = method_import_init;
+	method_import_sep.go = method_import_sep_go;
+	method_import_sep.uninit = method_import_uninit;
+	method_import_sep.guess_out_name = method_import_guess_out_name;
+	method_register(&method_import_sep);
+}
diff --git a/util/gsch2pcb-rnd/method_import.h b/util/gsch2pcb-rnd/method_import.h
new file mode 100644
index 0000000..3883955
--- /dev/null
+++ b/util/gsch2pcb-rnd/method_import.h
@@ -0,0 +1 @@
+void method_import_register(void);
diff --git a/util/gsch2pcb-rnd/gsch2pcb.c b/util/gsch2pcb-rnd/method_pcb.c
similarity index 57%
copy from util/gsch2pcb-rnd/gsch2pcb.c
copy to util/gsch2pcb-rnd/method_pcb.c
index bbe70a2..16904c2 100644
--- a/util/gsch2pcb-rnd/gsch2pcb.c
+++ b/util/gsch2pcb-rnd/method_pcb.c
@@ -16,344 +16,29 @@
  *
  *  To get a copy of the GNU General Puplic License, write to the Free Software
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
 
- * Retrieved from the official git (2015.07.15)
-
- Behavior different from the original:
-  - use getenv() instead of g_getenv(): on windows this won't do recursive variable expansion
-  - use rnd-specific .scm
-  - use pcb-rnd's conf system
-  - use popen() instead of glib's spawn (stderr is always printed to stderr)
- */
-/* for popen() */
-#define _DEFAULT_SOURCE
-#define _BSD_SOURCE
-
-#include "config.h"
-
-#include <stdarg.h>
 #include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
-#include <time.h>
-#include <unistd.h>
-#include <sys/stat.h>
+#include "gsch2pcb.h"
+#include "gsch2pcb_rnd_conf.h"
+#include "method_pcb.h"
+#include "run.h"
+#include "netlister.h"
+#include "method.h"
 #include "../src/plug_footprint.h"
 #include "../src/paths.h"
 #include "../src/conf.h"
 #include "../src/conf_core.h"
-#include "../src_3rd/genvector/vts0.h"
-#include "../src_3rd/genlist/gendlist.h"
-#include "../src_3rd/genlist/genadlist.h"
-#include "../src_3rd/qparse/qparse.h"
-#include "../config.h"
-#include "../src/error.h"
+#include "../src/compat_misc.h"
+#include "../src/compat_fs.h"
 #include "../src/plugins.h"
 #include "../src/plug_footprint.h"
-#include "../src/compat_misc.h"
-#include "help.h"
-#include "gsch2pcb_rnd_conf.h"
-
-#define TRUE 1
-#define FALSE 0
-
-#define GSCH2PCB_RND_VERSION "1.0.1"
-
-#define DEFAULT_PCB_INC "pcb.inc"
-
-#define SEP_STRING "--------\n"
-
-/* from scconfig str lib: */
-char *str_concat(const char *sep, ...);
-
-typedef struct {
-	char *refdes, *value, *description, *changed_description, *changed_value;
-	char *flags, *tail;
-	char *x, *y;
-	char *pkg_name_fix;
-	char res_char;
-
-	gdl_elem_t all_elems;
-
-	unsigned still_exists:1;
-	unsigned new_format:1;
-	unsigned hi_res_format:1;
-	unsigned quoted_flags:1;
-	unsigned omit_PKG:1;
-	unsigned nonetlist:1;
-
-} PcbElement;
-
-typedef struct {
-	char *part_number, *element_name;
-} ElementMap;
-
-gdl_list_t pcb_element_list; /* initialized to 0 */
-gadl_list_t schematics, extra_gnetlist_arg_list, extra_gnetlist_list;
-
-static int n_deleted, n_added_ef, n_fixed, n_PKG_removed_new,
-           n_PKG_removed_old, n_preserved, n_changed_value, n_not_found,
-           n_unknown, n_none, n_empty;
-
-static int bak_done, need_PKG_purge;
-
-conf_gsch2pcb_rnd_t conf_g2pr;
 
 static const char *element_search_path = NULL; /* queried once from the config, when the config is already stable */
 
-static char *loc_strndup(const char *str, size_t len)
-{
-	char *s;
-	int l;
-
-	if (str == NULL)
-		return NULL;
-	l = strlen(str);
-	if (l < len)
-		len = l;
-	s = malloc(len+1);
-	memcpy(s, str, len);
-	s[len] = '\0';
-	return s;
-}
-
-/* Return a pointer to the suffix if inp ends in that suffix */
-static char *loc_str_has_suffix(char *inp, const char *suffix, int suff_len)
-{
-	int len = strlen(inp);
-	if ((len >= suff_len) && (strcmp(inp + len - suff_len, suffix) == 0))
-		return inp + len - suff_len;
-	return NULL;
-}
-
-/* Checks if a file exists and is readable */
-static int file_exists(const char *fn)
-{
-	FILE *f;
-	f = fopen(fn, "r");
-	if (f == NULL)
-		return 0;
-	fclose(f);
-	return 1;
-}
-
-void ChdirErrorMessage(const char *DirName)
-{
-	fprintf(stderr, "gsch2pcb-rnd: warning: can't cd to %s\n", DirName);
-}
-
-void OpendirErrorMessage(const char *DirName)
-{
-	fprintf(stderr, "gsch2pcb-rnd: warning: can't opendir %s\n", DirName);
-}
-
-void PopenErrorMessage(const char *cmd)
-{
-	fprintf(stderr, "gsch2pcb-rnd: warning: can't popen %s\n", cmd);
-}
-
-void Message(enum pcb_message_level level, const char *fmt, ...)
-{
-	va_list args;
-	fprintf(stderr, "gsch2pcb-rnd: ");
-	va_start(args, fmt);
-	vfprintf(stderr, fmt, args);
-	va_end(args);
-	fprintf(stderr, "\n");
-}
-
-void pcb_trace(const char *Format, ...)
-{
-#ifndef NDEBUG
-	va_list args;
-	va_start(args, Format);
-	vfprintf(stderr, Format, args);
-	va_end(args);
-#endif
-}
-
-
-/**
- * Build and run a command. No redirection or error handling is
- * done.  Format string is split on whitespace. Specifiers %l and %s
- * are replaced with contents of positional args. To be recognized,
- * specifiers must be separated from other arguments in the format by
- * whitespace.
- *  - %L expects a gadl_list_t vith char * payload, contents used as separate arguments
- *  - %s expects a char*, contents used as a single argument (omitted if NULL)
- * @param[in] format  used to specify command to be executed
- * @param[in] ...     positional parameters
- */
-static int build_and_run_command(const char * format_, ...)
-{
-	va_list vargs;
-	int result = FALSE;
-	vts0_t args;
-	char *format, *s, *start;
-
-	/* Translate the format string; args elements point to const char *'s
-	   within a copy of the format string. The format string is copied so
-	   that these parts can be terminated by overwriting whitepsace with \0 */
-	va_start(vargs, format_);
-	format = pcb_strdup(format_);
-	vts0_init(&args);
-	for(s = start = format; *s != '\0'; s++) {
-		/* if word separator is reached, save the previous word */
-		if (isspace(s[0])) {
-			if (start == s) { /* empty word - skip */
-				start++;
-				continue;
-			}
-			*s = '\0';
-			vts0_append(&args, start);
-			start = s+1;
-			continue;
-		}
-
-		/* check if current word is a format */
-		if ((s == start) && (s[0] == '%') && (s[1] != '\0') && ((s[2] == '\0') || isspace(s[2]))) {
-			switch(s[1]) {
-				case 'L': /* append contents of char * gadl_list_t */
-					{
-						gadl_list_t *list = va_arg(vargs, gadl_list_t *);
-						gadl_iterator_t it;
-						char **s;
-						gadl_foreach(list, &it, s) {
-							vts0_append(&args, *s);
-						}
-					}
-					start = s+2;
-					s++;
-					continue;
-				case 's':
-					{
-						char *arg = va_arg(vargs, char *);
-						if (arg != NULL)
-							vts0_append(&args, arg);
-						start = s+2;
-						s++;
-					}
-					continue;
-			}
-		}
-	}
-	va_end(vargs);
-
-	if (args.used > 0) {
-		int i, l;
-		char *cmd, *end, line[1024];
-		FILE *f;
-
-		l = 0;
-		for (i = 0; i < args.used; i++)
-			l += strlen(args.array[i]) + 3;
-
-		end = cmd = malloc(l+1);
-		for (i = 0; i < args.used; i++) {
-			l = strlen(args.array[i]);
-			*end = '"'; end++;
-			memcpy(end, args.array[i], l);
-			end += l;
-			*end = '"'; end++;
-			*end = ' '; end++;
-		}
-		end--;
-		*end = '\0';
-
-		/* we have something in the list, build & call command */
-		if (conf_g2pr.utils.gsch2pcb_rnd.verbose) {
-			printf("Running command:\n\t%s\n", cmd);
-			printf("%s", SEP_STRING);
-		}
-
-		f = popen(cmd, "r");
-		while(fgets(line, sizeof(line), f) != NULL) {
-			if (conf_g2pr.utils.gsch2pcb_rnd.verbose)
-				fputs(line, stdout);
-		}
-
-		if (pclose(f) == 0)
-			result = TRUE;
-		else
-			fprintf(stderr, "Failed to execute external program\n");
-		free(cmd);
-
-		if (conf_g2pr.utils.gsch2pcb_rnd.verbose)
-			printf("\n%s", SEP_STRING);
-	}
-
-	free(format);
-	vts0_uninit(&args);
-	return result;
-}
-
-/* Run gnetlist to generate a netlist and a PCB board file.  gnetlist
- * has exit status of 0 even if it's given an invalid arg, so do some
- * stat() hoops to decide if gnetlist successfully generated the PCB
- * board file (only gnetlist >= 20030901 recognizes -m).
- */
-static int run_gnetlist(const char * pins_file, const char * net_file, const char * pcb_file, const char * basename, const gadl_list_t *largs)
-{
-	struct stat st;
-	time_t mtime;
-	static const char *gnetlist = NULL;
-	gadl_iterator_t it;
-	char **sp;
-	char *verbose_str = NULL;
-
-	/* Allow the user to specify a full path or a different name for
-	 * the gnetlist command.  Especially useful if multiple copies
-	 * are installed at once.
-	 */
-	if (gnetlist == NULL)
-		gnetlist = getenv("GNETLIST");
-	if (gnetlist == NULL)
-		gnetlist = "gnetlist";
-
-	if (!conf_g2pr.utils.gsch2pcb_rnd.verbose)
-		verbose_str = "-q";
-
-	if (!build_and_run_command("%s %s -g pcbpins -o %s %L %L", gnetlist, verbose_str, pins_file, &extra_gnetlist_arg_list, largs))
-		return FALSE;
-
-	if (!build_and_run_command("%s %s -g PCB -o %s %L %L", gnetlist, verbose_str, net_file, &extra_gnetlist_arg_list, largs))
-		return FALSE;
-
-	mtime = (stat(pcb_file, &st) == 0) ? st.st_mtime : 0;
-
-	if (!build_and_run_command("%s %s -L " SCMDIR " -g gsch2pcb-rnd -o %s %L %L",
-														 gnetlist, verbose_str, pcb_file, &extra_gnetlist_arg_list, largs)) {
-		if (stat(pcb_file, &st) != 0 || mtime == st.st_mtime) {
-			fprintf(stderr, "gsch2pcb: gnetlist command failed, `%s' not updated\n", pcb_file);
-			return FALSE;
-		}
-		return FALSE;
-	}
-
-	gadl_foreach(&extra_gnetlist_list, &it, sp) {
-		const char *s = *sp;
-		const char *s2 = strstr(s, " -o ");
-		char *out_file;
-		char *backend;
-		if (!s2) {
-			out_file = str_concat(NULL, basename, ".", s, NULL);
-			backend = pcb_strdup(s);
-		}
-		else {
-			out_file = pcb_strdup(s2 + 4);
-			backend = loc_strndup(s, s2 - s);
-		}
-
-		if (!build_and_run_command("%s %s -g %s -o %s %L %L",
-															 gnetlist, verbose_str, backend, out_file, &extra_gnetlist_arg_list, largs))
-			return FALSE;
-		free(out_file);
-		free(backend);
-	}
-
-	return TRUE;
-}
+static int insert_element(FILE * f_out, FILE * f_elem, char * footprint, char * refdes, char * value);
 
 static char *token(char * string, char ** next, int * quoted_ret, int parenth)
 {
@@ -396,24 +81,167 @@ static char *token(char * string, char ** next, int * quoted_ret, int parenth)
 				break;
 		}
 	}
-	ret = loc_strndup(str, s - str);
+	ret = pcb_strndup(str, s - str);
 	str = (quoted && *s) ? s + 1 : s;
 	if (next)
 		*next = str;
 	return ret;
 }
 
-static char *fix_spaces(char * str)
+static PcbElement *pkg_to_element(FILE * f, char * pkg_line)
+{
+	PcbElement *el;
+	char *s, *end, *refdes, *fp, *value;
+
+/*fprintf(stderr, "--- %s\n", pkg_line);*/
+
+	if (strncmp(pkg_line, "PKG", 3)
+			|| (s = strchr(pkg_line, (int) '(')) == NULL)
+		return NULL;
+
+/* remove trailing ")" */
+	end = s + strlen(s) - 2;
+	if (*end == ')')
+		*end = '\0';
+
+/* tokenize the line keeping () */
+	fp = token(s + 1, NULL, NULL, 1);
+	refdes = token(NULL, NULL, NULL, 1);
+	value = token(NULL, NULL, NULL, 1);
+
+
+/*fprintf(stderr, "refdes: %s\n", refdes);
+fprintf(stderr, "    fp: %s\n", fp);
+fprintf(stderr, "   val: %s\n", value);*/
+
+
+	if (!refdes || !fp || !value) {
+		if (refdes != NULL)
+			free(refdes);
+		if (fp != NULL)
+			free(fp);
+		if (value != NULL)
+			free(value);
+		fprintf(stderr, "Bad package line: %s\n", pkg_line);
+		return NULL;
+	}
+
+	fix_spaces(refdes);
+	fix_spaces(value);
+
+	el = calloc(sizeof(PcbElement), 1);
+	el->description = fp;
+	el->refdes = refdes;
+	el->value = value;
+
+/*
+// wtf?
+//  if ((s = strchr (el->value, (int) ')')) != NULL)
+//    *s = '\0';
+*/
+
+	if (conf_g2pr.utils.gsch2pcb_rnd.empty_footprint_name && !strcmp(el->description, conf_g2pr.utils.gsch2pcb_rnd.empty_footprint_name)) {
+		if (conf_g2pr.utils.gsch2pcb_rnd.verbose)
+			printf("%s: has the empty footprint attribute \"%s\" so won't be in the layout.\n", el->refdes, el->description);
+		n_empty += 1;
+		el->omit_PKG = TRUE;
+	}
+	else if (!strcmp(el->description, "none")) {
+		fprintf(stderr, "WARNING: %s has a footprint attribute \"%s\" so won't be in the layout.\n", el->refdes, el->description);
+		n_none += 1;
+		el->omit_PKG = TRUE;
+	}
+	else if (!strcmp(el->description, "unknown")) {
+		fprintf(stderr, "WARNING: %s has no footprint attribute so won't be in the layout.\n", el->refdes);
+		n_unknown += 1;
+		el->omit_PKG = TRUE;
+	}
+	return el;
+}
+
+
+/* Copies the content of fn to fout and returns 0 on success. */
+static int CatPCB(FILE * fout, const char *fn)
+{
+	FILE *fin;
+	fin = fopen(fn, "r");
+	if (fin == NULL)
+		return -1;
+
+	for (;;) {
+		char buff[1024];
+		int len;
+
+		len = fread(buff, 1, sizeof(buff), fin);
+		if (len <= 0)
+			break;
+
+		fwrite(buff, len, 1, fout);
+	}
+
+	fclose(fin);
+	return 0;
+}
+
+static void pcb_element_free(PcbElement * el)
+{
+	if (!el)
+		return;
+	free(el->flags);
+	free(el->description);
+	free(el->changed_description);
+	free(el->changed_value);
+	free(el->refdes);
+	free(el->value);
+	free(el->x);
+	free(el->y);
+	free(el->tail);
+	free(el->pkg_name_fix);
+	free(el);
+}
+
+static PcbElement *pcb_element_exists(PcbElement * el_test, int record)
+{
+	PcbElement *el;
+	gdl_iterator_t it;
+
+	gdl_foreach(&pcb_element_list, &it, el) {
+		if (strcmp(el_test->refdes, el->refdes))
+			continue;
+		if (strcmp(el_test->description, el->description)) {	/* footprint */
+			if (record)
+				el->changed_description = pcb_strdup(el_test->description);
+		}
+		else {
+			if (record) {
+				if (strcmp(el_test->value, el->value))
+					el->changed_value = pcb_strdup(el_test->value);
+				el->still_exists = TRUE;
+			}
+			return el;
+		}
+	}
+	return NULL;
+}
+
+/*
+static char *search_element(PcbElement * el)
 {
-	char *s;
+	char *elname = NULL, *path = NULL;
 
-	if (!str)
+	if (!elname)
+		elname = pcb_strdup(el->description);
+
+	if (!strcmp(elname, "unknown")) {
+		free(elname);
 		return NULL;
-	for (s = str; *s; ++s)
-		if (*s == ' ' || *s == '\t')
-			*s = '_';
-	return str;
+	}
+	if (conf_g2pr.utils.gsch2pcb_rnd.verbose > 1)
+		printf("\tSearching directories looking for file element: %s\n", elname);
+	free(elname);
+	return path;
 }
+*/
 
 	/* As of 1/9/2004 CVS hi_res Element[] line format:
 	 *   Element[element_flags, description, pcb-name, value, mark_x, mark_y,
@@ -430,7 +258,7 @@ static char *fix_spaces(char * str)
 	 *   is now relative.  The hi_res mark_x,mark_y and text_x,text_y resolutions
 	 *   are 100x the other formats.
 	 */
-PcbElement *pcb_element_line_parse(char * line)
+static PcbElement *pcb_element_line_parse(char * line)
 {
 	PcbElement *el = NULL;
 	char *s, *t, close_char;
@@ -501,22 +329,6 @@ PcbElement *pcb_element_line_parse(char * line)
 	return el;
 }
 
-static void pcb_element_free(PcbElement * el)
-{
-	if (!el)
-		return;
-	free(el->flags);
-	free(el->description);
-	free(el->changed_description);
-	free(el->changed_value);
-	free(el->refdes);
-	free(el->value);
-	free(el->x);
-	free(el->y);
-	free(el->tail);
-	free(el->pkg_name_fix);
-	free(el);
-}
 
 static void get_pcb_element_list(char * pcb_file)
 {
@@ -539,192 +351,85 @@ static void get_pcb_element_list(char * pcb_file)
 	fclose(f);
 }
 
-static PcbElement *pcb_element_exists(PcbElement * el_test, int record)
+static void prune_elements(char * pcb_file, char * bak)
 {
-	PcbElement *el;
+	FILE *f_in, *f_out;
+	PcbElement *el, *el_exists;
+	char *fmt, *tmp, *s, buf[1024];
+	int paren_level = 0;
+	int skipping = FALSE;
 	gdl_iterator_t it;
 
 	gdl_foreach(&pcb_element_list, &it, el) {
-		if (strcmp(el_test->refdes, el->refdes))
-			continue;
-		if (strcmp(el_test->description, el->description)) {	/* footprint */
-			if (record)
-				el->changed_description = pcb_strdup(el_test->description);
-		}
-		else {
-			if (record) {
-				if (strcmp(el_test->value, el->value))
-					el->changed_value = pcb_strdup(el_test->value);
-				el->still_exists = TRUE;
+		if (!el->still_exists) {
+			if ((conf_g2pr.utils.gsch2pcb_rnd.preserve) || (el->nonetlist)) {
+				++n_preserved;
+				if (conf_g2pr.utils.gsch2pcb_rnd.verbose > 1)
+					fprintf(stderr,
+									"Preserving PCB element not in the schematic:    %s (element   %s) %s\n",
+									el->refdes, el->description, el->nonetlist ? "nonetlist" : "");
 			}
-			return el;
-		}
-	}
-	return NULL;
-}
-
-/* A problem is that new PCB 1.7 file elements have the
- * (mark_x,mark_y) value set to wherever the element was created and
- * no equivalent of a gschem translate symbol was done.
- *
- * So, file elements inserted can be scattered over a big area and
- * this is bad when loading a file.new.pcb into an existing PC
- * board.  So, do a simple translate if (mark_x,mark_y) is
- * (arbitrarily) over 1000.  I'll assume that for values < 1000 the
- * element creator was concerned with a sane initial element
- * placement.  Unless someone has a better idea?  Don't bother with
- * pre PCB 1.7 formats as that would require parsing the mark().
- */
-static void simple_translate(PcbElement * el)
-{
-	if (el->x != NULL)
-		free(el->x);
-	if (el->y != NULL)
-		free(el->y);
-	el->x = pcb_strdup("0");
-	el->y = pcb_strdup("0");
-}
-
-static int insert_element(FILE * f_out, FILE * f_elem, char * footprint, char * refdes, char * value)
-{
-	PcbElement *el;
-	char *fmt, *s, buf[1024];
-	int retval = FALSE;
-
-	/* Copy the file element lines.  Substitute new parameters into the
-	 * Element() or Element[] line and strip comments.
-	 */
-	while ((fgets(buf, sizeof(buf), f_elem)) != NULL) {
-		for (s = buf; *s == ' ' || *s == '\t'; ++s);
-		if ((el = pcb_element_line_parse(s)) != NULL) {
-			simple_translate(el);
-			fmt = el->quoted_flags ? "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %s %s%s\n" : "Element%c%s \"%s\" \"%s\" \"%s\" %s %s%s\n";
-
-			fprintf(f_out, fmt, el->res_char, el->flags, footprint, refdes, value, el->x, el->y, el->tail);
-			retval = TRUE;
+			else
+				++n_deleted;
 		}
-		else if (*s != '#')
-			fputs(buf, f_out);
-		pcb_element_free(el);
-	}
-	return retval;
-}
-
-
-char *search_element(PcbElement * el)
-{
-	char *elname = NULL, *path = NULL;
-
-	if (!elname)
-		elname = pcb_strdup(el->description);
-
-	if (!strcmp(elname, "unknown")) {
-		free(elname);
-		return NULL;
-	}
-	if (conf_g2pr.utils.gsch2pcb_rnd.verbose > 1)
-		printf("\tSearching directories looking for file element: %s\n", elname);
-	free(elname);
-	return path;
-}
-
-/* The gnetlist backend gnet-gsch2pcb-rnd.scm generates PKG lines:
- *
- *        PKG(footprint,refdes,value)
- *
- */
-static PcbElement *pkg_to_element(FILE * f, char * pkg_line)
-{
-	PcbElement *el;
-	char *s, *end, *refdes, *fp, *value;
-
-/*fprintf(stderr, "--- %s\n", pkg_line);*/
-
-	if (strncmp(pkg_line, "PKG", 3)
-			|| (s = strchr(pkg_line, (int) '(')) == NULL)
-		return NULL;
-
-/* remove trailing ")" */
-	end = s + strlen(s) - 2;
-	if (*end == ')')
-		*end = '\0';
-
-/* tokenize the line keeping () */
-	fp = token(s + 1, NULL, NULL, 1);
-	refdes = token(NULL, NULL, NULL, 1);
-	value = token(NULL, NULL, NULL, 1);
-
-
-/*fprintf(stderr, "refdes: %s\n", refdes);
-fprintf(stderr, "    fp: %s\n", fp);
-fprintf(stderr, "   val: %s\n", value);*/
-
-
-	if (!refdes || !fp || !value) {
-		if (refdes != NULL)
-			free(refdes);
-		if (fp != NULL)
-			free(fp);
-		if (value != NULL)
-			free(value);
-		fprintf(stderr, "Bad package line: %s\n", pkg_line);
-		return NULL;
-	}
-
-	fix_spaces(refdes);
-	fix_spaces(value);
-
-	el = calloc(sizeof(PcbElement), 1);
-	el->description = fp;
-	el->refdes = refdes;
-	el->value = value;
-
-/*
-// wtf?
-//  if ((s = strchr (el->value, (int) ')')) != NULL)
-//    *s = '\0';
-*/
-
-	if (conf_g2pr.utils.gsch2pcb_rnd.empty_footprint_name && !strcmp(el->description, conf_g2pr.utils.gsch2pcb_rnd.empty_footprint_name)) {
-		if (conf_g2pr.utils.gsch2pcb_rnd.verbose)
-			printf("%s: has the empty footprint attribute \"%s\" so won't be in the layout.\n", el->refdes, el->description);
-		n_empty += 1;
-		el->omit_PKG = TRUE;
+		else if (el->changed_value)
+			++n_changed_value;
 	}
-	else if (!strcmp(el->description, "none")) {
-		fprintf(stderr, "WARNING: %s has a footprint attribute \"%s\" so won't be in the layout.\n", el->refdes, el->description);
-		n_none += 1;
-		el->omit_PKG = TRUE;
+	if ((pcb_element_list.length == 0) || (n_deleted == 0 && !need_PKG_purge && n_changed_value == 0)) {
+		return;
 	}
-	else if (!strcmp(el->description, "unknown")) {
-		fprintf(stderr, "WARNING: %s has no footprint attribute so won't be in the layout.\n", el->refdes);
-		n_unknown += 1;
-		el->omit_PKG = TRUE;
+	if ((f_in = fopen(pcb_file, "r")) == NULL) {
+		fprintf(stderr, "error: can not read %s\n", pcb_file);
+		return;
+	}
+	tmp = str_concat(NULL, pcb_file, ".tmp", NULL);
+	if ((f_out = fopen(tmp, "wb")) == NULL) {
+		fprintf(stderr, "error: can not write %s\n", tmp);
+		fclose(f_in);
+		return;
 	}
-	return el;
-}
-
-/* Copies the content of fn to fout and returns 0 on success. */
-static int CatPCB(FILE * fout, const char *fn)
-{
-	FILE *fin;
-	fin = fopen(fn, "r");
-	if (fin == NULL)
-		return -1;
-
-	for (;;) {
-		char buff[1024];
-		int len;
 
-		len = fread(buff, 1, sizeof(buff), fin);
-		if (len <= 0)
-			break;
+	while ((fgets(buf, sizeof(buf), f_in)) != NULL) {
+		for (s = buf; *s == ' ' || *s == '\t'; ++s);
+		if (skipping) {
+			if (*s == '(')
+				++paren_level;
+			else if (*s == ')' && --paren_level <= 0)
+				skipping = FALSE;
+			continue;
+		}
+		el_exists = NULL;
+		if ((el = pcb_element_line_parse(s)) != NULL
+				&& (el_exists = pcb_element_exists(el, FALSE)) != NULL && !el_exists->still_exists && !conf_g2pr.utils.gsch2pcb_rnd.preserve && !el->nonetlist) {
+			skipping = TRUE;
+			if (conf_g2pr.utils.gsch2pcb_rnd.verbose)
+				printf("%s: deleted element %s (value=%s)\n", el->refdes, el->description, el->value);
+			pcb_element_free(el);
+			continue;
+		}
+		if (el_exists && el_exists->changed_value) {
+			fmt = el->quoted_flags ? "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %s %s%s\n" : "Element%c%s \"%s\" \"%s\" \"%s\" %s %s%s\n";
+			fprintf(f_out, fmt,
+							el->res_char, el->flags, el->description, el->refdes, el_exists->changed_value, el->x, el->y, el->tail);
+			if (conf_g2pr.utils.gsch2pcb_rnd.verbose)
+				printf("%s: changed element %s value: %s -> %s\n", el->refdes, el->description, el->value, el_exists->changed_value);
+		}
+		else if (!strncmp(s, "PKG_", 4))
+			++n_PKG_removed_old;
+		else
+			fputs(buf, f_out);
+		pcb_element_free(el);
+	}
+	fclose(f_in);
+	fclose(f_out);
 
-		fwrite(buff, len, 1, fout);
+	if (!bak_done) {
+		build_and_run_command("mv %s %s", pcb_file, bak);
+		bak_done = TRUE;
 	}
 
-	fclose(fin);
-	return 0;
+	build_and_run_command("mv %s %s", tmp, pcb_file);
+	free(tmp);
 }
 
 /* Process the newly created pcb file which is the output from
@@ -813,7 +518,7 @@ static int add_elements(char * pcb_file)
 				}
 				else {
 					++n_not_found;
-					fputs(buf, f_out);		/* Copy PKG_ line */
+					fprintf(f_out, "# gsch2pcb-rnd: element not found: %s\n", buf); /* Copy PKG_ line as comment */
 				}
 			}
 			if (fp != NULL)
@@ -884,364 +589,103 @@ static void update_element_descriptions(char * pcb_file, char * bak)
 	free(tmp);
 }
 
-static void prune_elements(char * pcb_file, char * bak)
+/* A problem is that new PCB 1.7 file elements have the
+ * (mark_x,mark_y) value set to wherever the element was created and
+ * no equivalent of a gschem translate symbol was done.
+ *
+ * So, file elements inserted can be scattered over a big area and
+ * this is bad when loading a file.new.pcb into an existing PC
+ * board.  So, do a simple translate if (mark_x,mark_y) is
+ * (arbitrarily) over 1000.  I'll assume that for values < 1000 the
+ * element creator was concerned with a sane initial element
+ * placement.  Unless someone has a better idea?  Don't bother with
+ * pre PCB 1.7 formats as that would require parsing the mark().
+ */
+static void simple_translate(PcbElement * el)
 {
-	FILE *f_in, *f_out;
-	PcbElement *el, *el_exists;
-	char *fmt, *tmp, *s, buf[1024];
-	int paren_level = 0;
-	int skipping = FALSE;
-	gdl_iterator_t it;
+	if (el->x != NULL)
+		free(el->x);
+	if (el->y != NULL)
+		free(el->y);
+	el->x = pcb_strdup("0");
+	el->y = pcb_strdup("0");
+}
 
-	gdl_foreach(&pcb_element_list, &it, el) {
-		if (!el->still_exists) {
-			if ((conf_g2pr.utils.gsch2pcb_rnd.preserve) || (el->nonetlist)) {
-				++n_preserved;
-				if (conf_g2pr.utils.gsch2pcb_rnd.verbose > 1)
-					fprintf(stderr,
-									"Preserving PCB element not in the schematic:    %s (element   %s) %s\n",
-									el->refdes, el->description, el->nonetlist ? "nonetlist" : "");
-			}
-			else
-				++n_deleted;
-		}
-		else if (el->changed_value)
-			++n_changed_value;
-	}
-	if ((pcb_element_list.length == 0) || (n_deleted == 0 && !need_PKG_purge && n_changed_value == 0)) {
-		return;
-	}
-	if ((f_in = fopen(pcb_file, "r")) == NULL) {
-		fprintf(stderr, "error: can not read %s\n", pcb_file);
-		return;
-	}
-	tmp = str_concat(NULL, pcb_file, ".tmp", NULL);
-	if ((f_out = fopen(tmp, "wb")) == NULL) {
-		fprintf(stderr, "error: can not write %s\n", tmp);
-		fclose(f_in);
-		return;
-	}
+static int insert_element(FILE * f_out, FILE * f_elem, char * footprint, char * refdes, char * value)
+{
+	PcbElement *el;
+	char *fmt, *s, buf[1024];
+	int retval = FALSE;
 
-	while ((fgets(buf, sizeof(buf), f_in)) != NULL) {
+	/* Copy the file element lines.  Substitute new parameters into the
+	 * Element() or Element[] line and strip comments.
+	 */
+	while ((fgets(buf, sizeof(buf), f_elem)) != NULL) {
 		for (s = buf; *s == ' ' || *s == '\t'; ++s);
-		if (skipping) {
-			if (*s == '(')
-				++paren_level;
-			else if (*s == ')' && --paren_level <= 0)
-				skipping = FALSE;
-			continue;
-		}
-		el_exists = NULL;
-		if ((el = pcb_element_line_parse(s)) != NULL
-				&& (el_exists = pcb_element_exists(el, FALSE)) != NULL && !el_exists->still_exists && !conf_g2pr.utils.gsch2pcb_rnd.preserve && !el->nonetlist) {
-			skipping = TRUE;
-			if (conf_g2pr.utils.gsch2pcb_rnd.verbose)
-				printf("%s: deleted element %s (value=%s)\n", el->refdes, el->description, el->value);
-			pcb_element_free(el);
-			continue;
-		}
-		if (el_exists && el_exists->changed_value) {
+		if ((el = pcb_element_line_parse(s)) != NULL) {
+			simple_translate(el);
 			fmt = el->quoted_flags ? "Element%c\"%s\" \"%s\" \"%s\" \"%s\" %s %s%s\n" : "Element%c%s \"%s\" \"%s\" \"%s\" %s %s%s\n";
-			fprintf(f_out, fmt,
-							el->res_char, el->flags, el->description, el->refdes, el_exists->changed_value, el->x, el->y, el->tail);
-			if (conf_g2pr.utils.gsch2pcb_rnd.verbose)
-				printf("%s: changed element %s value: %s -> %s\n", el->refdes, el->description, el->value, el_exists->changed_value);
+
+			fprintf(f_out, fmt, el->res_char, el->flags, footprint, refdes, value, el->x, el->y, el->tail);
+			retval = TRUE;
 		}
-		else if (!strncmp(s, "PKG_", 4))
-			++n_PKG_removed_old;
-		else
+		else if (*s != '#')
 			fputs(buf, f_out);
 		pcb_element_free(el);
 	}
-	fclose(f_in);
-	fclose(f_out);
-
-	if (!bak_done) {
-		build_and_run_command("mv %s %s", pcb_file, bak);
-		bak_done = TRUE;
-	}
-
-	build_and_run_command("mv %s %s", tmp, pcb_file);
-	free(tmp);
-}
-
-static void add_schematic(char * sch)
-{
-	char **n;
-	n = gadl_new(&schematics);
-	*n = pcb_strdup(sch);
-	gadl_append(&schematics, n);
-	if (!conf_g2pr.utils.gsch2pcb_rnd.sch_basename) {
-		char *suff = loc_str_has_suffix(sch, ".sch", 4);
-		if (suff != NULL) {
-			char *tmp = loc_strndup(sch, suff - sch);
-			conf_set(CFR_CLI, "utils/gsch2pcb_rnd/sch_basename", -1, tmp, POL_OVERWRITE);
-			free(tmp);
-		}
-	}
-}
-
-static void add_multiple_schematics(const char * sch)
-{
-	/* parse the string using shell semantics */
-	int count;
-	char **args;
-	char *tmp = pcb_strdup(sch);
-
-	count = qparse(tmp, &args);
-	free(tmp);
-	if (count > 0) {
-		int i;
-		for (i = 0; i < count; ++i)
-			if (*args[i] != '\0')
-				add_schematic(args[i]);
-		qparse_free(count, &args);
-	}
-	else {
-		fprintf(stderr, "invalid `schematics' option: %s\n", sch);
-	}
-}
-
-static int parse_config(char * config, char * arg)
-{
-	char *s;
-
-	/* remove trailing white space otherwise strange things can happen */
-	if ((arg != NULL) && (strlen(arg) >= 1)) {
-		s = arg + strlen(arg) - 1;
-		while ((*s == ' ' || *s == '\t') && (s != arg))
-			s--;
-		s++;
-		*s = '\0';
-	}
-	if (conf_g2pr.utils.gsch2pcb_rnd.verbose)
-		printf("    %s \"%s\"\n", config, arg ? arg : "");
-
-	if (!strcmp(config, "remove-unfound") || !strcmp(config, "r")) {
-		/* This is default behavior set in header section */
-		conf_set(CFR_CLI, "utils/gsch2pcb_rnd/remove_unfound_elements", -1, "1", POL_OVERWRITE);
-		return 0;
-	}
-	if (!strcmp(config, "keep-unfound") || !strcmp(config, "k")) {
-		conf_set(CFR_CLI, "utils/gsch2pcb_rnd/remove_unfound_elements", -1, "0", POL_OVERWRITE);
-		return 0;
-	}
-	if (!strcmp(config, "quiet") || !strcmp(config, "q")) {
-		conf_set(CFR_CLI, "utils/gsch2pcb_rnd/quiet_mode", -1, "1", POL_OVERWRITE);
-		return 0;
-	}
-	if (!strcmp(config, "preserve") || !strcmp(config, "p")) {
-		conf_set(CFR_CLI, "utils/gsch2pcb_rnd/preserve", -1, "1", POL_OVERWRITE);
-		return 0;
-	}
-	if (!strcmp(config, "elements-shell") || !strcmp(config, "s"))
-		conf_set(CFR_CLI, "rc/library_shell", -1, arg, POL_OVERWRITE);
-	else if (!strcmp(config, "elements-dir") || !strcmp(config, "d")) {
-		static int warned = 0;
-		if (!warned) {
-			fprintf(stderr, "WARNING: using elements-dir from %s - this overrides the normal pcb-rnd configured library search paths\n", config);
-			warned = 1;
-		}
-		conf_set(CFR_CLI, "rc/library_search_paths", -1, arg, POL_PREPEND);
-	}
-	else if (!strcmp(config, "output-name") || !strcmp(config, "o"))
-		conf_set(CFR_CLI, "utils/gsch2pcb_rnd/sch_base", -1, arg, POL_OVERWRITE);
-	else if (!strcmp(config, "default-pcb") || !strcmp(config, "P"))
-		conf_set(CFR_CLI, "utils/gsch2pcb_rnd/default_pcb", -1, arg, POL_OVERWRITE);
-	else if (!strcmp(config, "schematics"))
-		add_multiple_schematics(arg);
-	else if (!strcmp(config, "gnetlist")) {
-		char **n;
-		n = gadl_new(&extra_gnetlist_list);
-		*n = pcb_strdup(arg);
-		gadl_append(&extra_gnetlist_list, n);
-	}
-	else if (!strcmp(config, "empty-footprint"))
-		conf_set(CFR_CLI, "utils/gsch2pcb_rnd/empty_footprint_name", -1, arg, POL_OVERWRITE);
-	else
-		return -1;
-
-	return 1;
+	return retval;
 }
 
-static void load_project(char * path)
-{
-	FILE *f;
-	char *s, buf[1024], config[32], arg[768];
+/********************** The actual actions we perform ************************/
 
-	f = fopen(path, "r");
-	if (!f)
-		return;
-	if (conf_g2pr.utils.gsch2pcb_rnd.verbose)
-		printf("Reading project file: %s\n", path);
-	while (fgets(buf, sizeof(buf), f)) {
-		for (s = buf; *s == ' ' || *s == '\t' || *s == '\n'; ++s);
-		if (!*s || *s == '#' || *s == '/' || *s == ';')
-			continue;
-		arg[0] = '\0';
-		sscanf(s, "%31s %767[^\n]", config, arg);
-		parse_config(config, arg);
-	}
-	fclose(f);
-}
+static char *pcb_file_name, *pcb_new_file_name, *bak_file_name, *pins_file_name, *net_file_name;
 
-static void load_extra_project_files(void)
+static void method_pcb_init(void)
 {
-	char *path;
-	static int done = FALSE;
-
-	if (done)
-		return;
-
-	load_project("/etc/gsch2pcb");
-	load_project("/usr/local/etc/gsch2pcb");
-
-	path = str_concat(PCB_DIR_SEPARATOR_S, conf_core.rc.path.home, ".gEDA", "gsch2pcb", NULL);
-	load_project(path);
-	free(path);
-
-	done = TRUE;
+	fp_init();
+	pins_file_name = str_concat(NULL, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, ".cmd", NULL);
+	net_file_name = str_concat(NULL, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, ".net", NULL);
+	pcb_file_name = str_concat(NULL, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, ".pcb", NULL);
+	local_project_pcb_name = pcb_file_name;
 }
 
-static void get_args(int argc, char ** argv)
+static void next_steps(int initial_pcb, int quiet_mode)
 {
-	char *opt, *arg;
-	int i, r;
-
-	for (i = 1; i < argc; ++i) {
-		opt = argv[i];
-		arg = argv[i + 1];
-		if (*opt == '-') {
-			++opt;
-			if (*opt == '-')
-				++opt;
-			if (!strcmp(opt, "version") || !strcmp(opt, "V")) {
-				printf("gsch2pcb-rnd %s\n", GSCH2PCB_RND_VERSION);
-				exit(0);
-			}
-			else if (!strcmp(opt, "verbose") || !strcmp(opt, "v")) {
-				char tmp[16];
-				sprintf(tmp, "%ld", conf_g2pr.utils.gsch2pcb_rnd.verbose + 1);
-				conf_set(CFR_CLI, "utils/gsch2pcb_rnd/verbose", -1, tmp, POL_OVERWRITE);
-				continue;
-			}
-			else if (!strcmp(opt, "c") || !strcmp(opt, "conf")) {
-				const char *stmp;
-				if (conf_set_from_cli(NULL, arg, NULL, &stmp) != 0) {
-					fprintf(stderr, "Error: failed to set config %s: %s\n", arg, stmp);
-					exit(1);
-				}
-				i++;
-				continue;
-			}
-			else if (!strcmp(opt, "fix-elements")) {
-				conf_set(CFR_CLI, "utils/gsch2pcb_rnd/fix_elements", -1, "1", POL_OVERWRITE);
-				continue;
-			}
-			else if (!strcmp(opt, "gnetlist-arg")) {
-				char **n;
-				n = gadl_new(&extra_gnetlist_arg_list);
-				*n = pcb_strdup(arg);
-				gadl_append(&extra_gnetlist_arg_list, n);
-				i++;
-				continue;
-			}
-			else if (!strcmp(opt, "help") || !strcmp(opt, "h"))
-				usage();
-			else if (i < argc && ((r = parse_config(opt, (i < argc - 1) ? arg : NULL))
-														>= 0)
-				) {
-				i += r;
-				continue;
-			}
-			printf("gsch2pcb: bad or incomplete arg: %s\n", argv[i]);
-			usage();
-		}
-		else {
-			if (loc_str_has_suffix(argv[i], ".sch", 4) == NULL) {
-				load_extra_project_files();
-				load_project(argv[i]);
-			}
-			else
-				add_schematic(argv[i]);
-		}
+	if (initial_pcb) {
+		printf("\nNext step:\n");
+		printf("1.  Run pcb on your file %s.\n", pcb_file_name);
+		printf("    You will find all your footprints in a bundle ready for you to place\n");
+		printf("    or disperse with \"Select -> Disperse all elements\" in PCB.\n\n");
+		printf("2.  From within PCB, select \"File -> Load netlist file\" and select \n");
+		printf("    %s to load the netlist.\n\n", net_file_name);
+		printf("3.  From within PCB, enter\n\n");
+		printf("           :ExecuteFile(%s)\n\n", pins_file_name);
+		printf("    to propagate the pin names of all footprints to the layout.\n\n");
 	}
-}
-
-void plugin_register(const char *name, const char *path, void *handle, int dynamic_loaded, void (*uninit)(void))
-{
-	if (conf_g2pr.utils.gsch2pcb_rnd.verbose)
-		printf("Plugin loaded: %s (%s)\n", name, path);
-}
-
-void free_strlist(gadl_list_t *lst)
-{
-	char **s;
-
-	while((s = gadl_first(lst)) != NULL) {
-		char *str = *s;
-		gadl_free(s);
-		free(str);
+	else if (!quiet_mode) {
+		printf("\nNext steps:\n");
+		printf("1.  Run pcb on your file %s.\n", pcb_file_name);
+		printf("2.  From within PCB, select \"File -> Load layout data to paste buffer\"\n");
+		printf("    and select %s to load the new footprints into your existing layout.\n", pcb_new_file_name);
+		printf("3.  From within PCB, select \"File -> Load netlist file\" and select \n");
+		printf("    %s to load the updated netlist.\n\n", net_file_name);
+		printf("4.  From within PCB, enter\n\n");
+		printf("           :ExecuteFile(%s)\n\n", pins_file_name);
+		printf("    to update the pin names of all footprints.\n\n");
 	}
 }
 
-#include "fp_init.h"
-
-/************************ main ***********************/
-char *pcb_file_name, *pcb_new_file_name, *bak_file_name, *pins_file_name, *net_file_name;
-int main(int argc, char ** argv)
+static void method_pcb_go()
 {
-	int i;
 	int initial_pcb = TRUE;
 	int created_pcb_file = TRUE;
 
-	if (argc < 2)
-		usage();
-
-
-	conf_init();
-	conf_core_init();
-
-
-	gadl_list_init(&schematics, sizeof(char *), NULL, NULL);
-	gadl_list_init(&extra_gnetlist_arg_list, sizeof(char *), NULL, NULL);
-	gadl_list_init(&extra_gnetlist_list, sizeof(char *), NULL, NULL);
-
-#define conf_reg(field,isarray,type_name,cpath,cname,desc,flags) \
-	conf_reg_field(conf_g2pr, field,isarray,type_name,cpath,cname,desc,flags);
-#include "gsch2pcb_rnd_conf_fields.h"
-
-	get_args(argc, argv);
-
-	conf_load_all(NULL, NULL);
-	conf_update(NULL);
-
-	{
-		pcb_uninit_t uninit_func;
-#		include "fp_init.c"
-	}
-
-	load_extra_project_files();
-
-	conf_update(NULL); /* because of CLI changes */
-
-	fp_init();
-
 	element_search_path = fp_default_search_path();
 
-	if (gadl_length(&schematics) == 0)
-		usage();
-
-	pins_file_name = str_concat(NULL, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, ".cmd", NULL);
-	net_file_name = str_concat(NULL, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, ".net", NULL);
-	pcb_file_name = str_concat(NULL, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, ".pcb", NULL);
-
-	conf_load_project(NULL, pcb_file_name);
-	conf_update(NULL); /* because of the project file */
-
 	{ /* set bak_file_name, finding the first number that results in a non-existing bak */
 		int len;
 		char *end;
+		int i;
 
 		len = strlen(conf_g2pr.utils.gsch2pcb_rnd.sch_basename);
 		bak_file_name = malloc(len+8+64); /* make room for ".pcb.bak" and an integer */
@@ -1250,11 +694,11 @@ int main(int argc, char ** argv)
 		strcpy(end, ".pcb.bak");
 		end += 8;
 
-		for (i = 0; file_exists(bak_file_name); ++i)
+		for (i = 0; pcb_file_readable(bak_file_name); ++i)
 			sprintf(end, "%d", i);
 	}
 
-	if (file_exists(pcb_file_name)) {
+	if (pcb_file_readable(pcb_file_name)) {
 		initial_pcb = FALSE;
 		pcb_new_file_name = str_concat(NULL, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, ".new.pcb", NULL);
 		get_pcb_element_list(pcb_file_name);
@@ -1337,15 +781,33 @@ int main(int argc, char ** argv)
 	free(pins_file_name);
 	free(pcb_file_name);
 	free(bak_file_name);
+}
 
-	conf_uninit();
+static int method_pcb_guess_out_name(void)
+{
+	int res;
+	char *name = str_concat(NULL, conf_g2pr.utils.gsch2pcb_rnd.sch_basename, ".pcb", NULL);
+	res = pcb_file_readable(name);
+	free(name);
+	return res;
+}
 
-	free_strlist(&schematics);
-	free_strlist(&extra_gnetlist_arg_list);
-	free_strlist(&extra_gnetlist_list);
 
+void method_pcb_uninit(void)
+{
 	if (pcb_new_file_name != NULL)
 		free(pcb_new_file_name);
+}
 
-	return 0;
+static method_t method_pcb;
+
+void method_pcb_register(void)
+{
+	method_pcb.name = "pcb";
+	method_pcb.desc = "traditional: load footprints and edit the .pcb files";
+	method_pcb.init = method_pcb_init;
+	method_pcb.go = method_pcb_go;
+	method_pcb.uninit = method_pcb_uninit;
+	method_pcb.guess_out_name = method_pcb_guess_out_name;
+	method_register(&method_pcb);
 }
diff --git a/util/gsch2pcb-rnd/method_pcb.h b/util/gsch2pcb-rnd/method_pcb.h
new file mode 100644
index 0000000..9fffa02
--- /dev/null
+++ b/util/gsch2pcb-rnd/method_pcb.h
@@ -0,0 +1 @@
+void method_pcb_register(void);
diff --git a/util/gsch2pcb-rnd/netlister.c b/util/gsch2pcb-rnd/netlister.c
new file mode 100644
index 0000000..e716c78
--- /dev/null
+++ b/util/gsch2pcb-rnd/netlister.c
@@ -0,0 +1,111 @@
+/* gsch2pcb-rnd
+ *
+ *  Original version: Bill Wilson    billw at wt.net
+ *  rnd-version: (C) 2015..2016, Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software which I release under the GNU General Public
+ *  License. You may redistribute and/or modify this program under the terms
+ *  of that 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.  Version 2 is in the
+ *  COPYRIGHT file in the top level directory of this distribution.
+ *
+ *  To get a copy of the GNU General Puplic License, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "config.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+/*#include <ctype.h>*/
+#include <time.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include "../src/plug_footprint.h"
+#include "../src/paths.h"
+#include "../src/conf.h"
+#include "../src/conf_core.h"
+/*#include "../src_3rd/qparse/qparse.h"*/
+#include "../config.h"
+/*#include "../src/error.h"*/
+/*#include "../src/plugins.h"
+#include "../src/plug_footprint.h"*/
+#include "../src/compat_misc.h"
+#include "gsch2pcb_rnd_conf.h"
+#include "gsch2pcb.h"
+#include "run.h"
+
+const char *gnetlist_name(void)
+{
+	const char *gnetlist;
+	/* Allow the user to specify a full path or a different name for
+	 * the gnetlist command.  Especially useful if multiple copies
+	 * are installed at once.
+	 */
+	gnetlist = getenv("GNETLIST");
+	if (gnetlist == NULL)
+		gnetlist = "gnetlist";
+	return gnetlist;
+}
+
+int run_gnetlist(const char *pins_file, const char *net_file, const char *pcb_file, const char * basename, const gadl_list_t *largs)
+{
+	struct stat st;
+	time_t mtime;
+	const char *gnetlist;
+	gadl_iterator_t it;
+	char **sp;
+	char *verbose_str = NULL;
+
+	gnetlist = gnetlist_name();
+
+	if (!conf_g2pr.utils.gsch2pcb_rnd.verbose)
+		verbose_str = "-q";
+
+	if (!build_and_run_command("%s %s -g pcbpins -o %s %L %L", gnetlist, verbose_str, pins_file, &extra_gnetlist_arg_list, largs))
+		return FALSE;
+
+	if (!build_and_run_command("%s %s -g PCB -o %s %L %L", gnetlist, verbose_str, net_file, &extra_gnetlist_arg_list, largs))
+		return FALSE;
+
+	mtime = (stat(pcb_file, &st) == 0) ? st.st_mtime : 0;
+
+	if (!build_and_run_command("%s %s -L " SCMDIR " -g gsch2pcb-rnd -o %s %L %L",
+														 gnetlist, verbose_str, pcb_file, &extra_gnetlist_arg_list, largs)) {
+		if (stat(pcb_file, &st) != 0 || mtime == st.st_mtime) {
+			fprintf(stderr, "gsch2pcb: gnetlist command failed, `%s' not updated\n", pcb_file);
+			return FALSE;
+		}
+		return FALSE;
+	}
+
+	gadl_foreach(&extra_gnetlist_list, &it, sp) {
+		const char *s = *sp;
+		const char *s2 = strstr(s, " -o ");
+		char *out_file;
+		char *backend;
+		if (!s2) {
+			out_file = str_concat(NULL, basename, ".", s, NULL);
+			backend = pcb_strdup(s);
+		}
+		else {
+			out_file = pcb_strdup(s2 + 4);
+			backend = pcb_strndup(s, s2 - s);
+		}
+
+		if (!build_and_run_command("%s %s -g %s -o %s %L %L",
+															 gnetlist, verbose_str, backend, out_file, &extra_gnetlist_arg_list, largs))
+			return FALSE;
+		free(out_file);
+		free(backend);
+	}
+
+	return TRUE;
+}
diff --git a/util/gsch2pcb-rnd/netlister.h b/util/gsch2pcb-rnd/netlister.h
new file mode 100644
index 0000000..05b54df
--- /dev/null
+++ b/util/gsch2pcb-rnd/netlister.h
@@ -0,0 +1,11 @@
+/* Run gnetlist to generate a netlist and a PCB board file.  gnetlist
+ * has exit status of 0 even if it's given an invalid arg, so do some
+ * stat() hoops to decide if gnetlist successfully generated the PCB
+ * board file (only gnetlist >= 20030901 recognizes -m).
+ */
+int run_gnetlist(const char *pins_file, const char *net_file, const char *pcb_file, const char * basename, const gadl_list_t *largs);
+
+
+/* Return the name of gnetlist that should be executed */
+const char *gnetlist_name(void);
+
diff --git a/util/gsch2pcb-rnd/run.c b/util/gsch2pcb-rnd/run.c
new file mode 100644
index 0000000..526bec9
--- /dev/null
+++ b/util/gsch2pcb-rnd/run.c
@@ -0,0 +1,137 @@
+/* gsch2pcb-rnd
+ *
+ *  Original version: Bill Wilson    billw at wt.net
+ *  rnd-version: (C) 2015..2016, Tibor 'Igor2' Palinkas
+ *
+ *  This program is free software which I release under the GNU General Public
+ *  License. You may redistribute and/or modify this program under the terms
+ *  of that 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.  Version 2 is in the
+ *  COPYRIGHT file in the top level directory of this distribution.
+ *
+ *  To get a copy of the GNU General Puplic License, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/* for popen() */
+#define _DEFAULT_SOURCE
+#define _BSD_SOURCE
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include "gsch2pcb.h"
+#include "../src_3rd/genvector/vts0.h"
+#include "../src/compat_misc.h"
+#include "gsch2pcb_rnd_conf.h"
+
+
+int build_and_run_command(const char * format_, ...)
+{
+	va_list vargs;
+	int result = FALSE;
+	vts0_t args;
+	char *format, *s, *start;
+
+	/* Translate the format string; args elements point to const char *'s
+	   within a copy of the format string. The format string is copied so
+	   that these parts can be terminated by overwriting whitepsace with \0 */
+	va_start(vargs, format_);
+	format = pcb_strdup(format_);
+	vts0_init(&args);
+	for(s = start = format; *s != '\0'; s++) {
+		/* if word separator is reached, save the previous word */
+		if (isspace(s[0])) {
+			if (start == s) { /* empty word - skip */
+				start++;
+				continue;
+			}
+			*s = '\0';
+			vts0_append(&args, start);
+			start = s+1;
+			continue;
+		}
+
+		/* check if current word is a format */
+		if ((s == start) && (s[0] == '%') && (s[1] != '\0') && ((s[2] == '\0') || isspace(s[2]))) {
+			switch(s[1]) {
+				case 'L': /* append contents of char * gadl_list_t */
+					{
+						gadl_list_t *list = va_arg(vargs, gadl_list_t *);
+						gadl_iterator_t it;
+						char **s;
+						gadl_foreach(list, &it, s) {
+							vts0_append(&args, *s);
+						}
+					}
+					start = s+2;
+					s++;
+					continue;
+				case 's':
+					{
+						char *arg = va_arg(vargs, char *);
+						if (arg != NULL)
+							vts0_append(&args, arg);
+						start = s+2;
+						s++;
+					}
+					continue;
+			}
+		}
+	}
+	va_end(vargs);
+
+	if (args.used > 0) {
+		int i, l;
+		char *cmd, *end, line[1024];
+		FILE *f;
+
+		l = 0;
+		for (i = 0; i < args.used; i++)
+			l += strlen(args.array[i]) + 3;
+
+		end = cmd = malloc(l+1);
+		for (i = 0; i < args.used; i++) {
+			l = strlen(args.array[i]);
+			*end = '"'; end++;
+			memcpy(end, args.array[i], l);
+			end += l;
+			*end = '"'; end++;
+			*end = ' '; end++;
+		}
+		end--;
+		*end = '\0';
+
+		/* we have something in the list, build & call command */
+		if (conf_g2pr.utils.gsch2pcb_rnd.verbose) {
+			printf("Running command:\n\t%s\n", cmd);
+			printf("%s", SEP_STRING);
+		}
+
+		f = popen(cmd, "r");
+		while(fgets(line, sizeof(line), f) != NULL) {
+			if (conf_g2pr.utils.gsch2pcb_rnd.verbose)
+				fputs(line, stdout);
+		}
+
+		if (pclose(f) == 0)
+			result = TRUE;
+		else
+			fprintf(stderr, "Failed to execute external program\n");
+		free(cmd);
+
+		if (conf_g2pr.utils.gsch2pcb_rnd.verbose)
+			printf("\n%s", SEP_STRING);
+	}
+
+	free(format);
+	vts0_uninit(&args);
+	return result;
+}
diff --git a/util/gsch2pcb-rnd/run.h b/util/gsch2pcb-rnd/run.h
new file mode 100644
index 0000000..5c1b5a8
--- /dev/null
+++ b/util/gsch2pcb-rnd/run.h
@@ -0,0 +1,13 @@
+/**
+ * Build and run a command. No redirection or error handling is
+ * done.  Format string is split on whitespace. Specifiers %l and %s
+ * are replaced with contents of positional args. To be recognized,
+ * specifiers must be separated from other arguments in the format by
+ * whitespace.
+ *  - %L expects a gadl_list_t vith char * payload, contents used as separate arguments
+ *  - %s expects a char*, contents used as a single argument (omitted if NULL)
+ * @param[in] format  used to specify command to be executed
+ * @param[in] ...     positional parameters
+ */
+int build_and_run_command(const char * format_, ...);
+

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-electronics/pcb-rnd.git



More information about the Pkg-electronics-commits mailing list