[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 ()),
+ >s_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 (°ree);
+ data[0] = g;
+ data[1] = °ree;
+ gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) compute_degree, data);
+ gts_range_update (°ree);
+ gts_range_print (°ree, fp);
+ fprintf (fp, "\n");
+ fprintf (fp, "# edges cut: %d edges cut weight: %g\n",
+ gts_graph_edges_cut (g),
+ gts_graph_edges_cut_weight (g));
+}
+
+struct _GtsGraphTraverse {
+ GtsFifo * q;
+ GtsGraph * g;
+};
+
+static void reset_level (GtsGNode * n)
+{
+ n->level = 0;
+}
+
+/**
+ * gts_graph_traverse_new:
+ * @g: a #GtsGraph.
+ * @n: a #GtsGNode belonging to @g.
+ * @type: the type of traversal.
+ * @reinit: if %TRUE, the traversal is reinitialized.
+ *
+ * Returns: a new #GtsGraphTraverse initialized for the traversal of
+ * @g of type @type, starting from @n.
+ */
+GtsGraphTraverse * gts_graph_traverse_new (GtsGraph * g,
+ GtsGNode * n,
+ GtsTraverseType type,
+ gboolean reinit)
+{
+ GtsGraphTraverse * t;
+
+ g_return_val_if_fail (g != NULL, NULL);
+ g_return_val_if_fail (n != NULL, NULL);
+ g_return_val_if_fail (gts_containee_is_contained (GTS_CONTAINEE (n),
+ GTS_CONTAINER (g)),
+ NULL);
+
+ if (reinit)
+ gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) reset_level, NULL);
+
+ t = g_malloc (sizeof (GtsGraphTraverse));
+ t->q = gts_fifo_new ();
+ t->g = g;
+ n->level = 1;
+ gts_fifo_push (t->q, n);
+
+ return t;
+}
+
+static void push_neighbor (GtsGNode * n, gpointer * data)
+{
+ GtsFifo * q = data[0];
+ GtsGNode * u = data[1];
+
+ if (n->level == 0) {
+ n->level = u->level + 1;
+ gts_fifo_push (q, n);
+ }
+}
+
+/**
+ * gts_graph_traverse_next:
+ * @t: a #GtsGraphTraverse.
+ *
+ * Returns: the next #GtsGNode of the traversal defined by @t or %NULL
+ * if the traversal is complete.
+ */
+GtsGNode * gts_graph_traverse_next (GtsGraphTraverse * t)
+{
+ GtsGNode * u;
+
+ g_return_val_if_fail (t != NULL, NULL);
+
+ u = gts_fifo_pop (t->q);
+ if (u) {
+ gpointer data[2];
+
+ data[0] = t->q;
+ data[1] = u;
+ gts_gnode_foreach_neighbor (u, t->g, (GtsFunc) push_neighbor, data);
+ }
+
+ return u;
+}
+
+/**
+ * gts_graph_traverse_what_next:
+ * @t: a #GtsGraphTraverse.
+ *
+ * Returns: the next #GtsGNode of the traversal defined by @t or %NULL
+ * if the traversal is complete but without advancing the traversal.
+ */
+GtsGNode * gts_graph_traverse_what_next (GtsGraphTraverse * t)
+{
+ g_return_val_if_fail (t != NULL, NULL);
+
+ return gts_fifo_top (t->q);
+}
+
+/**
+ * gts_graph_traverse_destroy:
+ * @t: a #GtsGraphTraverse.
+ *
+ * Frees all the memory allocated for @t.
+ */
+void gts_graph_traverse_destroy (GtsGraphTraverse * t)
+{
+ g_return_if_fail (t != NULL);
+
+ gts_fifo_destroy (t->q);
+ g_free (t);
+}
+
+static void edge_foreach_node (GtsGNode * n, gpointer * info)
+{
+ GtsFunc func = (GtsFunc) info[0];
+ gpointer data = info[1];
+ GHashTable * hash = info[2];
+ GSList * i = GTS_SLIST_CONTAINER (n)->items;
+
+ while (i) {
+ GtsGEdge * e = i->data;
+ if (!g_hash_table_lookup (hash, e)) {
+ (* func) (e, data);
+ g_hash_table_insert (hash, e, e);
+ }
+ i = i->next;
+ }
+}
+
+/**
+ * gts_graph_foreach_edge:
+ * @g: a #GtsGraph.
+ * @func: a #GtsFunc.
+ * @data: user data to be passed to @func.
+ *
+ * Calls @func for each #GtsEdge of @g.
+ */
+void gts_graph_foreach_edge (GtsGraph * g, GtsFunc func, gpointer data)
+{
+ gpointer info[3];
+ GHashTable * hash;
+
+ g_return_if_fail (g != NULL);
+ g_return_if_fail (func != NULL);
+
+ info[0] = func;
+ info[1] = data;
+ info[2] = hash = g_hash_table_new (NULL, NULL);
+ gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) edge_foreach_node, info);
+ g_hash_table_destroy (hash);
+}
+
+/**
+ * gts_graph_weight:
+ * @g: a #GtsGraph.
+ *
+ * Returns: the weight of graph @g as defined by the weight() method
+ * of #GtsGraphClass.
+ */
+gfloat gts_graph_weight (GtsGraph * g)
+{
+ g_return_val_if_fail (g != NULL, 0.);
+
+ if (GTS_GRAPH_CLASS (GTS_OBJECT (g)->klass)->weight)
+ return (* GTS_GRAPH_CLASS (GTS_OBJECT (g)->klass)->weight) (g);
+ return (gfloat) gts_container_size (GTS_CONTAINER (g));
+}
+
+/**
+ * gts_graph_distance_sum:
+ * @g: a #GtsGraph.
+ * @center: a #GtsGNode of @g.
+ *
+ * Returns: the sum of the distances between all the other #GtsGNode
+ * of @g and @center.
+ */
+guint gts_graph_distance_sum (GtsGraph * g, GtsGNode * center)
+{
+ GtsGraphTraverse * t;
+ GtsGNode * n;
+ guint sum = 0;
+
+ g_return_val_if_fail (g != NULL, 0);
+ g_return_val_if_fail (center != NULL, 0);
+
+ t = gts_graph_traverse_new (g, center, GTS_BREADTH_FIRST, TRUE);
+ while ((n = gts_graph_traverse_next (t)))
+ sum += n->level - 1;
+ gts_graph_traverse_destroy (t);
+
+ return sum;
+}
+
+/**
+ * gts_graph_farthest:
+ * @g: a #GtsGraph.
+ * @gnodes: a list of #GtsGNode belonging to @g.
+ *
+ * Returns: the #GtsGNode belonging to @g and farthest from all the nodes in
+ * @gnodes (hmmm, definition of "farthest"?).
+ */
+GtsGNode * gts_graph_farthest (GtsGraph * g, GSList * gnodes)
+{
+ GtsGNode * farthest = NULL;
+ GSList * i;
+ gboolean reinit = TRUE, changed = TRUE;
+ guint level = 1;
+
+ g_return_val_if_fail (g != NULL, NULL);
+
+ /* initialize traversals */
+ i = gnodes;
+ while (i) {
+ GTS_OBJECT (i->data)->reserved =
+ gts_graph_traverse_new (g, i->data, GTS_BREADTH_FIRST, reinit);
+ reinit = FALSE;
+ i = i->next;
+ }
+
+ while (changed) {
+ changed = FALSE;
+ i = gnodes;
+ while (i) {
+ GtsGraphTraverse * t = GTS_OBJECT (i->data)->reserved;
+ GtsGNode * n;
+ while ((n = gts_graph_traverse_what_next (t)) && n->level == level) {
+ changed = TRUE;
+ farthest = n;
+ gts_graph_traverse_next (t);
+ }
+ i = i->next;
+ }
+ level++;
+ }
+
+ /* destroy traversals */
+ i = gnodes;
+ while (i) {
+ gts_graph_traverse_destroy (GTS_OBJECT (i->data)->reserved);
+ GTS_OBJECT (i->data)->reserved = NULL;
+ i = i->next;
+ }
+ return farthest;
+}
+
+static void neighbor_count (GtsGNode * n, gpointer * data)
+{
+ guint * cuts = data[0];
+ GtsGraph * g = data[1];
+
+ if (!gts_containee_is_contained (GTS_CONTAINEE (n), GTS_CONTAINER (g)))
+ (*cuts)++;
+}
+
+static void count_edge_cuts (GtsGNode * n, gpointer * data)
+{
+ gts_gnode_foreach_neighbor (n, NULL, (GtsFunc) neighbor_count, data);
+}
+
+/**
+ * gts_graph_edges_cut:
+ * @g: a #GtsGraph.
+ *
+ * Returns: the number of edges of @g connecting nodes belonging to @g
+ * to nodes not belonging to @g.
+ */
+guint gts_graph_edges_cut (GtsGraph * g)
+{
+ guint cuts = 0;
+ gpointer data[2];
+
+ g_return_val_if_fail (g != NULL, 0);
+
+ data[0] = &cuts;
+ data[1] = g;
+ gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) count_edge_cuts, data);
+
+ return cuts;
+}
+
+static void sum_edge_cuts_weight (GtsGNode * n, gpointer * data)
+{
+ gfloat * weight = data[0];
+ GtsGraph * g = data[1];
+ GSList * i = GTS_SLIST_CONTAINER (n)->items;
+
+ while (i) {
+ GtsGNode * n1 = GTS_GNODE_NEIGHBOR (n, i->data);
+ if (!gts_containee_is_contained (GTS_CONTAINEE (n1), GTS_CONTAINER (g)))
+ *weight += gts_gedge_weight (i->data);
+ i = i->next;
+ }
+}
+
+/**
+ * gts_graph_edges_cut_weight:
+ * @g: a #GtsGraph.
+ *
+ * Returns: the sum of the weights of the edges of @g connecting nodes
+ * belonging to @g to nodes not belonging to @g.
+ */
+gfloat gts_graph_edges_cut_weight (GtsGraph * g)
+{
+ gfloat weight = 0.;
+ gpointer data[2];
+
+ g_return_val_if_fail (g != NULL, 0);
+
+ data[0] = &weight;
+ data[1] = g;
+ gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) sum_edge_cuts_weight,
+ data);
+
+ return weight;
+}
+
+/**
+ * gts_graph_read_jostle:
+ * @g: a #GtsGraph.
+ * @fp: a #GtsFile.
+ *
+ * Adds to @g the nodes and edges defined in the file pointed to by
+ * @fp. This file must use the Jostle "graph" ASCII format.
+ * The nodes created are of type #GtsNGNode and their identities are the
+ * line number at which they appear in @fp.
+ *
+ * Returns: 0 if the lecture was successful, the line number at which
+ * an error occured otherwise (in which case the @error field of @fp
+ * is set).
+ */
+guint gts_graph_read_jostle (GtsGraph * g, GtsFile * fp)
+{
+ guint nn, ne, n;
+ GtsGNode ** nodes;
+
+ g_return_val_if_fail (g != NULL, 1);
+ g_return_val_if_fail (fp != NULL, 1);
+
+ if (fp->type != GTS_INT) {
+ gts_file_error (fp, "expecting an integer (number of nodes)");
+ return fp->line;
+ }
+ nn = atoi (fp->token->str);
+ gts_file_next_token (fp);
+
+ if (fp->type != GTS_INT) {
+ gts_file_error (fp, "expecting an integer (number of edges)");
+ return fp->line;
+ }
+ ne = atoi (fp->token->str);
+
+ gts_file_first_token_after (fp, '\n');
+ nodes = g_malloc (sizeof (GtsGNode *)*(nn + 1));
+
+ n = 0;
+ while (n < nn && fp->type != GTS_ERROR) {
+ GtsNGNode * node = gts_ngnode_new (gts_ngnode_class (), fp->line);
+
+ nodes[n++] = GTS_GNODE (node);
+ gts_container_add (GTS_CONTAINER (g), GTS_CONTAINEE (node));
+ do {
+ if (fp->type != GTS_INT)
+ gts_file_error (fp, "expecting an integer (node index)");
+ else {
+ guint in = atoi (fp->token->str);
+
+ if (in == 0 || in > nn)
+ gts_file_error (fp, "node index `%d' is out of range `[1,%d]'",
+ in, nn);
+ else if (in == n)
+ gts_file_error (fp, "node index `%d' references itself", in);
+ else if (in < n) {
+ gts_gedge_new (g->edge_class, GTS_GNODE (node), nodes[in - 1]);
+ ne--;
+ gts_file_next_token (fp);
+ }
+ }
+ } while (fp->type != GTS_ERROR && fp->type != '\n');
+ }
+ g_free (nodes);
+
+ if (fp->type != GTS_ERROR) {
+ if (n != nn)
+ gts_file_error (fp, "only `%d' nodes read out of `%d'",
+ n, nn);
+ else if (ne > 0)
+ gts_file_error (fp, "`%d' unallocated edges remaining",
+ ne);
+ }
+
+ if (fp->type == GTS_ERROR)
+ return fp->line;
+ return 0;
+}
+
+static void count_edges (GtsGEdge * e, guint * nedge)
+{
+ (*nedge)++;
+}
+
+static void write_node (GtsObject * node, gpointer * data)
+{
+ FILE * fp = data[0];
+ guint * nnode = data[1];
+
+ node->reserved = GUINT_TO_POINTER ((*nnode)++);
+ if (node->klass->write)
+ (* node->klass->write) (node, fp);
+ fputc ('\n', fp);
+}
+
+static void write_edge (GtsGEdge * edge, FILE * fp)
+{
+ fprintf (fp, "%u %u",
+ GPOINTER_TO_UINT (GTS_OBJECT (edge->n1)->reserved),
+ GPOINTER_TO_UINT (GTS_OBJECT (edge->n2)->reserved));
+ if (GTS_OBJECT (edge)->klass->write)
+ (* GTS_OBJECT (edge)->klass->write) (GTS_OBJECT (edge), fp);
+ fputc ('\n', fp);
+}
+
+/**
+ * gts_graph_write:
+ * @g: a #GtsGraph.
+ * @fp: a file pointer.
+ *
+ * Writes in the file @fp an ASCII representation of @g. The file
+ * format is as follows.
+ *
+ * All the lines beginning with #GTS_COMMENTS are ignored. The first line
+ * contains two unsigned integers separated by spaces. The first
+ * integer is the number of nodes, nn, the second is the number of
+ * edges, ne.
+ *
+ * Follows nn lines containing node description.
+ * Follows ne lines containing the two indices (starting
+ * from one) of the nodes of each edge.
+ *
+ * The format described above is the least common denominator to all
+ * GTS files. Consistent with an object-oriented approach, the GTS
+ * file format is extensible. Each of the lines of the file can be
+ * extended with user-specific attributes accessible through the
+ * read() and write() virtual methods of each of the objects written
+ * (graph, nodes or edges). When read with different object classes,
+ * these extra attributes are just ignored.
+ */
+void gts_graph_write (GtsGraph * g, FILE * fp)
+{
+ guint nnode = 1, nedge = 0;
+ gpointer data[2];
+
+ g_return_if_fail (g != NULL);
+ g_return_if_fail (fp != NULL);
+
+ gts_graph_foreach_edge (g, (GtsFunc) count_edges, &nedge);
+ fprintf (fp, "%u %u", gts_container_size (GTS_CONTAINER (g)), nedge);
+ if (GTS_OBJECT (g)->klass->write)
+ (* GTS_OBJECT (g)->klass->write) (GTS_OBJECT (g), fp);
+ fputc ('\n', fp);
+ data[0] = fp;
+ data[1] = &nnode;
+ gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) write_node, data);
+ gts_graph_foreach_edge (g, (GtsFunc) write_edge, fp);
+ gts_container_foreach (GTS_CONTAINER (g),
+ (GtsFunc) gts_object_reset_reserved, NULL);
+}
+
+/**
+ * gts_graph_read:
+ * @fp: a #GtsFile.
+ *
+ * Reads a graph from a file.
+ *
+ * Returns: the new #GtsGraph or %NULL if an error occured (in which
+ * case the @error field of @fp is set).
+ */
+GtsGraph * gts_graph_read (GtsFile * fp)
+{
+ GtsGraph * g;
+ GtsGNode ** nodes;
+ guint nn, ne, n;
+
+ g_return_val_if_fail (fp != NULL, NULL);
+
+ if (fp->type != GTS_INT) {
+ gts_file_error (fp, "expecting an integer (number of nodes)");
+ return NULL;
+ }
+ nn = atoi (fp->token->str);
+ gts_file_next_token (fp);
+
+ if (fp->type != GTS_INT) {
+ gts_file_error (fp, "expecting an integer (number of edges)");
+ return NULL;
+ }
+ ne = atoi (fp->token->str);
+
+ gts_file_next_token (fp);
+ if (fp->type != '\n') {
+ GtsObjectClass * klass;
+
+ gts_graph_class ();
+ gts_gnode_class ();
+ gts_gedge_class ();
+
+ if (fp->type != GTS_STRING) {
+ gts_file_error (fp, "expecting a string (GtsGraphClass)");
+ return NULL;
+ }
+ klass = gts_object_class_from_name (fp->token->str);
+ if (klass == NULL) {
+ gts_file_error (fp, "unknown class `%s'", fp->token->str);
+ return NULL;
+ }
+ if (!gts_object_class_is_from_class (klass, gts_graph_class ())) {
+ gts_file_error (fp, "class `%s' is not a GtsGraphClass", fp->token->str);
+ return NULL;
+ }
+ g = GTS_GRAPH (gts_object_new (klass));
+ g->graph_class = GTS_GRAPH_CLASS (klass);
+ gts_file_next_token (fp);
+ (* klass->read) ((GtsObject **) &g, fp);
+ if (fp->type == GTS_ERROR) {
+ gts_object_destroy (GTS_OBJECT (g));
+ return NULL;
+ }
+ }
+ else
+ g = GTS_GRAPH (gts_object_new (GTS_OBJECT_CLASS (gts_graph_class ())));
+ gts_file_first_token_after (fp, '\n');
+ if (nn <= 0)
+ return g;
+
+ nodes = g_malloc ((nn + 1)*sizeof (GtsGNode *));
+
+ n = 0;
+ while (n < nn && fp->type != GTS_ERROR) {
+ GtsObject * new_node =
+ gts_object_new (GTS_OBJECT_CLASS (g->node_class));
+
+ gts_container_add (GTS_CONTAINER (g), GTS_CONTAINEE (new_node));
+ if (GTS_OBJECT_CLASS (g->node_class)->read)
+ (*GTS_OBJECT_CLASS (g->node_class)->read) (&new_node, fp);
+ gts_file_first_token_after (fp, '\n');
+ nodes[n++] = GTS_GNODE (new_node);
+ }
+ if (fp->type == GTS_ERROR)
+ nn = n;
+
+ n = 0;
+ while (n < ne && fp->type != GTS_ERROR) {
+ guint n1, n2;
+
+ if (fp->type != GTS_INT)
+ gts_file_error (fp, "expecting an integer (first node index)");
+ else {
+ n1 = atoi (fp->token->str);
+ if (n1 == 0 || n1 > nn)
+ gts_file_error (fp, "node index `%d' is out of range `[1,%d]'",
+ n1, nn);
+ else {
+ gts_file_next_token (fp);
+ if (fp->type != GTS_INT)
+ gts_file_error (fp, "expecting an integer (second node index)");
+ else {
+ n2 = atoi (fp->token->str);
+ if (n2 == 0 || n2 > nn)
+ gts_file_error (fp, "node index `%d' is out of range `[1,%d]'",
+ n2, nn);
+ else {
+ GtsGEdge * new_edge =
+ gts_gedge_new (g->edge_class, nodes[n1 - 1], nodes [n2 - 1]);
+
+ gts_file_next_token (fp);
+ if (fp->type != '\n')
+ if (GTS_OBJECT_CLASS (g->edge_class)->read)
+ (*GTS_OBJECT_CLASS (g->edge_class)->read)
+ ((GtsObject **) &new_edge, fp);
+ gts_file_first_token_after (fp, '\n');
+ n++;
+ }
+ }
+ }
+ }
+ }
+
+ if (fp->type == GTS_ERROR) {
+ gts_allow_floating_gnodes = TRUE;
+ while (nn)
+ gts_object_destroy (GTS_OBJECT (nodes[nn-- - 1]));
+ gts_allow_floating_gnodes = FALSE;
+ }
+ g_free (nodes);
+
+ if (fp->type == GTS_ERROR) {
+ gts_object_destroy (GTS_OBJECT (g));
+ return NULL;
+ }
+ return g;
+}
+
+static void write_dot_node (GtsGNode * node, gpointer * data)
+{
+ FILE * fp = data[0];
+ guint * nnode = data[1];
+
+ fprintf (fp, " n%u", *nnode);
+ if (GTS_GNODE_CLASS (GTS_OBJECT (node)->klass)->write) {
+ fputs (" [", fp);
+ (* GTS_GNODE_CLASS (GTS_OBJECT (node)->klass)->write) (node, fp);
+ fputc (']', fp);
+ }
+ fputs (";\n", fp);
+ GTS_OBJECT (node)->reserved = GUINT_TO_POINTER ((*nnode)++);
+}
+
+static void write_dot_edge (GtsGEdge * edge, FILE * fp)
+{
+ fprintf (fp, " n%u -> n%u",
+ GPOINTER_TO_UINT (GTS_OBJECT (edge->n1)->reserved),
+ GPOINTER_TO_UINT (GTS_OBJECT (edge->n2)->reserved));
+ if (GTS_GEDGE_CLASS (GTS_OBJECT (edge)->klass)->write) {
+ fputs (" [", fp);
+ (* GTS_GEDGE_CLASS (GTS_OBJECT (edge)->klass)->write) (edge, fp);
+ fputc (']', fp);
+ }
+ fputs (";\n", fp);
+}
+
+/**
+ * gts_graph_write_dot:
+ * @g: a #GtsGraph.
+ * @fp: a file pointer.
+ *
+ * Writes in the file @fp an ASCII representation of @g in the dot format of
+ * AT&T Bell Labs.
+ */
+void gts_graph_write_dot (GtsGraph * g, FILE * fp)
+{
+ guint nnode = 1;
+ gpointer data[2];
+
+ g_return_if_fail (g != NULL);
+ g_return_if_fail (fp != NULL);
+
+ fprintf (fp, "digraph \"%p\" {\n", g);
+ data[0] = fp;
+ data[1] = &nnode;
+ gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) write_dot_node, data);
+ gts_graph_foreach_edge (g, (GtsFunc) write_dot_edge, fp);
+ fputs ("}\n", fp);
+
+ gts_container_foreach (GTS_CONTAINER (g),
+ (GtsFunc) gts_object_reset_reserved, NULL);
+}
+
+/* GtsWGraph */
+
+static gfloat wgraph_weight (GtsGraph * g)
+{
+ return GTS_WGRAPH (g)->weight;
+}
+
+static void wgraph_add (GtsContainer * g, GtsContainee * n)
+{
+ GtsWGraph * wg = GTS_WGRAPH (g);
+ gfloat w = gts_gnode_weight (GTS_GNODE (n));
+
+ wg->weight += w;
+
+ (* GTS_CONTAINER_CLASS (GTS_OBJECT_CLASS (gts_wgraph_class ())->parent_class)->add) (g, n);
+}
+
+static void wgraph_remove (GtsContainer * g, GtsContainee * n)
+{
+ GTS_WGRAPH (g)->weight -= gts_gnode_weight (GTS_GNODE (n));
+
+ (* GTS_CONTAINER_CLASS (GTS_OBJECT_CLASS (gts_wgraph_class ())->parent_class)->remove) (g, n);
+}
+
+static void wgraph_class_init (GtsWGraphClass * klass)
+{
+ GTS_GRAPH_CLASS (klass)->weight = wgraph_weight;
+
+ GTS_CONTAINER_CLASS (klass)->add = wgraph_add;
+ GTS_CONTAINER_CLASS (klass)->remove = wgraph_remove;
+}
+
+static void wgraph_init (GtsWGraph * g)
+{
+ g->weight = 0.;
+}
+
+/**
+ * gts_wgraph_class:
+ *
+ * Returns: the #GtsWGraphClass.
+ */
+GtsWGraphClass * gts_wgraph_class (void)
+{
+ static GtsWGraphClass * klass = NULL;
+
+ if (klass == NULL) {
+ GtsObjectClassInfo wgraph_info = {
+ "GtsWGraph",
+ sizeof (GtsWGraph),
+ sizeof (GtsWGraphClass),
+ (GtsObjectClassInitFunc) wgraph_class_init,
+ (GtsObjectInitFunc) wgraph_init,
+ (GtsArgSetFunc) NULL,
+ (GtsArgGetFunc) NULL
+ };
+ klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_graph_class ()),
+ &wgraph_info);
+ }
+
+ return klass;
+}
+
+static void weight_max (GtsGNode * n, gfloat * wmax)
+{
+ gfloat w = gts_gnode_weight (n);
+
+ if (w > *wmax)
+ *wmax = w;
+}
+
+/**
+ * gts_wgraph_weight_max:
+ * @wg: a #GtsWGraph.
+ *
+ * Returns: the maximum weight of any vertices belonging to @g.
+ */
+gfloat gts_wgraph_weight_max (GtsWGraph * wg)
+{
+ gfloat wmax = - G_MAXFLOAT;
+
+ g_return_val_if_fail (wg != NULL, 0.);
+
+ gts_container_foreach (GTS_CONTAINER (wg), (GtsFunc) weight_max, &wmax);
+
+ return wmax;
+}
+
+/* Surface graph */
+
+static void create_node (GtsFace * f, GtsGraph * graph)
+{
+ GtsFNode * fn = gts_fnode_new (gts_fnode_class (), f);
+
+ gts_container_add (GTS_CONTAINER (graph), GTS_CONTAINEE (fn));
+ GTS_OBJECT (f)->reserved = fn;
+}
+
+static void create_edge (GtsEdge * e, GtsSurface * s)
+{
+ GSList * i = e->triangles;
+
+ while (i) {
+ GtsFace * f = i->data;
+ if (GTS_IS_FACE (f) && gts_face_has_parent_surface (f, s)) {
+ GSList * j = i->next;
+ while (j) {
+ GtsFace * f1 = j->data;
+ if (GTS_IS_FACE (f1) && gts_face_has_parent_surface (f1, s))
+ gts_pgedge_new (gts_pgedge_class (),
+ GTS_OBJECT (f)->reserved,
+ GTS_OBJECT (f1)->reserved,
+ e);
+ j = j->next;
+ }
+ }
+ i = i->next;
+ }
+}
+
+/**
+ * gts_surface_graph_new:
+ * @klass: a #GtsGraphClass.
+ * @s: a #GtsSurface.
+ *
+ * Returns: a new #GtsGraph representing the connectivity of the faces
+ * of @s. This graph uses #GtsFGNode as nodes which allows to store
+ * the dependencies between nodes and faces of @s.
+ */
+GtsGraph * gts_surface_graph_new (GtsGraphClass * klass,
+ GtsSurface * s)
+{
+ GtsGraph * graph;
+
+ g_return_val_if_fail (klass != NULL, NULL);
+ g_return_val_if_fail (s != NULL, NULL);
+
+ graph = GTS_GRAPH (gts_object_new (GTS_OBJECT_CLASS (klass)));
+ gts_surface_foreach_face (s, (GtsFunc) create_node, graph);
+ gts_surface_foreach_edge (s, (GtsFunc) create_edge, s);
+ gts_surface_foreach_face (s, (GtsFunc) gts_object_reset_reserved, NULL);
+
+ return graph;
+}
+
+static void create_segment_edge (GtsSegment * s, GtsGraph * graph)
+{
+ GtsGNode * n1 = GTS_OBJECT (s->v1)->reserved, * n2;
+
+ if (n1 == NULL) {
+ n1 = GTS_GNODE (gts_pnode_new (gts_pnode_class (), s->v1));
+ gts_container_add (GTS_CONTAINER (graph), GTS_CONTAINEE (n1));
+ GTS_OBJECT (s->v1)->reserved = n1;
+ }
+
+ n2 = GTS_OBJECT (s->v2)->reserved;
+ if (n2 == NULL) {
+ n2 = GTS_GNODE (gts_pnode_new (gts_pnode_class (), s->v2));
+ gts_container_add (GTS_CONTAINER (graph), GTS_CONTAINEE (n2));
+ GTS_OBJECT (s->v2)->reserved = n2;
+ }
+
+ gts_pgedge_new (gts_pgedge_class (), n1, n2, s);
+}
+
+static void reset_reserved (GtsSegment * s)
+{
+ GTS_OBJECT (s->v1)->reserved = GTS_OBJECT (s->v2)->reserved = NULL;
+}
+
+/**
+ * gts_segments_graph_new:
+ * @klass: a #GtsGraphClass.
+ * @segments: a list of #GtsSegment.
+ *
+ * Returns: a new #GtsGraph representing the connectivity of the segments
+ * in @segments.
+ */
+GtsGraph * gts_segments_graph_new (GtsGraphClass * klass,
+ GSList * segments)
+{
+ GtsGraph * graph;
+
+ g_return_val_if_fail (klass != NULL, NULL);
+
+ graph = GTS_GRAPH (gts_object_new (GTS_OBJECT_CLASS (klass)));
+ g_slist_foreach (segments, (GFunc) create_segment_edge, graph);
+ g_slist_foreach (segments, (GFunc) reset_reserved, NULL);
+
+ return graph;
+}
+
+static void add_to_surface (GtsGNode * n, GtsSurface * s)
+{
+ if (GTS_IS_FNODE (n))
+ gts_surface_add_face (s, GTS_FNODE (n)->f);
+}
+
+/**
+ * gts_surface_graph_surface:
+ * @surface_graph: a #GtsGraph using #GtsFGNode as nodes.
+ * @s: a #GtsSurface.
+ *
+ * Returns: a new #GtsSurface using the same classes as @s and
+ * composed of the faces defined by @surface_graph.
+ */
+GtsSurface * gts_surface_graph_surface (GtsGraph * surface_graph,
+ GtsSurface * s)
+{
+ GtsSurface * s1;
+
+ g_return_val_if_fail (surface_graph != NULL, NULL);
+ g_return_val_if_fail (s != NULL, NULL);
+
+ s1 = gts_surface_new (GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass),
+ s->face_class,
+ s->edge_class,
+ s->vertex_class);
+ gts_container_foreach (GTS_CONTAINER (surface_graph),
+ (GtsFunc) add_to_surface, s1);
+ return s1;
+}
+
diff --git a/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 ()),
+ >s_vertex_normal_info);
+ }
+
+ return klass;
+}
+
+/* GtsColorVertex: Object */
+
+GtsVertexClass * gts_color_vertex_class (void)
+{
+ static GtsVertexClass * klass = NULL;
+
+ if (klass == NULL) {
+ GtsObjectClassInfo gts_color_vertex_info = {
+ "GtsColorVertex",
+ sizeof (GtsColorVertex),
+ sizeof (GtsVertexClass),
+ (GtsObjectClassInitFunc) NULL,
+ (GtsObjectInitFunc) NULL,
+ (GtsArgSetFunc) NULL,
+ (GtsArgGetFunc) NULL
+ };
+ klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_vertex_class ()),
+ >s_color_vertex_info);
+ }
+
+ return klass;
+}
+
diff --git a/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