Index: toporouter/src_3rd/gts/Makefile.dep =================================================================== --- toporouter/src_3rd/gts/Makefile.dep (nonexistent) +++ toporouter/src_3rd/gts/Makefile.dep (revision 6803) @@ -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 Index: toporouter/src_3rd/gts/Makefile.in =================================================================== --- toporouter/src_3rd/gts/Makefile.in (nonexistent) +++ toporouter/src_3rd/gts/Makefile.in (revision 6803) @@ -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 Index: toporouter/src_3rd/gts/bbtree.c =================================================================== --- toporouter/src_3rd/gts/bbtree.c (nonexistent) +++ toporouter/src_3rd/gts/bbtree.c (revision 6803) @@ -0,0 +1,1289 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "gts.h" + +static void bbox_init (GtsBBox * bbox) +{ + bbox->bounded = NULL; +} + +/** + * gts_bbox_class: + * + * Returns: the #GtsBBoxClass. + */ +GtsBBoxClass * gts_bbox_class (void) +{ + static GtsBBoxClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo bbox_info = { + "GtsBBox", + sizeof (GtsBBox), + sizeof (GtsBBoxClass), + (GtsObjectClassInitFunc) NULL, + (GtsObjectInitFunc) bbox_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (gts_object_class (), &bbox_info); + } + + return klass; +} + +/** + * gts_bbox_set: + * @bbox: a #GtsBBox. + * @bounded: the object to be bounded. + * @x1: x-coordinate of the lower left corner. + * @y1: y-coordinate of the lower left corner. + * @z1: z-coordinate of the lower left corner. + * @x2: x-coordinate of the upper right corner. + * @y2: y-coordinate of the upper right corner. + * @z2: z-coordinate of the upper right corner. + * + * Sets fields of @bbox. + */ +void gts_bbox_set (GtsBBox * bbox, + gpointer bounded, + gdouble x1, gdouble y1, gdouble z1, + gdouble x2, gdouble y2, gdouble z2) +{ + g_return_if_fail (bbox != NULL); + g_return_if_fail (x2 >= x1 && y2 >= y1 && z2 >= z1); + + bbox->x1 = x1; bbox->y1 = y1; bbox->z1 = z1; + bbox->x2 = x2; bbox->y2 = y2; bbox->z2 = z2; + bbox->bounded = bounded; +} + +/** + * gts_bbox_new: + * @klass: a #GtsBBoxClass. + * @bounded: the object to be bounded. + * @x1: x-coordinate of the lower left corner. + * @y1: y-coordinate of the lower left corner. + * @z1: z-coordinate of the lower left corner. + * @x2: x-coordinate of the upper right corner. + * @y2: y-coordinate of the upper right corner. + * @z2: z-coordinate of the upper right corner. + * + * Returns: a new #GtsBBox. + */ +GtsBBox * gts_bbox_new (GtsBBoxClass * klass, + gpointer bounded, + gdouble x1, gdouble y1, gdouble z1, + gdouble x2, gdouble y2, gdouble z2) +{ + GtsBBox * bbox; + + g_return_val_if_fail (klass != NULL, NULL); + + bbox = GTS_BBOX (gts_object_new (GTS_OBJECT_CLASS (klass))); + gts_bbox_set (bbox, bounded, x1, y1, z1, x2, y2, z2); + return bbox; +} + +/** + * gts_bbox_triangle: + * @klass: a #GtsBBoxClass. + * @t: a #GtsTriangle. + * + * Returns: a new #GtsBBox bounding box of @t. + */ +GtsBBox * gts_bbox_triangle (GtsBBoxClass * klass, + GtsTriangle * t) +{ + GtsBBox * bbox; + GtsPoint * p; + + g_return_val_if_fail (t != NULL, NULL); + g_return_val_if_fail (klass != NULL, NULL); + + p = GTS_POINT (GTS_SEGMENT (t->e1)->v1); + bbox = gts_bbox_new (klass, t, p->x, p->y, p->z, p->x, p->y, p->z); + + p = GTS_POINT (GTS_SEGMENT (t->e1)->v2); + if (p->x > bbox->x2) bbox->x2 = p->x; + if (p->x < bbox->x1) bbox->x1 = p->x; + if (p->y > bbox->y2) bbox->y2 = p->y; + if (p->y < bbox->y1) bbox->y1 = p->y; + if (p->z > bbox->z2) bbox->z2 = p->z; + if (p->z < bbox->z1) bbox->z1 = p->z; + p = GTS_POINT (gts_triangle_vertex (t)); + if (p->x > bbox->x2) bbox->x2 = p->x; + if (p->x < bbox->x1) bbox->x1 = p->x; + if (p->y > bbox->y2) bbox->y2 = p->y; + if (p->y < bbox->y1) bbox->y1 = p->y; + if (p->z > bbox->z2) bbox->z2 = p->z; + if (p->z < bbox->z1) bbox->z1 = p->z; + + return bbox; +} + +/** + * gts_bbox_segment: + * @klass: a #GtsBBoxClass. + * @s: a #GtsSegment. + * + * Returns: a new #GtsBBox bounding box of @s. + */ +GtsBBox * gts_bbox_segment (GtsBBoxClass * klass, GtsSegment * s) +{ + GtsBBox * bbox; + GtsPoint * p1, * p2; + + g_return_val_if_fail (s != NULL, NULL); + g_return_val_if_fail (klass != NULL, NULL); + + bbox = gts_bbox_new (klass, s, 0., 0., 0., 0., 0., 0.); + + p1 = GTS_POINT (s->v1); + p2 = GTS_POINT (s->v2); + if (p1->x > p2->x) { + bbox->x2 = p1->x; bbox->x1 = p2->x; + } + else { + bbox->x1 = p1->x; bbox->x2 = p2->x; + } + if (p1->y > p2->y) { + bbox->y2 = p1->y; bbox->y1 = p2->y; + } + else { + bbox->y1 = p1->y; bbox->y2 = p2->y; + } + if (p1->z > p2->z) { + bbox->z2 = p1->z; bbox->z1 = p2->z; + } + else { + bbox->z1 = p1->z; bbox->z2 = p2->z; + } + + return bbox; +} + +static void bbox_foreach_vertex (GtsPoint * p, GtsBBox * bb) +{ + if (p->x < bb->x1) bb->x1 = p->x; + if (p->y < bb->y1) bb->y1 = p->y; + if (p->z < bb->z1) bb->z1 = p->z; + if (p->x > bb->x2) bb->x2 = p->x; + if (p->y > bb->y2) bb->y2 = p->y; + if (p->z > bb->z2) bb->z2 = p->z; +} + +/** + * gts_bbox_surface: + * @klass: a #GtsBBoxClass. + * @surface: a #GtsSurface. + * + * Returns: a new #GtsBBox bounding box of @surface. + */ +GtsBBox * gts_bbox_surface (GtsBBoxClass * klass, GtsSurface * surface) +{ + GtsBBox * bbox; + + g_return_val_if_fail (klass != NULL, NULL); + g_return_val_if_fail (surface != NULL, NULL); + + bbox = gts_bbox_new (klass, surface, 0., 0., 0., 0., 0., 0.); + bbox->x1 = bbox->y1 = bbox->z1 = G_MAXDOUBLE; + bbox->x2 = bbox->y2 = bbox->z2 = -G_MAXDOUBLE; + + gts_surface_foreach_vertex (surface, (GtsFunc) bbox_foreach_vertex, bbox); + + return bbox; +} + +/** + * gts_bbox_bboxes: + * @klass: a #GtsBBoxClass. + * @bboxes: a list of #GtsBBox. + * + * Returns: a new #GtsBBox bounding box of all the bounding boxes in + * @bboxes. + */ +GtsBBox * gts_bbox_bboxes (GtsBBoxClass * klass, GSList * bboxes) +{ + GtsBBox * bbox; + GtsBBox * bb; + + g_return_val_if_fail (bboxes != NULL, NULL); + g_return_val_if_fail (klass != NULL, NULL); + + bb = bboxes->data; + bbox = gts_bbox_new (klass, bboxes, + bb->x1, bb->y1, bb->z1, bb->x2, bb->y2, bb->z2); + bboxes = bboxes->next; + while (bboxes) { + bb = bboxes->data; + if (bb->x1 < bbox->x1) bbox->x1 = bb->x1; + if (bb->y1 < bbox->y1) bbox->y1 = bb->y1; + if (bb->z1 < bbox->z1) bbox->z1 = bb->z1; + if (bb->x2 > bbox->x2) bbox->x2 = bb->x2; + if (bb->y2 > bbox->y2) bbox->y2 = bb->y2; + if (bb->z2 > bbox->z2) bbox->z2 = bb->z2; + bboxes = bboxes->next; + } + + return bbox; +} + +/** + * gts_bbox_points: + * @klass: a #GtsBBoxClass. + * @points: a list of #GtsPoint. + * + * Returns: a new #GtsBBox bounding box of @points. + */ +GtsBBox * gts_bbox_points (GtsBBoxClass * klass, GSList * points) +{ + GtsPoint * p; + GtsBBox * bbox; + GSList * i; + + if (points == NULL) + return NULL; + + p = points->data; + bbox = gts_bbox_new (klass, points, p->x, p->y, p->z, p->x, p->y, p->z); + + i = points->next; + while (i) { + p = i->data; + if (p->x > bbox->x2) + bbox->x2 = p->x; + else if (p->x < bbox->x1) + bbox->x1 = p->x; + if (p->y > bbox->y2) + bbox->y2 = p->y; + else if (p->y < bbox->y1) + bbox->y1 = p->y; + if (p->z > bbox->z2) + bbox->z2 = p->z; + else if (p->z < bbox->z1) + bbox->z1 = p->z; + i = i->next; + } + + return bbox; +} + +/** + * gts_bboxes_are_overlapping: + * @bb1: a #GtsBBox. + * @bb2: a #GtsBBox. + * + * Returns: %TRUE if the bounding boxes @bb1 and @bb2 are overlapping + * (including just touching), %FALSE otherwise. + */ +gboolean gts_bboxes_are_overlapping (GtsBBox * bb1, GtsBBox * bb2) +{ + if (bb1 == bb2) + return TRUE; + if (bb1->x1 > bb2->x2) + return FALSE; + if (bb2->x1 > bb1->x2) + return FALSE; + if (bb1->y1 > bb2->y2) + return FALSE; + if (bb2->y1 > bb1->y2) + return FALSE; + if (bb1->z1 > bb2->z2) + return FALSE; + if (bb2->z1 > bb1->z2) + return FALSE; + return TRUE; +} + +#define bbox_volume(bb) (((bb)->x2 -\ + (bb)->x1)*\ + ((bb)->y2 -\ + (bb)->y1)*\ + ((bb)->z2 -\ + (bb)->z1)) + +/** + * gts_bbox_diagonal2: + * @bb: a #GtsBBox. + * + * Returns: the squared length of the diagonal of @bb. + */ +gdouble gts_bbox_diagonal2 (GtsBBox * bb) +{ + gdouble x, y, z; + + g_return_val_if_fail (bb != NULL, 0.); + + x = bb->x2 - bb->x1; + y = bb->y2 - bb->y1; + z = bb->z2 - bb->z1; + + return x*x + y*y + z*z; +} + +/** + * gts_bbox_draw: + * @bb: a #GtsBBox. + * @fptr: a file pointer. + * + * Writes in file @fptr an OOGL (Geomview) description of @bb. + */ +void gts_bbox_draw (GtsBBox * bb, FILE * fptr) +{ + g_return_if_fail (bb != NULL); + + fprintf (fptr, "OFF 8 6 12\n"); + fprintf (fptr, "%g %g %g\n", + bb->x1, bb->y1, bb->z1); + fprintf (fptr, "%g %g %g\n", + bb->x2, bb->y1, bb->z1); + fprintf (fptr, "%g %g %g\n", + bb->x2, bb->y2, bb->z1); + fprintf (fptr, "%g %g %g\n", + bb->x1, bb->y2, bb->z1); + fprintf (fptr, "%g %g %g\n", + bb->x1, bb->y1, bb->z2); + fprintf (fptr, "%g %g %g\n", + bb->x2, bb->y1, bb->z2); + fprintf (fptr, "%g %g %g\n", + bb->x2, bb->y2, bb->z2); + fprintf (fptr, "%g %g %g\n", + bb->x1, bb->y2, bb->z2); + fputs ("4 3 2 1 0\n" + "4 4 5 6 7\n" + "4 2 3 7 6\n" + "4 0 1 5 4\n" + "4 0 4 7 3\n" + "4 1 2 6 5\n", + fptr); +} + +#define MINMAX(x1, x2, xmin, xmax) { if (x1 < x2) { xmin = x1; xmax = x2; }\ + else { xmin = x2; xmax = x1; } } + +/** + * gts_bbox_point_distance2: + * @bb: a #GtsBBox. + * @p: a #GtsPoint. + * @min: a pointer on a gdouble. + * @max: a pointer on a gdouble. + * + * Sets @min and @max to lower and upper bounds for the square of the + * Euclidean distance between the object contained in @bb and @p. For these + * bounds to make any sense the bounding box must be "tight" i.e. each of the + * 6 faces of the box must at least be touched by one point of the bounded + * object. + */ +void gts_bbox_point_distance2 (GtsBBox * bb, GtsPoint * p, + gdouble * min, gdouble * max) +{ + gdouble x1, y1, z1, x2, y2, z2, x, y, z; + gdouble dmin, dmax, xd1, xd2, yd1, yd2, zd1, zd2; + gdouble mx, Mx, my, My, mz, Mz; + + g_return_if_fail (bb != NULL); + g_return_if_fail (p != NULL); + g_return_if_fail (min != NULL); + g_return_if_fail (max != NULL); + + x1 = bb->x1; y1 = bb->y1; z1 = bb->z1; + x2 = bb->x2; y2 = bb->y2; z2 = bb->z2; + x = p->x; y = p->y; z = p->z; + + xd1 = (x1 - x)*(x1 - x); + xd2 = (x - x2)*(x - x2); + yd1 = (y1 - y)*(y1 - y); + yd2 = (y - y2)*(y - y2); + zd1 = (z1 - z)*(z1 - z); + zd2 = (z - z2)*(z - z2); + + dmin = x < x1 ? xd1 : x > x2 ? xd2 : 0.0; + dmin += y < y1 ? yd1 : y > y2 ? yd2 : 0.0; + dmin += z < z1 ? zd1 : z > z2 ? zd2 : 0.0; + + MINMAX (xd1, xd2, mx, Mx); + MINMAX (yd1, yd2, my, My); + MINMAX (zd1, zd2, mz, Mz); + + dmax = mx + My + Mz; + dmax = MIN (dmax, Mx + my + Mz); + dmax = MIN (dmax, Mx + My + mz); + + *min = dmin; + *max = dmax; +} + +/** + * gts_bbox_is_stabbed: + * @bb: a #GtsBBox. + * @p: a #GtsPoint. + * + * Returns: %TRUE if the ray starting at @p and ending at (+infty, + * @p->y, @p->z) intersects with @bb, %FALSE otherwise. + */ +gboolean gts_bbox_is_stabbed (GtsBBox * bb, GtsPoint * p) +{ + g_return_val_if_fail (bb != NULL, FALSE); + g_return_val_if_fail (p != NULL, FALSE); + + if (p->x > bb->x2 || + p->y < bb->y1 || p->y > bb->y2 || + p->z < bb->z1 || p->z > bb->z2) + return FALSE; + return TRUE; +} + +extern int triBoxOverlap (double boxcenter[3], + double boxhalfsize[3], + double triverts[3][3]); + +/** + * gts_bbox_overlaps_triangle: + * @bb: a #GtsBBox. + * @t: a #GtsTriangle. + * + * This is a wrapper around the fast overlap test of Tomas + * Akenine-Moller (http://www.cs.lth.se/home/Tomas_Akenine_Moller/). + * + * Returns: %TRUE if @bb overlaps with @t, %FALSE otherwise. + */ +gboolean gts_bbox_overlaps_triangle (GtsBBox * bb, GtsTriangle * t) +{ + double bc[3], bh[3], tv[3][3]; + GtsPoint * p1, * p2, * p3; + + g_return_val_if_fail (bb != NULL, FALSE); + g_return_val_if_fail (t != NULL, FALSE); + + bc[0] = (bb->x2 + bb->x1)/2.; + bh[0] = (bb->x2 - bb->x1)/2.; + bc[1] = (bb->y2 + bb->y1)/2.; + bh[1] = (bb->y2 - bb->y1)/2.; + bc[2] = (bb->z2 + bb->z1)/2.; + bh[2] = (bb->z2 - bb->z1)/2.; + p1 = GTS_POINT (GTS_SEGMENT (t->e1)->v1); + p2 = GTS_POINT (GTS_SEGMENT (t->e1)->v2); + p3 = GTS_POINT (gts_triangle_vertex (t)); + tv[0][0] = p1->x; tv[0][1] = p1->y; tv[0][2] = p1->z; + tv[1][0] = p2->x; tv[1][1] = p2->y; tv[1][2] = p2->z; + tv[2][0] = p3->x; tv[2][1] = p3->y; tv[2][2] = p3->z; + + return triBoxOverlap (bc, bh, tv); +} + +/** + * gts_bbox_overlaps_segment: + * @bb: a #GtsBBox. + * @s: a #GtsSegment. + * + * This functions uses gts_bbox_overlaps_triangle() with a degenerate + * triangle. + * + * Returns: %TRUE if @bb overlaps with @s, %FALSE otherwise. + */ +gboolean gts_bbox_overlaps_segment (GtsBBox * bb, GtsSegment * s) +{ + double bc[3], bh[3], tv[3][3]; + GtsPoint * p1, * p2, * p3; + + g_return_val_if_fail (bb != NULL, FALSE); + g_return_val_if_fail (s != NULL, FALSE); + + bc[0] = (bb->x2 + bb->x1)/2.; + bh[0] = (bb->x2 - bb->x1)/2.; + bc[1] = (bb->y2 + bb->y1)/2.; + bh[1] = (bb->y2 - bb->y1)/2.; + bc[2] = (bb->z2 + bb->z1)/2.; + bh[2] = (bb->z2 - bb->z1)/2.; + p1 = GTS_POINT (s->v1); + p2 = GTS_POINT (s->v2); + p3 = p1; + tv[0][0] = p1->x; tv[0][1] = p1->y; tv[0][2] = p1->z; + tv[1][0] = p2->x; tv[1][1] = p2->y; tv[1][2] = p2->z; + tv[2][0] = p3->x; tv[2][1] = p3->y; tv[2][2] = p3->z; + + return triBoxOverlap (bc, bh, tv); +} + +/** + * gts_bb_tree_new: + * @bboxes: a list of #GtsBBox. + * + * Builds a new hierarchy of bounding boxes for @bboxes. At each + * level, the GNode->data field contains a #GtsBBox bounding box of + * all the children. The tree is binary and is built by repeatedly + * cutting in two approximately equal halves the bounding boxes at + * each level until a leaf node (i.e. a bounding box given in @bboxes) + * is reached. In order to minimize the depth of the tree, the cutting + * direction is always chosen as perpendicular to the longest + * dimension of the bounding box. + * + * Returns: a new hierarchy of bounding boxes. + */ +GNode * gts_bb_tree_new (GSList * bboxes) +{ + GSList * i, * positive = NULL, * negative = NULL; + GNode * node; + GtsBBox * bbox; + guint dir, np = 0, nn = 0; + gdouble * p1, * p2; + gdouble cut; + + g_return_val_if_fail (bboxes != NULL, NULL); + + if (bboxes->next == NULL) /* leaf node */ + return g_node_new (bboxes->data); + + bbox = gts_bbox_bboxes (gts_bbox_class (), bboxes); + node = g_node_new (bbox); + + if (bbox->x2 - bbox->x1 > bbox->y2 - bbox->y1) { + if (bbox->z2 - bbox->z1 > bbox->x2 - bbox->x1) + dir = 2; + else + dir = 0; + } + else if (bbox->z2 - bbox->z1 > bbox->y2 - bbox->y1) + dir = 2; + else + dir = 1; + + p1 = (gdouble *) &bbox->x1; + p2 = (gdouble *) &bbox->x2; + cut = (p1[dir] + p2[dir])/2.; + i = bboxes; + while (i) { + bbox = i->data; + p1 = (gdouble *) &bbox->x1; + p2 = (gdouble *) &bbox->x2; + if ((p1[dir] + p2[dir])/2. > cut) { + positive = g_slist_prepend (positive, bbox); + np++; + } + else { + negative = g_slist_prepend (negative, bbox); + nn++; + } + i = i->next; + } + if (!positive) { + GSList * last = g_slist_nth (negative, (nn - 1)/2); + positive = last->next; + last->next = NULL; + } + else if (!negative) { + GSList * last = g_slist_nth (positive, (np - 1)/2); + negative = last->next; + last->next = NULL; + } + g_node_prepend (node, gts_bb_tree_new (positive)); + g_slist_free (positive); + g_node_prepend (node, gts_bb_tree_new (negative)); + g_slist_free (negative); + + return node; +} + +static void prepend_triangle_bbox (GtsTriangle * t, GSList ** bboxes) +{ + *bboxes = g_slist_prepend (*bboxes, + gts_bbox_triangle (gts_bbox_class (), t)); +} + +/** + * gts_bb_tree_surface: + * @s: a #GtsSurface. + * + * Returns: a new hierarchy of bounding boxes bounding the faces of @s. + */ +GNode * gts_bb_tree_surface (GtsSurface * s) +{ + GSList * bboxes = NULL; + GNode * tree; + + g_return_val_if_fail (s != NULL, NULL); + + gts_surface_foreach_face (s, (GtsFunc) prepend_triangle_bbox, &bboxes); + tree = gts_bb_tree_new (bboxes); + g_slist_free (bboxes); + + return tree; +} + +/** + * gts_bb_tree_stabbed: + * @tree: a bounding box tree. + * @p: a #GtsPoint. + * + * Returns: a list of bounding boxes, leaves of @tree which are + * stabbed by the ray defined by @p (see gts_bbox_is_stabbed()). + */ +GSList * gts_bb_tree_stabbed (GNode * tree, GtsPoint * p) +{ + GSList * list = NULL; + GtsBBox * bb; + GNode * i; + + g_return_val_if_fail (tree != NULL, NULL); + g_return_val_if_fail (p != NULL, NULL); + + bb = tree->data; + if (!gts_bbox_is_stabbed (bb, p)) + return NULL; + if (tree->children == NULL) /* leaf node */ + return g_slist_prepend (NULL, bb); + i = tree->children; + while (i) { + list = g_slist_concat (list, gts_bb_tree_stabbed (i, p)); + i = i->next; + } + return list; +} + +/** + * gts_bb_tree_overlap: + * @tree: a bounding box tree. + * @bbox: a #GtsBBox. + * + * Returns: a list of bounding boxes, leaves of @tree which overlap @bbox. + */ +GSList * gts_bb_tree_overlap (GNode * tree, GtsBBox * bbox) +{ + GSList * list = NULL; + GtsBBox * bb; + GNode * i; + + g_return_val_if_fail (tree != NULL, NULL); + g_return_val_if_fail (bbox != NULL, NULL); + + bb = tree->data; + if (!gts_bboxes_are_overlapping (bbox, bb)) + return NULL; + if (tree->children == NULL) /* leaf node */ + return g_slist_prepend (NULL, bb); + i = tree->children; + while (i) { + list = g_slist_concat (list, gts_bb_tree_overlap (i, bbox)); + i = i->next; + } + return list; +} + +/** + * gts_bb_tree_is_overlapping: + * @tree: a bounding box tree. + * @bbox: a #GtsBBox. + * + * Returns: %TRUE if any leaf of @tree overlaps @bbox, %FALSE otherwise. + */ +gboolean gts_bb_tree_is_overlapping (GNode * tree, GtsBBox * bbox) +{ + GtsBBox * bb; + GNode * i; + + g_return_val_if_fail (tree != NULL, FALSE); + g_return_val_if_fail (bbox != NULL, FALSE); + + bb = tree->data; + if (!gts_bboxes_are_overlapping (bbox, bb)) + return FALSE; + if (tree->children == NULL) /* leaf node */ + return TRUE; + i = tree->children; + while (i) { + if (gts_bb_tree_is_overlapping (i, bbox)) + return TRUE; + i = i->next; + } + return FALSE; +} + +/** + * gts_bb_tree_traverse_overlapping: + * @tree1: a bounding box tree. + * @tree2: a bounding box tree. + * @func: a #GtsBBTreeTraverseFunc. + * @data: user data to be passed to @func. + * + * Calls @func for each overlapping pair of leaves of @tree1 and @tree2. + */ +void gts_bb_tree_traverse_overlapping (GNode * tree1, GNode * tree2, + GtsBBTreeTraverseFunc func, + gpointer data) +{ + GtsBBox * bb1, * bb2; + + g_return_if_fail (tree1 != NULL && tree2 != NULL); + + bb1 = tree1->data; bb2 = tree2->data; + if (!gts_bboxes_are_overlapping (bb1, bb2)) + return; + + if (tree1->children == NULL && tree2->children == NULL) + (*func) (tree1->data, tree2->data, data); + else if (tree2->children == NULL || + (tree1->children != NULL && + bbox_volume (bb1) > bbox_volume (bb2))) { + GNode * i = tree1->children; + while (i) { + gts_bb_tree_traverse_overlapping (i, tree2, func, data); + i = i->next; + } + } + else { + GNode * i = tree2->children; + while (i) { + gts_bb_tree_traverse_overlapping (tree1, i, func, data); + i = i->next; + } + } +} + +/** + * gts_bb_tree_draw: + * @tree: a bounding box tree. + * @depth: a specified depth. + * @fptr: a file pointer. + * + * Write in @fptr an OOGL (Geomview) description of @tree for the + * depth specified by @depth. + */ +void gts_bb_tree_draw (GNode * tree, guint depth, FILE * fptr) +{ + guint d; + + g_return_if_fail (tree != NULL); + g_return_if_fail (fptr != NULL); + + d = g_node_depth (tree); + + if (d == 1) + fprintf (fptr, "{ LIST"); + + if (d == depth) + gts_bbox_draw (tree->data, fptr); + else if (d < depth) { + GNode * i = tree->children; + while (i) { + gts_bb_tree_draw (i, depth, fptr); + i = i->next; + } + } + + if (d == 1) + fprintf (fptr, "}\n"); +} + +static void bb_tree_free (GNode * tree, gboolean free_leaves) +{ + GNode * i; + + g_return_if_fail (tree != NULL); + + if (!free_leaves && tree->children == NULL) /* leaf node */ + return; + + gts_object_destroy (tree->data); + + i = tree->children; + while (i) { + bb_tree_free (i, free_leaves); + i = i->next; + } +} + +/** + * gts_bb_tree_destroy: + * @tree: a bounding box tree. + * @free_leaves: if %TRUE the bounding boxes given by the user are freed. + * + * Destroys all the bounding boxes created by @tree and destroys the + * tree itself. If @free_leaves is set to %TRUE, destroys boxes given + * by the user when creating the tree (i.e. leaves of the tree). + */ +void gts_bb_tree_destroy (GNode * tree, gboolean free_leaves) +{ + g_return_if_fail (tree != NULL); + + bb_tree_free (tree, free_leaves); + g_node_destroy (tree); +} + +static gdouble bb_tree_min_max (GNode * tree, + GtsPoint * p, + gdouble min_max, + GSList ** list) +{ + GNode * tree1, * tree2; + gdouble min1, max1, min2, max2; + + if (tree->children == NULL) { + *list = g_slist_prepend (*list, tree->data); + return min_max; + } + tree1 = tree->children; + gts_bbox_point_distance2 (tree1->data, p, &min1, &max1); + if (max1 < min_max) + min_max = max1; + + tree2 = tree1->next; + gts_bbox_point_distance2 (tree2->data, p, &min2, &max2); + if (max2 < min_max) + min_max = max2; + + if (min1 < min2) { + if (min1 <= min_max) { + min_max = bb_tree_min_max (tree1, p, min_max, list); + if (min2 <= min_max) + min_max = bb_tree_min_max (tree2, p, min_max, list); + } + } + else { + if (min2 <= min_max) { + min_max = bb_tree_min_max (tree2, p, min_max, list); + if (min1 <= min_max) + min_max = bb_tree_min_max (tree1, p, min_max, list); + } + } + + return min_max; +} + +/** + * gts_bb_tree_point_closest_bboxes: + * @tree: a bounding box tree. + * @p: a #GtsPoint. + * + * Returns: a list of #GtsBBox. One of the bounding boxes is assured to contain + * the object of @tree closest to @p. + */ +GSList * gts_bb_tree_point_closest_bboxes (GNode * tree, + GtsPoint * p) +{ + gdouble min, min_max; + GSList * list = NULL, * i, * prev = NULL; + + g_return_val_if_fail (tree != NULL, NULL); + g_return_val_if_fail (p != NULL, NULL); + + gts_bbox_point_distance2 (tree->data, p, &min, &min_max); + min_max = bb_tree_min_max (tree, p, min_max, &list); + + i = list; + while (i) { + GSList * next = i->next; + gdouble min, max; + + gts_bbox_point_distance2 (i->data, p, &min, &max); + + if (min > min_max) { + if (prev == NULL) + list = next; + else + prev->next = next; + g_slist_free_1 (i); + } + else + prev = i; + i = next; + } + + return list; +} + +/** + * gts_bb_tree_point_distance: + * @tree: a bounding box tree. + * @p: a #GtsPoint. + * @distance: a #GtsBBoxDistFunc. + * @bbox: if not %NULL is set to the bounding box containing the closest + * object. + * + * Returns: the distance as evaluated by @distance between @p and the closest + * object in @tree. + */ +gdouble gts_bb_tree_point_distance (GNode * tree, + GtsPoint * p, + GtsBBoxDistFunc distance, + GtsBBox ** bbox) +{ + GSList * list, * i; + gdouble dmin = G_MAXDOUBLE; + + g_return_val_if_fail (tree != NULL, dmin); + g_return_val_if_fail (p != NULL, dmin); + g_return_val_if_fail (distance != NULL, dmin); + + i = list = gts_bb_tree_point_closest_bboxes (tree, p); + while (i) { + gdouble d = (*distance) (p, GTS_BBOX (i->data)->bounded); + + if (fabs (d) < fabs (dmin)) { + dmin = d; + if (bbox) + *bbox = i->data; + } + i = i->next; + } + g_slist_free (list); + + return dmin; +} + +/** + * gts_bb_tree_point_closest: + * @tree: a bounding box tree. + * @p: a #GtsPoint. + * @closest: a #GtsBBoxClosestFunc. + * @distance: if not %NULL is set to the distance between @p and the + * new #GtsPoint. + * + * Returns: a new #GtsPoint, closest point to @p and belonging to an object of + * @tree. + */ +GtsPoint * gts_bb_tree_point_closest (GNode * tree, + GtsPoint * p, + GtsBBoxClosestFunc closest, + gdouble * distance) +{ + GSList * list, * i; + gdouble dmin = G_MAXDOUBLE; + GtsPoint * np = NULL; + + g_return_val_if_fail (tree != NULL, NULL); + g_return_val_if_fail (p != NULL, NULL); + g_return_val_if_fail (closest != NULL, NULL); + + i = list = gts_bb_tree_point_closest_bboxes (tree, p); + while (i) { + GtsPoint * tp = (*closest) (p, GTS_BBOX (i->data)->bounded); + gdouble d = gts_point_distance2 (tp, p); + + if (d < dmin) { + if (np) + gts_object_destroy (GTS_OBJECT (np)); + np = tp; + dmin = d; + } + else + gts_object_destroy (GTS_OBJECT (tp)); + i = i->next; + } + g_slist_free (list); + + if (distance) + *distance = dmin; + + return np; +} + +/** + * gts_bb_tree_triangle_distance: + * @tree: a bounding box tree. + * @t: a #GtsTriangle. + * @distance: a #GtsBBoxDistFunc. + * @delta: spatial scale of the sampling to be used. + * @range: a #GtsRange to be filled with the results. + * + * Given a triangle @t, points are sampled regularly on its surface + * using @delta as increment. The distance from each of these points + * to the closest object of @tree is computed using @distance and the + * gts_bb_tree_point_distance() function. The fields of @range are + * filled with the number of points sampled, the minimum, average and + * maximum value and the standard deviation. + */ +void gts_bb_tree_triangle_distance (GNode * tree, + GtsTriangle * t, + GtsBBoxDistFunc distance, + gdouble delta, + GtsRange * range) +{ + GtsPoint * p1, * p2, * p3, * p; + GtsVector p1p2, p1p3; + gdouble l1, t1, dt1; + guint i, n1; + + g_return_if_fail (tree != NULL); + g_return_if_fail (t != NULL); + g_return_if_fail (distance != NULL); + g_return_if_fail (delta > 0.); + g_return_if_fail (range != NULL); + + gts_triangle_vertices (t, + (GtsVertex **) &p1, + (GtsVertex **) &p2, + (GtsVertex **) &p3); + + gts_vector_init (p1p2, p1, p2); + gts_vector_init (p1p3, p1, p3); + gts_range_init (range); + p = GTS_POINT (gts_object_new (GTS_OBJECT_CLASS (gts_point_class ()))); + + l1 = sqrt (gts_vector_scalar (p1p2, p1p2)); + n1 = l1/delta + 1; + dt1 = 1.0/(gdouble) n1; + t1 = 0.0; + for (i = 0; i <= n1; i++, t1 += dt1) { + gdouble t2 = 1. - t1; + gdouble x = t2*p1p3[0]; + gdouble y = t2*p1p3[1]; + gdouble z = t2*p1p3[2]; + gdouble l2 = sqrt (x*x + y*y + z*z); + guint j, n2 = (guint) (l2/delta + 1); + gdouble dt2 = t2/(gdouble) n2; + + x = t2*p1->x + t1*p2->x; + y = t2*p1->y + t1*p2->y; + z = t2*p1->z + t1*p2->z; + + t2 = 0.0; + for (j = 0; j <= n2; j++, t2 += dt2) { + p->x = x + t2*p1p3[0]; + p->y = y + t2*p1p3[1]; + p->z = z + t2*p1p3[2]; + + gts_range_add_value (range, + gts_bb_tree_point_distance (tree, p, distance, NULL)); + } + } + + gts_object_destroy (GTS_OBJECT (p)); + gts_range_update (range); +} + +/** + * gts_bb_tree_segment_distance: + * @tree: a bounding box tree. + * @s: a #GtsSegment. + * @distance: a #GtsBBoxDistFunc. + * @delta: spatial scale of the sampling to be used. + * @range: a #GtsRange to be filled with the results. + * + * Given a segment @s, points are sampled regularly on its length + * using @delta as increment. The distance from each of these points + * to the closest object of @tree is computed using @distance and the + * gts_bb_tree_point_distance() function. The fields of @range are + * filled with the number of points sampled, the minimum, average and + * maximum value and the standard deviation. + */ +void gts_bb_tree_segment_distance (GNode * tree, + GtsSegment * s, + gdouble (*distance) (GtsPoint *, + gpointer), + gdouble delta, + GtsRange * range) +{ + GtsPoint * p1, * p2, * p; + GtsVector p1p2; + gdouble l, t, dt; + guint i, n; + + g_return_if_fail (tree != NULL); + g_return_if_fail (s != NULL); + g_return_if_fail (distance != NULL); + g_return_if_fail (delta > 0.); + g_return_if_fail (range != NULL); + + p1 = GTS_POINT (s->v1); + p2 = GTS_POINT (s->v2); + + gts_vector_init (p1p2, p1, p2); + gts_range_init (range); + p = GTS_POINT (gts_object_new (GTS_OBJECT_CLASS (gts_point_class()))); + + l = sqrt (gts_vector_scalar (p1p2, p1p2)); + n = (guint) (l/delta + 1); + dt = 1.0/(gdouble) n; + t = 0.0; + for (i = 0; i <= n; i++, t += dt) { + p->x = p1->x + t*p1p2[0]; + p->y = p1->y + t*p1p2[1]; + p->z = p1->z + t*p1p2[2]; + + gts_range_add_value (range, + gts_bb_tree_point_distance (tree, p, distance, NULL)); + } + + gts_object_destroy (GTS_OBJECT (p)); + gts_range_update (range); +} + +static void surface_distance_foreach_triangle (GtsTriangle * t, + gpointer * data) +{ + gdouble * delta = data[1]; + GtsRange * range = data[2]; + gdouble * total_area = data[3], area; + GtsRange range_triangle; + + gts_bb_tree_triangle_distance (data[0], t, data[4], *delta, &range_triangle); + + if (range_triangle.min < range->min) + range->min = range_triangle.min; + if (range_triangle.max > range->max) + range->max = range_triangle.max; + range->n += range_triangle.n; + + area = gts_triangle_area (t); + *total_area += area; + range->sum += area*range_triangle.mean; + range->sum2 += area*range_triangle.mean*range_triangle.mean; +} + +/** + * gts_bb_tree_surface_distance: + * @tree: a bounding box tree. + * @s: a #GtsSurface. + * @distance: a #GtsBBoxDistFunc. + * @delta: a sampling increment defined as the percentage of the diagonal + * of the root bounding box of @tree. + * @range: a #GtsRange to be filled with the results. + * + * Calls gts_bb_tree_triangle_distance() for each face of @s. The + * fields of @range are filled with the minimum, maximum and average + * distance. The average distance is defined as the sum of the average + * distances for each triangle weighthed by their area and divided by + * the total area of the surface. The standard deviation is defined + * accordingly. The @n field of @range is filled with the number of + * sampled points used. + */ +void gts_bb_tree_surface_distance (GNode * tree, + GtsSurface * s, + GtsBBoxDistFunc distance, + gdouble delta, + GtsRange * range) +{ + gpointer data[5]; + gdouble total_area = 0.; + + g_return_if_fail (tree != NULL); + g_return_if_fail (s != NULL); + g_return_if_fail (delta > 0. && delta < 1.); + g_return_if_fail (range != NULL); + + gts_range_init (range); + delta *= sqrt (gts_bbox_diagonal2 (tree->data)); + data[0] = tree; + data[1] = δ + data[2] = range; + data[3] = &total_area; + data[4] = distance; + + gts_surface_foreach_face (s, + (GtsFunc) surface_distance_foreach_triangle, + data); + + if (total_area > 0.) { + if (range->sum2 - range->sum*range->sum/total_area >= 0.) + range->stddev = sqrt ((range->sum2 - range->sum*range->sum/total_area) + /total_area); + else + range->stddev = 0.; + range->mean = range->sum/total_area; + } + else + range->min = range->max = range->mean = range->stddev = 0.; +} + +static void surface_distance_foreach_boundary (GtsEdge * e, + gpointer * data) +{ + gdouble * delta = data[1]; + GtsRange * range = data[2]; + gdouble * total_length = data[3], length; + GtsRange range_edge; + + if (gts_edge_is_boundary (e, NULL)) { + GtsSegment * s = GTS_SEGMENT (e); + + gts_bb_tree_segment_distance (data[0], s, data[4], *delta, &range_edge); + + if (range_edge.min < range->min) + range->min = range_edge.min; + if (range_edge.max > range->max) + range->max = range_edge.max; + range->n += range_edge.n; + + length = gts_point_distance (GTS_POINT (s->v1), GTS_POINT (s->v2)); + *total_length += length; + range->sum += length*range_edge.mean; + range->sum2 += length*range_edge.mean*range_edge.mean; + } +} + +/** + * gts_bb_tree_surface_boundary_distance: + * @tree: a bounding box tree. + * @s: a #GtsSurface. + * @distance: a #GtsBBoxDistFunc. + * @delta: a sampling increment defined as the percentage of the diagonal + * of the root bounding box of @tree. + * @range: a #GtsRange to be filled with the results. + * + * Calls gts_bb_tree_segment_distance() for each edge boundary of @s. + * The fields of @range are filled with the minimum, maximum and + * average distance. The average distance is defined as the sum of the + * average distances for each boundary edge weighthed by their length + * and divided by the total length of the boundaries. The standard + * deviation is defined accordingly. The @n field of @range is filled + * with the number of sampled points used. + */ +void gts_bb_tree_surface_boundary_distance (GNode * tree, + GtsSurface * s, + gdouble (*distance) (GtsPoint *, + gpointer), + gdouble delta, + GtsRange * range) +{ + gpointer data[5]; + gdouble total_length = 0.; + + g_return_if_fail (tree != NULL); + g_return_if_fail (s != NULL); + g_return_if_fail (delta > 0. && delta < 1.); + g_return_if_fail (range != NULL); + + gts_range_init (range); + delta *= sqrt (gts_bbox_diagonal2 (tree->data)); + data[0] = tree; + data[1] = δ + data[2] = range; + data[3] = &total_length; + data[4] = distance; + + gts_surface_foreach_edge (s, + (GtsFunc) surface_distance_foreach_boundary, + data); + + if (total_length > 0.) { + if (range->sum2 - range->sum*range->sum/total_length >= 0.) + range->stddev = sqrt ((range->sum2 - + range->sum*range->sum/total_length) + /total_length); + else + range->stddev = 0.; + range->mean = range->sum/total_length; + } + else + range->min = range->max = range->mean = range->stddev = 0.; +} Index: toporouter/src_3rd/gts/boolean.c =================================================================== --- toporouter/src_3rd/gts/boolean.c (nonexistent) +++ toporouter/src_3rd/gts/boolean.c (revision 6803) @@ -0,0 +1,2048 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999--2002 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "gts.h" + +/*#define DEBUG*/ +/*#define DEBUG_BOOLEAN*/ +/*#define CHECK_ORIENTED*/ + +#ifdef DEBUG +# include "gts-private.h" +#endif /* DEBUG */ + +static void surface_inter_destroy (GtsObject * object) +{ + GtsSurfaceInter * si = GTS_SURFACE_INTER (object); + + gts_object_destroy (GTS_OBJECT (si->s1)); + gts_object_destroy (GTS_OBJECT (si->s2)); + g_slist_free (si->edges); + + (* GTS_OBJECT_CLASS (gts_surface_inter_class ())->parent_class->destroy) + (object); +} + +static void surface_inter_class_init (GtsObjectClass * klass) +{ + klass->destroy = surface_inter_destroy; +} + +static void surface_inter_init (GtsSurfaceInter * si) +{ + si->s1 = si->s2 = NULL; + si->edges = NULL; +} + +/** + * gts_surface_inter_class: + * + * Returns: the #GtsSurfaceInterClass. + */ +GtsSurfaceInterClass * gts_surface_inter_class (void) +{ + static GtsSurfaceInterClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo surface_inter_info = { + "GtsSurfaceInter", + sizeof (GtsSurfaceInter), + sizeof (GtsSurfaceInterClass), + (GtsObjectClassInitFunc) surface_inter_class_init, + (GtsObjectInitFunc) surface_inter_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (gts_object_class (), &surface_inter_info); + } + + return klass; +} + +/* EdgeInter: Header */ + +typedef struct _EdgeInter EdgeInter; + +struct _EdgeInter { + GtsEdge parent; + + GtsTriangle * t1, * t2; +}; + +#define EDGE_INTER(obj) GTS_OBJECT_CAST (obj,\ + EdgeInter,\ + edge_inter_class ()) +#define IS_EDGE_INTER(obj) (gts_object_is_from_class (obj,\ + edge_inter_class ())) + +static GtsEdgeClass * edge_inter_class (void); +static EdgeInter * edge_inter_new (GtsVertex * v1, GtsVertex * v2, + GtsTriangle * t1, GtsTriangle * t2); + +/* EdgeInter: Object */ + +static GtsEdgeClass * edge_inter_class (void) +{ + static GtsEdgeClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo edge_inter_info = { + "EdgeInter", + sizeof (EdgeInter), + sizeof (GtsEdgeClass), + (GtsObjectClassInitFunc) NULL, + (GtsObjectInitFunc) NULL, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_constraint_class ()), + &edge_inter_info); + } + + return klass; +} + +static EdgeInter * edge_inter_new (GtsVertex * v1, GtsVertex * v2, + GtsTriangle * t1, GtsTriangle * t2) +{ + EdgeInter * object; + + object = EDGE_INTER (gts_edge_new (GTS_EDGE_CLASS (edge_inter_class ()), + v1, v2)); + object->t1 = t1; + object->t2 = t2; + + return object; +} + +#ifdef DEBUG +static void write_surface_graph (GtsSurface * s, FILE * fp) +{ + GSList * l = NULL; + GtsGraph * g; + static void add_to_list (gpointer data, GSList ** l) { + *l = g_slist_prepend (*l, data); + } + + gts_surface_foreach_vertex (s, (GtsFunc) gts_object_reset_reserved, NULL); + gts_surface_foreach_edge (s, (GtsFunc) gts_object_reset_reserved, NULL); + gts_surface_foreach_edge (s, (GtsFunc) add_to_list, &l); + g = gts_segments_graph_new (gts_graph_class (), l); + gts_graph_write_dot (g, fp); + gts_object_destroy (GTS_OBJECT (g)); + g_slist_free (l); +} +#endif /* DEBUG */ + +static GtsPoint * segment_triangle_intersection (GtsSegment * s, + GtsTriangle * t, + GtsPointClass * klass) +{ + GtsPoint * A, * B, * C, * D, * E; + gint ABCE, ABCD, ADCE, ABDE, BCDE; + GtsEdge * AB, * BC, * CA; + gdouble a, b, c; + + g_return_val_if_fail (s != NULL, NULL); + g_return_val_if_fail (t != NULL, NULL); + g_return_val_if_fail (klass != NULL, NULL); + + gts_triangle_vertices_edges (t, NULL, + (GtsVertex **) &A, + (GtsVertex **) &B, + (GtsVertex **) &C, + &AB, &BC, &CA); + D = GTS_POINT (s->v1); + E = GTS_POINT (s->v2); + + ABCE = gts_point_orientation_3d_sos (A, B, C, E); + ABCD = gts_point_orientation_3d_sos (A, B, C, D); + if (ABCE < 0 || ABCD > 0) { + GtsPoint * tmpp; + gint tmp; + + tmpp = E; E = D; D = tmpp; + tmp = ABCE; ABCE = ABCD; ABCD = tmp; + } + if (ABCE < 0 || ABCD > 0) + return NULL; + ADCE = gts_point_orientation_3d_sos (A, D, C, E); + if (ADCE < 0) + return NULL; + ABDE = gts_point_orientation_3d_sos (A, B, D, E); + if (ABDE < 0) + return NULL; + BCDE = gts_point_orientation_3d_sos (B, C, D, E); + if (BCDE < 0) + return NULL; + a = gts_point_orientation_3d (A, B, C, E); + b = gts_point_orientation_3d (A, B, C, D); + if (a != b) { + c = a/(a - b); + return gts_point_new (klass, + E->x + c*(D->x - E->x), + E->y + c*(D->y - E->y), + E->z + c*(D->z - E->z)); + } + /* D and E are contained within ABC */ +#ifdef DEBUG + fprintf (stderr, + "segment: %p:%s triangle: %p:%s intersection\n" + "D and E contained in ABC\n", + s, GTS_NEDGE (s)->name, t, GTS_NFACE (t)->name); +#endif /* DEBUG */ + g_assert (a == 0.); + return gts_point_new (klass, + (E->x + D->x)/2., + (E->y + D->y)/2., + (E->z + D->z)/2.); +} + +static gint triangle_triangle_orientation (GtsPoint * p1, + GtsPoint * p2, GtsPoint * p3, + GtsPoint * p4, GtsPoint * p5, + GtsPoint * p6) +{ + gint o4 = 0, o5 = 0, o6 = 0; + + if (p4 != p1 && p4 != p2 && p4 != p3) + o4 = gts_point_orientation_3d_sos (p1, p2, p3, p4); + if (p5 != p1 && p5 != p2 && p5 != p3) + o5 = gts_point_orientation_3d_sos (p1, p2, p3, p5); + if (o4*o5 < 0) + return 0; + if (p6 != p1 && p6 != p2 && p6 != p3) + o6 = gts_point_orientation_3d_sos (p1, p2, p3, p6); + if (o4*o6 < 0 || o5*o6 < 0) + return 0; + if (o4) return o4; + if (o5) return o5; + g_assert (o6); + return o6; +} + +static gint triangle_point_orientation (GtsTriangle * t1, + GtsTriangle * t2, + gint o1, + GtsPoint * p) +{ + GtsPoint * p1 = GTS_POINT (GTS_SEGMENT (t1->e1)->v1); + GtsPoint * p2 = GTS_POINT (GTS_SEGMENT (t1->e1)->v2); + GtsPoint * p3 = GTS_POINT (gts_triangle_vertex (t1)); + GtsPoint * p4 = GTS_POINT (GTS_SEGMENT (t2->e1)->v1); + GtsPoint * p5 = GTS_POINT (GTS_SEGMENT (t2->e1)->v2); + GtsPoint * p6 = GTS_POINT (gts_triangle_vertex (t2)); + gint o = triangle_triangle_orientation (p1, p2, p3, p4, p5, p6); + + if (o != 0) + return o; + o = triangle_triangle_orientation (p4, p5, p6, p1, p2, p3); + if (o != 0) { + gint o2 = gts_point_orientation_3d_sos (p4, p5, p6, p); + + return - o*o1*o2; + } + return 0; +} + +static void add_edge_inter (GtsEdge * e, + GtsTriangle * t, + GtsVertex * v) +{ + GtsVertex * ev1 = GTS_SEGMENT (e)->v1, * ev2 = GTS_SEGMENT (e)->v2; + GList * i = GTS_OBJECT (e)->reserved; + + GTS_OBJECT (v)->reserved = t; + if (i == NULL) { + GTS_OBJECT (e)->reserved = g_list_prepend (NULL, v); +#ifdef DEBUG + fprintf (stderr, "add_edge_inter: inserting %p (%p,%p)\n", v, e, t); +#endif /* DEBUG */ + } + else { + GtsPoint * p1 = GTS_POINT (GTS_SEGMENT (t->e1)->v1); + GtsPoint * p2 = GTS_POINT (GTS_SEGMENT (t->e1)->v2); + GtsPoint * p3 = GTS_POINT (gts_triangle_vertex (t)); + gint o1, oref = gts_point_orientation_3d_sos (p1, p2, p3, GTS_POINT (ev1)); + + o1 = oref; + while (i) { + gint o2 = triangle_point_orientation (t, GTS_OBJECT (i->data)->reserved, + oref, GTS_POINT (ev1)); + + if (o2 == 0) { +#ifdef DEBUG + g_warning ("add_edge_inter: safe sign evaluation failed\n"); +#endif /* DEBUG */ + o2 = gts_point_orientation_3d_sos (p1, p2, p3, i->data); + } + + if (o1*o2 < 0) + break; + ev1 = i->data; + o1 = o2; + i = i->next; + } + if (i != NULL) { + GList * n = g_list_prepend (NULL, v); + + ev2 = i->data; + n->next = i; + n->prev = i->prev; + i->prev = n; + if (n->prev == NULL) + GTS_OBJECT (e)->reserved = n; + else + n->prev->next = n; + } + else { + g_assert (o1*gts_point_orientation_3d_sos (p1, p2, p3, GTS_POINT (ev2)) + < 0); + GTS_OBJECT (e)->reserved = g_list_append (GTS_OBJECT (e)->reserved, v); + } +#ifdef DEBUG + fprintf (stderr, + "add_edge_inter: inserting %p (%p,%p) between %p and %p\n", + v, e, t, ev1, ev2); + i = GTS_OBJECT (e)->reserved; + while (i) { + fprintf (stderr, " %p", i->data); + i = i->next; + } + fprintf (stderr, "\n"); +#endif /* DEBUG */ + } +} + +static GtsVertex * intersects (GtsEdge * e, + GtsTriangle * t, + GtsSurface * s) +{ + GList * i = GTS_OBJECT (e)->reserved; + GtsVertex * v; + + while (i) { + if (GTS_OBJECT (i->data)->reserved == t) + return i->data; + i = i->next; + } + + v = GTS_VERTEX (segment_triangle_intersection (GTS_SEGMENT (e), t, + GTS_POINT_CLASS (s->vertex_class))); + if (v != NULL) { +#ifdef DEBUG + if (GTS_IS_NVERTEX (v) && GTS_IS_NEDGE (e) && GTS_IS_NFACE (t) && + GTS_NVERTEX (v)->name[0] == '\0') + g_snprintf (GTS_NVERTEX (v)->name, GTS_NAME_LENGTH, "%s|%s", + GTS_NEDGE (e)->name, GTS_NFACE (t)->name); +#endif /* DEBUG */ + if (s->vertex_class->intersection_attributes) + (*s->vertex_class->intersection_attributes) + (v, GTS_OBJECT (e), GTS_OBJECT (t)); + add_edge_inter (e, t, v); + } + return v; +} + +/* see figure misc/orientation.fig */ +static gint intersection_orientation (GtsTriangle * t1, + GtsEdge * e, + GtsTriangle * t2) +{ + GtsVertex * v1, * v2, * v3; + GtsEdge * e2, * e3; + GtsVertex * v4, * v5, * v6; + + gts_triangle_vertices_edges (t1, e, &v1, &v2, &v3, &e, &e2, &e3); + gts_triangle_vertices (t2, &v4, &v5, &v6); + + return gts_point_orientation_3d_sos (GTS_POINT (v4), + GTS_POINT (v5), + GTS_POINT (v6), + GTS_POINT (v2)); +} + +#define UPDATE_ORIENTATION if (o > 0) { vi2 = v; /* e2 = e; */ } else { vi2 = vi1;\ + /* e2 = e1; */\ + vi1 = v;\ + /* e1 = e; */ } + +static void intersect_edges (GtsBBox * bb1, GtsBBox * bb2, + GtsSurfaceInter * si) +{ + GtsSurface * s1 = GTS_OBJECT (si->s1)->reserved; + GtsTriangle * t1 = GTS_TRIANGLE (bb1->bounded); + GtsTriangle * t2 = GTS_TRIANGLE (bb2->bounded); + GtsVertex * v, * vi1 = NULL, * vi2 = NULL; + //GtsEdge * e1 = NULL, * e2 = NULL, * e; + + vi1 = intersects (t2->e1, t1, s1); + //e1 = t2->e1; + v = intersects (t2->e2, t1, s1); + //e = t2->e2; + if (!vi1) { + vi1 = v; + //e1 = e; + } + else if (v) { + gint o = intersection_orientation (t2, t2->e2, t1); + UPDATE_ORIENTATION; + } + if (!vi2) { + v = intersects (t2->e3, t1, s1); + //e = t2->e3; + if (!vi1) { + vi1 = v; + //e1 = e; + } + else if (v) { + gint o = intersection_orientation (t2, t2->e3, t1); + UPDATE_ORIENTATION; + } + } + if (!vi2) { + v = intersects (t1->e1, t2, s1); + //e = t1->e1; + if (!vi1) { + vi1 = v; + //e1 = e; + } + else if (v) { + gint o = - intersection_orientation (t1, t1->e1, t2); + UPDATE_ORIENTATION; + } + } + if (!vi2) { + v = intersects (t1->e2, t2, s1); + //e = t1->e2; + if (!vi1) { + vi1 = v; + //e1 = e; + } + else if (v) { + gint o = - intersection_orientation (t1, t1->e2, t2); + UPDATE_ORIENTATION; + } + } + if (!vi2) { + v = intersects (t1->e3, t2, s1); + //e = t1->e3; + if (!vi1) { + vi1 = v; + //e1 = e; + } + else if (v) { + gint o = - intersection_orientation (t1, t1->e3, t2); + UPDATE_ORIENTATION; + } + } + + g_assert ((!vi1 && !vi2) || (vi1 && vi2)); + if (vi1) { + GtsEdge * e = GTS_EDGE (edge_inter_new (vi1, vi2, t1, t2)); + +#ifdef DEBUG + fprintf (stderr, "creating constraint %p: %p->%p: %p/%p\n", + e, vi1, vi2, t1, t2); +#endif /* DEBUG */ + gts_surface_add_face (si->s1, GTS_FACE (t1)); + gts_surface_add_face (si->s2, GTS_FACE (t2)); + si->edges = g_slist_prepend (si->edges, e); + GTS_OBJECT (t1)->reserved = g_slist_prepend (GTS_OBJECT (t1)->reserved, e); + GTS_OBJECT (t2)->reserved = g_slist_prepend (GTS_OBJECT (t2)->reserved, e); + } +} + +static GtsSurfaceInter * surface_inter_new (GtsSurfaceInterClass * klass, + GtsSurface * s1, + GtsSurface * s2, + GNode * faces_tree1, + GNode * faces_tree2) +{ + GtsSurfaceInter * si; + + si = GTS_SURFACE_INTER (gts_object_new (GTS_OBJECT_CLASS (klass))); + si->s1 = gts_surface_new (gts_surface_class (), + s1->face_class, + s1->edge_class, + s1->vertex_class); + GTS_OBJECT (si->s1)->reserved = s1; + si->s2 = gts_surface_new (gts_surface_class (), + s2->face_class, + s2->edge_class, + s2->vertex_class); + GTS_OBJECT (si->s2)->reserved = s2; + gts_bb_tree_traverse_overlapping (faces_tree1, faces_tree2, + (GtsBBTreeTraverseFunc) intersect_edges, + si); + + return si; +} + +static void free_slist (GtsObject * o) +{ + g_slist_free (o->reserved); + o->reserved = NULL; +} + +static void free_glist (GtsObject * o) +{ + g_list_foreach (o->reserved, (GFunc) gts_object_reset_reserved, NULL); + g_list_free (o->reserved); + o->reserved = NULL; +} + +/** + * gts_surface_intersection: + * @s1: a #GtsSurface. + * @s2: a #GtsSurface. + * @faces_tree1: a bounding box tree (see gts_bb_tree_new()) for + * the faces of @s1. + * @faces_tree2: a bounding box tree for the faces of @s2. + * + * Returns: a list of #GtsEdge defining the curve intersection of the + * two surfaces. + */ +GSList * gts_surface_intersection (GtsSurface * s1, + GtsSurface * s2, + GNode * faces_tree1, + GNode * faces_tree2) +{ + GtsSurfaceInter * si; + GSList * inter; + + g_return_val_if_fail (s1 != NULL, NULL); + g_return_val_if_fail (s2 != NULL, NULL); + g_return_val_if_fail (faces_tree1 != NULL, NULL); + g_return_val_if_fail (faces_tree2 != NULL, NULL); + + si = surface_inter_new (gts_surface_inter_class (), + s1, s2, faces_tree1, faces_tree2); + + gts_surface_foreach_face (si->s1, (GtsFunc) free_slist, NULL); + gts_surface_foreach_face (si->s2, (GtsFunc) free_slist, NULL); + gts_surface_foreach_edge (si->s1, (GtsFunc) free_glist, NULL); + gts_surface_foreach_edge (si->s2, (GtsFunc) free_glist, NULL); + inter = si->edges; + si->edges = NULL; + gts_object_destroy (GTS_OBJECT (si)); + + return inter; +} + +typedef enum { + INTERIOR = 1 << (GTS_USER_FLAG), + RELEVANT = 1 << (GTS_USER_FLAG + 1) +} CurveFlag; + +#define IS_SET(s, f) ((GTS_OBJECT_FLAGS (s) & (f)) != 0) +#define SET(s, f) (GTS_OBJECT_FLAGS (s) |= (f)) +#define UNSET(s, f) (GTS_OBJECT_FLAGS (s) &= ~(f)) +#define NEXT(s) (GTS_OBJECT (s)->reserved) + +#ifdef DEBUG +static void print_segment (GtsSegment * s) +{ + fprintf (stderr, "%p: %s->%s ", s, + GTS_NVERTEX (s->v1)->name, + GTS_NVERTEX (s->v2)->name); + if (NEXT (s)) { + GtsSegment * next = NEXT (s); + + fprintf (stderr, "next %p: %s->%s\n", next, + GTS_NVERTEX (next->v1)->name, + GTS_NVERTEX (next->v2)->name); + } + else + fprintf (stderr, "next NULL\n"); +} + +static void write_nodes (GSList * i, GHashTable * hash, guint * nn, + FILE * fp) +{ + while (i) { + GtsSegment * s = i->data; + + if (!g_hash_table_lookup (hash, s->v1)) { + fprintf (fp, " %u [ label = \"%p\" ];\n", *nn, s->v1); + g_hash_table_insert (hash, s->v1, GUINT_TO_POINTER ((*nn)++)); + } + if (!g_hash_table_lookup (hash, s->v2)) { + fprintf (fp, " %u [ label = \"%p\" ];\n", *nn, s->v2); + g_hash_table_insert (hash, s->v2, GUINT_TO_POINTER ((*nn)++)); + } + i = i->next; + } +} + +static void write_edges (GSList * i, GHashTable * hash, + GtsSurface * surface, + FILE * fp) +{ + while (i) { + GtsSegment * s = i->data; + + fprintf (fp, " %u -> %u [ label = \"%p:%d\" ];\n", + GPOINTER_TO_UINT (g_hash_table_lookup (hash, s->v1)), + GPOINTER_TO_UINT (g_hash_table_lookup (hash, s->v2)), + s, + gts_edge_face_number (GTS_EDGE (s), surface)); + i = i->next; + } +} + +static void write_graph (GSList * boundary, GSList * interior, + GtsSurface * surface, + FILE * fp) +{ + GHashTable * hash = g_hash_table_new (NULL, NULL); + guint nn = 1; + + fprintf (fp, "digraph oriented_curve {\n"); + write_nodes (boundary, hash, &nn, fp); + write_nodes (interior, hash, &nn, fp); + write_edges (boundary, hash, surface, fp); + fprintf (fp, " edge [ color = red ];\n"); + write_edges (interior, hash, surface, fp); + fprintf (fp, "}\n"); + g_hash_table_destroy (hash); +} + +static void write_graph1 (GtsSegment * start, GSList * i, + GtsSurface * surface, + FILE * fp) +{ + GSList * boundary = NULL, * interior = NULL; + GtsSegment * s = start; + + do { + boundary = g_slist_prepend (boundary, s); + s = NEXT (s); + } while (s != start); + while (i) { + if (IS_SET (i->data, INTERIOR)) + interior = g_slist_prepend (interior, i->data); + i = i->next; + } + write_graph (boundary, interior, surface, fp); + g_slist_free (boundary); + g_slist_free (interior); +} + +static void print_loop (GtsSegment * start, FILE * fp) +{ + GtsSegment * s = start; + + do { + fprintf (fp, " %p: %p:%s -> %p:%s\n", + s, + s->v1, GTS_NVERTEX (s->v1)->name, + s->v2, GTS_NVERTEX (s->v2)->name); + s = NEXT (s); + } while (s != start && s != NULL); +} + +static void draw_vector (GtsPoint * p1, GtsPoint * p2, FILE * fp) +{ + gdouble x = p2->x - p1->x; + gdouble y = p2->y - p1->y; + gdouble z = p2->z - p1->z; + + fprintf (fp, "VECT 1 3 0 3 0 %g %g %g %g %g %g %g %g %g\n", + p1->x + x - (x - y/2.)/5., + p1->y + y - (x/2. + y)/5., + p1->z + z - (x/2. + z)/5., + p1->x + x, + p1->y + y, + p1->z + z, + p1->x + x - (x + y/2.)/5., + p1->y + y + (x/2. - y)/5., + p1->z + z + (x/2. - z)/5.); + fprintf (fp, "VECT 1 2 0 2 0 %g %g %g %g %g %g\n", + p1->x, p1->y, p1->z, + p1->x + x, + p1->y + y, + p1->z + z); +} + +static void draw_vector1 (GtsPoint * p1, GtsPoint * p2, GtsPoint * o, + FILE * fp) +{ + gdouble x1 = o->x + 0.9*(p1->x - o->x); + gdouble y1 = o->y + 0.9*(p1->y - o->y); + gdouble z1 = o->z + 0.9*(p1->z - o->z); + gdouble x2 = o->x + 0.9*(p2->x - o->x); + gdouble y2 = o->y + 0.9*(p2->y - o->y); + gdouble z2 = o->z + 0.9*(p2->z - o->z); + gdouble x = x2 - x1; + gdouble y = y2 - y1; + gdouble z = z2 - z1; + + fprintf (fp, "VECT 1 3 0 3 0 %g %g %g %g %g %g %g %g %g\n", + x1 + x - (x - y/2.)/5., + y1 + y - (x/2. + y)/5., + z1 + z - (x/2. + z)/5., + x1 + x, + y1 + y, + z1 + z, + x1 + x - (x + y/2.)/5., + y1 + y + (x/2. - y)/5., + z1 + z + (x/2. - z)/5.); + fprintf (fp, "VECT 1 2 0 2 0 %g %g %g %g %g %g\n", + x1, y1, z1, + x1 + x, + y1 + y, + z1 + z); +} + +static void write_segments (GSList * boundary, GSList * interior, + FILE * fp) +{ + GSList * i = boundary; + + fprintf (fp, "LIST {\n"); + while (i) { + GSList * inext = i->next; + GtsSegment * s = i->data; + GtsSegment * next = inext ? inext->data : boundary->data; + GtsVertex * v1, * v2; + + if (s->v1 != next->v1 && s->v1 != next->v2) { + v1 = s->v1; + v2 = s->v2; + } + else { + v1 = s->v2; + v2 = s->v1; + } + draw_vector (GTS_POINT (v1), GTS_POINT (v2), fp); + i = inext; + } + i = interior; + while (i) { + GtsSegment * s = i->data; + + draw_vector (GTS_POINT (s->v1), GTS_POINT (s->v2), fp); + i = i->next; + } + fprintf (fp, "}\n"); +} + +static void write_loops (GSList * i, FILE * fp) +{ + guint nl = 0; + + while (i) { + GtsSegment * start = i->data, * s; + GtsPoint os; + guint n = 0; + + fprintf (fp, "(geometry \"loop%d\" = LIST {\n", nl++); + + os.x = os.y = os.z = 0.; + s = start; + do { + GtsSegment * next = NEXT (s); + GtsPoint * p; + + if (s->v1 != next->v1 && s->v1 != next->v2) + p = GTS_POINT (s->v1); + else + p = GTS_POINT (s->v2); + os.x += p->x; os.y += p->y; os.z += p->z; n++; + s = next; + } while (s != start); + os.x /= n; os.y /= n; os.z /= n; + + s = start; + do { + GtsSegment * next = NEXT (s); + + if (s->v1 != next->v1 && s->v1 != next->v2) + draw_vector1 (GTS_POINT (s->v1), GTS_POINT (s->v2), &os, fp); + else + draw_vector1 (GTS_POINT (s->v2), GTS_POINT (s->v1), &os, fp); + s = next; + } while (s != start); + + fprintf (fp, "})\n"); + + i = i->next; + } +} + +#define NAME(v) (GTS_IS_NVERTEX (v) ? GTS_NVERTEX (v)->name : "") +#endif /* DEBUG */ + +static GtsSegment * prev_flag (GtsSegment * s, CurveFlag flag) +{ + GSList * i = s->v1->segments; + + while (i) { + if (i->data != s && IS_SET (i->data, flag)) + return i->data; + i = i->next; + } + return NULL; +} + +static GtsSegment * next_flag (GtsSegment * s, CurveFlag flag) +{ + GSList * i = s->v2->segments; + + while (i) { + if (i->data != s && IS_SET (i->data, flag)) + return i->data; + i = i->next; + } + return NULL; +} + +static GtsSegment * next_interior (GtsVertex * v) +{ + GSList * i = v->segments; + + while (i) { + GtsSegment * s = i->data; + + if (s->v1 == v && IS_SET (s, INTERIOR)) + return s; + i = i->next; + } + return NULL; +} + +static GtsSegment * prev_interior (GtsVertex * v) +{ + GSList * i = v->segments; + + while (i) { + GtsSegment * s = i->data; + + if (s->v2 == v && IS_SET (s, INTERIOR)) + return s; + i = i->next; + } + return NULL; +} + +static GtsSegment * reverse (GtsSegment * start, + gboolean interior, + gboolean * isloop) +{ + GtsSegment * s = start, * prev = NULL, * rprev = NULL; + GtsSegment * rstart = NULL, * rstart1 = NULL; + + do { + GtsSegment * rs; + + g_assert (IS_EDGE_INTER (s)); + rs = GTS_SEGMENT (edge_inter_new (s->v2, s->v1, + EDGE_INTER (s)->t1, EDGE_INTER (s)->t2)); + + if (rstart == NULL) + rstart = rs; + else if (rstart1 == NULL) + rstart1 = rs; + if (interior) + SET (rs, INTERIOR); + NEXT (rs) = rprev; + rprev = rs; + prev = s; + s = NEXT (s); + } while (s != NULL && s != start); + if (s == start) { + NEXT (rstart) = rprev; + *isloop = TRUE; + } + else { + NEXT (rstart) = start; + NEXT (prev) = rprev; + *isloop = FALSE; + } + return rstart1; +} + +static GSList * interior_loops (GSList * interior) +{ + GSList * i = interior; + GSList * loops = NULL; + + i = interior; + while (i) { + GtsSegment * s = i->data; + + if (IS_SET (s, RELEVANT)) { + GtsSegment * start = s, * end; + + do { + GtsSegment * next = next_flag (s, INTERIOR); + + UNSET (s, RELEVANT); + end = s; + s = NEXT (s) = next; + } while (s != NULL && s != start); + + if (s == start) + loops = g_slist_prepend (loops, start); + else { + GtsSegment * next, * prev; + gboolean isloop; + + s = prev_flag (start, INTERIOR); + while (s) { + UNSET (s, RELEVANT); + NEXT (s) = start; + start = s; + s = prev_flag (s, INTERIOR); + } + next = next_flag (end, RELEVANT); + prev = prev_flag (start, RELEVANT); + if (prev != NULL) + SET (start->v1, INTERIOR); + if (next != NULL) + SET (end->v2, INTERIOR); + if (next == NULL && prev == NULL) + loops = g_slist_prepend (loops, start); + else + reverse (start, TRUE, &isloop); + } + } + i = i->next; + } + return loops; +} + +#define ORIENTATION(p1,p2,p3,o) (gts_point_orientation_3d (p1, p2, o, p3)) +#define ORIENTATION_SOS(p1,p2,p3,o) (gts_point_orientation_3d_sos (p1, p2, o, p3)) + +#define ORIENTED_VERTICES(s,next,w1,w2) {\ + if ((s)->v1 == (next)->v1 || (s)->v1 == (next)->v2) {\ + w1 = (s)->v2;\ + w2 = (s)->v1;\ + }\ + else {\ + w1 = (s)->v1;\ + w2 = (s)->v2;\ + }\ +} + +#if 0 +static GtsSegment * segment_intersects (GtsPoint * p1, GtsPoint * p2, + GSList * i, + GtsPoint * o) +{ + while (i) { + GtsSegment * s = i->data; + GtsPoint * p3 = GTS_POINT (s->v1); + GtsPoint * p4 = GTS_POINT (s->v2); + + if (p3 != p1 && p3 != p2 && p4 != p1 && p4 != p2) { + gdouble o1 = ORIENTATION (p3, p4, p1, o); + gdouble o2 = ORIENTATION (p3, p4, p2, o); + + if ((o1 < 0. && o2 > 0.) || (o1 > 0. && o2 < 0.)) { + o1 = ORIENTATION (p1, p2, p3, o); + o2 = ORIENTATION (p1, p2, p4, o); + + if ((o1 <= 0. && o2 >= 0.) || (o1 >= 0. && o2 <= 0.)) + return s; + } + } + i = i->next; + } + return NULL; +} +#else +static GtsSegment * segment_intersects (GtsPoint * p1, GtsPoint * p2, + GSList * i, + GtsPoint * o) +{ + while (i) { + GtsSegment * s = i->data; + GtsPoint * p3 = GTS_POINT (s->v1); + GtsPoint * p4 = GTS_POINT (s->v2); + + if (p3 != p1 && p3 != p2 && p4 != p1 && p4 != p2) { + gint o1 = ORIENTATION_SOS (p3, p4, p1, o); + gint o2 = ORIENTATION_SOS (p3, p4, p2, o); + + if (o1*o2 < 0) { + o1 = ORIENTATION_SOS (p1, p2, p3, o); + o2 = ORIENTATION_SOS (p1, p2, p4, o); + + if (o1*o2 < 0) + return s; + } + } + i = i->next; + } + return NULL; +} +#endif + +static gboolean is_inside_wedge (GtsSegment * s1, GtsSegment * s2, + GtsPoint * p, GtsPoint * o) +{ + GtsVertex * v1, * v2, * v3; + + ORIENTED_VERTICES (s1, s2, v1, v2); + v3 = s2->v1 != v2 ? s2->v1 : s2->v2; + + if (ORIENTATION (GTS_POINT (v1), GTS_POINT (v2), + GTS_POINT (v3), o) >= 0.) { + if (ORIENTATION (GTS_POINT (v1), GTS_POINT (v2), p, o) <= 0. || + ORIENTATION (GTS_POINT (v2), GTS_POINT (v3), p, o) <= 0.) + return FALSE; + } + else if (ORIENTATION (GTS_POINT (v1), GTS_POINT (v2), p, o) <= 0. && + ORIENTATION (GTS_POINT (v2), GTS_POINT (v3), p, o) <= 0.) + return FALSE; + return TRUE; +} + +static GtsSegment * connection (GtsPoint * p, + GSList * interior, + GSList * bloops, + GtsPoint * o) +{ + while (bloops) { + GtsSegment * start = bloops->data, * s = start; + + do { + GtsSegment * next = NEXT (s); + GtsVertex * v2 = s->v1 == next->v1 || s->v1 == next->v2 ? s->v1 : s->v2; + + if (is_inside_wedge (s, next, p, o) && + !segment_intersects (p, GTS_POINT (v2), interior, o)) + return s; + s = next; + } while (s != start); + bloops = bloops->next; + } + return NULL; +} + +static gdouble loop_orientation (GtsSegment * start, + GtsPoint * p, GtsPoint * o) +{ + GtsSegment * s = start; + gdouble or = 0.; + + do { + GtsSegment * next = NEXT (s); + GtsVertex * v1, * v2; + + ORIENTED_VERTICES (s, next, v1, v2); + or += ORIENTATION (p, GTS_POINT (v1), GTS_POINT (v2), o); + s = next; + } while (s != start); + +#ifdef DEBUG + fprintf (stderr, "loop orientation: %g\n", or); +#endif /* DEBUG */ + + return or; +} + +static void connect_interior_loop (GtsSegment * start, + GSList ** interior, + GSList ** bloops, + GtsSurface * surface, + GtsPoint * o) +{ + GtsSegment * s = start, * c = NULL, * next, * s1, * rs1, * rs; + GtsVertex * v, * cv; + gboolean isloop; + + do { + if (!(c = connection (GTS_POINT (s->v2), *interior, *bloops, o))) + s = NEXT (s); + } while (s != start && !c); + g_assert (c); + next = NEXT (c); + v = c->v1 == next->v1 || c->v1 == next->v2 ? c->v1 : c->v2; + cv = s->v2; +#ifdef DEBUG + fprintf (stderr, "connecting %p:%s with %p:%s\n", + cv, NAME (cv), v, NAME (v)); + fprintf (stderr, " c: %p: %p:%s %p:%s\n", c, + c->v1, NAME (c->v1), + c->v2, NAME (c->v2)); + fprintf (stderr, " next: %p: %p:%s %p:%s\n", next, + next->v1, NAME (next->v1), + next->v2, NAME (next->v2)); +#endif /* DEBUG */ + rs = reverse (s, FALSE, &isloop); + if (isloop) { + if (loop_orientation (rs, GTS_POINT (v), o) < 0.) { + GtsSegment * tmp = s; + s = rs; + rs = tmp; + } + *bloops = g_slist_prepend (*bloops, rs); + } + s1 = GTS_SEGMENT (gts_edge_new (surface->edge_class, v, cv)); + rs1 = GTS_SEGMENT (gts_edge_new (surface->edge_class, cv, v)); + NEXT (c) = s1; + NEXT (rs1) = next; + *interior = g_slist_prepend (*interior, s1); + NEXT (s1) = NEXT (s); + NEXT (s) = rs1; +} + +static GSList * boundary_loops (GSList * boundary) +{ + GSList * i = boundary; + GtsSegment * start = i->data; + GSList * loops = NULL; + + while (i) { + GtsSegment * s = i->data; + GSList * inext = i->next; + GtsSegment * next = inext ? inext->data : start; + GtsVertex * v = s->v1 == next->v1 || s->v1 == next->v2 ? s->v1 : s->v2; + + if (IS_SET (v, INTERIOR)) { + GtsSegment * intprev = prev_interior (v); + + NEXT (intprev) = next; + NEXT (s) = next_interior (v); + UNSET (v, INTERIOR); + } + else + NEXT (s) = next; + i = inext; + } + + i = boundary; + while (i) { + start = i->data; + + if (IS_SET (start, RELEVANT)) { + GtsSegment * s = start; + + do { + UNSET (s, RELEVANT); + UNSET (s, INTERIOR); + s = NEXT (s); + } while (s != start); + loops = g_slist_prepend (loops, start); + } + i = i->next; + } + + return loops; +} + +typedef struct _Ear Ear; + +struct _Ear { + GtsVertex * v1, * v2, * v3; + GtsSegment * s1, * s2, * s3; +}; + +static gboolean point_in_wedge (GtsPoint * p1, GtsPoint * p2, GtsPoint * p3, + GtsPoint * p, gboolean closed, GtsPoint * o) +{ + gdouble o1; + + if (p == p2 || p == p3) + return FALSE; + o1 = ORIENTATION (p1, p2, p, o); + if ((closed && o1 < 0.) || (!closed && o1 <= 0.)) return FALSE; + o1 = ORIENTATION (p3, p1, p, o); + if ((closed && o1 < 0.) || (!closed && o1 <= 0.)) return FALSE; + return TRUE; +} + +#if 0 +static gboolean segment_intersects1 (GtsPoint * p1, GtsPoint * p2, + GtsPoint * p3, GtsPoint * p4, + gboolean closed, GtsPoint * o) +{ + gdouble o1 = ORIENTATION (p3, p4, p1, o); + gdouble o2 = ORIENTATION (p3, p4, p2, o); + gdouble o3, o4; + + if ((closed && ((o1 > 0. && o2 > 0.) || (o1 < 0. && o2 < 0.))) || + (!closed && ((o1 >= 0. && o2 >= 0.) || (o1 <= 0. && o2 <= 0.)))) + return FALSE; + o3 = ORIENTATION (p1, p2, p3, o); + o4 = ORIENTATION (p1, p2, p4, o); + if ((o3 > 0. && o4 > 0.) || (o3 < 0. && o4 < 0.)) + return FALSE; + if (closed) return TRUE; + if ((o3 == 0. && o4 > 0.) || (o4 == 0. && o3 > 0.)) + return TRUE; + return FALSE; +} +#else +static gboolean segment_intersects1 (GtsPoint * p1, GtsPoint * p2, + GtsPoint * p3, GtsPoint * p4, + gboolean closed, GtsPoint * o) +{ + gint o1, o2; + + o1 = ORIENTATION_SOS (p3, p4, p1, o); + o2 = ORIENTATION_SOS (p3, p4, p2, o); + if (o1*o2 > 0) + return FALSE; + o1 = ORIENTATION_SOS (p1, p2, p3, o); + o2 = ORIENTATION_SOS (p1, p2, p4, o); + if (o1*o2 > 0) + return FALSE; + return TRUE; +} +#endif + +static GtsSegment * triangle_intersects_segments (GtsPoint * p1, + GtsPoint * p2, + GtsPoint * p3, + gboolean closed, + GtsSegment * start, + GtsPoint * o) +{ + GtsSegment * s = start; + + do { + GtsPoint * p4 = GTS_POINT (s->v1); + GtsPoint * p5 = GTS_POINT (s->v2); + + if (p4 == p1) { + if (point_in_wedge (p1, p2, p3, p5, closed, o)) + return s; + } + else if (p4 == p2) { + if (point_in_wedge (p2, p3, p1, p5, closed, o)) + return s; + } + else if (p4 == p3) { + if (point_in_wedge (p3, p1, p2, p5, closed, o)) + return s; + } + else if (p5 == p1) { + if (point_in_wedge (p1, p2, p3, p4, closed, o)) + return s; + } + else if (p5 == p2) { + if (point_in_wedge (p2, p3, p1, p4, closed, o)) + return s; + } + else if (p5 == p3) { + if (point_in_wedge (p3, p1, p2, p4, closed, o)) + return s; + } + else if (segment_intersects1 (p1, p2, p4, p5, closed, o) || + segment_intersects1 (p2, p3, p4, p5, closed, o) || + segment_intersects1 (p3, p1, p4, p5, closed, o)) + return s; + s = NEXT (s); + } while (s != start); + return NULL; +} + +static gboolean new_ear (GtsSegment * s, + Ear * e, + GtsSegment * start, + guint sloppy, + GtsPoint * o) +{ + gdouble or; + + e->s1 = s; + e->s2 = NEXT (s); + + g_return_val_if_fail (e->s2, FALSE); + g_return_val_if_fail (e->s2 != e->s1, FALSE); + + ORIENTED_VERTICES (e->s1, e->s2, e->v1, e->v2); + e->v3 = e->s2->v1 != e->v2 ? e->s2->v1 : e->s2->v2; + if (e->v3 == e->v1) + return FALSE; + e->s3 = NEXT (e->s2); + if (gts_segment_connect (e->s3, e->v1, e->v3)) { + if (NEXT (e->s3) != e->s1) + return FALSE; + } + else if (gts_vertices_are_connected (e->v1, e->v3)) + return FALSE; + else + e->s3 = NULL; + or = ORIENTATION (GTS_POINT (e->v1), GTS_POINT (e->v2), GTS_POINT (e->v3),o); + switch (sloppy) { + case 0: + if (or <= 0. || + triangle_intersects_segments (GTS_POINT (e->v1), GTS_POINT (e->v2), + GTS_POINT (e->v3), TRUE, start, o)) + return FALSE; + break; + case 1: + if (or < 0. || + (or > 0. && + triangle_intersects_segments (GTS_POINT (e->v1), GTS_POINT (e->v2), + GTS_POINT (e->v3), FALSE, start, o))) + return FALSE; + break; + case 2: + if ((or > 0. && + triangle_intersects_segments (GTS_POINT (e->v1), GTS_POINT (e->v2), + GTS_POINT (e->v3), FALSE, start, o)) || + (or < 0. && + triangle_intersects_segments (GTS_POINT (e->v2), GTS_POINT (e->v1), + GTS_POINT (e->v3), FALSE, start, o))) + return FALSE; + break; + case 3: + if (or < 0.) + return FALSE; + break; + } +#ifdef DEBUG + if (or <= 0.) + fprintf (stderr, "or: %g\n", or); +#endif /* DEBUG */ + g_assert (or > -1e-6); + return TRUE; +} + +static void triangulate_loop (GtsSegment * start, + GtsSurface * surface, + GtsPoint * o) +{ + GtsSegment * prev = start, * s; + guint sloppy = 0; +#ifdef DEBUG + guint nt = 0; +#endif /* DEBUG */ + + s = NEXT (start); + while (NEXT (s) != s) { + GtsSegment * next = NEXT (s); + Ear e; + +#ifdef DEBUG + fprintf (stderr, "prev: %p s: %p next: %p\n", prev, s, next); +#endif /* DEBUG */ + + if (!new_ear (s, &e, start, sloppy, o)) { + if (s == start) { + sloppy++; +#ifdef DEBUG + fprintf (stderr, "sloppy: %u\n", sloppy); +#endif /* DEBUG */ + } + prev = s; + s = next; + } + else { + GtsFace * f; + + if (!GTS_IS_EDGE (e.s3)) + e.s3 = GTS_SEGMENT (gts_edge_new (surface->edge_class, e.v1, e.v3)); + f = gts_face_new (surface->face_class, + GTS_EDGE (e.s1), GTS_EDGE (e.s2), GTS_EDGE (e.s3)); + gts_surface_add_face (surface, f); + UNSET (e.s1, RELEVANT); + UNSET (e.s1, INTERIOR); + UNSET (e.s2, RELEVANT); + UNSET (e.s2, INTERIOR); + NEXT (prev) = e.s3; + NEXT (e.s3) = NEXT (e.s2); + NEXT (e.s1) = NEXT (e.s2) = NULL; + start = prev; + s = NEXT (prev); + sloppy = 0; +#ifdef DEBUG + { + gchar name[80]; + FILE * fp; + + fprintf (stderr, " t.%u: (%p:%s,%p:%s,%p:%s)\n", + nt, + e.v1, NAME (e.v1), + e.v2, NAME (e.v2), + e.v3, NAME (e.v3)); + sprintf (name, "/tmp/t.%u", nt++); + fp = fopen (name, "wt"); + // gts_surface_write (surface, fp); + gts_write_triangle (GTS_TRIANGLE (f), NULL, fp); + // write_graph1 (start, interior, surface, fp); + fclose (fp); + print_loop (start, stderr); + } +#endif /* DEBUG */ + } + } + UNSET (s, RELEVANT); + UNSET (s, INTERIOR); + NEXT (s) = NULL; +} + +#ifdef CHECK_ORIENTED +static void check_object (GtsObject * o) +{ + g_assert (o->reserved == NULL); + g_assert (o->flags == 0); +} + +static void check_boundary (GtsEdge * e, GtsSurface * s) +{ + check_object (GTS_OBJECT (e)); + check_object (GTS_OBJECT (GTS_SEGMENT (e)->v1)); + check_object (GTS_OBJECT (GTS_SEGMENT (e)->v2)); + g_assert (gts_edge_face_number (e, s) == 1); +} + +static void check_interior (GtsEdge * e, GtsSurface * s) +{ + guint n; + check_object (GTS_OBJECT (e)); + check_object (GTS_OBJECT (GTS_SEGMENT (e)->v1)); + check_object (GTS_OBJECT (GTS_SEGMENT (e)->v2)); + + n = gts_edge_face_number (e, s); +#ifdef DEBUG + if (n != 2) + gts_surface_print_stats (s, stderr); +#endif /* DEBUG */ + g_assert (n == 2); +} + +static void check_boundary_interior_triangulation (GSList * boundary, + GSList * interior, + GtsSurface * surface) +{ + g_slist_foreach (boundary, (GFunc) check_boundary, surface); + g_slist_foreach (interior, (GFunc) check_interior, surface); +} +#endif /*ifdef CHECK_ORIENTED */ + +static void merge_duplicate (GtsEdge * e) +{ + GtsEdge * dup = gts_edge_is_duplicate (e); + + g_assert (dup); + gts_edge_replace (dup, e); + gts_object_destroy (GTS_OBJECT (dup)); +} + +static void triangulate_boundary_interior (GSList * boundary, + GSList * interior, + GtsSurface * s, + GtsPoint * o) +{ + GSList * iloops, * bloops, * i; + + i = boundary; + while (i) { + SET (i->data, RELEVANT); + i = i->next; + } + i = interior; + while (i) { + SET (i->data, RELEVANT); + SET (i->data, INTERIOR); + i = i->next; + } + + iloops = interior_loops (interior); + bloops = boundary_loops (boundary); + + i = iloops; + while (i) { +#ifdef DEBUG + fprintf (stderr, "--- interior loop ---\n"); + print_loop (i->data, stderr); +#endif /* DEBUG */ + connect_interior_loop (i->data, &interior, &bloops, s, o); + i = i->next; + } + +#ifdef DEBUG + { + FILE * fp = fopen ("/tmp/bloops", "w"); + write_loops (bloops, fp); + fclose (fp); + } +#endif /* DEBUG */ + + i = bloops; + while (i) { +#ifdef DEBUG + fprintf (stderr, "--- boundary loop ---\n"); + print_loop (i->data, stderr); +#endif /* DEBUG */ + triangulate_loop (i->data, s, o); + i = i->next; + } + + g_slist_foreach (interior, (GFunc) merge_duplicate, NULL); + g_slist_free (iloops); + g_slist_free (bloops); + +#ifdef CHECK_ORIENTED + check_boundary_interior_triangulation (boundary, interior, s); +#endif /* CHECK_ORIENTED */ +} + +static void create_edges (GtsSegment * s, GtsSurface * surface) +{ + if (GTS_OBJECT (s)->reserved) { + GList * i = GTS_OBJECT (s)->reserved; + GtsVertex * v1 = i->data; + + GTS_OBJECT (s)->reserved = g_list_prepend (i, + gts_edge_new (surface->edge_class, s->v1, v1)); + while (i) { + GList * next = i->next; + GtsVertex * v2 = next ? next->data : s->v2; + + GTS_OBJECT (i->data)->reserved = NULL; + i->data = gts_edge_new (surface->edge_class, v1, v2); + v1 = v2; + i = next; + } + } +} + +static void add_boundary (GtsSegment * s, GtsSegment * next, + GSList ** boundary) +{ + if (GTS_OBJECT (s)->reserved == NULL) + *boundary = g_slist_prepend (*boundary, s); + else { + if (s->v2 == next->v2 || s->v2 == next->v1) { + GList * i = g_list_last (GTS_OBJECT (s)->reserved); + + while (i) { + *boundary = g_slist_prepend (*boundary, i->data); + i = i->prev; + } + } + else { + GList * i = GTS_OBJECT (s)->reserved; + + while (i) { + *boundary = g_slist_prepend (*boundary, i->data); + i = i->next; + } + } + } +} + +static void triangulate_face (GtsTriangle * t, GtsSurface * surface) +{ + GSList * interior = GTS_OBJECT (t)->reserved; + GSList * boundary = NULL; + GtsSurface * s = gts_surface_new (gts_surface_class (), + surface->face_class, + surface->edge_class, + surface->vertex_class); + gdouble x, y, z; + GtsPoint * p = GTS_POINT (GTS_SEGMENT (t->e1)->v1); + GtsPoint * o; + + GTS_OBJECT (t)->reserved = NULL; + gts_triangle_normal (t, &x, &y, &z); + g_assert (x != 0. || y != 0. || z != 0.); + o = gts_point_new (gts_point_class (), p->x + x, p->y + y, p->z + z); + add_boundary (GTS_SEGMENT (t->e3), GTS_SEGMENT (t->e1), &boundary); + add_boundary (GTS_SEGMENT (t->e2), GTS_SEGMENT (t->e3), &boundary); + add_boundary (GTS_SEGMENT (t->e1), GTS_SEGMENT (t->e2), &boundary); +#ifdef DEBUG + { + static guint nt = 0; + char name[80]; + FILE * fp; + + fprintf (stderr, "%u: triangulating %p\n", nt, t); +if (nt == 28) + fprintf (stderr, "tintin!!!!\n"); + sprintf (name, "/tmp/oc.%u", nt++); + fp = fopen (name, "wt"); + // write_graph (boundary, interior, s, fp); + write_segments (boundary, interior, fp); + fclose (fp); + } +#endif /* DEBUG */ + triangulate_boundary_interior (boundary, interior, s, o); + g_slist_free (interior); + g_slist_free (boundary); + if (GTS_OBJECT (t)->klass->attributes) + gts_surface_foreach_face (s, (GtsFunc) gts_object_attributes, t); + gts_surface_merge (surface, s); + gts_object_destroy (GTS_OBJECT (s)); + gts_object_destroy (GTS_OBJECT (o)); +} + +static void free_edge_list (GtsObject * o) +{ + g_list_free (o->reserved); + o->reserved = NULL; +} + +/** + * gts_surface_inter_new: + * @klass: a #GtsSurfaceInterClass. + * @s1: a #GtsSurface. + * @s2: a #GtsSurface. + * @faces_tree1: a bounding box tree (see gts_bb_tree_new()) for + * the faces of @s1. + * @faces_tree2: a bounding box tree for the faces of @s2. + * @is_open1: whether @s1 is an "open" surface. + * @is_open2: whether @s2 is an "open" surface. + * + * When triangulating the cut faces, the new faces inherit the + * attributes of these original faces through their attributes() + * method. + * + * Returns: a new #GtsSurfaceInter describing the intersection of @s1 + * and @s2. + */ +GtsSurfaceInter * gts_surface_inter_new (GtsSurfaceInterClass * klass, + GtsSurface * s1, + GtsSurface * s2, + GNode * faces_tree1, + GNode * faces_tree2, + gboolean is_open1, + gboolean is_open2) +{ + GtsSurfaceInter * si; + GtsSurface * s; + + g_return_val_if_fail (klass != NULL, NULL); + g_return_val_if_fail (s1 != NULL, NULL); + g_return_val_if_fail (s2 != NULL, NULL); + g_return_val_if_fail (faces_tree1 != NULL, NULL); + g_return_val_if_fail (faces_tree2 != NULL, NULL); + + si = surface_inter_new (klass, s1, s2, faces_tree1, faces_tree2); + + gts_surface_foreach_edge (si->s1, (GtsFunc) create_edges, si->s1); + gts_surface_foreach_edge (si->s2, (GtsFunc) create_edges, si->s2); + +#ifdef DEBUG + fprintf (stderr, "====== triangulating s1 ======\n"); +#endif /* DEBUG */ + s = gts_surface_new (gts_surface_class (), + s1->face_class, + s1->edge_class, + s1->vertex_class); + gts_surface_foreach_face (si->s1, (GtsFunc) triangulate_face, s); + gts_surface_foreach_edge (si->s1, (GtsFunc) free_edge_list, NULL); + gts_object_destroy (GTS_OBJECT (si->s1)); + si->s1 = s; + GTS_OBJECT (si->s1)->reserved = s1; + +#ifdef DEBUG + fprintf (stderr, "====== triangulating s2 ======\n"); +#endif /* DEBUG */ + s = gts_surface_new (gts_surface_class (), + s2->face_class, + s2->edge_class, + s2->vertex_class); + gts_surface_foreach_face (si->s2, (GtsFunc) triangulate_face, s); + gts_surface_foreach_edge (si->s2, (GtsFunc) free_edge_list, NULL); + gts_object_destroy (GTS_OBJECT (si->s2)); + si->s2 = s; + GTS_OBJECT (si->s2)->reserved = s2; + + return si; +} + +static void check_surface_edge (GtsEdge * e, gpointer * data) +{ + gboolean * ok = data[0]; + GtsSurface * s = data[1]; + GtsSurface * bs = GTS_OBJECT (s)->reserved; + guint nf = gts_edge_face_number (e, s); + + if (nf < 1 || nf > 2) { + *ok = FALSE; + g_return_if_fail (nf >= 1 && nf <= 2); + } + if (nf == 1 && gts_edge_face_number (e, bs) == 0) { + *ok = FALSE; + g_return_if_fail (gts_edge_face_number (e, bs) > 0); + } +} + +static void mark_edge (GtsObject * o, gpointer data) +{ + o->reserved = data; +} + +static gint triangle_orientation (GtsTriangle * t, GtsEdge * e) +{ + GtsSegment * s = GTS_SEGMENT (t->e1 == e ? t->e2 + : + t->e2 == e ? t->e3 + : + t->e1); + GtsVertex * v2 = GTS_SEGMENT (e)->v2; + + if (s->v1 == v2 || s->v2 == v2) + return 1; + return -1; +} + +static gboolean check_orientation (GtsEdge * e, GtsSurface * s) +{ + GtsTriangle * t1 = NULL, * t2 = NULL; + GSList * i = e->triangles; + gint o1 = 0, o2 = 0; + + while (i) { + if (GTS_IS_FACE (i->data) && + gts_face_has_parent_surface (i->data, s)) { + if (t1 == NULL) { + t1 = i->data; + o1 = triangle_orientation (t1, e); + } + else if (t2 == NULL) { + t2 = i->data; + o2 = triangle_orientation (t2, e); + g_return_val_if_fail (o1*o2 < 0, FALSE); + } + else + g_assert_not_reached (); + } + i = i->next; + } + g_return_val_if_fail (t1 && t2, FALSE); + return TRUE; +} + +static void check_edge (GtsSegment * s, gpointer * data) +{ + gboolean * ok = data[0]; + GtsSurfaceInter * si = data[1]; + gboolean * closed = data[2]; + GSList * j; + guint nn = 0; + + j = s->v1->segments; + while (j && *ok) { + GtsSegment * s1 = j->data; + + if (s1 != s && GTS_OBJECT (s1)->reserved == si) { + if (s1->v2 != s->v1) + *ok = FALSE; + nn++; + } + j = j->next; + } + j = s->v2->segments; + while (j && *ok) { + GtsSegment * s1 = j->data; + + if (s1 != s && GTS_OBJECT (s1)->reserved == si) { + if (s1->v1 != s->v2) + *ok = FALSE; + nn++; + } + j = j->next; + } + if (nn != 2) + *closed = FALSE; + + if (!check_orientation (GTS_EDGE (s), si->s1)) + *ok = FALSE; + if (!check_orientation (GTS_EDGE (s), si->s2)) + *ok = FALSE; +} + +/** + * gts_surface_inter_check: + * @si: a #GtsSurfaceInter. + * @closed: is set to %TRUE if @si->edges is a closed curve, %FALSE + * otherwise. + * + * Returns: %TRUE if the curve described by @si is an orientable + * manifold, %FALSE otherwise. + */ +gboolean gts_surface_inter_check (GtsSurfaceInter * si, + gboolean * closed) +{ + gboolean ok = TRUE; + gpointer data[3]; + + g_return_val_if_fail (si != NULL, FALSE); + g_return_val_if_fail (closed != NULL, FALSE); + + *closed = si->edges ? TRUE : FALSE; + + /* mark edges as used by si */ + g_slist_foreach (si->edges, (GFunc) mark_edge, si); + + data[0] = &ok; + data[1] = si; + data[2] = closed; + g_slist_foreach (si->edges, (GFunc) check_edge, data); + g_slist_foreach (si->edges, (GFunc) gts_object_reset_reserved, NULL); + + /* check connectivity of the faces of @si */ + if (*closed) { + gpointer data[2]; + + data[0] = &ok; + data[1] = si->s1; + gts_surface_foreach_edge (si->s1, (GtsFunc) check_surface_edge, data); + data[1] = si->s2; + gts_surface_foreach_edge (si->s2, (GtsFunc) check_surface_edge, data); + } + + return ok; +} + +/* Given @e and @f returns a #GtsFace compatible with @f and belonging to + @s1 or @s2 */ +static GtsFace * next_compatible_face (GtsEdge * e, + GtsFace * f, + GtsSurface * s1, + GtsSurface * s2) +{ + GSList * i = e->triangles; + GtsFace * f2 = NULL, * f3 = NULL; + + while (i) { + GtsFace * f1 = i->data; + + if (f1 != f && GTS_IS_FACE (f1)) { + if (gts_face_has_parent_surface (f1, s1)) + return f1; + if (gts_face_has_parent_surface (f1, s2)) { + if (f2 == NULL) f2 = f1; + else if (f3 == NULL) f3 = f1; + else g_assert_not_reached (); /* s2 is a non-manifold surface */ + } + } + i = i->next; + } + if (f3 == NULL) { + if (gts_edge_is_boundary (e, s2)) + return NULL; + return f2; + } + g_assert (gts_face_has_parent_surface (f, s1)); + if (gts_triangles_are_compatible (GTS_TRIANGLE (f), GTS_TRIANGLE (f2), e)) + return f2; + return f3; +} + +static void walk_faces (GtsEdge * e, GtsFace * f, + GtsSurface * s1, + GtsSurface * s2, + GtsSurface * s) +{ + GtsFifo * faces = gts_fifo_new (); + GtsFifo * edges = gts_fifo_new (); + + gts_fifo_push (faces, f); + gts_fifo_push (edges, e); + while ((f = gts_fifo_pop (faces)) && (e = gts_fifo_pop (edges))) { + if (!GTS_OBJECT (f)->reserved) { + GtsTriangle * t = GTS_TRIANGLE (f); + GtsFace * f1; + + gts_surface_add_face (s, f); + GTS_OBJECT (f)->reserved = s; + if (t->e1 != e && !GTS_OBJECT (t->e1)->reserved && + (f1 = next_compatible_face (t->e1, f, s1, s2))) { + gts_fifo_push (faces, f1); + gts_fifo_push (edges, t->e1); + } + if (t->e2 != e && !GTS_OBJECT (t->e2)->reserved && + (f1 = next_compatible_face (t->e2, f, s1, s2))) { + gts_fifo_push (faces, f1); + gts_fifo_push (edges, t->e2); + } + if (t->e3 != e && !GTS_OBJECT (t->e3)->reserved && + (f1 = next_compatible_face (t->e3, f, s1, s2))) { + gts_fifo_push (faces, f1); + gts_fifo_push (edges, t->e3); + } + } + } + gts_fifo_destroy (faces); + gts_fifo_destroy (edges); +} + +/** + * gts_surface_inter_boolean: + * @si: a #GtsSurfaceInter. + * @surface: a #GtsSurface. + * @op: a #GtsBooleanOperation. + * + * Adds to @surface the part of the surface described by @si and @op. + */ +void gts_surface_inter_boolean (GtsSurfaceInter * si, + GtsSurface * surface, + GtsBooleanOperation op) +{ + GtsSurface * s = NULL; + gint orient = 1; + GSList * i; + + g_return_if_fail (si != NULL); + g_return_if_fail (surface != NULL); + + switch (op) { + case GTS_1_OUT_2: s = si->s1; orient = 1; break; + case GTS_1_IN_2: s = si->s1; orient = -1; break; + case GTS_2_OUT_1: s = si->s2; orient = -1; break; + case GTS_2_IN_1: s = si->s2; orient = 1; break; + default: g_assert_not_reached (); + } + + /* mark edges as belonging to intersection */ + g_slist_foreach (si->edges, (GFunc) mark_edge, si); + + i = si->edges; + while (i) { + GtsEdge * e = i->data; + GSList * j = e->triangles; + + while (j) { + if (gts_face_has_parent_surface (j->data, s) && + orient*triangle_orientation (j->data, e) > 0) { +#ifdef DEBUG_BOOLEAN + GtsFace * boundary = gts_edge_is_boundary (e, surface); + + g_assert (!boundary || boundary == j->data); +#endif /* DEBUG_BOOLEAN */ + walk_faces (e, j->data, s, GTS_OBJECT (s)->reserved, surface); + break; + } + j = j->next; + } + i = i->next; + } + g_slist_foreach (si->edges, (GFunc) gts_object_reset_reserved, NULL); + gts_surface_foreach_face (surface, + (GtsFunc) gts_object_reset_reserved, NULL); +} + +static void self_intersecting (GtsBBox * bb1, GtsBBox * bb2, + gpointer * d) +{ + GtsTriangle * t1 = bb1->bounded; + GtsTriangle * t2 = bb2->bounded; + + if (t1 != t2) { + GtsSegment * s1 = GTS_SEGMENT (t1->e1); + GtsSegment * s2 = GTS_SEGMENT (t1->e2); + GtsSegment * s3 = GTS_SEGMENT (t1->e3); + GtsSegment * s4 = GTS_SEGMENT (t2->e1); + GtsSegment * s5 = GTS_SEGMENT (t2->e2); + GtsSegment * s6 = GTS_SEGMENT (t2->e3); + GtsPoint * pi; + + if ((!gts_segments_touch (s4, s1) && + !gts_segments_touch (s4, s2) && + !gts_segments_touch (s4, s3) && + (pi = segment_triangle_intersection (s4, t1, gts_point_class ())) + != NULL) || + (!gts_segments_touch (s5, s1) && + !gts_segments_touch (s5, s2) && + !gts_segments_touch (s5, s3) && + (pi = segment_triangle_intersection (s5, t1, gts_point_class ())) + != NULL) || + (!gts_segments_touch (s6, s1) && + !gts_segments_touch (s6, s2) && + !gts_segments_touch (s6, s3) && + (pi = segment_triangle_intersection (s6, t1, gts_point_class ())) + != NULL)) { + GtsBBTreeTraverseFunc func = d[0]; + gpointer data = d[1]; + gboolean * self_inter = d[2]; + + gts_object_destroy (GTS_OBJECT (pi)); + *self_inter = TRUE; + (* func) (bb1, bb2, data); + } + } +} + +/** + * gts_surface_foreach_intersecting_face: + * @s: a #GtsSurface. + * @func: a #GtsBBTreeTraverseFunc. + * @data: user data to pass to @func. + * + * Calls @func for each intersecting pair of faces of @s. + * + * Returns: %TRUE if @func was called at least once, %FALSE otherwise. + */ +gboolean gts_surface_foreach_intersecting_face (GtsSurface * s, + GtsBBTreeTraverseFunc func, + gpointer data) +{ + GNode * tree; + gpointer d[3]; + gboolean self_inter = FALSE; + + g_return_val_if_fail (s != NULL, FALSE); + g_return_val_if_fail (func != NULL, FALSE); + + tree = gts_bb_tree_surface (s); + d[0] = func; + d[1] = data; + d[2] = &self_inter; + gts_bb_tree_traverse_overlapping (tree, tree, + (GtsBBTreeTraverseFunc) self_intersecting, + d); + gts_bb_tree_destroy (tree, TRUE); + + return self_inter; +} + +static void add_intersecting (GtsBBox * bb1, GtsBBox * bb2, + GtsSurface * intersected) +{ + gts_surface_add_face (intersected, bb1->bounded); + gts_surface_add_face (intersected, bb2->bounded); +} + +/** + * gts_surface_is_self_intersecting: + * @s: a #GtsSurface. + * + * Returns: a new #GtsSurface containing the faces of @s which are + * self-intersecting or %NULL if no faces of @s are self-intersecting. + */ +GtsSurface * gts_surface_is_self_intersecting (GtsSurface * s) +{ + GtsSurface * intersected; + + g_return_val_if_fail (s != NULL, NULL); + + intersected = gts_surface_new (GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass), + s->face_class, + s->edge_class, + s->vertex_class); + if (!gts_surface_foreach_intersecting_face (s, + (GtsBBTreeTraverseFunc) add_intersecting, intersected)) { + gts_object_destroy (GTS_OBJECT (intersected)); + intersected = NULL; + } + return intersected; +} Index: toporouter/src_3rd/gts/cdt.c =================================================================== --- toporouter/src_3rd/gts/cdt.c (nonexistent) +++ toporouter/src_3rd/gts/cdt.c (revision 6803) @@ -0,0 +1,1173 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + + +#include +#include "gts.h" + +#ifdef USE_SURFACE_BTREE + +static gint find_closest (GtsTriangle * t, gpointer value, gpointer * data) +{ + guint * ns = data[2]; + guint * n = data[3]; + + if (*n >= *ns) + return TRUE; + else { + gdouble * dmin = data[0]; + gpointer * closest = data[1]; + GtsPoint * p = data[4]; + + if (gts_triangle_orientation (t) > 0.) { + GtsPoint * p1 = GTS_POINT (GTS_SEGMENT (t->e1)->v1); + gdouble d = (p->x - p1->x)*(p->x - p1->x) + (p->y - p1->y)*(p->y - p1->y); + + if (d < *dmin) { + *dmin = d; + *closest = t; + } + (*n)++; + } + } + return FALSE; +} + +/* select the face closest to @p among n^1/3 randomly picked faces + * of @surface */ +static GtsFace * closest_face (GtsSurface * s, GtsPoint * p) +{ + guint n = 0, nt, ns; + gdouble dmin = G_MAXDOUBLE; + GtsFace * closest = NULL; + gpointer data[5]; + + nt = gts_surface_face_number (s); + if (!nt) + return NULL; + ns = exp (log ((gdouble) nt)/3.); + + data[0] = &dmin; + data[1] = &closest; + data[2] = &ns; + data[3] = &n; + data[4] = p; + g_tree_traverse (s->faces, (GTraverseFunc) find_closest, G_IN_ORDER, data); + + return closest; +} + +#else /* not USE_SURFACE_BTREE */ + +typedef struct _SFindClosest SFindClosest; + +struct _SFindClosest { + gdouble dmin; + GtsFace *closest; + GtsPoint * p; + gint stop; +}; + +# if GLIB_CHECK_VERSION(2,4,0) +/* finally, with g_hash_table_find we are able to stop iteration over the hash + table in the middle */ + +static gboolean find_closest (gpointer key, gpointer value, gpointer user_data) +{ + SFindClosest * data = (SFindClosest *) user_data; + GtsFace * f = GTS_FACE (value); + + if (gts_triangle_orientation (GTS_TRIANGLE (f)) > 0.) { + GtsPoint * p1 = GTS_POINT (GTS_SEGMENT (GTS_TRIANGLE (f)->e1)->v1); + gdouble d = ((data->p->x - p1->x)*(data->p->x - p1->x) + + (data->p->y - p1->y)*(data->p->y - p1->y)); + + if (d < data->dmin) { + data->dmin = d; + data->closest = f; + } + } + data->stop--; + return !(data->stop > 0); +} + +static GtsFace * closest_face (GtsSurface * s, GtsPoint * p) +{ + SFindClosest fc; + + fc.dmin = G_MAXDOUBLE; + fc.closest = NULL; + fc.p = p; + fc.stop = (gint) exp (log ((gdouble) g_hash_table_size (s->faces))/3.); + g_hash_table_find (s->faces, find_closest, &fc); + + return fc.closest; +} + +# else /* VERSION < 2.4.0 */ + +static void +find_closest (gpointer key, gpointer value, gpointer user_data) +{ + SFindClosest * data = (SFindClosest *) user_data; + GtsFace * f = GTS_FACE (value); + + if (gts_triangle_orientation (GTS_TRIANGLE (f)) > 0.) { + GtsPoint * p1 = GTS_POINT (GTS_SEGMENT (GTS_TRIANGLE (f)->e1)->v1); + gdouble d = ((data->p->x - p1->x)*(data->p->x - p1->x) + + (data->p->y - p1->y)*(data->p->y - p1->y)); + + if (d < data->dmin) { + data->dmin = d; + data->closest = f; + } + } + data->stop--; +} + +/* select the face closest to @p among n^1/3 randomly picked faces + * of @surface */ +static GtsFace * closest_face (GtsSurface * s, GtsPoint * p) +{ + SFindClosest fc; + + if (!g_hash_table_size (s->faces)) + return NULL; + + fc.dmin = G_MAXDOUBLE; + fc.closest = NULL; + fc.p = p; + fc.stop = (gint) exp (log ((gdouble) g_hash_table_size (s->faces))/3.); + g_hash_table_foreach (s->faces, find_closest, &fc); + return fc.closest; +} +# endif /* VERSION < 2.4.0 */ +#endif /* not USE_SURFACE_BTREE */ + +/* returns the face belonging to @surface and neighbor of @f via @e */ +static GtsFace * neighbor (GtsFace * f, + GtsEdge * e, + GtsSurface * surface) +{ + GSList * i = e->triangles; + GtsTriangle * t = GTS_TRIANGLE (f); + + while (i) { + GtsTriangle * t1 = i->data; + if (t1 != t && + GTS_IS_FACE (t1) && + gts_face_has_parent_surface (GTS_FACE (t1), surface)) + return GTS_FACE (t1); + i = i->next; + } + return NULL; +} + +/* given a triangle @t and a segment s (@o -> @p). + @o must be in @t. Returns the + edge of @t which is intersected by s or %NULL if @p is also + contained in @t (on_summit is set to %FALSE) or if s intersects @t + exactly on one of its summit (on_summit is set to %TRUE). */ +static GtsEdge * triangle_next_edge (GtsTriangle * t, + GtsPoint * o, GtsPoint * p, + gboolean * on_summit) +{ + GtsVertex * v1, * v2, * v3; + GtsEdge * e1, * e2, * e3; + gdouble orient = 0.0; + + gts_triangle_vertices_edges (t, NULL, + &v1, &v2, &v3, + &e1, &e2, &e3); + + *on_summit = FALSE; + orient = gts_point_orientation (o, GTS_POINT (v1), p); + if (orient > 0.0) { + orient = gts_point_orientation (o, GTS_POINT (v2), p); + if (orient > 0.0) { + if (gts_point_orientation (GTS_POINT (v2), GTS_POINT (v3), p) >= 0.0) + return NULL; + return e2; + } + if (orient < 0.0) { + if (gts_point_orientation (GTS_POINT (v1), GTS_POINT (v2), p) >= 0.0) + return NULL; + return e1; + } + if (gts_point_orientation (GTS_POINT (v1), GTS_POINT (v2), p) < 0.0) + *on_summit = TRUE; + return NULL; + } + + if (orient < 0.0) { + orient = gts_point_orientation (o, GTS_POINT (v3), p); + if (orient > 0.0) { + if (gts_point_orientation (GTS_POINT (v3), GTS_POINT (v1), p) >= 0.0) + return NULL; + return e3; + } + if (orient < 0.0) { + if (gts_point_orientation (GTS_POINT (v2), GTS_POINT (v3), p) >= 0.0) + return NULL; + return e2; + } + if (gts_point_orientation (GTS_POINT (v3), GTS_POINT (v1), p) < 0.0) + *on_summit = TRUE; + return NULL; + } + + if (gts_point_orientation (GTS_POINT (v2), GTS_POINT (v3), p) < 0.0) + return e2; + if (gts_point_orientation (GTS_POINT (v1), GTS_POINT (v2), p) < 0.0) + *on_summit = TRUE; + return NULL; +} + +static void triangle_barycenter (GtsTriangle * t, GtsPoint * b) +{ + GtsPoint * p = GTS_POINT (gts_triangle_vertex (t)); + b->x = (p->x + + GTS_POINT (GTS_SEGMENT(t->e1)->v1)->x + + GTS_POINT (GTS_SEGMENT(t->e1)->v2)->x)/3.; + b->y = (p->y + + GTS_POINT (GTS_SEGMENT(t->e1)->v1)->y + + GTS_POINT (GTS_SEGMENT(t->e1)->v2)->y)/3.; +} + +static GtsFace * point_locate (GtsPoint * o, + GtsPoint * p, + GtsFace * f, + GtsSurface * surface) +{ + GtsEdge * prev; + gboolean on_summit; + GtsVertex * v1, * v2, * v3; + GtsEdge * e2, * e3; + + prev = triangle_next_edge (GTS_TRIANGLE (f), o, p, &on_summit); + + if (!prev) { + GtsFace * f1; + + if (!on_summit) + return f; /* p is inside f */ + + /* s intersects f exactly on a summit: restarts from a neighbor of f */ + if ((f1 = neighbor (f, GTS_TRIANGLE (f)->e1, surface)) || + (f1 = neighbor (f, GTS_TRIANGLE (f)->e2, surface)) || + (f1 = neighbor (f, GTS_TRIANGLE (f)->e3, surface))) { + triangle_barycenter (GTS_TRIANGLE (f1), o); + return point_locate (o, p, f1, surface); + } + return NULL; + } + + f = neighbor (f, prev, surface); + if (f) + gts_triangle_vertices_edges (GTS_TRIANGLE (f), prev, + &v1, &v2, &v3, &prev, &e2, &e3); + while (f) { + gdouble orient = gts_point_orientation (o, GTS_POINT (v3), p); + + if (orient < 0.0) { + if (gts_point_orientation (GTS_POINT (v2), GTS_POINT (v3), p) >= 0.0) + return f; /* p is inside f */ + f = neighbor (f, e2, surface); + prev = e2; + v1 = v3; + } + else if (orient > 0.0) { + if (gts_point_orientation (GTS_POINT (v3), GTS_POINT (v1), p) >= 0.0) + return f; /* p is inside f */ + f = neighbor (f, e3, surface); + prev = e3; + v2 = v3; + } + else { + GtsFace * f1; + + if (gts_point_orientation (GTS_POINT (v2), GTS_POINT (v3), p) >= 0.0) + return f; /* p is inside f */ + + /* s intersects f exactly on v3: restarts from a neighbor of f */ + if ((f1 = neighbor (f, e2, surface)) || + (f1 = neighbor (f, e3, surface))) { + triangle_barycenter (GTS_TRIANGLE (f1), o); + return point_locate (o, p, f1, surface); + } + return NULL; + } + /* update e2, e3, v3 for the new triangle */ + if (f) { + if (prev == GTS_TRIANGLE (f)->e1) { + e2 = GTS_TRIANGLE (f)->e2; e3 = GTS_TRIANGLE (f)->e3; + } + else if (prev == GTS_TRIANGLE (f)->e2) { + e2 = GTS_TRIANGLE (f)->e3; e3 = GTS_TRIANGLE (f)->e1; + } + else { + e2 = GTS_TRIANGLE (f)->e1; e3 = GTS_TRIANGLE (f)->e2; + } + if (GTS_SEGMENT (e2)->v1 == v1 || GTS_SEGMENT (e2)->v1 == v2) + v3 = GTS_SEGMENT (e2)->v2; + else + v3 = GTS_SEGMENT (e2)->v1; + } + } + return NULL; +} + +/** + * gts_point_locate: + * @p: a #GtsPoint. + * @surface: a #GtsSurface. + * @guess: %NULL or a face of @surface close to @p. + * + * Locates the face of the planar projection of @surface containing + * @p. The planar projection of @surface must define a connected set + * of triangles without holes and bounded by a convex boundary. The + * algorithm is randomized and performs in O(n^1/3) expected time + * where n is the number of triangles of @surface. + * + * If a good @guess is given the point location can be significantly faster. + * + * Returns: a #GtsFace of @surface containing @p or %NULL if @p is not + * contained within the boundary of @surface. + */ +GtsFace * gts_point_locate (GtsPoint * p, + GtsSurface * surface, + GtsFace * guess) +{ + GtsFace * fr; + GtsPoint * o; + + g_return_val_if_fail (p != NULL, NULL); + g_return_val_if_fail (surface != NULL, NULL); + g_return_val_if_fail (guess == NULL || + gts_face_has_parent_surface (guess, surface), NULL); + + if (guess == NULL) + guess = closest_face (surface, p); + else + g_return_val_if_fail (gts_triangle_orientation (GTS_TRIANGLE (guess)) > 0., NULL); + + if (guess == NULL) + return NULL; + + o = GTS_POINT (gts_object_new (GTS_OBJECT_CLASS (gts_point_class ()))); + triangle_barycenter (GTS_TRIANGLE (guess), o); + fr = point_locate (o, p, guess, surface); + gts_object_destroy (GTS_OBJECT (o)); + + return fr; +} + + +/** + * gts_constraint_class: + * + * Returns: the #GtsConstraintClass. + */ +GtsConstraintClass * gts_constraint_class (void) +{ + static GtsConstraintClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo constraint_info = { + "GtsConstraint", + sizeof (GtsConstraint), + sizeof (GtsConstraintClass), + (GtsObjectClassInitFunc) NULL, + (GtsObjectInitFunc) NULL, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_edge_class ()), + &constraint_info); + } + + return klass; +} + +static void split_list (GtsListFace * f, GtsListFace * f1, GtsListFace * f2, + GtsPoint * p1, GtsPoint * p2, + GSList ** last1, GSList ** last2) +{ + GSList * i = f->points, * l1 = *last1, * l2 = *last2; + + while (i) { + GtsPoint * p = i->data; + + if (gts_point_orientation (p1, p2, p) >= 0.) { + if (l1) l1->next = i; else f1->points = i; + l1 = i; + } + else { + if (l2) l2->next = i; else f2->points = i; + l2 = i; + } + i = i->next; + } + f->points = NULL; + *last1 = l1; + *last2 = l2; +} + +/* cf. figure misc/swap.fig */ +static void swap_if_in_circle (GtsFace * f1, + GtsVertex * v1, + GtsVertex * v2, + GtsVertex * v3, + GtsEdge * e1, + GtsEdge * e2, + GtsEdge * e3, + GtsSurface * surface) +{ + GtsFace * f2; + GtsEdge * e4, *e5; + GtsVertex * v4; + + if (GTS_IS_CONSTRAINT (e1)) /* @e1 is a constraint can not swap */ + return; + + f2 = neighbor (f1, e1, surface); + if (f2 == NULL) /* @e1 is a boundary of @surface */ + return; + + if (GTS_TRIANGLE (f2)->e1 == e1) { + e4 = GTS_TRIANGLE (f2)->e2; e5 = GTS_TRIANGLE (f2)->e3; + } + else if (GTS_TRIANGLE (f2)->e2 == e1) { + e4 = GTS_TRIANGLE (f2)->e3; e5 = GTS_TRIANGLE (f2)->e1; + } + else { + e4 = GTS_TRIANGLE (f2)->e1; e5 = GTS_TRIANGLE (f2)->e2; + } + if (GTS_SEGMENT (e4)->v1 == GTS_SEGMENT (e1)->v1 || + GTS_SEGMENT (e4)->v1 == GTS_SEGMENT (e1)->v2) + v4 = GTS_SEGMENT (e4)->v2; + else + v4 = GTS_SEGMENT (e4)->v1; + + if (gts_point_in_circle (GTS_POINT (v4), GTS_POINT (v1), + GTS_POINT (v2), GTS_POINT (v3)) > 0.0) { + GtsEdge * en; + GtsSegment * sn = gts_vertices_are_connected (v3, v4); + GtsFace * f3, * f4; + + if (!GTS_IS_EDGE (sn)) + en = gts_edge_new (surface->edge_class, v3, v4); + else + en = GTS_EDGE (sn); + + f3 = gts_face_new (surface->face_class, en, e5, e2); + gts_object_attributes (GTS_OBJECT (f3), GTS_OBJECT (f1)); + f4 = gts_face_new (surface->face_class, en, e3, e4); + gts_object_attributes (GTS_OBJECT (f4), GTS_OBJECT (f2)); + + if (GTS_IS_LIST_FACE (f3)) { + GSList * last3 = NULL, * last4 = NULL; + + if (GTS_IS_LIST_FACE (f1)) + split_list (GTS_LIST_FACE (f1), GTS_LIST_FACE (f3), GTS_LIST_FACE (f4), + GTS_POINT (v3), GTS_POINT (v4), &last3, &last4); + if (GTS_IS_LIST_FACE (f2)) + split_list (GTS_LIST_FACE (f2), GTS_LIST_FACE (f3), GTS_LIST_FACE (f4), + GTS_POINT (v3), GTS_POINT (v4), &last3, &last4); + if (last3) last3->next = NULL; + if (last4) last4->next = NULL; + } + + gts_surface_remove_face (surface, f1); + gts_surface_remove_face (surface, f2); + gts_surface_add_face (surface, f3); + gts_surface_add_face (surface, f4); + + swap_if_in_circle (f3, v4, v2, v3, e5, e2, en, surface); + swap_if_in_circle (f4, v1, v4, v3, e4, en, e3, surface); + } +} + +/** + * gts_delaunay_add_vertex_to_face: + * @surface: a #GtsSurface. + * @v: a #GtsVertex. + * @f: a #GtsFace belonging to @surface. + * + * Adds vertex @v to the face @f of the Delaunay triangulation defined + * by @surface. + * + * Returns: %NULL is @v has been successfully added to @surface or was + * already contained in @surface or a #GtsVertex having the same x and + * y coordinates as @v. + */ +GtsVertex * gts_delaunay_add_vertex_to_face (GtsSurface * surface, + GtsVertex * v, + GtsFace * f) +{ + GtsEdge * e1, * e2, * e3; + GtsSegment * s4, * s5, * s6; + GtsEdge * e4, * e5, * e6; + GtsVertex * v1, * v2, * v3; + GtsFace * nf[3]; + + g_return_val_if_fail (surface != NULL, v); + g_return_val_if_fail (v != NULL, v); + g_return_val_if_fail (f != NULL, v); + + gts_triangle_vertices_edges (GTS_TRIANGLE (f), NULL, + &v1, &v2, &v3, &e1, &e2, &e3); + if (v == v1 || v == v2 || v == v3) /* v already in @surface */ + return NULL; + if (GTS_POINT (v)->x == GTS_POINT (v1)->x && + GTS_POINT (v)->y == GTS_POINT (v1)->y) + return v1; + if (GTS_POINT (v)->x == GTS_POINT (v2)->x && + GTS_POINT (v)->y == GTS_POINT (v2)->y) + return v2; + if (GTS_POINT (v)->x == GTS_POINT (v3)->x && + GTS_POINT (v)->y == GTS_POINT (v3)->y) + return v3; + + s4 = gts_vertices_are_connected (v, v1); + if (!GTS_IS_EDGE (s4)) + e4 = gts_edge_new (surface->edge_class, v, v1); + else + e4 = GTS_EDGE (s4); + s5 = gts_vertices_are_connected (v, v2); + if (!GTS_IS_EDGE (s5)) + e5 = gts_edge_new (surface->edge_class, v, v2); + else + e5 = GTS_EDGE (s5); + s6 = gts_vertices_are_connected (v, v3); + if (!GTS_IS_EDGE (s6)) + e6 = gts_edge_new (surface->edge_class, v, v3); + else + e6 = GTS_EDGE (s6); + + /* cf. figure misc/swap.fig */ + nf[0] = gts_face_new (surface->face_class, e4, e1, e5); + gts_object_attributes (GTS_OBJECT (nf[0]), GTS_OBJECT (f)); + nf[1] = gts_face_new (surface->face_class, e5, e2, e6); + gts_object_attributes (GTS_OBJECT (nf[1]), GTS_OBJECT (f)); + nf[2] = gts_face_new (surface->face_class, e6, e3, e4); + gts_object_attributes (GTS_OBJECT (nf[2]), GTS_OBJECT (f)); + + if (GTS_IS_LIST_FACE (f) && GTS_IS_LIST_FACE (nf[0])) { + GSList * i = GTS_LIST_FACE (f)->points, * last[3] = { NULL, NULL, NULL }; + + while (i) { + GtsPoint * p = i->data; + GSList * next = i->next; + guint j; + + if (p != GTS_POINT (v)) { + if (gts_point_orientation (GTS_POINT (v), GTS_POINT (v1), p) >= 0.) { + gdouble o = gts_point_orientation (GTS_POINT (v), GTS_POINT (v2), p); + + if (o != 0.) + j = o > 0. ? 1 : 0; + else + j = gts_point_orientation (GTS_POINT (v), GTS_POINT (v3), p) + > 0. ? 0 : 1; + } + else if (gts_point_orientation (GTS_POINT (v), GTS_POINT (v3), p) > 0.) + j = 2; + else + j = 1; + if (last[j]) + last[j]->next = i; + else + GTS_LIST_FACE (nf[j])->points = i; + last[j] = i; + } + else + g_slist_free_1 (i); + i = next; + } + GTS_LIST_FACE (f)->points = NULL; + if (last[0]) last[0]->next = NULL; + if (last[1]) last[1]->next = NULL; + if (last[2]) last[2]->next = NULL; + } + + gts_surface_remove_face (surface, f); + gts_surface_add_face (surface, nf[0]); + gts_surface_add_face (surface, nf[1]); + gts_surface_add_face (surface, nf[2]); + + swap_if_in_circle (nf[0], v1, v2, v, e1, e5, e4, surface); + swap_if_in_circle (nf[1], v2, v3, v, e2, e6, e5, surface); + swap_if_in_circle (nf[2], v3, v1, v, e3, e4, e6, surface); + + return NULL; +} + +/** + * gts_delaunay_add_vertex: + * @surface: a #GtsSurface. + * @v: a #GtsVertex. + * @guess: %NULL or a #GtsFace belonging to @surface to be used as an initial + * guess for point location. + * + * Adds vertex @v to the Delaunay triangulation defined by + * @surface. If @v is not contained in the convex hull bounding + * @surface, @v is not added to the triangulation. + * + * Returns: %NULL is @v has been successfully added to @surface or was + * already contained in @surface, @v if @v is not contained in the + * convex hull bounding surface or a #GtsVertex having the same x and + * y coordinates as @v. + */ +GtsVertex * gts_delaunay_add_vertex (GtsSurface * surface, + GtsVertex * v, + GtsFace * guess) +{ + GtsFace * f; + + g_return_val_if_fail (surface != NULL, v); + g_return_val_if_fail (v != NULL, v); + + if (!(f = gts_point_locate (GTS_POINT (v), surface, guess))) + return v; + return gts_delaunay_add_vertex_to_face (surface, v, f); +} + +static gboolean polygon_in_circle (GSList * poly, + GtsPoint * p1, + GtsPoint * p2, + GtsPoint * p3) +{ + GtsVertex * v1 = NULL, * v2 = NULL; + + while (poly) { + GtsSegment * s = poly->data; + GtsVertex * v; + v = s->v1; + if (v != v1 && v != v2 && + v != GTS_VERTEX (p1) && + v != GTS_VERTEX (p2) && + v != GTS_VERTEX (p3) && + gts_point_in_circle (GTS_POINT (v), p1, p2, p3) > 0.) + return TRUE; + v = s->v2; + if (v != v1 && v != v2 && + v != GTS_VERTEX (p1) && + v != GTS_VERTEX (p2) && + v != GTS_VERTEX (p3) && + gts_point_in_circle (GTS_POINT (v), p1, p2, p3) > 0.) + return TRUE; + v1 = s->v1; + v2 = s->v2; + poly = poly->next; + } + return FALSE; +} + +static void triangulate_polygon (GSList * poly, + GtsSurface * surface, + GtsFace * ref) +{ + GSList * i, * poly1, * poly2; + GtsVertex * v1, * v2, * v3 = NULL; + gboolean found = FALSE; + GtsSegment * s, * s1, * s2; + GtsEdge * e1, * e2; + GtsFace * f; + + if (poly == NULL || poly->next == NULL) { + g_slist_free (poly); + return; + } + + s = poly->data; + s1 = poly->next->data; + if (s->v1 == s1->v1 || s->v1 == s1->v2) { + v1 = s->v2; + v2 = s->v1; + } + else { + g_assert (s->v2 == s1->v1 || s->v2 == s1->v2); + v1 = s->v1; + v2 = s->v2; + } + + i = poly->next; + v3 = v2; + while (i && !found) { + s1 = i->data; + if (s1->v1 == v3) + v3 = s1->v2; + else { + g_assert (s1->v2 == v3); + v3 = s1->v1; + } + if (v3 != v1 && + gts_point_orientation (GTS_POINT (v1), + GTS_POINT (v2), + GTS_POINT (v3)) >= 0. && + !polygon_in_circle (poly, + GTS_POINT (v1), + GTS_POINT (v2), + GTS_POINT (v3))) + found = TRUE; + else + i = i->next; + } + + if (!found) { + g_slist_free (poly); + return; + } + + s1 = gts_vertices_are_connected (v2, v3); + if (!GTS_IS_EDGE (s1)) + e1 = gts_edge_new (surface->edge_class, v2, v3); + else + e1 = GTS_EDGE (s1); + s2 = gts_vertices_are_connected (v3, v1); + if (!GTS_IS_EDGE (s2)) + e2 = gts_edge_new (surface->edge_class, v3, v1); + else + e2 = GTS_EDGE (s2); + f = gts_face_new (surface->face_class, GTS_EDGE (s), e1, e2); + gts_object_attributes (GTS_OBJECT (f), GTS_OBJECT (ref)); + gts_surface_add_face (surface, f); + + poly1 = poly->next; + g_slist_free_1 (poly); + if (i->next && e2 != i->next->data) + poly2 = g_slist_prepend (i->next, e2); + else + poly2 = i->next; + if (e1 != i->data) + i->next = g_slist_prepend (NULL, e1); + else + i->next = NULL; + + triangulate_polygon (poly1, surface, ref); + triangulate_polygon (poly2, surface, ref); +} + +/** + * gts_delaunay_remove_vertex: + * @surface: a #GtsSurface. + * @v: a #GtsVertex. + * + * Removes @v from the Delaunay triangulation defined by @surface and + * restores the Delaunay property. Vertex @v must not be used by any + * constrained edge otherwise the triangulation is not guaranteed to + * be Delaunay. + */ +void gts_delaunay_remove_vertex (GtsSurface * surface, GtsVertex * v) +{ + GSList * triangles, * i; + GtsFace * ref = NULL; + + g_return_if_fail (surface != NULL); + g_return_if_fail (v != NULL); + + i = triangles = gts_vertex_triangles (v, NULL); + while (i && !ref) { + if (GTS_IS_FACE (i->data) && + gts_face_has_parent_surface (i->data, surface)) + ref = i->data; + i = i->next; + } + if (!ref) { + g_slist_free (triangles); + g_return_if_fail (ref); + } + triangulate_polygon (gts_vertex_fan_oriented (v, surface), surface, ref); + i = triangles; + while (i) { + if (GTS_IS_FACE (i->data) && + gts_face_has_parent_surface (i->data, surface)) + gts_surface_remove_face (surface, i->data); + i = i->next; + } + g_slist_free (triangles); +} + +#define NEXT_CUT(edge, edge1, list) { next = neighbor (f, edge, surface);\ + remove_triangles (e, surface);\ + if (!constraint && !e->triangles)\ + gts_object_destroy (GTS_OBJECT (e));\ + g_assert (next);\ + *list = g_slist_prepend (*list, edge1);\ + return g_slist_concat (constraint,\ + remove_intersected_edge (s, edge,\ + next, surface, left, right));\ + } + +static void remove_triangles (GtsEdge * e, GtsSurface * s) +{ + GSList * i = e->triangles; + + while (i) { + GSList * next = i->next; + + if (GTS_IS_FACE (i->data) && gts_face_has_parent_surface (i->data, s)) + gts_surface_remove_face (s, i->data); + i = next; + } +} + +static GSList * +remove_intersected_edge (GtsSegment * s, + GtsEdge * e, + GtsFace * f, + GtsSurface * surface, + GSList ** left, GSList ** right) +{ + GtsVertex * v1, * v2, * v3; + GtsEdge * e1, * e2; + gdouble o1, o2; + GtsFace * next; + GSList * constraint = NULL; + + if (GTS_IS_CONSTRAINT (e)) + constraint = g_slist_prepend (NULL, e); + + gts_triangle_vertices_edges (GTS_TRIANGLE (f), e, + &v1, &v2, &v3, &e, &e1, &e2); + + o1 = gts_point_orientation (GTS_POINT (v2), GTS_POINT (v3), + GTS_POINT (s->v2)); + o2 = gts_point_orientation (GTS_POINT (v3), GTS_POINT (v1), + GTS_POINT (s->v2)); + + if (o1 == 0. && o2 == 0.) { +/* if(o2 != 0.) { + fprintf(stderr, "o1 = %f o2 = %f\n", o1, o2); + fprintf(stderr, "v1 = %f, %f\n", GTS_POINT(v1)->x, GTS_POINT(v1)->y); + fprintf(stderr, "v2 = %f, %f\n", GTS_POINT(v2)->x, GTS_POINT(v2)->y); + fprintf(stderr, "v3 = %f, %f\n", GTS_POINT(v3)->x, GTS_POINT(v3)->y); + fprintf(stderr, "s->v2 = %f, %f\n", GTS_POINT(s->v2)->x, GTS_POINT(s->v2)->y); + + g_assert (o2 == 0.); + }*/ + // if(o2 == 0.) { + remove_triangles (e, surface); + if (!constraint && !e->triangles) + gts_object_destroy (GTS_OBJECT (e)); + *left = g_slist_prepend (*left, e2); + *right = g_slist_prepend (*right, e1); +// } + } + else if (o1 > 0.) { + g_assert (o2 <= 0.); + NEXT_CUT (e2, e1, right) + } + else if (o2 >= 0.) + NEXT_CUT (e1, e2, left) + else { + gdouble o3 = gts_point_orientation (GTS_POINT (s->v1), GTS_POINT (s->v2), + GTS_POINT (v3)); + if (o3 > 0.) + NEXT_CUT (e1, e2, left) + else + NEXT_CUT (e2, e1, right) + } + return constraint; +} + +static GSList * +remove_intersected_vertex (GtsSegment * s, + GtsVertex * v, + GtsSurface * surface, + GSList ** left, + GSList ** right, + GtsFace ** ref) +{ + GSList * triangles = gts_vertex_triangles (v, NULL); + GSList * i; + + i = triangles; + while (i) { + GtsTriangle * t = i->data; + if (GTS_IS_FACE (t) && + gts_face_has_parent_surface (GTS_FACE (t), surface)) { + GtsVertex * v1, * v2, * v3; + gdouble o1, o2; + + gts_triangle_vertices (t, &v1, &v2, &v3); + if (v == v2) { + v2 = v3; + v3 = v1; + } + else if (v == v3) { + v3 = v2; + v2 = v1; + } + else + g_assert (v == v1); + + if ((o1 = gts_point_orientation (GTS_POINT (v), GTS_POINT (v2), + GTS_POINT (s->v2))) >= 0. && + (o2 = gts_point_orientation (GTS_POINT (v3), GTS_POINT (v), + GTS_POINT (s->v2))) >= 0.) { + gdouble o3 = gts_point_orientation (GTS_POINT (v2), GTS_POINT (v3), + GTS_POINT (s->v2)); + GtsEdge * e = gts_triangle_edge_opposite (t, v); + GtsEdge * e1, * e2; + GtsFace * next = neighbor (GTS_FACE (t), e, surface); + + *ref = GTS_FACE (t); + gts_triangle_vertices_edges (t, e, &v2, &v3, &v, &e, &e2, &e1); + + g_slist_free (triangles); + + if (o3 >= 0.) /* @s->v2 is inside (or on the edge) of t */ + return NULL; + + gts_allow_floating_faces = TRUE; + gts_surface_remove_face (surface, GTS_FACE (t)); + gts_allow_floating_faces = FALSE; + + *left = g_slist_prepend (*left, e2); + *right = g_slist_prepend (*right, e1); + + g_assert (next); + return remove_intersected_edge (s, e, next, surface, left, right); + } + } + i = i->next; + } + + g_assert_not_reached (); + return NULL; +} + +/** + * gts_delaunay_add_constraint: + * @surface: a #GtsSurface. + * @c: a #GtsConstraint. + * + * Add constraint @c to the constrained Delaunay triangulation defined by + * @surface. + * + * Returns: a list of #GtsConstraint conflicting (i.e. intersecting) with @c + * which were removed from @surface (%NULL if there was none). + */ +GSList * gts_delaunay_add_constraint (GtsSurface * surface, + GtsConstraint * c) +{ + GSList * constraints; + GtsVertex * v1; //, * v2; + GSList * left = NULL, * right = NULL; + GtsFace * ref = NULL; + + g_return_val_if_fail (surface != NULL, NULL); + g_return_val_if_fail (c != NULL, NULL); + g_return_val_if_fail (GTS_IS_CONSTRAINT (c), NULL); + + v1 = GTS_SEGMENT (c)->v1; + //v2 = GTS_SEGMENT (c)->v2; + + gts_allow_floating_edges = TRUE; + constraints = remove_intersected_vertex (GTS_SEGMENT (c), v1, surface, + &left, &right, &ref); + gts_allow_floating_edges = FALSE; +#if 1 + triangulate_polygon (g_slist_prepend (g_slist_reverse (right), c), + surface, ref); + triangulate_polygon (g_slist_prepend (left, c), + surface, ref); +#else + right = g_slist_prepend (g_slist_reverse (right), c); + left = g_slist_prepend (left, c); + { + FILE * fp0 = fopen ("hole", "wt"); + FILE * fp1 = fopen ("right", "wt"); + FILE * fp2 = fopen ("left", "wt"); + GSList * i = left; + + gts_surface_write (surface, fp0); + fclose (fp0); + + fprintf (fp2, "LIST {\n"); + while (i) { + GtsSegment * s = i->data; + fprintf (fp2, + "# %p: %p->%p\n" + "VECT 1 2 0 2 0 %g %g 0 %g %g 0\n", + s, s->v1, s->v2, + GTS_POINT (s->v1)->x, GTS_POINT (s->v1)->y, + GTS_POINT (s->v2)->x, GTS_POINT (s->v2)->y); + i = i->next; + } + fprintf (fp2, "}\n"); + fprintf (fp1, "LIST {\n"); + i = right; + while (i) { + GtsSegment * s = i->data; + fprintf (fp1, + "# %p: %p->%p\n" + "VECT 1 2 0 2 0 %g %g 0 %g %g 0\n", + s, s->v1, s->v2, + GTS_POINT (s->v1)->x, GTS_POINT (s->v1)->y, + GTS_POINT (s->v2)->x, GTS_POINT (s->v2)->y); + i = i->next; + } + fprintf (fp1, "}\n"); + fclose (fp1); + fclose (fp2); + } + triangulate_polygon (right, surface); + triangulate_polygon (left, surface); +#endif + if (ref && !ref->surfaces) { + gts_allow_floating_edges = TRUE; + gts_object_destroy (GTS_OBJECT (ref)); + gts_allow_floating_edges = FALSE; + } + return constraints; +} + +static void delaunay_check (GtsTriangle * t, gpointer * data) +{ + GtsSurface * surface = data[0]; + GtsFace ** face = data[1]; + + if (*face == NULL) { + GSList * i, * list; + GtsVertex * v1, * v2, * v3; + + gts_triangle_vertices (t, &v1, &v2, &v3); + list = gts_vertex_neighbors (v1, NULL, surface); + list = gts_vertex_neighbors (v2, list, surface); + list = gts_vertex_neighbors (v3, list, surface); + i = list; + while (i && *face == NULL) { + GtsVertex * v = i->data; + if (v != v1 && v != v2 && v != v3 && + gts_point_in_circle (GTS_POINT (v), + GTS_POINT (v1), + GTS_POINT (v2), + GTS_POINT (v3)) > 0.) + *face = GTS_FACE (t); + i = i->next; + } + g_slist_free (list); + } +} + +/** + * gts_delaunay_check: + * @surface: a #GtsSurface. + * + * Returns: %NULL if the planar projection of @surface is a Delaunay + * triangulation (unconstrained), a #GtsFace violating the Delaunay + * property otherwise. + */ +GtsFace * gts_delaunay_check (GtsSurface * surface) +{ + GtsFace * face = NULL; + gpointer data[2]; + + g_return_val_if_fail (surface != NULL, FALSE); + + data[0] = surface; + data[1] = &face; + gts_surface_foreach_face (surface, (GtsFunc) delaunay_check, data); + + return face; +} + +/** + * gts_delaunay_remove_hull: + * @surface: a #GtsSurface. + * + * Removes all the edges of the boundary of @surface which are not + * constraints. + */ +void gts_delaunay_remove_hull (GtsSurface * surface) +{ + GSList * boundary; + + g_return_if_fail (surface != NULL); + + boundary = gts_surface_boundary (surface); + gts_allow_floating_edges = TRUE; + while (boundary) { + GSList * i = boundary; + GtsEdge * e = i->data; + + boundary = i->next; + g_slist_free_1 (i); + if (!GTS_IS_CONSTRAINT (e)) { + GtsTriangle * t = GTS_TRIANGLE (gts_edge_is_boundary (e, surface)); + + if (t != NULL) { + if (t->e1 != e && !GTS_IS_CONSTRAINT (t->e1) && + !gts_edge_is_boundary (t->e1, surface)) + boundary = g_slist_prepend (boundary, t->e1); + if (t->e2 != e && !GTS_IS_CONSTRAINT (t->e2) && + !gts_edge_is_boundary (t->e2, surface)) + boundary = g_slist_prepend (boundary, t->e2); + if (t->e3 != e && !GTS_IS_CONSTRAINT (t->e3) && + !gts_edge_is_boundary (t->e3, surface)) + boundary = g_slist_prepend (boundary, t->e3); + gts_surface_remove_face (surface, GTS_FACE (t)); + } + if (!e->triangles) + gts_object_destroy (GTS_OBJECT (e)); + } + } + gts_allow_floating_edges = FALSE; +} + +/* GtsListFace: Object */ + +static void gts_list_face_destroy (GtsObject * object) +{ + g_slist_free (GTS_LIST_FACE (object)->points); + + (* GTS_OBJECT_CLASS (gts_list_face_class ())->parent_class->destroy) + (object); +} + +static void gts_list_face_class_init (GtsFaceClass * klass) +{ + GTS_OBJECT_CLASS (klass)->destroy = gts_list_face_destroy; +} + +GtsFaceClass * gts_list_face_class (void) +{ + static GtsFaceClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo gts_list_face_info = { + "GtsListFace", + sizeof (GtsListFace), + sizeof (GtsFaceClass), + (GtsObjectClassInitFunc) gts_list_face_class_init, + (GtsObjectInitFunc) NULL, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_face_class ()), + >s_list_face_info); + } + + return klass; +} Index: toporouter/src_3rd/gts/container.c =================================================================== --- toporouter/src_3rd/gts/container.c (nonexistent) +++ toporouter/src_3rd/gts/container.c (revision 6803) @@ -0,0 +1,493 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "gts.h" + +/* GtsContainee */ + +static void containee_class_init (GtsContaineeClass * klass) +{ + klass->remove_container = NULL; + klass->add_container = NULL; + klass->foreach = NULL; + klass->is_contained = NULL; + klass->replace = NULL; +} + +GtsContaineeClass * gts_containee_class (void) +{ + static GtsContaineeClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo containee_info = { + "GtsContainee", + sizeof (GtsContainee), + sizeof (GtsContaineeClass), + (GtsObjectClassInitFunc) containee_class_init, + (GtsObjectInitFunc) NULL, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (gts_object_class (), + &containee_info); + } + + return klass; +} + +GtsContainee * gts_containee_new (GtsContaineeClass * klass) +{ + GtsContainee * object; + + object = GTS_CONTAINEE (gts_object_new (GTS_OBJECT_CLASS (klass))); + + return object; +} + +gboolean gts_containee_is_contained (GtsContainee * item, + GtsContainer * c) +{ + g_return_val_if_fail (item != NULL, FALSE); + g_return_val_if_fail (c != NULL, FALSE); + + if (GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->is_contained) + return + (* GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->is_contained) + (item, c); + return FALSE; +} + +void gts_containee_replace (GtsContainee * item, + GtsContainee * with) +{ + if (GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->replace) + (* GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->replace) (item, with); + if (GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->foreach) { + (* GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->foreach) + (item, (GtsFunc) gts_container_add, with); + (* GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->foreach) + (item, (GtsFunc) gts_container_remove, item); + } +} + +/* GtsSListContainee */ + +static void slist_containee_destroy (GtsObject * object) +{ + GtsSListContainee * item = GTS_SLIST_CONTAINEE (object); + GSList * i; + + i = item->containers; + while (i) { + GSList * next = i->next; + + gts_container_remove (i->data, GTS_CONTAINEE (item)); + i = next; + } + g_assert (item->containers == NULL); + + (* GTS_OBJECT_CLASS (gts_slist_containee_class ())->parent_class->destroy) + (object); +} + +static void slist_containee_remove_container (GtsContainee * i, + GtsContainer * c) +{ + GtsSListContainee * item = GTS_SLIST_CONTAINEE (i); + item->containers = g_slist_remove (item->containers, c); +} + +static void slist_containee_add_container (GtsContainee * i, + GtsContainer * c) +{ + GtsSListContainee * item = GTS_SLIST_CONTAINEE (i); + if (!g_slist_find (item->containers, c)) + item->containers = g_slist_prepend (item->containers, c); +} + +static void slist_containee_foreach (GtsContainee * c, + GtsFunc func, + gpointer data) +{ + GSList * i = GTS_SLIST_CONTAINEE (c)->containers; + + while (i) { + GSList * next = i->next; + + (* func) (i->data, data); + i = next; + } +} + +static gboolean slist_containee_is_contained (GtsContainee * i, + GtsContainer * c) +{ + return g_slist_find (GTS_SLIST_CONTAINEE (i)->containers, c) ? TRUE : FALSE; +} + +static void slist_containee_class_init (GtsSListContaineeClass * klass) +{ + GTS_CONTAINEE_CLASS (klass)->remove_container = + slist_containee_remove_container; + GTS_CONTAINEE_CLASS (klass)->add_container = + slist_containee_add_container; + GTS_CONTAINEE_CLASS (klass)->foreach = + slist_containee_foreach; + GTS_CONTAINEE_CLASS (klass)->is_contained = + slist_containee_is_contained; + + GTS_OBJECT_CLASS (klass)->destroy = slist_containee_destroy; +} + +static void slist_containee_init (GtsSListContainee * object) +{ + object->containers = NULL; +} + +GtsSListContaineeClass * gts_slist_containee_class (void) +{ + static GtsSListContaineeClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo slist_containee_info = { + "GtsSListContainee", + sizeof (GtsSListContainee), + sizeof (GtsSListContaineeClass), + (GtsObjectClassInitFunc) slist_containee_class_init, + (GtsObjectInitFunc) slist_containee_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_containee_class ()), + &slist_containee_info); + } + + return klass; +} + +/* GtsContainer */ + +static void remove_container (GtsContainee * item, GtsContainer * c) +{ + if (GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->remove_container) + (* GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->remove_container) + (item, c); +} + +static void container_destroy (GtsObject * object) +{ + GtsContainer * c = GTS_CONTAINER (object); + + gts_container_foreach (c, (GtsFunc) remove_container, c); + + (* GTS_OBJECT_CLASS (gts_container_class ())->parent_class->destroy) + (object); +} + +static void container_add (GtsContainer * c, GtsContainee * item) +{ + if (GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->add_container) + (* GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->add_container) + (item, c); +} + +static void container_remove (GtsContainer * c, GtsContainee * item) +{ + if (GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->remove_container) + (* GTS_CONTAINEE_CLASS (GTS_OBJECT (item)->klass)->remove_container) + (item, c); +} + +static void container_clone_add (GtsContainee * item, GtsContainer * clone) +{ + gts_container_add (clone, item); +} + +static void container_clone (GtsObject * clone, GtsObject * object) +{ + gts_object_init (clone, object->klass); + gts_container_foreach (GTS_CONTAINER (object), + (GtsFunc) container_clone_add, clone); +} + +static void container_class_init (GtsContainerClass * klass) +{ + klass->add = container_add; + klass->remove = container_remove; + klass->foreach = NULL; + klass->size = NULL; + + GTS_OBJECT_CLASS (klass)->destroy = container_destroy; + GTS_OBJECT_CLASS (klass)->clone = container_clone; +} + +GtsContainerClass * gts_container_class (void) +{ + static GtsContainerClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo container_info = { + "GtsContainer", + sizeof (GtsContainer), + sizeof (GtsContainerClass), + (GtsObjectClassInitFunc) container_class_init, + (GtsObjectInitFunc) NULL, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = + gts_object_class_new (GTS_OBJECT_CLASS (gts_slist_containee_class ()), + &container_info); + } + + return klass; +} + +GtsContainer * gts_container_new (GtsContainerClass * klass) +{ + GtsContainer * object; + + object = GTS_CONTAINER (gts_object_new (GTS_OBJECT_CLASS (klass))); + + return object; +} + +void gts_container_add (GtsContainer * c, + GtsContainee * item) +{ + g_return_if_fail (c != NULL); + g_return_if_fail (item != NULL); + + g_assert (GTS_CONTAINER_CLASS (GTS_OBJECT (c)->klass)->add); + (* GTS_CONTAINER_CLASS (GTS_OBJECT (c)->klass)->add) (c, item); +} + +void gts_container_remove (GtsContainer * c, + GtsContainee * item) +{ + g_return_if_fail (c != NULL); + g_return_if_fail (item != NULL); + + g_assert (GTS_CONTAINER_CLASS (GTS_OBJECT (c)->klass)->remove); + (* GTS_CONTAINER_CLASS (GTS_OBJECT (c)->klass)->remove) (c, item); +} + +void gts_container_foreach (GtsContainer * c, + GtsFunc func, + gpointer data) +{ + g_return_if_fail (c != NULL); + g_return_if_fail (func != NULL); + + if (GTS_CONTAINER_CLASS (GTS_OBJECT (c)->klass)->foreach) + (* GTS_CONTAINER_CLASS (GTS_OBJECT (c)->klass)->foreach) (c, func, data); +} + +guint gts_container_size (GtsContainer * c) +{ + g_return_val_if_fail (c != NULL, 0); + + if (GTS_CONTAINER_CLASS (GTS_OBJECT (c)->klass)->size) + return (* GTS_CONTAINER_CLASS (GTS_OBJECT (c)->klass)->size) (c); + return 0; +} + +/* GtsHashContainer */ + +static void hash_container_destroy (GtsObject * object) +{ + GHashTable * items = GTS_HASH_CONTAINER (object)->items; + + (* GTS_OBJECT_CLASS (gts_hash_container_class ())->parent_class->destroy) + (object); + + g_hash_table_destroy (items); +} + +static void hash_container_add (GtsContainer * c, GtsContainee * item) +{ + g_return_if_fail (GTS_HASH_CONTAINER (c)->frozen == FALSE); + + g_hash_table_insert (GTS_HASH_CONTAINER (c)->items, item, NULL); + + (* GTS_CONTAINER_CLASS (GTS_OBJECT_CLASS (gts_hash_container_class ())->parent_class)->add) (c, item); +} + +static void hash_container_remove (GtsContainer * c, GtsContainee * item) +{ + g_return_if_fail (GTS_HASH_CONTAINER (c)->frozen == FALSE); + + g_hash_table_remove (GTS_HASH_CONTAINER (c)->items, item); + + (* GTS_CONTAINER_CLASS (GTS_OBJECT_CLASS (gts_hash_container_class ())->parent_class)->remove) (c, item); +} + +static void hash_foreach (GtsContainee * item, + gpointer item_data, + gpointer * info) +{ + (* ((GtsFunc) info[0])) (item, info[1]); +} + +static void hash_container_foreach (GtsContainer * c, + GtsFunc func, + gpointer data) +{ + gpointer info[2]; + + info[0] = func; + info[1] = data; + /* prevent removing or adding items */ + GTS_HASH_CONTAINER (c)->frozen = TRUE; + g_hash_table_foreach (GTS_HASH_CONTAINER (c)->items, + (GHFunc) hash_foreach, info); + GTS_HASH_CONTAINER (c)->frozen = FALSE; +} + +static guint hash_container_size (GtsContainer * c) +{ + return g_hash_table_size (GTS_HASH_CONTAINER (c)->items); +} + +static void hash_container_class_init (GtsHashContainerClass * klass) +{ + GTS_CONTAINER_CLASS (klass)->add = hash_container_add; + GTS_CONTAINER_CLASS (klass)->remove = hash_container_remove; + GTS_CONTAINER_CLASS (klass)->foreach = hash_container_foreach; + GTS_CONTAINER_CLASS (klass)->size = hash_container_size; + + GTS_OBJECT_CLASS (klass)->destroy = hash_container_destroy; +} + +static void hash_container_init (GtsHashContainer * object) +{ + object->items = g_hash_table_new (NULL, NULL); + object->frozen = FALSE; +} + +GtsHashContainerClass * gts_hash_container_class (void) +{ + static GtsHashContainerClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo hash_container_info = { + "GtsHashContainer", + sizeof (GtsHashContainer), + sizeof (GtsHashContainerClass), + (GtsObjectClassInitFunc) hash_container_class_init, + (GtsObjectInitFunc) hash_container_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_container_class ()), + &hash_container_info); + } + + return klass; +} + +/* GtsSListContainer */ + +static void slist_container_destroy (GtsObject * object) +{ + GSList * items = GTS_SLIST_CONTAINER (object)->items; + + (* GTS_OBJECT_CLASS (gts_slist_container_class ())->parent_class->destroy) + (object); + + g_slist_free (items); +} + +static void slist_container_add (GtsContainer * c, GtsContainee * item) +{ + g_return_if_fail (GTS_SLIST_CONTAINER (c)->frozen == FALSE); + + if (!g_slist_find (GTS_SLIST_CONTAINER (c)->items, item)) + GTS_SLIST_CONTAINER (c)->items = + g_slist_prepend (GTS_SLIST_CONTAINER (c)->items, item); + + (* GTS_CONTAINER_CLASS (GTS_OBJECT_CLASS (gts_slist_container_class ())->parent_class)->add) (c, item); +} + +static void slist_container_remove (GtsContainer * c, GtsContainee * item) +{ + g_return_if_fail (GTS_SLIST_CONTAINER (c)->frozen == FALSE); + + GTS_SLIST_CONTAINER (c)->items = + g_slist_remove (GTS_SLIST_CONTAINER (c)->items, item); + + (* GTS_CONTAINER_CLASS (GTS_OBJECT_CLASS (gts_slist_container_class ())->parent_class)->remove) (c, item); +} + +static void slist_container_foreach (GtsContainer * c, + GtsFunc func, + gpointer data) +{ + GSList * i; + + i = GTS_SLIST_CONTAINER (c)->items; + while (i) { + GSList * next = i->next; + + (* func) (i->data, data); + i = next; + } +} + +static guint slist_container_size (GtsContainer * c) +{ + return g_slist_length (GTS_SLIST_CONTAINER (c)->items); +} + +static void slist_container_class_init (GtsSListContainerClass * klass) +{ + GTS_CONTAINER_CLASS (klass)->add = slist_container_add; + GTS_CONTAINER_CLASS (klass)->remove = slist_container_remove; + GTS_CONTAINER_CLASS (klass)->foreach = slist_container_foreach; + GTS_CONTAINER_CLASS (klass)->size = slist_container_size; + + GTS_OBJECT_CLASS (klass)->destroy = slist_container_destroy; +} + +static void slist_container_init (GtsSListContainer * object) +{ + object->items = NULL; + object->frozen = FALSE; +} + +GtsSListContainerClass * gts_slist_container_class (void) +{ + static GtsSListContainerClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo slist_container_info = { + "GtsSListContainer", + sizeof (GtsSListContainer), + sizeof (GtsSListContainerClass), + (GtsObjectClassInitFunc) slist_container_class_init, + (GtsObjectInitFunc) slist_container_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_container_class ()), + &slist_container_info); + } + + return klass; +} Index: toporouter/src_3rd/gts/curvature.c =================================================================== --- toporouter/src_3rd/gts/curvature.c (nonexistent) +++ toporouter/src_3rd/gts/curvature.c (revision 6803) @@ -0,0 +1,621 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999-2002 Ray Jones, Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "gts.h" + +static gboolean angle_obtuse (GtsVertex * v, GtsFace * f) +{ + GtsEdge * e = gts_triangle_edge_opposite (GTS_TRIANGLE (f), v); + GtsVector vec1, vec2; + + gts_vector_init (vec1, GTS_POINT (v), GTS_POINT (GTS_SEGMENT (e)->v1)); + gts_vector_init (vec2, GTS_POINT (v), GTS_POINT (GTS_SEGMENT (e)->v2)); + + return (gts_vector_scalar (vec1, vec2) < 0.0); +} + +static gboolean triangle_obtuse (GtsVertex * v, GtsFace * f) +{ + GtsEdge * e = gts_triangle_edge_opposite (GTS_TRIANGLE (f), v); + + return (angle_obtuse (v, f) || + angle_obtuse (GTS_SEGMENT (e)->v1, f) || + angle_obtuse (GTS_SEGMENT (e)->v2, f)); +} + +static gdouble cotan (GtsVertex * vo, GtsVertex * v1, GtsVertex * v2) +{ + /* cf. Appendix B of [Meyer et al 2002] */ + GtsVector u, v; + gdouble udotv, denom; + + gts_vector_init (u, GTS_POINT (vo), GTS_POINT (v1)); + gts_vector_init (v, GTS_POINT (vo), GTS_POINT (v2)); + + udotv = gts_vector_scalar (u, v); + denom = sqrt (gts_vector_scalar (u,u)*gts_vector_scalar (v,v) - + udotv*udotv); + + + /* denom can be zero if u==v. Returning 0 is acceptable, based on + * the callers of this function below. */ + if (denom == 0.0) return (0.0); + + return (udotv/denom); +} + +static gdouble angle_from_cotan (GtsVertex * vo, + GtsVertex * v1, GtsVertex * v2) +{ + /* cf. Appendix B and the caption of Table 1 from [Meyer et al 2002] */ + GtsVector u, v; + gdouble udotv, denom; + + gts_vector_init (u, GTS_POINT (vo), GTS_POINT (v1)); + gts_vector_init (v, GTS_POINT (vo), GTS_POINT (v2)); + + udotv = gts_vector_scalar (u, v); + denom = sqrt (gts_vector_scalar (u,u)*gts_vector_scalar (v,v) + - udotv*udotv); + + /* Note: I assume this is what they mean by using atan2 (). -Ray Jones */ + + /* tan = denom/udotv = y/x (see man page for atan2) */ + return (fabs (atan2 (denom, udotv))); +} + +static gdouble region_area (GtsVertex * v, GtsFace * f) +{ + /* cf. Section 3.3 of [Meyer et al 2002] */ + + if (gts_triangle_area (GTS_TRIANGLE (f)) == 0.0) return (0.0); + + if (triangle_obtuse (v, f)) { + if (angle_obtuse (v, f)) + return (gts_triangle_area (GTS_TRIANGLE (f))/2.0); + else + return (gts_triangle_area (GTS_TRIANGLE (f))/4.0); + } else { + GtsEdge * e = gts_triangle_edge_opposite (GTS_TRIANGLE (f), v); + + return ((cotan (GTS_SEGMENT (e)->v1, v, GTS_SEGMENT (e)->v2)* + gts_point_distance2 (GTS_POINT (v), + GTS_POINT (GTS_SEGMENT (e)->v2)) + + cotan (GTS_SEGMENT (e)->v2, v, GTS_SEGMENT (e)->v1)* + gts_point_distance2 (GTS_POINT (v), + GTS_POINT (GTS_SEGMENT (e)->v1))) + /8.0); + } +} + +/** + * gts_vertex_mean_curvature_normal: + * @v: a #GtsVertex. + * @s: a #GtsSurface. + * @Kh: the Mean Curvature Normal at @v. + * + * Computes the Discrete Mean Curvature Normal approximation at @v. + * The mean curvature at @v is half the magnitude of the vector @Kh. + * + * Note: the normal computed is not unit length, and may point either + * into or out of the surface, depending on the curvature at @v. It + * is the responsibility of the caller of the function to use the mean + * curvature normal appropriately. + * + * This approximation is from the paper: + * Discrete Differential-Geometry Operators for Triangulated 2-Manifolds + * Mark Meyer, Mathieu Desbrun, Peter Schroder, Alan H. Barr + * VisMath '02, Berlin (Germany) + * http://www-grail.usc.edu/pubs.html + * + * Returns: %TRUE if the operator could be evaluated, %FALSE if the + * evaluation failed for some reason (@v is boundary or is the + * endpoint of a non-manifold edge.) + */ +gboolean gts_vertex_mean_curvature_normal (GtsVertex * v, GtsSurface * s, + GtsVector Kh) +{ + GSList * faces, * edges, * i; + gdouble area = 0.0; + + g_return_val_if_fail (v != NULL, FALSE); + g_return_val_if_fail (s != NULL, FALSE); + + /* this operator is not defined for boundary edges */ + if (gts_vertex_is_boundary (v, s)) return (FALSE); + + faces = gts_vertex_faces (v, s, NULL); + g_return_val_if_fail (faces != NULL, FALSE); + + edges = gts_vertex_fan_oriented (v, s); + if (edges == NULL) { + g_slist_free (faces); + return (FALSE); + } + + i = faces; + while (i) { + GtsFace * f = i->data; + + area += region_area (v, f); + i = i->next; + } + g_slist_free (faces); + + Kh[0] = Kh[1] = Kh[2] = 0.0; + + i = edges; + while (i) { + GtsEdge * e = i->data; + GtsVertex * v1 = GTS_SEGMENT (e)->v1; + GtsVertex * v2 = GTS_SEGMENT (e)->v2; + gdouble temp; + + temp = cotan (v1, v, v2); + Kh[0] += temp*(GTS_POINT (v2)->x - GTS_POINT (v)->x); + Kh[1] += temp*(GTS_POINT (v2)->y - GTS_POINT (v)->y); + Kh[2] += temp*(GTS_POINT (v2)->z - GTS_POINT (v)->z); + + temp = cotan (v2, v, v1); + Kh[0] += temp*(GTS_POINT (v1)->x - GTS_POINT (v)->x); + Kh[1] += temp*(GTS_POINT (v1)->y - GTS_POINT (v)->y); + Kh[2] += temp*(GTS_POINT (v1)->z - GTS_POINT (v)->z); + + i = i->next; + } + g_slist_free (edges); + + if (area > 0.0) { + Kh[0] /= 2*area; + Kh[1] /= 2*area; + Kh[2] /= 2*area; + } else { + return (FALSE); + } + + return TRUE; +} + +/** + * gts_vertex_gaussian_curvature: + * @v: a #GtsVertex. + * @s: a #GtsSurface. + * @Kg: the Discrete Gaussian Curvature approximation at @v. + * + * Computes the Discrete Gaussian Curvature approximation at @v. + * + * This approximation is from the paper: + * Discrete Differential-Geometry Operators for Triangulated 2-Manifolds + * Mark Meyer, Mathieu Desbrun, Peter Schroder, Alan H. Barr + * VisMath '02, Berlin (Germany) + * http://www-grail.usc.edu/pubs.html + * + * Returns: %TRUE if the operator could be evaluated, %FALSE if the + * evaluation failed for some reason (@v is boundary or is the + * endpoint of a non-manifold edge.) + */ +gboolean gts_vertex_gaussian_curvature (GtsVertex * v, GtsSurface * s, + gdouble * Kg) +{ + GSList * faces, * edges, * i; + gdouble area = 0.0; + gdouble angle_sum = 0.0; + + g_return_val_if_fail (v != NULL, FALSE); + g_return_val_if_fail (s != NULL, FALSE); + g_return_val_if_fail (Kg != NULL, FALSE); + + /* this operator is not defined for boundary edges */ + if (gts_vertex_is_boundary (v, s)) return (FALSE); + + faces = gts_vertex_faces (v, s, NULL); + g_return_val_if_fail (faces != NULL, FALSE); + + edges = gts_vertex_fan_oriented (v, s); + if (edges == NULL) { + g_slist_free (faces); + return (FALSE); + } + + i = faces; + while (i) { + GtsFace * f = i->data; + + area += region_area (v, f); + i = i->next; + } + g_slist_free (faces); + + i = edges; + while (i) { + GtsEdge * e = i->data; + GtsVertex * v1 = GTS_SEGMENT (e)->v1; + GtsVertex * v2 = GTS_SEGMENT (e)->v2; + + angle_sum += angle_from_cotan (v, v1, v2); + i = i->next; + } + g_slist_free (edges); + + *Kg = (2.0*M_PI - angle_sum)/area; + + return TRUE; +} + +/** + * gts_vertex_principal_curvatures: + * @Kh: mean curvature. + * @Kg: Gaussian curvature. + * @K1: first principal curvature. + * @K2: second principal curvature. + * + * Computes the principal curvatures at a point given the mean and + * Gaussian curvatures at that point. + * + * The mean curvature can be computed as one-half the magnitude of the + * vector computed by gts_vertex_mean_curvature_normal(). + * + * The Gaussian curvature can be computed with + * gts_vertex_gaussian_curvature(). + */ +void gts_vertex_principal_curvatures (gdouble Kh, gdouble Kg, + gdouble * K1, gdouble * K2) +{ + gdouble temp = Kh*Kh - Kg; + + g_return_if_fail (K1 != NULL); + g_return_if_fail (K2 != NULL); + + if (temp < 0.0) temp = 0.0; + temp = sqrt (temp); + *K1 = Kh + temp; + *K2 = Kh - temp; +} + +/* from Maple */ +static void linsolve (gdouble m11, gdouble m12, gdouble b1, + gdouble m21, gdouble m22, gdouble b2, + gdouble * x1, gdouble * x2) +{ + gdouble temp; + + temp = 1.0 / (m21*m12 - m11*m22); + *x1 = (m12*b2 - m22*b1)*temp; + *x2 = (m11*b2 - m21*b1)*temp; +} + +/* from Maple - largest eigenvector of [a b; b c] */ +static void eigenvector (gdouble a, gdouble b, gdouble c, + GtsVector e) +{ + if (b == 0.0) { + e[0] = 0.0; + } else { + e[0] = -(c - a - sqrt (c*c - 2*a*c + a*a + 4*b*b))/(2*b); + } + e[1] = 1.0; + e[2] = 0.0; +} + +/** + * gts_vertex_principal_directions: + * @v: a #GtsVertex. + * @s: a #GtsSurface. + * @Kh: mean curvature normal (a #GtsVector). + * @Kg: Gaussian curvature (a gdouble). + * @e1: first principal curvature direction (direction of largest curvature). + * @e2: second principal curvature direction. + * + * Computes the principal curvature directions at a point given @Kh + * and @Kg, the mean curvature normal and Gaussian curvatures at that + * point, computed with gts_vertex_mean_curvature_normal() and + * gts_vertex_gaussian_curvature(), respectively. + * + * Note that this computation is very approximate and tends to be + * unstable. Smoothing of the surface or the principal directions may + * be necessary to achieve reasonable results. + */ +void gts_vertex_principal_directions (GtsVertex * v, GtsSurface * s, + GtsVector Kh, gdouble Kg, + GtsVector e1, GtsVector e2) +{ + GtsVector N; + gdouble normKh; + GSList * i, * j; + GtsVector basis1, basis2, d, eig; + gdouble ve2, vdotN; + gdouble aterm_da, bterm_da, cterm_da, const_da; + gdouble aterm_db, bterm_db, cterm_db, const_db; + gdouble a, b, c; + gdouble K1, K2; + gdouble *weights, *kappas, *d1s, *d2s; + gint edge_count; + gdouble err_e1, err_e2; + int e; + + /* compute unit normal */ + normKh = sqrt (gts_vector_scalar (Kh, Kh)); + + if (normKh > 0.0) { + N[0] = Kh[0] / normKh; + N[1] = Kh[1] / normKh; + N[2] = Kh[2] / normKh; + } else { + /* This vertex is a point of zero mean curvature (flat or saddle + * point). Compute a normal by averaging the adjacent triangles + */ + N[0] = N[1] = N[2] = 0.0; + i = gts_vertex_faces (v, s, NULL); + while (i) { + gdouble x, y, z; + gts_triangle_normal (GTS_TRIANGLE ((GtsFace *) i->data), + &x, &y, &z); + N[0] += x; + N[1] += y; + N[2] += z; + + i = i->next; + } + g_return_if_fail (gts_vector_norm (N) > 0.0); + gts_vector_normalize (N); + } + + + /* construct a basis from N: */ + /* set basis1 to any component not the largest of N */ + basis1[0] = basis1[1] = basis1[2] = 0.0; + if (fabs (N[0]) > fabs (N[1])) + basis1[1] = 1.0; + else + basis1[0] = 1.0; + + /* make basis2 orthogonal to N */ + gts_vector_cross (basis2, N, basis1); + gts_vector_normalize (basis2); + + /* make basis1 orthogonal to N and basis2 */ + gts_vector_cross (basis1, N, basis2); + gts_vector_normalize (basis1); + + aterm_da = bterm_da = cterm_da = const_da = 0.0; + aterm_db = bterm_db = cterm_db = const_db = 0.0; + + weights = g_malloc (sizeof (gdouble)*g_slist_length (v->segments)); + kappas = g_malloc (sizeof (gdouble)*g_slist_length (v->segments)); + d1s = g_malloc (sizeof (gdouble)*g_slist_length (v->segments)); + d2s = g_malloc (sizeof (gdouble)*g_slist_length (v->segments)); + edge_count = 0; + + i = v->segments; + while (i) { + GtsEdge * e; + GtsFace * f1, * f2; + gdouble weight, kappa, d1, d2; + GtsVector vec_edge; + + if (! GTS_IS_EDGE (i->data)) { + i = i->next; + continue; + } + + e = i->data; + + /* since this vertex passed the tests in + * gts_vertex_mean_curvature_normal(), this should be true. */ + g_assert (gts_edge_face_number (e, s) == 2); + + /* identify the two triangles bordering e in s */ + f1 = f2 = NULL; + j = e->triangles; + while (j) { + if ((! GTS_IS_FACE (j->data)) || + (! gts_face_has_parent_surface (GTS_FACE (j->data), s))) { + j = j->next; + continue; + } + if (f1 == NULL) + f1 = GTS_FACE (j->data); + else { + f2 = GTS_FACE (j->data); + break; + } + j = j->next; + } + g_assert (f2 != NULL); + + /* We are solving for the values of the curvature tensor + * B = [ a b ; b c ]. + * The computations here are from section 5 of [Meyer et al 2002]. + * + * The first step is to calculate the linear equations governing + * the values of (a,b,c). These can be computed by setting the + * derivatives of the error E to zero (section 5.3). + * + * Since a + c = norm(Kh), we only compute the linear equations + * for dE/da and dE/db. (NB: [Meyer et al 2002] has the + * equation a + b = norm(Kh), but I'm almost positive this is + * incorrect.) + * + * Note that the w_ij (defined in section 5.2) are all scaled by + * (1/8*A_mixed). We drop this uniform scale factor because the + * solution of the linear equations doesn't rely on it. + * + * The terms of the linear equations are xterm_dy with x in + * {a,b,c} and y in {a,b}. There are also const_dy terms that are + * the constant factors in the equations. + */ + + /* find the vector from v along edge e */ + gts_vector_init (vec_edge, GTS_POINT (v), + GTS_POINT ((GTS_SEGMENT (e)->v1 == v) ? + GTS_SEGMENT (e)->v2 : GTS_SEGMENT (e)->v1)); + ve2 = gts_vector_scalar (vec_edge, vec_edge); + vdotN = gts_vector_scalar (vec_edge, N); + + /* section 5.2 - There is a typo in the computation of kappa. The + * edges should be x_j-x_i. + */ + kappa = 2.0 * vdotN / ve2; + + /* section 5.2 */ + + /* I don't like performing a minimization where some of the + * weights can be negative (as can be the case if f1 or f2 are + * obtuse). To ensure all-positive weights, we check for + * obtuseness and use values similar to those in region_area(). */ + weight = 0.0; + if (! triangle_obtuse(v, f1)) { + weight += ve2 * + cotan (gts_triangle_vertex_opposite (GTS_TRIANGLE (f1), e), + GTS_SEGMENT (e)->v1, GTS_SEGMENT (e)->v2) / 8.0; + } else { + if (angle_obtuse (v, f1)) { + weight += ve2 * gts_triangle_area (GTS_TRIANGLE (f1)) / 4.0; + } else { + weight += ve2 * gts_triangle_area (GTS_TRIANGLE (f1)) / 8.0; + } + } + + if (! triangle_obtuse(v, f2)) { + weight += ve2 * + cotan (gts_triangle_vertex_opposite (GTS_TRIANGLE (f2), e), + GTS_SEGMENT (e)->v1, GTS_SEGMENT (e)->v2) / 8.0; + } else { + if (angle_obtuse (v, f2)) { + weight += ve2 * gts_triangle_area (GTS_TRIANGLE (f2)) / 4.0; + } else { + weight += ve2 * gts_triangle_area (GTS_TRIANGLE (f2)) / 8.0; + } + } + + /* projection of edge perpendicular to N (section 5.3) */ + d[0] = vec_edge[0] - vdotN * N[0]; + d[1] = vec_edge[1] - vdotN * N[1]; + d[2] = vec_edge[2] - vdotN * N[2]; + gts_vector_normalize (d); + + /* not explicit in the paper, but necessary. Move d to 2D basis. */ + d1 = gts_vector_scalar (d, basis1); + d2 = gts_vector_scalar (d, basis2); + + /* store off the curvature, direction of edge, and weights for later use */ + weights[edge_count] = weight; + kappas[edge_count] = kappa; + d1s[edge_count] = d1; + d2s[edge_count] = d2; + edge_count++; + + /* Finally, update the linear equations */ + aterm_da += weight * d1 * d1 * d1 * d1; + bterm_da += weight * d1 * d1 * 2 * d1 * d2; + cterm_da += weight * d1 * d1 * d2 * d2; + const_da += weight * d1 * d1 * (- kappa); + + aterm_db += weight * d1 * d2 * d1 * d1; + bterm_db += weight * d1 * d2 * 2 * d1 * d2; + cterm_db += weight * d1 * d2 * d2 * d2; + const_db += weight * d1 * d2 * (- kappa); + + i = i->next; + } + + /* now use the identity (Section 5.3) a + c = |Kh| = 2 * kappa_h */ + aterm_da -= cterm_da; + const_da += cterm_da * normKh; + + aterm_db -= cterm_db; + const_db += cterm_db * normKh; + + /* check for solvability of the linear system */ + if (((aterm_da * bterm_db - aterm_db * bterm_da) != 0.0) && + ((const_da != 0.0) || (const_db != 0.0))) { + linsolve (aterm_da, bterm_da, -const_da, + aterm_db, bterm_db, -const_db, + &a, &b); + + c = normKh - a; + + eigenvector (a, b, c, eig); + } else { + /* region of v is planar */ + eig[0] = 1.0; + eig[1] = 0.0; + } + + /* Although the eigenvectors of B are good estimates of the + * principal directions, it seems that which one is attached to + * which curvature direction is a bit arbitrary. This may be a bug + * in my implementation, or just a side-effect of the inaccuracy of + * B due to the discrete nature of the sampling. + * + * To overcome this behavior, we'll evaluate which assignment best + * matches the given eigenvectors by comparing the curvature + * estimates computed above and the curvatures calculated from the + * discrete differential operators. */ + + gts_vertex_principal_curvatures (0.5 * normKh, Kg, &K1, &K2); + + err_e1 = err_e2 = 0.0; + /* loop through the values previously saved */ + for (e = 0; e < edge_count; e++) { + gdouble weight, kappa, d1, d2; + gdouble temp1, temp2; + gdouble delta; + + weight = weights[e]; + kappa = kappas[e]; + d1 = d1s[e]; + d2 = d2s[e]; + + temp1 = fabs (eig[0] * d1 + eig[1] * d2); + temp1 = temp1 * temp1; + temp2 = fabs (eig[1] * d1 - eig[0] * d2); + temp2 = temp2 * temp2; + + /* err_e1 is for K1 associated with e1 */ + delta = K1 * temp1 + K2 * temp2 - kappa; + err_e1 += weight * delta * delta; + + /* err_e2 is for K1 associated with e2 */ + delta = K2 * temp1 + K1 * temp2 - kappa; + err_e2 += weight * delta * delta; + } + g_free (weights); + g_free (kappas); + g_free (d1s); + g_free (d2s); + + /* rotate eig by a right angle if that would decrease the error */ + if (err_e2 < err_e1) { + gdouble temp = eig[0]; + + eig[0] = eig[1]; + eig[1] = -temp; + } + + e1[0] = eig[0] * basis1[0] + eig[1] * basis2[0]; + e1[1] = eig[0] * basis1[1] + eig[1] * basis2[1]; + e1[2] = eig[0] * basis1[2] + eig[1] * basis2[2]; + gts_vector_normalize (e1); + + /* make N,e1,e2 a right handed coordinate sytem */ + gts_vector_cross (e2, N, e1); + gts_vector_normalize (e2); +} Index: toporouter/src_3rd/gts/edge.c =================================================================== --- toporouter/src_3rd/gts/edge.c (nonexistent) +++ toporouter/src_3rd/gts/edge.c (revision 6803) @@ -0,0 +1,582 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "gts.h" + +gboolean gts_allow_floating_edges = FALSE; + +static void edge_destroy (GtsObject * object) +{ + GtsEdge * edge = GTS_EDGE (object); + GSList * i; + + i = edge->triangles; + while (i) { + GSList * next = i->next; + gts_object_destroy (i->data); + i = next; + } + g_assert (edge->triangles == NULL); + + (* GTS_OBJECT_CLASS (gts_edge_class ())->parent_class->destroy) (object); +} + +static void edge_clone (GtsObject * clone, GtsObject * object) +{ + (* GTS_OBJECT_CLASS (gts_edge_class ())->parent_class->clone) (clone, + object); + GTS_SEGMENT (clone)->v1 = GTS_SEGMENT (clone)->v2 = NULL; + GTS_EDGE (clone)->triangles = NULL; +} + +static void edge_class_init (GtsObjectClass * klass) +{ + klass->clone = edge_clone; + klass->destroy = edge_destroy; +} + +static void edge_init (GtsEdge * edge) +{ + edge->triangles = NULL; +} + +/** + * gts_edge_class: + * + * Returns: the #GtsEdgeClass. + */ +GtsEdgeClass * gts_edge_class (void) +{ + static GtsEdgeClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo edge_info = { + "GtsEdge", + sizeof (GtsEdge), + sizeof (GtsEdgeClass), + (GtsObjectClassInitFunc) edge_class_init, + (GtsObjectInitFunc) edge_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_segment_class ()), + &edge_info); + } + + return klass; +} + +/** + * gts_edge_new: + * @klass: a #GtsEdgeClass. + * @v1: a #GtsVertex. + * @v2: a #GtsVertex. + * + * Returns: a new #GtsEdge linking @v1 and @v2. + */ +GtsEdge * gts_edge_new (GtsEdgeClass * klass, + GtsVertex * v1, GtsVertex * v2) +{ + return GTS_EDGE (gts_segment_new (GTS_SEGMENT_CLASS (klass), v1, v2)); +} + +void gts_edge_remove(GtsEdge *edge) +{ + edge->segment.v1->segments = g_slist_remove(edge->segment.v1->segments, &edge->segment); + edge->segment.v2->segments = g_slist_remove(edge->segment.v2->segments, &edge->segment); + edge_destroy(GTS_OBJECT (edge)); +} + +/** + * gts_edge_replace: + * @e: a #GtsEdge. + * @with: a #GtsEdge. + * + * Replaces @e with @with. For each triangle which uses @e as an + * edge, @e is replaced with @with. The @with->triangles list is + * updated appropriately and the @e->triangles list is freed and set + * to %NULL. + */ +void gts_edge_replace (GtsEdge * e, GtsEdge * with) +{ + GSList * i; + + g_return_if_fail (e != NULL && with != NULL && e != with); + + i = e->triangles; + while (i) { + GtsTriangle * t = i->data; + if (t->e1 == e) t->e1 = with; + if (t->e2 == e) t->e2 = with; + if (t->e3 == e) t->e3 = with; + if (!g_slist_find (with->triangles, t)) + with->triangles = g_slist_prepend (with->triangles, t); + i = i->next; + } + g_slist_free (e->triangles); + e->triangles = NULL; +} + +/** + * gts_edge_has_parent_surface: + * @e: a #GtsEdge. + * @surface: a #GtsSurface. + * + * Returns: a #GtsFace of @surface having @e as an edge, %NULL otherwise. + */ +GtsFace * gts_edge_has_parent_surface (GtsEdge * e, GtsSurface * surface) +{ + GSList * i; + + g_return_val_if_fail (e != NULL, NULL); + + i = e->triangles; + while (i) { + if (GTS_IS_FACE (i->data) && + gts_face_has_parent_surface (i->data, surface)) + return i->data; + i = i->next; + } + return NULL; +} + +/** + * gts_edge_has_any_parent_surface: + * @e: a #GtsEdge. + * + * Returns: %NULL if @e is not an edge of any triangle or if all the + * faces having @e has an edge do not belong to any surface, + * a #GtsFace belonging to a surface and having @e as an edge. + */ +GtsFace * gts_edge_has_any_parent_surface (GtsEdge * e) +{ + GSList * i; + + g_return_val_if_fail (e != NULL, NULL); + + i = e->triangles; + while (i) { + GtsTriangle * t = i->data; + if (GTS_IS_FACE (t) && GTS_FACE (t)->surfaces != NULL) + return GTS_FACE (t); + i = i->next; + } + return NULL; +} + +/** + * gts_edge_is_boundary: + * @e: a #GtsEdge. + * @surface: a #GtsSurface or %NULL. + * + * Returns: the unique #GtsFace (which belongs to @surface) and which + * has @e as an edge (i.e. @e is a boundary edge (of @surface)) or %NULL + * if there is more than one or no faces (belonging to @surface) and + * with @e as an edge. + */ +GtsFace * gts_edge_is_boundary (GtsEdge * e, GtsSurface * surface) +{ + GSList * i; + GtsFace * f = NULL; + + g_return_val_if_fail (e != NULL, NULL); + + i = e->triangles; + while (i) { + if (GTS_IS_FACE (i->data)) { + if (!surface || gts_face_has_parent_surface (i->data, surface)) { + if (f != NULL) + return NULL; + f = i->data; + } + } + i = i->next; + } + return f; +} + +/** + * gts_edges_from_vertices: + * @vertices: a list of #GtsVertex. + * @parent: a #GtsSurface. + * + * Returns: a list of unique #GtsEdge which have one of their vertices in + * @vertices and are used by a face of @parent. + */ +GSList * gts_edges_from_vertices (GSList * vertices, GtsSurface * parent) +{ + GHashTable * hash; + GSList * edges = NULL, * i; + + g_return_val_if_fail (parent != NULL, NULL); + + hash = g_hash_table_new (NULL, NULL); + i = vertices; + while (i) { + GSList * j = GTS_VERTEX (i->data)->segments; + while (j) { + GtsSegment * s = j->data; + if (GTS_IS_EDGE (s) && + gts_edge_has_parent_surface (GTS_EDGE (s), parent) && + g_hash_table_lookup (hash, s) == NULL) { + edges = g_slist_prepend (edges, s); + g_hash_table_insert (hash, s, i); + } + j = j->next; + } + i = i->next; + } + g_hash_table_destroy (hash); + return edges; +} + +/** + * gts_edge_face_number: + * @e: a #GtsEdge. + * @s: a #GtsSurface. + * + * Returns: the number of faces using @e and belonging to @s. + */ +guint gts_edge_face_number (GtsEdge * e, GtsSurface * s) +{ + GSList * i; + guint nt = 0; + + g_return_val_if_fail (e != NULL, 0); + g_return_val_if_fail (s != NULL, 0); + + i = e->triangles; + while (i) { + if (GTS_IS_FACE (i->data) && + gts_face_has_parent_surface (GTS_FACE (i->data), s)) + nt++; + i = i->next; + } + return nt; +} + +/** + * gts_edge_is_duplicate: + * @e: a #GtsEdge. + * + * Returns: the first #GtsEdge different from @e which shares the + * same endpoints or %NULL if there is none. + */ +GtsEdge * gts_edge_is_duplicate (GtsEdge * e) +{ + GSList * i; + GtsVertex * v2; + + g_return_val_if_fail (e != NULL, NULL); + + v2 = GTS_SEGMENT (e)->v2; + i = GTS_SEGMENT (e)->v1->segments; + if (GTS_SEGMENT (e)->v1 == v2) /* e is degenerate: special treatment */ + while (i) { + GtsSegment * s = i->data; + if (s != GTS_SEGMENT (e) && + GTS_IS_EDGE (s) && + s->v1 == v2 && s->v2 == v2) + return GTS_EDGE (s); + i = i->next; + } + else /* e is not degenerate */ + while (i) { + GtsSegment * s = i->data; + if (s != GTS_SEGMENT (e) && + GTS_IS_EDGE (s) && + (s->v1 == v2 || s->v2 == v2)) + return GTS_EDGE (s); + i = i->next; + } + return NULL; +} + +/** + * gts_edges_merge: + * @edges: a list of #GtsEdge. + * + * For each edge in @edges check if it is duplicated (as + * returned by gts_edge_is_duplicate()). If it is replace it by its + * duplicate, destroy it and remove it from the list. + * + * Returns: the updated @edges list. + */ +GList * gts_edges_merge (GList * edges) +{ + GList * i = edges; + + /* we want to control edge destruction */ + gts_allow_floating_edges = TRUE; + while (i) { + GtsEdge * e = i->data; + GtsEdge * de = gts_edge_is_duplicate (e); + if (de) { + GList * next = i->next; + edges = g_list_remove_link (edges, i); + g_list_free_1 (i); + i = next; + gts_edge_replace (e, de); + gts_object_destroy (GTS_OBJECT (e)); + } + else + i = i->next; + } + gts_allow_floating_edges = FALSE;; + + return edges; +} + +static void triangle_vertices_edges (GtsTriangle * t, + GtsEdge * e, + GtsVertex ** v, + GtsEdge ** ee1, + GtsEdge ** ee2) +{ + GtsEdge * e1 = t->e1, * e2 = t->e2, * e3 = t->e3; + GtsVertex * v1 = GTS_SEGMENT (e)->v1; + + if (e1 == e) e1 = e3; + else if (e2 == e) e2 = e3; + else g_assert (e3 == e); + + if (GTS_SEGMENT (e2)->v1 == v1 || GTS_SEGMENT (e2)->v2 == v1) { + e3 = e1; e1 = e2; e2 = e3; + } + if (GTS_SEGMENT (e1)->v1 == v1) + *v = GTS_SEGMENT (e1)->v2; + else + *v = GTS_SEGMENT (e1)->v1; + *ee1 = e1; + *ee2 = e2; +} + +/** + * gts_edge_belongs_to_tetrahedron: + * @e: a #GtsEdge. + * + * Returns: %TRUE if @e is used by faces forming a tetrahedron, %FALSE + * otherwise. + */ +gboolean gts_edge_belongs_to_tetrahedron (GtsEdge * e) +{ + GSList * i; + + g_return_val_if_fail (e != NULL, FALSE); + + i = e->triangles; + while (i) { + GtsEdge * e1, * e2; + GtsVertex * vt1; + GSList * j = i->next; + triangle_vertices_edges (i->data, e, &vt1, &e1, &e2); + while (j) { + GtsSegment * s5; + GtsEdge * e3, * e4; + GtsVertex * vt2; + + triangle_vertices_edges (j->data, e, &vt2, &e3, &e4); + s5 = gts_vertices_are_connected (vt1, vt2); + if (GTS_IS_EDGE (s5) && + gts_triangle_use_edges (e1, e3, GTS_EDGE (s5)) && + gts_triangle_use_edges (e2, e4, GTS_EDGE (s5))) + return TRUE; + j = j->next; + } + i = i->next; + } + + return FALSE; +} + +#define edge_use_vertex(e, v) (GTS_SEGMENT(e)->v1 == v ||\ + GTS_SEGMENT(e)->v2 == v) + +static GtsEdge * next_edge (GtsTriangle * t, + GtsEdge * e1, + GtsEdge * e) +{ + GtsVertex * v1 = GTS_SEGMENT (e)->v1; + GtsVertex * v2 = GTS_SEGMENT (e)->v2; + + if (t->e1 != e1 && t->e1 != e && + (edge_use_vertex (t->e1, v1) || edge_use_vertex (t->e1, v2))) + return t->e1; + else if (t->e2 != e1 && t->e2 != e && + (edge_use_vertex (t->e2, v1) || edge_use_vertex (t->e2, v2))) + return t->e2; + else if (t->e3 != e1 && t->e3 != e && + (edge_use_vertex (t->e3, v1) || edge_use_vertex (t->e3, v2))) + return t->e3; + g_assert_not_reached (); + return NULL; +} + +static void triangle_next (GtsEdge * e1, GtsEdge * e) +{ + GSList * i; + + i = e1->triangles; + while (i) { + GtsTriangle * t = i->data; + if (GTS_OBJECT (t)->reserved) { + GTS_OBJECT (t)->reserved = NULL; + triangle_next (next_edge (t, e1, e), e); + } + i = i->next; + } +} + +/** + * gts_edge_is_contact: + * @e: a #GtsEdge. + * + * Returns: the number of sets of connected triangles sharing @e as a + * contact edge. + */ +guint gts_edge_is_contact (GtsEdge * e) +{ + GSList * i, * triangles; + guint ncomponent = 0; + + g_return_val_if_fail (e != NULL, 0); + + triangles = gts_vertex_triangles (GTS_SEGMENT (e)->v1, NULL); + i = triangles = gts_vertex_triangles (GTS_SEGMENT (e)->v2, triangles); + while (i) { + GTS_OBJECT (i->data)->reserved = i; + i = i->next; + } + + i = e->triangles; + while (i) { + GtsTriangle * t = i->data; + if (GTS_OBJECT (t)->reserved) { + GtsEdge * e1; + GTS_OBJECT (t)->reserved = NULL; + e1 = next_edge (t, NULL, e); + triangle_next (e1, e); + triangle_next (next_edge (t, e1, e), e); + ncomponent++; + } + i = i->next; + } + + g_slist_foreach (triangles, (GFunc) gts_object_reset_reserved, NULL); + g_slist_free (triangles); + + return ncomponent; +} + +/** + * gts_edge_swap: + * @e: a #GtsEdge. + * @s: a #GtsSurface. + * + * Performs an "edge swap" on the two triangles sharing @e and + * belonging to @s. + */ +void gts_edge_swap (GtsEdge * e, GtsSurface * s) +{ + GtsTriangle * t1 = NULL, * t2 = NULL, * t; + GtsFace * f; + GSList * i; + GtsVertex * v1, * v2, * v3, * v4, * v5, * v6; + GtsEdge * e1, * e2, * e3, * e4; + GtsSegment * v3v6; + + g_return_if_fail (e != NULL); + g_return_if_fail (s != NULL); + + i = e->triangles; + while (i) { + if (GTS_IS_FACE (i->data) && gts_face_has_parent_surface (i->data, s)) { + if (!t1) + t1 = i->data; + else if (!t2) + t2 = i->data; + else + g_return_if_fail (gts_edge_face_number (e, s) == 2); + } + i = i->next; + } + g_assert (t1 && t2); + + gts_triangle_vertices_edges (t1, e, &v1, &v2, &v3, &e, &e1, &e2); + gts_triangle_vertices_edges (t2, e, &v4, &v5, &v6, &e, &e3, &e4); + g_assert (v2 == v4 && v1 == v5); + + v3v6 = gts_vertices_are_connected (v3, v6); + if (!GTS_IS_EDGE (v3v6)) + v3v6 = GTS_SEGMENT (gts_edge_new (s->edge_class, v3, v6)); + f = gts_face_new (s->face_class, e1, GTS_EDGE (v3v6), e4); + if ((t = gts_triangle_is_duplicate (GTS_TRIANGLE (f))) && + GTS_IS_FACE (t)) { + gts_object_destroy (GTS_OBJECT (f)); + f = GTS_FACE (t); + } + gts_surface_add_face (s, f); + + f = gts_face_new (s->face_class, GTS_EDGE (v3v6), e2, e3); + if ((t = gts_triangle_is_duplicate (GTS_TRIANGLE (f))) && + GTS_IS_FACE (t)) { + gts_object_destroy (GTS_OBJECT (f)); + f = GTS_FACE (t); + } + gts_surface_add_face (s, f); + + gts_surface_remove_face (s, GTS_FACE (t1)); + gts_surface_remove_face (s, GTS_FACE (t2)); +} + +/** + * gts_edge_manifold_faces: + * @e: a #GtsEdge. + * @s: a #GtsSurface. + * @f1: pointer for first face. + * @f2: pointer for second face. + * + * If @e is a manifold edge of surface @s, fills @f1 and @f2 with the + * faces belonging to @s and sharing @e. + * + * Returns: %TRUE if @e is a manifold edge, %FALSE otherwise. + */ +gboolean gts_edge_manifold_faces (GtsEdge * e, GtsSurface * s, + GtsFace ** f1, GtsFace ** f2) +{ + GSList * i; + + g_return_val_if_fail (e != NULL, FALSE); + g_return_val_if_fail (s != NULL, FALSE); + g_return_val_if_fail (f1 != NULL, FALSE); + g_return_val_if_fail (f2 != NULL, FALSE); + + *f1 = *f2 = NULL; + i = e->triangles; + while (i) { + if (GTS_IS_FACE (i->data) && gts_face_has_parent_surface (i->data, s)) { + if (!(*f1)) *f1 = i->data; + else if (!(*f2)) *f2 = i->data; + else return FALSE; + } + i = i->next; + } + + return (*f1 && *f2); +} Index: toporouter/src_3rd/gts/eheap.c =================================================================== --- toporouter/src_3rd/gts/eheap.c (nonexistent) +++ toporouter/src_3rd/gts/eheap.c (revision 6803) @@ -0,0 +1,461 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "gts.h" + +#define PARENT(i) ((i) >= 2 ? (i)/2 : 0) +#define LEFT_CHILD(i) (2*(i)) +#define RIGHT_CHILD(i) (2*(i) + 1) + + +/** + * gts_eheap_new: + * @key_func: a #GtsKeyFunc or %NULL. + * @data: user data to be passed to @key_func. + * + * Returns: a new #GtsEHeap using @key_func as key. + */ +GtsEHeap * gts_eheap_new (GtsKeyFunc key_func, + gpointer data) +{ + GtsEHeap * heap; + + heap = g_malloc (sizeof(GtsEHeap)); + heap->elts = g_ptr_array_new (); + heap->func = key_func; + heap->data = data; + heap->frozen = FALSE; + heap->randomized = FALSE; + return heap; +} + +static void sift_up (GtsEHeap * heap, guint i) +{ + GtsEHeapPair * parent, * child; + guint p; + gpointer * pdata = heap->elts->pdata; + gdouble key; + + child = pdata[i - 1]; + key = child->key; + while ((p = PARENT (i))) { + parent = pdata[p - 1]; + if (parent->key > key || + (heap->randomized && parent->key == key && rand () < RAND_MAX/2)) { + pdata[p - 1] = child; + pdata[i - 1] = parent; + child->pos = p; + parent->pos = i; + i = p; + } + else + i = 0; + } +} + +/** + * gts_eheap_insert: + * @heap: a #GtsEHeap. + * @p: a pointer to add to the heap. + * + * Inserts a new element @p in the heap. + * + * Returns: a #GtsEHeapPair describing the position of the element in the heap. + * This pointer is necessary for gts_eheap_remove() and + * gts_eheap_decrease_key(). + */ +GtsEHeapPair * gts_eheap_insert (GtsEHeap * heap, gpointer p) +{ + GtsEHeapPair * pair; + GPtrArray * elts; + + g_return_val_if_fail (heap != NULL, NULL); + g_return_val_if_fail (heap->func != NULL, NULL); + + elts = heap->elts; + pair = g_malloc (sizeof (GtsEHeapPair)); + g_ptr_array_add (elts, pair); + pair->data = p; + pair->pos = elts->len; + pair->key = (*heap->func) (p, heap->data); + if (!heap->frozen) + sift_up (heap, elts->len); + return pair; +} + +/** + * gts_eheap_insert_with_key: + * @heap: a #GtsEHeap. + * @p: a pointer to add to the heap. + * @key: the value of the key associated to @p. + * + * Inserts a new element @p in the heap. + * + * Returns: a #GtsEHeapPair describing the position of the element in the heap. + * This pointer is necessary for gts_eheap_remove() and + * gts_eheap_decrease_key(). + */ +GtsEHeapPair * gts_eheap_insert_with_key (GtsEHeap * heap, + gpointer p, + gdouble key) +{ + GtsEHeapPair * pair; + GPtrArray * elts; + + g_return_val_if_fail (heap != NULL, NULL); + + elts = heap->elts; + pair = g_malloc (sizeof (GtsEHeapPair)); + g_ptr_array_add (elts, pair); + pair->data = p; + pair->pos = elts->len; + pair->key = key; + if (!heap->frozen) + sift_up (heap, elts->len); + return pair; +} + +static void sift_down (GtsEHeap * heap, guint i) +{ + GtsEHeapPair * left_child, * right_child, * child, * parent; + guint lc, rc, c; + gpointer * pdata = heap->elts->pdata; + guint len = heap->elts->len; + gdouble key; + + lc = LEFT_CHILD (i); + rc = RIGHT_CHILD (i); + left_child = lc <= len ? pdata[lc - 1] : NULL; + right_child = rc <= len ? pdata[rc - 1] : NULL; + + parent = pdata[i - 1]; + key = parent->key; + while (left_child != NULL) { + if (right_child == NULL || left_child->key < right_child->key) { + child = left_child; + c = lc; + } + else { + child = right_child; + c = rc; + } + if (key > child->key) { + pdata[i - 1] = child; + child->pos = i; + pdata[c - 1] = parent; + parent->pos = c; + i = c; + lc = LEFT_CHILD (i); + rc = RIGHT_CHILD (i); + left_child = lc <= len ? pdata[lc - 1] : NULL; + right_child = rc <= len ? pdata[rc - 1] : NULL; + } + else + left_child = NULL; + } +} + +/** + * gts_eheap_remove_top: + * @heap: a #GtsEHeap. + * @key: a pointer on a gdouble or %NULL. + * + * Removes the element at the top of the heap and optionally (if @key is not + * %NULL) returns the value of its key. + * + * Returns: the element at the top of the heap. + */ +gpointer gts_eheap_remove_top (GtsEHeap * heap, gdouble * key) +{ + gpointer root; + GPtrArray * elts; + guint len; + GtsEHeapPair * pair; + + g_return_val_if_fail (heap != NULL, NULL); + + elts = heap->elts; + len = elts->len; + + if (len == 0) + return NULL; + if (len == 1) { + pair = g_ptr_array_remove_index (elts, 0); + root = pair->data; + if (key) + *key = pair->key; + g_free (pair); + return root; + } + + pair = elts->pdata[0]; + root = pair->data; + if (key) + *key = pair->key; + g_free (pair); + pair = g_ptr_array_remove_index (elts, len - 1); + elts->pdata[0] = pair; + pair->pos = 1; + sift_down (heap, 1); + return root; +} + +/** + * gts_eheap_top: + * @heap: a #GtsEHeap. + * @key: a pointer on a gdouble or %NULL. + * + * Returns: the element at the top of the heap and optionally (if @key is not + * %NULL) its key. + */ +gpointer gts_eheap_top (GtsEHeap * heap, gdouble * key) +{ + GtsEHeapPair * pair; + GPtrArray * elts; + + g_return_val_if_fail (heap != NULL, NULL); + + elts = heap->elts; + + if (elts->len == 0) + return NULL; + + pair = elts->pdata[0]; + if (key) + *key = pair->key; + return pair->data; +} + +/** + * gts_eheap_destroy: + * @heap: a #GtsEHeap. + * + * Free all the memory allocated for @heap. + */ +void gts_eheap_destroy (GtsEHeap * heap) +{ + guint i; + + g_return_if_fail (heap != NULL); + + for (i = 0; i < heap->elts->len; i++) + g_free (heap->elts->pdata[i]); + g_ptr_array_free (heap->elts, TRUE); + g_free (heap); +} + +/** + * gts_eheap_thaw: + * @heap: a #GtsEHeap. + * + * If @heap has been frozen previously using gts_eheap_freeze(), reorder it + * in O(n) time and unfreeze it. + */ +void gts_eheap_thaw (GtsEHeap * heap) +{ + guint i; + + g_return_if_fail (heap != NULL); + + if (!heap->frozen) + return; + + for (i = heap->elts->len/2; i > 0; i--) + sift_down (heap, i); + + heap->frozen = FALSE; +} + +/** + * gts_eheap_foreach: + * @heap: a #GtsEHeap. + * @func: the function to call for each element in the heap. + * @data: to pass to @func. + */ +void gts_eheap_foreach (GtsEHeap * heap, + GFunc func, + gpointer data) +{ + guint i; + GPtrArray * elts; + + g_return_if_fail (heap != NULL); + g_return_if_fail (func != NULL); + + elts = heap->elts; + for (i = 0; i < elts->len; i++) + (*func) (((GtsEHeapPair *) elts->pdata[i])->data, data); +} + +/** + * gts_eheap_remove: + * @heap: a #GtsEHeap. + * @p: a #GtsEHeapPair. + * + * Removes element corresponding to @p from @heap in O(log n). + * + * Returns: the element just removed from @heap. + */ +gpointer gts_eheap_remove (GtsEHeap * heap, GtsEHeapPair * p) +{ + GtsEHeapPair ** pdata; + GtsEHeapPair * parent; + guint i, par; + gpointer data; + + g_return_val_if_fail (heap != NULL, NULL); + g_return_val_if_fail (p != NULL, NULL); + + pdata = (GtsEHeapPair **)heap->elts->pdata; + i = p->pos; + data = p->data; + + g_return_val_if_fail (i > 0 && i <= heap->elts->len, NULL); + g_return_val_if_fail (p == pdata[i - 1], NULL); + + /* move element to the top */ + while ((par = PARENT (i))) { + parent = pdata[par - 1]; + pdata[par - 1] = p; + pdata[i - 1] = parent; + p->pos = par; + parent->pos = i; + i = par; + } + + gts_eheap_remove_top (heap, NULL); + + return data; +} + +/** + * gts_eheap_decrease_key: + * @heap: a #GtsEHeap. + * @p: a #GtsEHeapPair. + * @new_key: the new value of the key for this element. Must be smaller than + * the current key. + * + * Decreases the value of the key of the element at position @p. + */ +void gts_eheap_decrease_key (GtsEHeap * heap, + GtsEHeapPair * p, + gdouble new_key) +{ + guint i; + + g_return_if_fail (heap != NULL); + g_return_if_fail (p != NULL); + + i = p->pos; + g_return_if_fail (i > 0 && i <= heap->elts->len); + g_return_if_fail (p == heap->elts->pdata[i - 1]); + + g_return_if_fail (new_key <= p->key); + + p->key = new_key; + if (!heap->frozen) + sift_up (heap, i); +} + +/** + * gts_eheap_freeze: + * @heap: a #GtsEHeap. + * + * Freezes the heap. Any subsequent operation will not preserve the heap + * property. Used in conjunction with gts_eheap_insert() and gts_eheap_thaw() + * to create a heap in O(n) time. + */ +void gts_eheap_freeze (GtsEHeap * heap) +{ + g_return_if_fail (heap != NULL); + + heap->frozen = TRUE; +} + +/** + * gts_eheap_size: + * @heap: a #GtsEHeap. + * + * Returns: the number of items in @heap. + */ +guint gts_eheap_size (GtsEHeap * heap) +{ + g_return_val_if_fail (heap != NULL, 0); + + return heap->elts->len; +} + +/** + * gts_eheap_update: + * @heap: a #GtsEHeap. + * + * Updates the key of each element of @heap and reorders it. + */ +void gts_eheap_update (GtsEHeap * heap) +{ + guint i, len; + GtsEHeapPair ** pairs; + gpointer data; + GtsKeyFunc func; + + g_return_if_fail (heap != NULL); + g_return_if_fail (heap->func != NULL); + + heap->frozen = TRUE; + + len = heap->elts->len; + pairs = (GtsEHeapPair **) heap->elts->pdata; + data = heap->data; + func = heap->func; + + for (i = 0; i < len; i++) { + GtsEHeapPair * pair = pairs[i]; + pair->key = (*func) (pair->data, data); + } + + gts_eheap_thaw (heap); +} + +/** + * gts_eheap_key: + * @heap: a #GtsEHeap. + * @p: a pointer to be tested; + * + * Returns: the value of the key for pointer @p. + */ +gdouble gts_eheap_key (GtsEHeap * heap, gpointer p) +{ + g_return_val_if_fail (heap != NULL, 0.); + g_return_val_if_fail (heap->func != NULL, 0.); + + return (* heap->func) (p, heap->data); +} + +/** + * gts_eheap_randomized: + * @heap: a #GtsEHeap. + * @randomized: whether @heap should be randomized. + */ +void gts_eheap_randomized (GtsEHeap * heap, gboolean randomized) +{ + g_return_if_fail (heap != NULL); + + heap->randomized = randomized; +} Index: toporouter/src_3rd/gts/face.c =================================================================== --- toporouter/src_3rd/gts/face.c (nonexistent) +++ toporouter/src_3rd/gts/face.c (revision 6803) @@ -0,0 +1,297 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "gts.h" + +gboolean gts_allow_floating_faces = FALSE; + +static void face_destroy (GtsObject * object) +{ + GtsFace * face = GTS_FACE (object); + GSList * i; + + i = face->surfaces; + while (i) { + GSList * next = i->next; + gts_surface_remove_face (i->data, face); + i = next; + } + g_assert (face->surfaces == NULL); + + (* GTS_OBJECT_CLASS (gts_face_class ())->parent_class->destroy) (object); +} + +static void face_clone (GtsObject * clone, GtsObject * object) +{ + (* GTS_OBJECT_CLASS (gts_face_class ())->parent_class->clone) (clone, + object); + GTS_FACE (clone)->surfaces = NULL; +} + +static void face_class_init (GtsFaceClass * klass) +{ + GTS_OBJECT_CLASS (klass)->clone = face_clone; + GTS_OBJECT_CLASS (klass)->destroy = face_destroy; +} + +static void face_init (GtsFace * face) +{ + face->surfaces = NULL; +} + +/** + * gts_face_class: + * + * Returns: the #GtsFaceClass. + */ +GtsFaceClass * gts_face_class (void) +{ + static GtsFaceClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo face_info = { + "GtsFace", + sizeof (GtsFace), + sizeof (GtsFaceClass), + (GtsObjectClassInitFunc) face_class_init, + (GtsObjectInitFunc) face_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_triangle_class ()), + &face_info); + } + + return klass; +} + +/** + * gts_face_new: + * @klass: a #GtsFaceClass. + * @e1: a #GtsEdge. + * @e2: a #GtsEdge. + * @e3: a #GtsEdge. + * + * Returns: a new #GtsFace using @e1, @e2 and @e3 as edges. + */ +GtsFace * gts_face_new (GtsFaceClass * klass, + GtsEdge * e1, GtsEdge * e2, GtsEdge * e3) +{ + GtsFace * f; + + f = GTS_FACE (gts_object_new (GTS_OBJECT_CLASS (klass))); + gts_triangle_set (GTS_TRIANGLE (f), e1, e2, e3); + + return f; +} + +/** + * gts_face_has_parent_surface: + * @f: a #GtsFace. + * @s: a #GtsSurface. + * + * Returns: %TRUE if @f belongs to @s, %FALSE otherwise. + */ +gboolean gts_face_has_parent_surface (GtsFace * f, GtsSurface * s) +{ + GSList * i; + + g_return_val_if_fail (f != NULL, FALSE); + + i = f->surfaces; + while (i) { + if (i->data == s) + return TRUE; + i = i->next; + } + return FALSE; +} + +/** + * gts_faces_from_edges: + * @edges: a list of #GtsEdge. + * @s: a #GtsSurface or %NULL. + * + * Builds a list of unique faces which belong to @s and have + * one of their edges in @edges. + * + * Returns: the list of faces. + */ +GSList * gts_faces_from_edges (GSList * edges, GtsSurface * s) +{ + GHashTable * hash; + GSList * faces = NULL, * i; + + hash = g_hash_table_new (NULL, NULL); + i = edges; + while (i) { + GSList * j = GTS_EDGE (i->data)->triangles; + while (j) { + GtsTriangle * t = j->data; + if (GTS_IS_FACE (t) && + (!s || gts_face_has_parent_surface (GTS_FACE (t), s)) && + g_hash_table_lookup (hash, t) == NULL) { + faces = g_slist_prepend (faces, t); + g_hash_table_insert (hash, t, i); + } + j = j->next; + } + i = i->next; + } + g_hash_table_destroy (hash); + + return faces; +} + +/** + * gts_face_neighbor_number: + * @f: a #GtsFace. + * @s: a #GtsSurface or %NULL. + * + * Returns: the number of faces neighbors of @f and belonging to @s. + */ +guint gts_face_neighbor_number (GtsFace * f, GtsSurface * s) +{ + GSList * i; + guint nn = 0; + GtsEdge * e[4], ** e1 = e; + + g_return_val_if_fail (f != NULL, 0); + + e[0] = GTS_TRIANGLE (f)->e1; + e[1] = GTS_TRIANGLE (f)->e2; + e[2] = GTS_TRIANGLE (f)->e3; + e[3] = NULL; + while (*e1) { + i = (*e1++)->triangles; + while (i) { + GtsTriangle * t = i->data; + if (GTS_FACE (t) != f && + GTS_IS_FACE (t) && + (!s || gts_face_has_parent_surface (GTS_FACE (t), s))) + nn++; + i = i->next; + } + } + + return nn; +} + +/** + * gts_face_neighbors: + * @f: a #GtsFace. + * @s: a #GtsSurface or %NULL. + * + * Returns: a list of unique #GtsFace neighbors of @f and belonging to @s. + */ +GSList * gts_face_neighbors (GtsFace * f, GtsSurface * s) +{ + GSList * i, * list = NULL; + GtsEdge * e[4], ** e1 = e; + + g_return_val_if_fail (f != NULL, NULL); + + e[0] = GTS_TRIANGLE (f)->e1; + e[1] = GTS_TRIANGLE (f)->e2; + e[2] = GTS_TRIANGLE (f)->e3; + e[3] = NULL; + while (*e1) { + i = (*e1++)->triangles; + while (i) { + GtsTriangle * t = i->data; + if (GTS_FACE (t) != f && + GTS_IS_FACE (t) && + (!s || gts_face_has_parent_surface (GTS_FACE (t), s))) + list = g_slist_prepend (list, t); + i = i->next; + } + } + + return list; +} + +/** + * gts_face_foreach_neighbor: + * @f: a #GtsFace. + * @s: a #GtsSurface or %NULL. + * @func: a #GtsFunc. + * @data: user data to pass to @func. + * + * Calls @func for each neighbor of @f belonging to @s (if not %NULL). + */ +void gts_face_foreach_neighbor (GtsFace * f, + GtsSurface * s, + GtsFunc func, + gpointer data) +{ + GSList * i; + GtsEdge * e[4], ** e1 = e; + + g_return_if_fail (f != NULL); + g_return_if_fail (func != NULL); + + e[0] = GTS_TRIANGLE (f)->e1; + e[1] = GTS_TRIANGLE (f)->e2; + e[2] = GTS_TRIANGLE (f)->e3; + e[3] = NULL; + while (*e1) { + i = (*e1++)->triangles; + while (i) { + GtsTriangle * t = i->data; + if (GTS_FACE (t) != f && + GTS_IS_FACE (t) && + (!s || gts_face_has_parent_surface (GTS_FACE (t), s))) + (* func) (t, data); + i = i->next; + } + } +} + +static gboolean triangle_is_incompatible (GtsTriangle * t, GtsEdge * e, GtsSurface * s) +{ + GSList * i = e->triangles; + + while (i) { + if (i->data != t && + GTS_IS_FACE (i->data) && + gts_face_has_parent_surface (i->data, s) && + !gts_triangles_are_compatible (t, i->data, e)) + return TRUE; + i = i->next; + } + return FALSE; +} + +/** + * gts_face_is_compatible: + * @f: a #GtsFace. + * @s: a #GtsSurface. + * + * Returns: %TRUE if @f is compatible with all its neighbors belonging + * to @s, %FALSE otherwise. + */ +gboolean gts_face_is_compatible (GtsFace * f, GtsSurface * s) +{ + g_return_val_if_fail (f != NULL, FALSE); + g_return_val_if_fail (s != NULL, FALSE); + + return !(triangle_is_incompatible (GTS_TRIANGLE (f), GTS_TRIANGLE (f)->e1, s) || + triangle_is_incompatible (GTS_TRIANGLE (f), GTS_TRIANGLE (f)->e2, s) || + triangle_is_incompatible (GTS_TRIANGLE (f), GTS_TRIANGLE (f)->e3, s)); +} Index: toporouter/src_3rd/gts/fifo.c =================================================================== --- toporouter/src_3rd/gts/fifo.c (nonexistent) +++ toporouter/src_3rd/gts/fifo.c (revision 6803) @@ -0,0 +1,192 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "gts.h" + +struct _GtsFifo { + GList * head; + GList * tail; +}; + +/** + * gts_fifo_new: + * + * Returns: a new #GtsFifo. + */ +GtsFifo * gts_fifo_new () +{ + GtsFifo * fifo = g_malloc (sizeof (GtsFifo)); + + fifo->head = fifo->tail = NULL; + return fifo; +} + +/** + * gts_fifo_write: + * @fifo: a #GtsFifo. + * @fp: a file pointer. + * + * Writes the content of @fifo in @fp. + */ +void gts_fifo_write (GtsFifo * fifo, FILE * fp) +{ + GList * i; + + g_return_if_fail (fifo != NULL); + g_return_if_fail (fp != NULL); + + fprintf (fp, "["); + i = fifo->head; + while (i) { + fprintf (fp, "%p ", i->data); + i = i->next; + } + fprintf (fp, "]"); +} + +/** + * gts_fifo_push: + * @fifo: a #GtsFifo. + * @data: data to add to @fifo. + * + * Push @data into @fifo. + */ +void gts_fifo_push (GtsFifo * fifo, gpointer data) +{ + g_return_if_fail (fifo != NULL); + + fifo->head = g_list_prepend (fifo->head, data); + if (fifo->tail == NULL) + fifo->tail = fifo->head; +} + +/** + * gts_fifo_pop: + * @fifo: a #GtsFifo. + * + * Removes the first element from @fifo. + * + * Returns: the first element in @fifo or %NULL if @fifo is empty. + */ +gpointer gts_fifo_pop (GtsFifo * fifo) +{ + gpointer data; + GList * tail; + + g_return_val_if_fail (fifo != NULL, NULL); + + if (fifo->tail == NULL) + return NULL; + tail = fifo->tail->prev; + data = fifo->tail->data; + fifo->head = g_list_remove_link (fifo->head, fifo->tail); + g_list_free_1 (fifo->tail); + fifo->tail = tail; + return data; +} + +/** + * gts_fifo_top: + * @fifo: a #GtsFifo. + * + * Returns: the first element in @fifo or %NULL if @fifo is empty. + */ +gpointer gts_fifo_top (GtsFifo * fifo) +{ + g_return_val_if_fail (fifo != NULL, NULL); + + if (fifo->tail == NULL) + return NULL; + return fifo->tail->data; +} + +/** + * gts_fifo_size: + * @fifo: a #GtsFifo. + * + * Returns: the number of elements in @fifo. + */ +guint gts_fifo_size (GtsFifo * fifo) +{ + g_return_val_if_fail (fifo != NULL, 0); + + return g_list_length (fifo->head); +} + +/** + * gts_fifo_destroy: + * @fifo: a #GtsFifo. + * + * Frees all the memory allocated for @fifo. + */ +void gts_fifo_destroy (GtsFifo * fifo) +{ + g_return_if_fail (fifo != NULL); + g_list_free (fifo->head); + g_free (fifo); +} + +/** + * gts_fifo_is_empty: + * @fifo: a #GtsFifo. + * + * Returns: %TRUE if @fifo is empty, %FALSE otherwise. + */ +gboolean gts_fifo_is_empty (GtsFifo * fifo) +{ + g_return_val_if_fail (fifo != NULL, TRUE); + + return (fifo->head == NULL); +} + +/** + * gts_fifo_foreach: + * @fifo: a #GtsFifo. + * @func: a #GtsFunc. + * @data: user data to be passed to @func. + * + * Calls @func in order for each item in @fifo, passing @data. + */ +void gts_fifo_foreach (GtsFifo * fifo, GtsFunc func, gpointer data) +{ + GList * i; + + g_return_if_fail (fifo != NULL); + g_return_if_fail (func != NULL); + + i = fifo->tail; + while (i) { + (* func) (i->data, data); + i = i->prev; + } +} + +/** + * gts_fifo_reverse: + * @fifo: a #GtsFifo. + * + * Reverses the order of elements in @fifo. + */ +void gts_fifo_reverse (GtsFifo * fifo) +{ + g_return_if_fail (fifo != NULL); + + fifo->tail = fifo->head; + fifo->head = g_list_reverse (fifo->head); +} Index: toporouter/src_3rd/gts/graph.c =================================================================== --- toporouter/src_3rd/gts/graph.c (nonexistent) +++ toporouter/src_3rd/gts/graph.c (revision 6803) @@ -0,0 +1,1776 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include +#include "gts.h" + +/* GtsGNode */ + +gboolean gts_allow_floating_gnodes = FALSE; + +static void gnode_remove_container (GtsContainee * i, GtsContainer * c) +{ + (* GTS_CONTAINEE_CLASS (GTS_OBJECT_CLASS (gts_gnode_class ())->parent_class)->remove_container) (i, c); + if (GTS_SLIST_CONTAINEE (i)->containers == NULL && + !gts_allow_floating_gnodes && + !GTS_OBJECT_DESTROYED(GTS_OBJECT (i))) + gts_object_destroy (GTS_OBJECT (i)); +} + +static void gnode_class_init (GtsGNodeClass * klass) +{ + klass->weight = NULL; + + GTS_CONTAINEE_CLASS (klass)->remove_container = gnode_remove_container; +} + +static void gnode_init (GtsGNode * n) +{ + n->level = 0; +} + +/** + * gts_gnode_class: + * + * Returns: the #GtsGNodeClass. + */ +GtsGNodeClass * gts_gnode_class (void) +{ + static GtsGNodeClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo gnode_info = { + "GtsGNode", + sizeof (GtsGNode), + sizeof (GtsGNodeClass), + (GtsObjectClassInitFunc) gnode_class_init, + (GtsObjectInitFunc) gnode_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = + gts_object_class_new (GTS_OBJECT_CLASS (gts_slist_container_class ()), + &gnode_info); + } + + return klass; +} + +/** + * gts_gnode_new: + * @klass: a #GtsGNodeClass. + * + * Returns: a new #GtsGNode. + */ +GtsGNode * gts_gnode_new (GtsGNodeClass * klass) +{ + GtsGNode * object; + + object = GTS_GNODE (gts_object_new (GTS_OBJECT_CLASS (klass))); + + return object; +} + +/** + * gts_gnode_foreach_neighbor: + * @n: a #GtsGNode. + * @g: a #GtsGraph or %NULL. + * @func: a #GtsFunc. + * @data: user data to be passed to @func. + * + * Calls @func for each neighbor #GtsGNode of @n (belonging to @g if + * @g is not %NULL. + */ +void gts_gnode_foreach_neighbor (GtsGNode * n, + GtsGraph * g, + GtsFunc func, + gpointer data) +{ + GSList * i; + + g_return_if_fail (n != NULL); + g_return_if_fail (func != NULL); + + i = GTS_SLIST_CONTAINER (n)->items; + while (i) { + GtsGNode * n1 = GTS_GNODE_NEIGHBOR (n, i->data); + if (g == NULL || gts_containee_is_contained (GTS_CONTAINEE (n1), + GTS_CONTAINER (g))) + (* func) (n1, data); + i = i->next; + } +} + +/** + * gts_gnode_foreach_edge: + * @n: a #GtsGNode. + * @g: a #GtsGraph or %NULL. + * @func: a #GtsFunc. + * @data: user data to be passed to @func. + * + * Calls @func for each #GtsGEdge connecting @n to another #GtsGNode + * (belonging to @g if @g is not %NULL. + */ +void gts_gnode_foreach_edge (GtsGNode * n, + GtsGraph * g, + GtsFunc func, + gpointer data) +{ + GSList * i; + + g_return_if_fail (n != NULL); + g_return_if_fail (func != NULL); + + i = GTS_SLIST_CONTAINER (n)->items; + while (i) { + GtsGNode * n1 = GTS_GNODE_NEIGHBOR (n, i->data); + if (g == NULL || gts_containee_is_contained (GTS_CONTAINEE (n1), + GTS_CONTAINER (g))) + (* func) (i->data, data); + i = i->next; + } +} + +/** + * gts_gnode_degree: + * @n: a #GtsGNode. + * @g: a #GtsGraph or %NULL. + * + * Returns: the number of neighbors of @n (belonging to @g if @g is not %NULL). + */ +guint gts_gnode_degree (GtsGNode * n, + GtsGraph * g) +{ + GSList * i; + guint nn = 0; + + g_return_val_if_fail (n != NULL, 0); + + i = GTS_SLIST_CONTAINER (n)->items; + while (i) { + GtsGNode * n1 = GTS_GNODE_NEIGHBOR (n, i->data); + if (g == NULL || gts_containee_is_contained (GTS_CONTAINEE (n1), + GTS_CONTAINER (g))) + nn++; + i = i->next; + } + + return nn; +} + +/** + * gts_gnode_move_cost: + * @n: a #GtsGNode. + * @src: a #GtsGraph containing @n. + * @dst: another #GtsGraph. + * + * Returns: the cost (increase in the sum of the weights of the edges cut) of + * moving @n from @src to @dst. + */ +gfloat gts_gnode_move_cost (GtsGNode * n, + GtsGraph * src, + GtsGraph * dst) +{ + GSList * i; + gfloat cost = 0.; + + g_return_val_if_fail (n != NULL, G_MAXFLOAT); + g_return_val_if_fail (src != NULL, G_MAXFLOAT); + g_return_val_if_fail (dst != NULL, G_MAXFLOAT); + g_return_val_if_fail (gts_containee_is_contained (GTS_CONTAINEE (n), + GTS_CONTAINER (src)), + G_MAXFLOAT); + + i = GTS_SLIST_CONTAINER (n)->items; + while (i) { + GtsGEdge * ge = i->data; + GtsGNode * neighbor = GTS_GNODE_NEIGHBOR (n, ge); + + if (gts_containee_is_contained (GTS_CONTAINEE (neighbor), + GTS_CONTAINER (src))) + cost += gts_gedge_weight (ge); + else if (gts_containee_is_contained (GTS_CONTAINEE (neighbor), + GTS_CONTAINER (dst))) + cost -= gts_gedge_weight (ge); + i = i->next; + } + + return cost; +} + +/** + * gts_gnode_weight: + * @n: a #GtsGNode. + * + * Returns: the weight of @n as defined by the weight() method of the + * #GtsGNodeClass. + */ +gfloat gts_gnode_weight (GtsGNode * n) +{ + g_return_val_if_fail (n != NULL, 0.); + + if (GTS_GNODE_CLASS (GTS_OBJECT (n)->klass)->weight) + return (* GTS_GNODE_CLASS (GTS_OBJECT (n)->klass)->weight) (n); + return 1.; +} + +/* GtsNGNode */ + +static void ngnode_init (GtsNGNode * n) +{ + n->id = 0; +} + +/** + * gts_ngnode_class: + * + * Returns: the #GtsNGNodeClass. + */ +GtsNGNodeClass * gts_ngnode_class (void) +{ + static GtsNGNodeClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo ngnode_info = { + "GtsNGNode", + sizeof (GtsNGNode), + sizeof (GtsNGNodeClass), + (GtsObjectClassInitFunc) NULL, + (GtsObjectInitFunc) ngnode_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_gnode_class ()), + &ngnode_info); + } + + return klass; +} + +/** + * gts_ngnode_new: + * @klass: a #GtsNGNodeClass. + * + * Returns: a new #GtsNGNode with identity @id. + */ +GtsNGNode * gts_ngnode_new (GtsNGNodeClass * klass, + guint id) +{ + GtsNGNode * n; + + n = GTS_NGNODE (gts_gnode_new (GTS_GNODE_CLASS (klass))); + n->id = id; + + return n; +} + +/* GtsWGNode */ + +static gfloat wgnode_weight (GtsGNode * n) +{ + return GTS_WGNODE (n)->weight; +} + +static void wgnode_class_init (GtsWGNodeClass * klass) +{ + GTS_GNODE_CLASS (klass)->weight = wgnode_weight; +} + +static void wgnode_init (GtsWGNode * n) +{ + n->weight = 1.; +} + +/** + * gts_wgnode_class: + * + * Returns: the #GtsWGNodeClass. + */ +GtsWGNodeClass * gts_wgnode_class (void) +{ + static GtsWGNodeClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo wgnode_info = { + "GtsWGNode", + sizeof (GtsWGNode), + sizeof (GtsWGNodeClass), + (GtsObjectClassInitFunc) wgnode_class_init, + (GtsObjectInitFunc) wgnode_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_gnode_class ()), + &wgnode_info); + } + + return klass; +} + +/** + * gts_wgnode_new: + * @klass: a #GtsWGNodeClass. + * @weight: the weight of the #GtsWGNode to create. + * + * Returns: a new #GtsWGNode of weight @weight. + */ +GtsWGNode * gts_wgnode_new (GtsWGNodeClass * klass, + gfloat weight) +{ + GtsWGNode * n; + + n = GTS_WGNODE (gts_gnode_new (GTS_GNODE_CLASS (klass))); + n->weight = weight; + + return n; +} + +/* GtsPNode */ + +static void pnode_write (GtsGNode * n, FILE * fp) +{ + if (GTS_IS_NVERTEX (GTS_PNODE (n)->data)) + fprintf (fp, "label=\"%p:%s\",", + GTS_PNODE (n)->data, + GTS_NVERTEX (GTS_PNODE (n)->data)->name); + else + fprintf (fp, "label=\"%p\",", GTS_PNODE (n)->data); +} + +static void pnode_class_init (GtsPNodeClass * klass) +{ + GTS_GNODE_CLASS (klass)->write = pnode_write; +} + +static void pnode_init (GtsPNode * pn) +{ + pn->data = NULL; +} + +/** + * gts_pnode_class: + * + * Returns: the #GtsPNodeClass. + */ +GtsPNodeClass * gts_pnode_class (void) +{ + static GtsPNodeClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo pnode_info = { + "GtsPNode", + sizeof (GtsPNode), + sizeof (GtsPNodeClass), + (GtsObjectClassInitFunc) pnode_class_init, + (GtsObjectInitFunc) pnode_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_gnode_class ()), + &pnode_info); + } + + return klass; +} + +/** + * gts_pnode_new: + * @klass: a #GtsPNodeClass. + * @data: user data. + * + * Returns: a new #GtsPNode associated with @data. + */ +GtsPNode * gts_pnode_new (GtsPNodeClass * klass, gpointer data) +{ + GtsPNode * pn; + + pn = GTS_PNODE (gts_object_new (GTS_OBJECT_CLASS (klass))); + pn->data = data; + + return pn; +} + +/* GtsFNode */ + +static void fnode_write (GtsGNode * n, FILE * fp) +{ + fprintf (fp, "label=\"%p\",", GTS_FNODE (n)->f); +} + +static void fnode_class_init (GtsGNodeClass * klass) +{ + klass->write = fnode_write; +} + +static void fnode_init (GtsFNode * fn) +{ + fn->f = NULL; +} + +/** + * gts_fnode_class: + * + * Returns: the #GtsFNodeClass. + */ +GtsFNodeClass * gts_fnode_class (void) +{ + static GtsFNodeClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo fnode_info = { + "GtsFNode", + sizeof (GtsFNode), + sizeof (GtsFNodeClass), + (GtsObjectClassInitFunc) fnode_class_init, + (GtsObjectInitFunc) fnode_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_gnode_class ()), + &fnode_info); + } + + return klass; +} + +/** + * gts_fnode_new: + * @klass: a #GtsFNodeClass. + * @f: a #GtsFace. + * + * Returns: a new #GtsFNode associated with face @f. + */ +GtsFNode * gts_fnode_new (GtsFNodeClass * klass, GtsFace * f) +{ + GtsFNode * fn; + + g_return_val_if_fail (f != NULL, NULL); + + fn = GTS_FNODE (gts_object_new (GTS_OBJECT_CLASS (klass))); + fn->f = f; + + return fn; +} + +/* GtsGEdge */ + +static void gedge_destroy (GtsObject * object) +{ + GtsGEdge * ge = GTS_GEDGE (object); + + if (ge->n1) + gts_container_remove (GTS_CONTAINER (ge->n1), GTS_CONTAINEE (ge)); + if (ge->n2) + gts_container_remove (GTS_CONTAINER (ge->n2), GTS_CONTAINEE (ge)); + + (* GTS_OBJECT_CLASS (gts_gedge_class ())->parent_class->destroy) (object); +} + +static void gedge_remove_container (GtsContainee * i, GtsContainer * c) +{ + GtsGEdge * ge = GTS_GEDGE (i); + GtsGNode * n1 = ge->n1; + GtsGNode * n2 = ge->n2; + + ge->n1 = ge->n2 = NULL; + if (n1 != NULL && n2 != NULL) { + if (GTS_CONTAINER (n1) == c) { + if (n2 && n2 != n1) gts_container_remove (GTS_CONTAINER (n2), i); + } + else if (GTS_CONTAINER (n2) == c) { + if (n1 && n1 != n2) gts_container_remove (GTS_CONTAINER (n1), i); + } + else + g_assert_not_reached (); + (* GTS_OBJECT_CLASS (gts_gedge_class ())->parent_class->destroy) + (GTS_OBJECT (i)); + } +} + +static gboolean gedge_is_contained (GtsContainee * i, GtsContainer * c) +{ + GtsGEdge * ge = GTS_GEDGE (i); + + if (GTS_CONTAINER (ge->n1) == c || GTS_CONTAINER (ge->n2) == c) + return TRUE; + return FALSE; +} + +static void gedge_class_init (GtsGEdgeClass * klass) +{ + klass->link = NULL; + klass->weight = NULL; + + GTS_CONTAINEE_CLASS (klass)->remove_container = gedge_remove_container; + GTS_CONTAINEE_CLASS (klass)->is_contained = gedge_is_contained; + + GTS_OBJECT_CLASS (klass)->destroy = gedge_destroy; +} + +static void gedge_init (GtsGEdge * object) +{ + object->n1 = object->n2 = NULL; +} + +/** + * gts_gedge_class: + * + * Returns: the #GtsGEdgeClass. + */ +GtsGEdgeClass * gts_gedge_class (void) +{ + static GtsGEdgeClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo gedge_info = { + "GtsGEdge", + sizeof (GtsGEdge), + sizeof (GtsGEdgeClass), + (GtsObjectClassInitFunc) gedge_class_init, + (GtsObjectInitFunc) gedge_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_containee_class ()), + &gedge_info); + } + + return klass; +} + +/** + * gts_gedge_new: + * @klass: a #GtsGEdgeClass. + * @n1: a #GtsGNode. + * @n2: another #GtsGNode. + * + * Returns: a new #GtsGEdge linking @n1 and @n2. + */ +GtsGEdge * gts_gedge_new (GtsGEdgeClass * klass, GtsGNode * n1, GtsGNode * n2) +{ + GtsGEdge * object; + + g_return_val_if_fail (n1 != NULL, NULL); + g_return_val_if_fail (n2 != NULL, NULL); + + object = GTS_GEDGE (gts_object_new (GTS_OBJECT_CLASS (klass))); + object->n1 = n1; + gts_container_add (GTS_CONTAINER (n1), GTS_CONTAINEE (object)); + object->n2 = n2; + if (n1 != n2) + gts_container_add (GTS_CONTAINER (n2), GTS_CONTAINEE (object)); + + if (klass->link) + object = (* klass->link) (object, n1, n2); + + return object; +} + +/** + * gts_gedge_weight: + * @e: a #GtsGEdge. + * + * Returns: the weight of edge @e as defined by the weight() method of + * #GtsGEdgeClass. + */ +gfloat gts_gedge_weight (GtsGEdge * e) +{ + g_return_val_if_fail (e != NULL, 0.); + + if (GTS_GEDGE_CLASS (GTS_OBJECT (e)->klass)->weight) + return (* GTS_GEDGE_CLASS (GTS_OBJECT (e)->klass)->weight) (e); + return 1.; +} + +/* GtsPGEdge */ + +static void pgedge_write (GtsGEdge * ge, FILE * fp) +{ + if (GTS_IS_EDGE (GTS_PGEDGE (ge)->data)) { + GtsEdge * e = GTS_PGEDGE (ge)->data; + guint n = g_slist_length (e->triangles); + + fprintf (fp, "label=\"%p:%s:%d\",color=%s", e, + GTS_IS_NEDGE (e) ? GTS_NEDGE (e)->name : "", + n, + n == 0 ? "black" : + n == 1 ? "blue" : + n == 2 ? "green" : + n == 3 ? "violet" : + n == 4 ? "red" : + "pink"); + } + else + fprintf (fp, "label=\"%p\",", GTS_PGEDGE (ge)->data); +} + +static void pgedge_class_init (GtsPGEdgeClass * klass) +{ + GTS_GEDGE_CLASS (klass)->write = pgedge_write; +} + +static void pgedge_init (GtsPGEdge * e) +{ + e->data = NULL; +} + +/** + * gts_pgedge_class: + * + * Returns: the #GtsPGEdgeClass. + */ +GtsPGEdgeClass * gts_pgedge_class (void) +{ + static GtsPGEdgeClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo pgedge_info = { + "GtsPGEdge", + sizeof (GtsPGEdge), + sizeof (GtsPGEdgeClass), + (GtsObjectClassInitFunc) pgedge_class_init, + (GtsObjectInitFunc) pgedge_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_gedge_class ()), + &pgedge_info); + } + + return klass; +} + +/** + * gts_pgedge_new: + * @klass: a #GtsPGEdgeClass. + * @n1: a #GtsGNode. + * @n2: another #GtsGNode. + * @data: user data. + * + * Returns: a new #GtsPGEdge associated with @data linking @n1 and @n2. + */ +GtsPGEdge * gts_pgedge_new (GtsPGEdgeClass * klass, + GtsGNode * g1, + GtsGNode * g2, + gpointer data) +{ + GtsPGEdge * we; + + we = GTS_PGEDGE (gts_gedge_new (GTS_GEDGE_CLASS (klass), g1, g2)); + we->data = data; + + return we; +} + +/* GtsWGEdge */ + +static gfloat wgedge_weight (GtsGEdge * e) +{ + return GTS_WGEDGE (e)->weight; +} + +static void wgedge_class_init (GtsWGEdgeClass * klass) +{ + GTS_GEDGE_CLASS (klass)->weight = wgedge_weight; +} + +static void wgedge_init (GtsWGEdge * e) +{ + e->weight = 1.; +} + +/** + * gts_wgedge_class: + * + * Returns: the #GtsWGEdgeClass. + */ +GtsWGEdgeClass * gts_wgedge_class (void) +{ + static GtsWGEdgeClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo wgedge_info = { + "GtsWGEdge", + sizeof (GtsWGEdge), + sizeof (GtsWGEdgeClass), + (GtsObjectClassInitFunc) wgedge_class_init, + (GtsObjectInitFunc) wgedge_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_gedge_class ()), + &wgedge_info); + } + + return klass; +} + +/** + * gts_wgedge_new: + * @klass: a #GtsWGEdgeClass. + * @n1: a #GtsGNode. + * @n2: another #GtsGNode. + * @weight: the weight of the new edge. + * + * Returns: a new #GtsWGEdge of weight @weight linking @n1 and @n2. + */ +GtsWGEdge * gts_wgedge_new (GtsWGEdgeClass * klass, + GtsGNode * g1, + GtsGNode * g2, + gfloat weight) +{ + GtsWGEdge * we; + + we = GTS_WGEDGE (gts_gedge_new (GTS_GEDGE_CLASS (klass), g1, g2)); + we->weight = weight; + + return we; +} + +/* GtsGraph */ + +static void graph_init (GtsGraph * g) +{ + g->graph_class = gts_graph_class (); + g->node_class = gts_gnode_class (); + g->edge_class = gts_gedge_class (); +} + +static void graph_write (GtsObject * object, FILE * fp) +{ + GtsGraph * graph = GTS_GRAPH (object); + + fprintf (fp, " %s %s %s", + object->klass->info.name, + GTS_OBJECT_CLASS (graph->node_class)->info.name, + GTS_OBJECT_CLASS (graph->edge_class)->info.name); +} + +static void graph_read (GtsObject ** object, GtsFile * f) +{ + GtsObjectClass * klass; + + if (f->type != GTS_STRING) { + gts_file_error (f, "expecting a string (GtsGNodeClass)"); + return; + } + klass = gts_object_class_from_name (f->token->str); + if (klass == NULL) { + gts_file_error (f, "unknown class `%s'", f->token->str); + return; + } + if (!gts_object_class_is_from_class (klass, gts_gnode_class ())) { + gts_file_error (f, "class `%s' is not a GtsGNodeClass", f->token->str); + return; + } + GTS_GRAPH (*object)->node_class = GTS_GNODE_CLASS (klass); + gts_file_next_token (f); + + if (f->type != GTS_STRING) { + gts_file_error (f, "expecting a string (GtsGEdgeClass)"); + return; + } + klass = gts_object_class_from_name (f->token->str); + if (klass == NULL) { + gts_file_error (f, "unknown class `%s'", f->token->str); + return; + } + if (!gts_object_class_is_from_class (klass, gts_gedge_class ())) { + gts_file_error (f, "class `%s' is not a GtsGEdgeClass", f->token->str); + return; + } + GTS_GRAPH (*object)->edge_class = GTS_GEDGE_CLASS (klass); + gts_file_next_token (f); +} + +static void graph_class_init (GtsGraphClass * klass) +{ + klass->weight = NULL; + + GTS_OBJECT_CLASS (klass)->write = graph_write; + GTS_OBJECT_CLASS (klass)->read = graph_read; +} + +/** + * gts_graph_class: + * + * Returns: the #GtsGraphClass. + */ +GtsGraphClass * gts_graph_class (void) +{ + static GtsGraphClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo graph_info = { + "GtsGraph", + sizeof (GtsGraph), + sizeof (GtsGraphClass), + (GtsObjectClassInitFunc) graph_class_init, + (GtsObjectInitFunc) graph_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_hash_container_class ()), + &graph_info); + } + + return klass; +} + +/** + * gts_graph_new: + * @klass: a #GtsGraphClass. + * @node_class: a #GtsGNodeClass. + * @edge_class: a #GtsGEdgeClass. + * + * Returns: a new #GtsGraph using @node_class and @edge_class as node types. + */ +GtsGraph * gts_graph_new (GtsGraphClass * klass, + GtsGNodeClass * node_class, + GtsGEdgeClass * edge_class) +{ + GtsGraph * g; + + g_return_val_if_fail (klass != NULL, NULL); + g_return_val_if_fail (node_class != NULL, NULL); + g_return_val_if_fail (edge_class != NULL, NULL); + + g = GTS_GRAPH (gts_object_new (GTS_OBJECT_CLASS (klass))); + g->node_class = node_class; + g->edge_class = edge_class; + + return g; +} + +static void compute_degree (GtsGNode * n, gpointer * data) +{ + GtsGraph * g = data[0]; + GtsRange * degree = data[1]; + + gts_range_add_value (degree, gts_gnode_degree (n, g)); +} + +/** + * gts_graph_print_stats: + * @g: a #GtsGraph. + * @fp: a file pointer. + * + * Writes to @fp a summary of the properties of @g. + */ +void gts_graph_print_stats (GtsGraph * g, FILE * fp) +{ + GtsRange degree; + gpointer data[2]; + + g_return_if_fail (g != NULL); + g_return_if_fail (fp != NULL); + + fprintf (fp, "# nodes: %d weight: %g\n", + gts_container_size (GTS_CONTAINER (g)), + gts_graph_weight (g)); + fprintf (fp, "# degree: "); + gts_range_init (°ree); + data[0] = g; + data[1] = °ree; + gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) compute_degree, data); + gts_range_update (°ree); + gts_range_print (°ree, fp); + fprintf (fp, "\n"); + fprintf (fp, "# edges cut: %d edges cut weight: %g\n", + gts_graph_edges_cut (g), + gts_graph_edges_cut_weight (g)); +} + +struct _GtsGraphTraverse { + GtsFifo * q; + GtsGraph * g; +}; + +static void reset_level (GtsGNode * n) +{ + n->level = 0; +} + +/** + * gts_graph_traverse_new: + * @g: a #GtsGraph. + * @n: a #GtsGNode belonging to @g. + * @type: the type of traversal. + * @reinit: if %TRUE, the traversal is reinitialized. + * + * Returns: a new #GtsGraphTraverse initialized for the traversal of + * @g of type @type, starting from @n. + */ +GtsGraphTraverse * gts_graph_traverse_new (GtsGraph * g, + GtsGNode * n, + GtsTraverseType type, + gboolean reinit) +{ + GtsGraphTraverse * t; + + g_return_val_if_fail (g != NULL, NULL); + g_return_val_if_fail (n != NULL, NULL); + g_return_val_if_fail (gts_containee_is_contained (GTS_CONTAINEE (n), + GTS_CONTAINER (g)), + NULL); + + if (reinit) + gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) reset_level, NULL); + + t = g_malloc (sizeof (GtsGraphTraverse)); + t->q = gts_fifo_new (); + t->g = g; + n->level = 1; + gts_fifo_push (t->q, n); + + return t; +} + +static void push_neighbor (GtsGNode * n, gpointer * data) +{ + GtsFifo * q = data[0]; + GtsGNode * u = data[1]; + + if (n->level == 0) { + n->level = u->level + 1; + gts_fifo_push (q, n); + } +} + +/** + * gts_graph_traverse_next: + * @t: a #GtsGraphTraverse. + * + * Returns: the next #GtsGNode of the traversal defined by @t or %NULL + * if the traversal is complete. + */ +GtsGNode * gts_graph_traverse_next (GtsGraphTraverse * t) +{ + GtsGNode * u; + + g_return_val_if_fail (t != NULL, NULL); + + u = gts_fifo_pop (t->q); + if (u) { + gpointer data[2]; + + data[0] = t->q; + data[1] = u; + gts_gnode_foreach_neighbor (u, t->g, (GtsFunc) push_neighbor, data); + } + + return u; +} + +/** + * gts_graph_traverse_what_next: + * @t: a #GtsGraphTraverse. + * + * Returns: the next #GtsGNode of the traversal defined by @t or %NULL + * if the traversal is complete but without advancing the traversal. + */ +GtsGNode * gts_graph_traverse_what_next (GtsGraphTraverse * t) +{ + g_return_val_if_fail (t != NULL, NULL); + + return gts_fifo_top (t->q); +} + +/** + * gts_graph_traverse_destroy: + * @t: a #GtsGraphTraverse. + * + * Frees all the memory allocated for @t. + */ +void gts_graph_traverse_destroy (GtsGraphTraverse * t) +{ + g_return_if_fail (t != NULL); + + gts_fifo_destroy (t->q); + g_free (t); +} + +static void edge_foreach_node (GtsGNode * n, gpointer * info) +{ + GtsFunc func = (GtsFunc) info[0]; + gpointer data = info[1]; + GHashTable * hash = info[2]; + GSList * i = GTS_SLIST_CONTAINER (n)->items; + + while (i) { + GtsGEdge * e = i->data; + if (!g_hash_table_lookup (hash, e)) { + (* func) (e, data); + g_hash_table_insert (hash, e, e); + } + i = i->next; + } +} + +/** + * gts_graph_foreach_edge: + * @g: a #GtsGraph. + * @func: a #GtsFunc. + * @data: user data to be passed to @func. + * + * Calls @func for each #GtsEdge of @g. + */ +void gts_graph_foreach_edge (GtsGraph * g, GtsFunc func, gpointer data) +{ + gpointer info[3]; + GHashTable * hash; + + g_return_if_fail (g != NULL); + g_return_if_fail (func != NULL); + + info[0] = func; + info[1] = data; + info[2] = hash = g_hash_table_new (NULL, NULL); + gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) edge_foreach_node, info); + g_hash_table_destroy (hash); +} + +/** + * gts_graph_weight: + * @g: a #GtsGraph. + * + * Returns: the weight of graph @g as defined by the weight() method + * of #GtsGraphClass. + */ +gfloat gts_graph_weight (GtsGraph * g) +{ + g_return_val_if_fail (g != NULL, 0.); + + if (GTS_GRAPH_CLASS (GTS_OBJECT (g)->klass)->weight) + return (* GTS_GRAPH_CLASS (GTS_OBJECT (g)->klass)->weight) (g); + return (gfloat) gts_container_size (GTS_CONTAINER (g)); +} + +/** + * gts_graph_distance_sum: + * @g: a #GtsGraph. + * @center: a #GtsGNode of @g. + * + * Returns: the sum of the distances between all the other #GtsGNode + * of @g and @center. + */ +guint gts_graph_distance_sum (GtsGraph * g, GtsGNode * center) +{ + GtsGraphTraverse * t; + GtsGNode * n; + guint sum = 0; + + g_return_val_if_fail (g != NULL, 0); + g_return_val_if_fail (center != NULL, 0); + + t = gts_graph_traverse_new (g, center, GTS_BREADTH_FIRST, TRUE); + while ((n = gts_graph_traverse_next (t))) + sum += n->level - 1; + gts_graph_traverse_destroy (t); + + return sum; +} + +/** + * gts_graph_farthest: + * @g: a #GtsGraph. + * @gnodes: a list of #GtsGNode belonging to @g. + * + * Returns: the #GtsGNode belonging to @g and farthest from all the nodes in + * @gnodes (hmmm, definition of "farthest"?). + */ +GtsGNode * gts_graph_farthest (GtsGraph * g, GSList * gnodes) +{ + GtsGNode * farthest = NULL; + GSList * i; + gboolean reinit = TRUE, changed = TRUE; + guint level = 1; + + g_return_val_if_fail (g != NULL, NULL); + + /* initialize traversals */ + i = gnodes; + while (i) { + GTS_OBJECT (i->data)->reserved = + gts_graph_traverse_new (g, i->data, GTS_BREADTH_FIRST, reinit); + reinit = FALSE; + i = i->next; + } + + while (changed) { + changed = FALSE; + i = gnodes; + while (i) { + GtsGraphTraverse * t = GTS_OBJECT (i->data)->reserved; + GtsGNode * n; + while ((n = gts_graph_traverse_what_next (t)) && n->level == level) { + changed = TRUE; + farthest = n; + gts_graph_traverse_next (t); + } + i = i->next; + } + level++; + } + + /* destroy traversals */ + i = gnodes; + while (i) { + gts_graph_traverse_destroy (GTS_OBJECT (i->data)->reserved); + GTS_OBJECT (i->data)->reserved = NULL; + i = i->next; + } + return farthest; +} + +static void neighbor_count (GtsGNode * n, gpointer * data) +{ + guint * cuts = data[0]; + GtsGraph * g = data[1]; + + if (!gts_containee_is_contained (GTS_CONTAINEE (n), GTS_CONTAINER (g))) + (*cuts)++; +} + +static void count_edge_cuts (GtsGNode * n, gpointer * data) +{ + gts_gnode_foreach_neighbor (n, NULL, (GtsFunc) neighbor_count, data); +} + +/** + * gts_graph_edges_cut: + * @g: a #GtsGraph. + * + * Returns: the number of edges of @g connecting nodes belonging to @g + * to nodes not belonging to @g. + */ +guint gts_graph_edges_cut (GtsGraph * g) +{ + guint cuts = 0; + gpointer data[2]; + + g_return_val_if_fail (g != NULL, 0); + + data[0] = &cuts; + data[1] = g; + gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) count_edge_cuts, data); + + return cuts; +} + +static void sum_edge_cuts_weight (GtsGNode * n, gpointer * data) +{ + gfloat * weight = data[0]; + GtsGraph * g = data[1]; + GSList * i = GTS_SLIST_CONTAINER (n)->items; + + while (i) { + GtsGNode * n1 = GTS_GNODE_NEIGHBOR (n, i->data); + if (!gts_containee_is_contained (GTS_CONTAINEE (n1), GTS_CONTAINER (g))) + *weight += gts_gedge_weight (i->data); + i = i->next; + } +} + +/** + * gts_graph_edges_cut_weight: + * @g: a #GtsGraph. + * + * Returns: the sum of the weights of the edges of @g connecting nodes + * belonging to @g to nodes not belonging to @g. + */ +gfloat gts_graph_edges_cut_weight (GtsGraph * g) +{ + gfloat weight = 0.; + gpointer data[2]; + + g_return_val_if_fail (g != NULL, 0); + + data[0] = &weight; + data[1] = g; + gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) sum_edge_cuts_weight, + data); + + return weight; +} + +/** + * gts_graph_read_jostle: + * @g: a #GtsGraph. + * @fp: a #GtsFile. + * + * Adds to @g the nodes and edges defined in the file pointed to by + * @fp. This file must use the Jostle "graph" ASCII format. + * The nodes created are of type #GtsNGNode and their identities are the + * line number at which they appear in @fp. + * + * Returns: 0 if the lecture was successful, the line number at which + * an error occured otherwise (in which case the @error field of @fp + * is set). + */ +guint gts_graph_read_jostle (GtsGraph * g, GtsFile * fp) +{ + guint nn, ne, n; + GtsGNode ** nodes; + + g_return_val_if_fail (g != NULL, 1); + g_return_val_if_fail (fp != NULL, 1); + + if (fp->type != GTS_INT) { + gts_file_error (fp, "expecting an integer (number of nodes)"); + return fp->line; + } + nn = atoi (fp->token->str); + gts_file_next_token (fp); + + if (fp->type != GTS_INT) { + gts_file_error (fp, "expecting an integer (number of edges)"); + return fp->line; + } + ne = atoi (fp->token->str); + + gts_file_first_token_after (fp, '\n'); + nodes = g_malloc (sizeof (GtsGNode *)*(nn + 1)); + + n = 0; + while (n < nn && fp->type != GTS_ERROR) { + GtsNGNode * node = gts_ngnode_new (gts_ngnode_class (), fp->line); + + nodes[n++] = GTS_GNODE (node); + gts_container_add (GTS_CONTAINER (g), GTS_CONTAINEE (node)); + do { + if (fp->type != GTS_INT) + gts_file_error (fp, "expecting an integer (node index)"); + else { + guint in = atoi (fp->token->str); + + if (in == 0 || in > nn) + gts_file_error (fp, "node index `%d' is out of range `[1,%d]'", + in, nn); + else if (in == n) + gts_file_error (fp, "node index `%d' references itself", in); + else if (in < n) { + gts_gedge_new (g->edge_class, GTS_GNODE (node), nodes[in - 1]); + ne--; + gts_file_next_token (fp); + } + } + } while (fp->type != GTS_ERROR && fp->type != '\n'); + } + g_free (nodes); + + if (fp->type != GTS_ERROR) { + if (n != nn) + gts_file_error (fp, "only `%d' nodes read out of `%d'", + n, nn); + else if (ne > 0) + gts_file_error (fp, "`%d' unallocated edges remaining", + ne); + } + + if (fp->type == GTS_ERROR) + return fp->line; + return 0; +} + +static void count_edges (GtsGEdge * e, guint * nedge) +{ + (*nedge)++; +} + +static void write_node (GtsObject * node, gpointer * data) +{ + FILE * fp = data[0]; + guint * nnode = data[1]; + + node->reserved = GUINT_TO_POINTER ((*nnode)++); + if (node->klass->write) + (* node->klass->write) (node, fp); + fputc ('\n', fp); +} + +static void write_edge (GtsGEdge * edge, FILE * fp) +{ + fprintf (fp, "%u %u", + GPOINTER_TO_UINT (GTS_OBJECT (edge->n1)->reserved), + GPOINTER_TO_UINT (GTS_OBJECT (edge->n2)->reserved)); + if (GTS_OBJECT (edge)->klass->write) + (* GTS_OBJECT (edge)->klass->write) (GTS_OBJECT (edge), fp); + fputc ('\n', fp); +} + +/** + * gts_graph_write: + * @g: a #GtsGraph. + * @fp: a file pointer. + * + * Writes in the file @fp an ASCII representation of @g. The file + * format is as follows. + * + * All the lines beginning with #GTS_COMMENTS are ignored. The first line + * contains two unsigned integers separated by spaces. The first + * integer is the number of nodes, nn, the second is the number of + * edges, ne. + * + * Follows nn lines containing node description. + * Follows ne lines containing the two indices (starting + * from one) of the nodes of each edge. + * + * The format described above is the least common denominator to all + * GTS files. Consistent with an object-oriented approach, the GTS + * file format is extensible. Each of the lines of the file can be + * extended with user-specific attributes accessible through the + * read() and write() virtual methods of each of the objects written + * (graph, nodes or edges). When read with different object classes, + * these extra attributes are just ignored. + */ +void gts_graph_write (GtsGraph * g, FILE * fp) +{ + guint nnode = 1, nedge = 0; + gpointer data[2]; + + g_return_if_fail (g != NULL); + g_return_if_fail (fp != NULL); + + gts_graph_foreach_edge (g, (GtsFunc) count_edges, &nedge); + fprintf (fp, "%u %u", gts_container_size (GTS_CONTAINER (g)), nedge); + if (GTS_OBJECT (g)->klass->write) + (* GTS_OBJECT (g)->klass->write) (GTS_OBJECT (g), fp); + fputc ('\n', fp); + data[0] = fp; + data[1] = &nnode; + gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) write_node, data); + gts_graph_foreach_edge (g, (GtsFunc) write_edge, fp); + gts_container_foreach (GTS_CONTAINER (g), + (GtsFunc) gts_object_reset_reserved, NULL); +} + +/** + * gts_graph_read: + * @fp: a #GtsFile. + * + * Reads a graph from a file. + * + * Returns: the new #GtsGraph or %NULL if an error occured (in which + * case the @error field of @fp is set). + */ +GtsGraph * gts_graph_read (GtsFile * fp) +{ + GtsGraph * g; + GtsGNode ** nodes; + guint nn, ne, n; + + g_return_val_if_fail (fp != NULL, NULL); + + if (fp->type != GTS_INT) { + gts_file_error (fp, "expecting an integer (number of nodes)"); + return NULL; + } + nn = atoi (fp->token->str); + gts_file_next_token (fp); + + if (fp->type != GTS_INT) { + gts_file_error (fp, "expecting an integer (number of edges)"); + return NULL; + } + ne = atoi (fp->token->str); + + gts_file_next_token (fp); + if (fp->type != '\n') { + GtsObjectClass * klass; + + gts_graph_class (); + gts_gnode_class (); + gts_gedge_class (); + + if (fp->type != GTS_STRING) { + gts_file_error (fp, "expecting a string (GtsGraphClass)"); + return NULL; + } + klass = gts_object_class_from_name (fp->token->str); + if (klass == NULL) { + gts_file_error (fp, "unknown class `%s'", fp->token->str); + return NULL; + } + if (!gts_object_class_is_from_class (klass, gts_graph_class ())) { + gts_file_error (fp, "class `%s' is not a GtsGraphClass", fp->token->str); + return NULL; + } + g = GTS_GRAPH (gts_object_new (klass)); + g->graph_class = GTS_GRAPH_CLASS (klass); + gts_file_next_token (fp); + (* klass->read) ((GtsObject **) &g, fp); + if (fp->type == GTS_ERROR) { + gts_object_destroy (GTS_OBJECT (g)); + return NULL; + } + } + else + g = GTS_GRAPH (gts_object_new (GTS_OBJECT_CLASS (gts_graph_class ()))); + gts_file_first_token_after (fp, '\n'); + if (nn <= 0) + return g; + + nodes = g_malloc ((nn + 1)*sizeof (GtsGNode *)); + + n = 0; + while (n < nn && fp->type != GTS_ERROR) { + GtsObject * new_node = + gts_object_new (GTS_OBJECT_CLASS (g->node_class)); + + gts_container_add (GTS_CONTAINER (g), GTS_CONTAINEE (new_node)); + if (GTS_OBJECT_CLASS (g->node_class)->read) + (*GTS_OBJECT_CLASS (g->node_class)->read) (&new_node, fp); + gts_file_first_token_after (fp, '\n'); + nodes[n++] = GTS_GNODE (new_node); + } + if (fp->type == GTS_ERROR) + nn = n; + + n = 0; + while (n < ne && fp->type != GTS_ERROR) { + guint n1, n2; + + if (fp->type != GTS_INT) + gts_file_error (fp, "expecting an integer (first node index)"); + else { + n1 = atoi (fp->token->str); + if (n1 == 0 || n1 > nn) + gts_file_error (fp, "node index `%d' is out of range `[1,%d]'", + n1, nn); + else { + gts_file_next_token (fp); + if (fp->type != GTS_INT) + gts_file_error (fp, "expecting an integer (second node index)"); + else { + n2 = atoi (fp->token->str); + if (n2 == 0 || n2 > nn) + gts_file_error (fp, "node index `%d' is out of range `[1,%d]'", + n2, nn); + else { + GtsGEdge * new_edge = + gts_gedge_new (g->edge_class, nodes[n1 - 1], nodes [n2 - 1]); + + gts_file_next_token (fp); + if (fp->type != '\n') + if (GTS_OBJECT_CLASS (g->edge_class)->read) + (*GTS_OBJECT_CLASS (g->edge_class)->read) + ((GtsObject **) &new_edge, fp); + gts_file_first_token_after (fp, '\n'); + n++; + } + } + } + } + } + + if (fp->type == GTS_ERROR) { + gts_allow_floating_gnodes = TRUE; + while (nn) + gts_object_destroy (GTS_OBJECT (nodes[nn-- - 1])); + gts_allow_floating_gnodes = FALSE; + } + g_free (nodes); + + if (fp->type == GTS_ERROR) { + gts_object_destroy (GTS_OBJECT (g)); + return NULL; + } + return g; +} + +static void write_dot_node (GtsGNode * node, gpointer * data) +{ + FILE * fp = data[0]; + guint * nnode = data[1]; + + fprintf (fp, " n%u", *nnode); + if (GTS_GNODE_CLASS (GTS_OBJECT (node)->klass)->write) { + fputs (" [", fp); + (* GTS_GNODE_CLASS (GTS_OBJECT (node)->klass)->write) (node, fp); + fputc (']', fp); + } + fputs (";\n", fp); + GTS_OBJECT (node)->reserved = GUINT_TO_POINTER ((*nnode)++); +} + +static void write_dot_edge (GtsGEdge * edge, FILE * fp) +{ + fprintf (fp, " n%u -> n%u", + GPOINTER_TO_UINT (GTS_OBJECT (edge->n1)->reserved), + GPOINTER_TO_UINT (GTS_OBJECT (edge->n2)->reserved)); + if (GTS_GEDGE_CLASS (GTS_OBJECT (edge)->klass)->write) { + fputs (" [", fp); + (* GTS_GEDGE_CLASS (GTS_OBJECT (edge)->klass)->write) (edge, fp); + fputc (']', fp); + } + fputs (";\n", fp); +} + +/** + * gts_graph_write_dot: + * @g: a #GtsGraph. + * @fp: a file pointer. + * + * Writes in the file @fp an ASCII representation of @g in the dot format of + * AT&T Bell Labs. + */ +void gts_graph_write_dot (GtsGraph * g, FILE * fp) +{ + guint nnode = 1; + gpointer data[2]; + + g_return_if_fail (g != NULL); + g_return_if_fail (fp != NULL); + + fprintf (fp, "digraph \"%p\" {\n", g); + data[0] = fp; + data[1] = &nnode; + gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) write_dot_node, data); + gts_graph_foreach_edge (g, (GtsFunc) write_dot_edge, fp); + fputs ("}\n", fp); + + gts_container_foreach (GTS_CONTAINER (g), + (GtsFunc) gts_object_reset_reserved, NULL); +} + +/* GtsWGraph */ + +static gfloat wgraph_weight (GtsGraph * g) +{ + return GTS_WGRAPH (g)->weight; +} + +static void wgraph_add (GtsContainer * g, GtsContainee * n) +{ + GtsWGraph * wg = GTS_WGRAPH (g); + gfloat w = gts_gnode_weight (GTS_GNODE (n)); + + wg->weight += w; + + (* GTS_CONTAINER_CLASS (GTS_OBJECT_CLASS (gts_wgraph_class ())->parent_class)->add) (g, n); +} + +static void wgraph_remove (GtsContainer * g, GtsContainee * n) +{ + GTS_WGRAPH (g)->weight -= gts_gnode_weight (GTS_GNODE (n)); + + (* GTS_CONTAINER_CLASS (GTS_OBJECT_CLASS (gts_wgraph_class ())->parent_class)->remove) (g, n); +} + +static void wgraph_class_init (GtsWGraphClass * klass) +{ + GTS_GRAPH_CLASS (klass)->weight = wgraph_weight; + + GTS_CONTAINER_CLASS (klass)->add = wgraph_add; + GTS_CONTAINER_CLASS (klass)->remove = wgraph_remove; +} + +static void wgraph_init (GtsWGraph * g) +{ + g->weight = 0.; +} + +/** + * gts_wgraph_class: + * + * Returns: the #GtsWGraphClass. + */ +GtsWGraphClass * gts_wgraph_class (void) +{ + static GtsWGraphClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo wgraph_info = { + "GtsWGraph", + sizeof (GtsWGraph), + sizeof (GtsWGraphClass), + (GtsObjectClassInitFunc) wgraph_class_init, + (GtsObjectInitFunc) wgraph_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_graph_class ()), + &wgraph_info); + } + + return klass; +} + +static void weight_max (GtsGNode * n, gfloat * wmax) +{ + gfloat w = gts_gnode_weight (n); + + if (w > *wmax) + *wmax = w; +} + +/** + * gts_wgraph_weight_max: + * @wg: a #GtsWGraph. + * + * Returns: the maximum weight of any vertices belonging to @g. + */ +gfloat gts_wgraph_weight_max (GtsWGraph * wg) +{ + gfloat wmax = - G_MAXFLOAT; + + g_return_val_if_fail (wg != NULL, 0.); + + gts_container_foreach (GTS_CONTAINER (wg), (GtsFunc) weight_max, &wmax); + + return wmax; +} + +/* Surface graph */ + +static void create_node (GtsFace * f, GtsGraph * graph) +{ + GtsFNode * fn = gts_fnode_new (gts_fnode_class (), f); + + gts_container_add (GTS_CONTAINER (graph), GTS_CONTAINEE (fn)); + GTS_OBJECT (f)->reserved = fn; +} + +static void create_edge (GtsEdge * e, GtsSurface * s) +{ + GSList * i = e->triangles; + + while (i) { + GtsFace * f = i->data; + if (GTS_IS_FACE (f) && gts_face_has_parent_surface (f, s)) { + GSList * j = i->next; + while (j) { + GtsFace * f1 = j->data; + if (GTS_IS_FACE (f1) && gts_face_has_parent_surface (f1, s)) + gts_pgedge_new (gts_pgedge_class (), + GTS_OBJECT (f)->reserved, + GTS_OBJECT (f1)->reserved, + e); + j = j->next; + } + } + i = i->next; + } +} + +/** + * gts_surface_graph_new: + * @klass: a #GtsGraphClass. + * @s: a #GtsSurface. + * + * Returns: a new #GtsGraph representing the connectivity of the faces + * of @s. This graph uses #GtsFGNode as nodes which allows to store + * the dependencies between nodes and faces of @s. + */ +GtsGraph * gts_surface_graph_new (GtsGraphClass * klass, + GtsSurface * s) +{ + GtsGraph * graph; + + g_return_val_if_fail (klass != NULL, NULL); + g_return_val_if_fail (s != NULL, NULL); + + graph = GTS_GRAPH (gts_object_new (GTS_OBJECT_CLASS (klass))); + gts_surface_foreach_face (s, (GtsFunc) create_node, graph); + gts_surface_foreach_edge (s, (GtsFunc) create_edge, s); + gts_surface_foreach_face (s, (GtsFunc) gts_object_reset_reserved, NULL); + + return graph; +} + +static void create_segment_edge (GtsSegment * s, GtsGraph * graph) +{ + GtsGNode * n1 = GTS_OBJECT (s->v1)->reserved, * n2; + + if (n1 == NULL) { + n1 = GTS_GNODE (gts_pnode_new (gts_pnode_class (), s->v1)); + gts_container_add (GTS_CONTAINER (graph), GTS_CONTAINEE (n1)); + GTS_OBJECT (s->v1)->reserved = n1; + } + + n2 = GTS_OBJECT (s->v2)->reserved; + if (n2 == NULL) { + n2 = GTS_GNODE (gts_pnode_new (gts_pnode_class (), s->v2)); + gts_container_add (GTS_CONTAINER (graph), GTS_CONTAINEE (n2)); + GTS_OBJECT (s->v2)->reserved = n2; + } + + gts_pgedge_new (gts_pgedge_class (), n1, n2, s); +} + +static void reset_reserved (GtsSegment * s) +{ + GTS_OBJECT (s->v1)->reserved = GTS_OBJECT (s->v2)->reserved = NULL; +} + +/** + * gts_segments_graph_new: + * @klass: a #GtsGraphClass. + * @segments: a list of #GtsSegment. + * + * Returns: a new #GtsGraph representing the connectivity of the segments + * in @segments. + */ +GtsGraph * gts_segments_graph_new (GtsGraphClass * klass, + GSList * segments) +{ + GtsGraph * graph; + + g_return_val_if_fail (klass != NULL, NULL); + + graph = GTS_GRAPH (gts_object_new (GTS_OBJECT_CLASS (klass))); + g_slist_foreach (segments, (GFunc) create_segment_edge, graph); + g_slist_foreach (segments, (GFunc) reset_reserved, NULL); + + return graph; +} + +static void add_to_surface (GtsGNode * n, GtsSurface * s) +{ + if (GTS_IS_FNODE (n)) + gts_surface_add_face (s, GTS_FNODE (n)->f); +} + +/** + * gts_surface_graph_surface: + * @surface_graph: a #GtsGraph using #GtsFGNode as nodes. + * @s: a #GtsSurface. + * + * Returns: a new #GtsSurface using the same classes as @s and + * composed of the faces defined by @surface_graph. + */ +GtsSurface * gts_surface_graph_surface (GtsGraph * surface_graph, + GtsSurface * s) +{ + GtsSurface * s1; + + g_return_val_if_fail (surface_graph != NULL, NULL); + g_return_val_if_fail (s != NULL, NULL); + + s1 = gts_surface_new (GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass), + s->face_class, + s->edge_class, + s->vertex_class); + gts_container_foreach (GTS_CONTAINER (surface_graph), + (GtsFunc) add_to_surface, s1); + return s1; +} + Index: toporouter/src_3rd/gts/gts-private.h =================================================================== --- toporouter/src_3rd/gts/gts-private.h (nonexistent) +++ toporouter/src_3rd/gts/gts-private.h (revision 6803) @@ -0,0 +1,37 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GTS_PRIVATE_H__ +#define __GTS_PRIVATE_H__ + +/* Debugging flags */ + +/* #define DEBUG_FUNCTIONS */ + +#ifdef DEBUG_FUNCTIONS +/* #define DEBUG_LEAKS */ +#define DEBUG_IDENTITY +guint id (gpointer p); +void id_insert (gpointer p); +void id_remove (gpointer p); +void gts_write_triangle (GtsTriangle * t, GtsPoint * o, FILE * fptr); +void gts_write_segment (GtsSegment * s, GtsPoint * o, FILE * fptr); +#endif /* DEBUG_FUNCTIONS */ + +#endif /* __GTS_PRIVATE_H__ */ Index: toporouter/src_3rd/gts/gts.h =================================================================== --- toporouter/src_3rd/gts/gts.h (nonexistent) +++ toporouter/src_3rd/gts/gts.h (revision 6803) @@ -0,0 +1,2577 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GTS_H__ +#define __GTS_H__ + +#include +#include + +#define GTS_MAJOR_VERSION 0 +#define GTS_MINOR_VERSION 7 +#define GTS_MICRO_VERSION 6 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Added based on glib.h by M J Loehr 01/01/01 */ +/* GTS version. + * we prefix variable declarations so they can + * properly get exported in windows dlls. + */ +#ifdef NATIVE_WIN32 +# ifdef GTS_COMPILATION +# define GTS_C_VAR __declspec(dllexport) +# else /* not GTS_COMPILATION */ +# define GTS_C_VAR extern __declspec(dllimport) +# endif /* not GTS_COMPILATION */ +#else /* not NATIVE_WIN32 */ +# define GTS_C_VAR extern +#endif /* not NATIVE_WIN32 */ + +GTS_C_VAR const guint gts_major_version; +GTS_C_VAR const guint gts_minor_version; +GTS_C_VAR const guint gts_micro_version; +GTS_C_VAR const guint gts_interface_age; +GTS_C_VAR const guint gts_binary_age; + +#define GTS_CHECK_VERSION(major,minor,micro) \ + (gts_major_version > (major) || \ + (gts_major_version == (major) && gts_minor_version > (minor)) || \ + (gts_major_version == (major) && gts_minor_version == (minor) && \ + gts_micro_version >= (micro))) + +#define GTS_COMMENTS "#!" +#define GTS_MAINTAINER "popinet@users.sourceforge.net" + + + + +void gts_predicates_init(); + + +/* Class declarations for base types */ + +typedef struct _GtsObjectClassInfo GtsObjectClassInfo; +typedef struct _GtsObject GtsObject; +typedef struct _GtsObjectClass GtsObjectClass; +typedef struct _GtsPoint GtsPoint; +typedef struct _GtsPointClass GtsPointClass; +typedef struct _GtsVertex GtsVertex; +typedef struct _GtsVertexClass GtsVertexClass; +typedef struct _GtsSegment GtsSegment; +typedef struct _GtsSegmentClass GtsSegmentClass; +typedef struct _GtsEdge GtsEdge; +typedef struct _GtsEdgeClass GtsEdgeClass; +typedef struct _GtsTriangle GtsTriangle; +typedef struct _GtsTriangleClass GtsTriangleClass; +typedef struct _GtsFace GtsFace; +typedef struct _GtsFaceClass GtsFaceClass; +typedef struct _GtsBBox GtsBBox; +typedef struct _GtsBBoxClass GtsBBoxClass; +typedef struct _GtsSurface GtsSurface; +typedef struct _GtsSurfaceClass GtsSurfaceClass; + +typedef void (*GtsObjectClassInitFunc) (GtsObjectClass * objclass); +typedef void (*GtsObjectInitFunc) (GtsObject * obj); +typedef void (*GtsArgSetFunc) (GtsObject * obj); +typedef void (*GtsArgGetFunc) (GtsObject * obj); + +typedef gdouble GtsVector[3]; +typedef gdouble GtsVector4[4]; +typedef GtsVector4 GtsMatrix; +/** + * GtsKeyFunc: + * @item: A pointer to an item to be stored in the heap. + * @data: User data passed to gts_eheap_new(). + * + * Returns: the value of the key for the given item. + */ +typedef gdouble (*GtsKeyFunc) (gpointer item, + gpointer data); +typedef enum +{ + GTS_OUT = -1, + GTS_ON = 0, + GTS_IN = 1 +} GtsIntersect; + +typedef struct _GtsColor GtsColor; + +struct _GtsColor { + gfloat r, g, b; +}; + +typedef gint (*GtsFunc) (gpointer item, + gpointer data); + +/* misc.c */ + +typedef struct _GtsFile GtsFile; + +typedef enum { + GTS_NONE = 1 << 8, + GTS_INT = 1 << 9, + GTS_UINT = 1 << 10, + GTS_FLOAT = 1 << 11, + GTS_DOUBLE = 1 << 12, + GTS_STRING = 1 << 13, + GTS_FILE = 1 << 14, + GTS_ERROR = 1 << 15 +} GtsTokenType; + +struct _GtsFile { + FILE * fp; + gchar * s, * s1; + guint line, pos; + GString * token; + GtsTokenType type; + gchar * error; + + guint curline, curpos; + guint scope, scope_max; + gint next_token; + gchar * delimiters; + gchar * comments; + gchar * tokens; +}; + +typedef struct _GtsFileVariable GtsFileVariable; + +struct _GtsFileVariable { + GtsTokenType type; + gchar name[30]; + gboolean unique; + gpointer data; + gboolean set; + guint line, pos; +}; + + +GtsFile * gts_file_new (FILE * fp); +GtsFile * gts_file_new_from_string (const gchar * s); +void gts_file_verror (GtsFile * f, + const gchar * format, + va_list args); +void gts_file_error (GtsFile * f, + const gchar * format, + ...); +gint gts_file_getc (GtsFile * f); +guint gts_file_read (GtsFile * f, + gpointer ptr, + guint size, + guint nmemb); +gint gts_file_getc_scope (GtsFile * f); +void gts_file_next_token (GtsFile * f); +void gts_file_first_token_after (GtsFile * f, + GtsTokenType type); +void gts_file_assign_start (GtsFile * f, + GtsFileVariable * vars); +GtsFileVariable * gts_file_assign_next (GtsFile * f, + GtsFileVariable * vars); +void gts_file_assign_variables (GtsFile * f, + GtsFileVariable * vars); +void gts_file_variable_error (GtsFile * f, + GtsFileVariable * vars, + const gchar * name, + const gchar * format, + ...); +void gts_file_destroy (GtsFile * f); + +/* Objects: object.c */ + +#ifdef GTS_CHECK_CASTS +# define GTS_OBJECT_CAST(obj, type, klass) ((type *) gts_object_check_cast (obj, klass)) +# define GTS_OBJECT_CLASS_CAST(objklass, type, klass) ((type *) gts_object_class_check_cast (objklass, klass)) +#else /* not GTS_CHECK_CASTS */ +# define GTS_OBJECT_CAST(obj, type, klass) ((type *) (obj)) +# define GTS_OBJECT_CLASS_CAST(objklass, type, klass) ((type *) (objklass)) +#endif /* not GTS_CHECK_CASTS */ + +#define GTS_CLASS_NAME_LENGTH 40 +#define GTS_OBJECT(obj) GTS_OBJECT_CAST (obj,\ + GtsObject,\ + gts_object_class ()) +#define GTS_OBJECT_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsObjectClass,\ + gts_object_class()) +#define GTS_IS_OBJECT(obj) (gts_object_is_from_class (obj,\ + gts_object_class ())) + +typedef enum +{ + GTS_DESTROYED = 1 << 0, + GTS_USER_FLAG = 1 /* user flags start from here */ +} GtsObjectFlags; + +#define GTS_OBJECT_FLAGS(obj) (GTS_OBJECT (obj)->flags) +#define GTS_OBJECT_DESTROYED(obj) ((GTS_OBJECT_FLAGS (obj) & GTS_DESTROYED) != 0) +#define GTS_OBJECT_SET_FLAGS(obj,flag) G_STMT_START{ (GTS_OBJECT_FLAGS (obj) |= (flag)); }G_STMT_END +#define GTS_OBJECT_UNSET_FLAGS(obj,flag) G_STMT_START{ (GTS_OBJECT_FLAGS (obj) &= ~(flag)); }G_STMT_END + +struct _GtsObjectClassInfo { + gchar name[GTS_CLASS_NAME_LENGTH]; + guint object_size; + guint class_size; + GtsObjectClassInitFunc class_init_func; + GtsObjectInitFunc object_init_func; + GtsArgSetFunc arg_set_func; + GtsArgGetFunc arg_get_func; +}; + +struct _GtsObject { + GtsObjectClass * klass; + + gpointer reserved; + guint32 flags; +}; + +struct _GtsObjectClass { + GtsObjectClassInfo info; + GtsObjectClass * parent_class; + + void (* clone) (GtsObject *, GtsObject *); + void (* destroy) (GtsObject *); + void (* read) (GtsObject **, GtsFile *); + void (* write) (GtsObject *, FILE *); + GtsColor (* color) (GtsObject *); + void (* attributes) (GtsObject *, GtsObject *); +}; + +gpointer gts_object_class_new (GtsObjectClass * parent_class, + GtsObjectClassInfo * info); +GtsObjectClass * gts_object_class (void); +gpointer gts_object_check_cast (gpointer object, + gpointer klass); +gpointer gts_object_class_check_cast (gpointer klass, + gpointer from); + +static inline +gpointer gts_object_is_from_class (gpointer object, + gpointer klass) +{ + GtsObjectClass * c; + + g_return_val_if_fail (klass != NULL, NULL); + + if (object == NULL) + return NULL; + + c = ((GtsObject *) object)->klass; + + g_return_val_if_fail (c != NULL, NULL); + + while (c) { + if (c == klass) + return object; + c = c->parent_class; + } + + return NULL; +} + +static inline +gpointer gts_object_class_is_from_class (gpointer klass, + gpointer from) +{ + GtsObjectClass * c; + + g_return_val_if_fail (klass != NULL, NULL); + g_return_val_if_fail (from != NULL, NULL); + + c = (GtsObjectClass *) klass; + while (c) { + if (c == from) + return klass; + c = c->parent_class; + } + + return NULL; +} + +GtsObjectClass * gts_object_class_from_name (const gchar * name); + +GtsObject * gts_object_new (GtsObjectClass * klass); +GtsObject * gts_object_clone (GtsObject * object); +void gts_object_attributes (GtsObject * object, + GtsObject * from); +void gts_object_init (GtsObject * object, + GtsObjectClass * klass); +void gts_object_reset_reserved (GtsObject * object); +void gts_object_destroy (GtsObject * object); +void gts_finalize (void); + +/* Ranges: surface.c */ +typedef struct _GtsRange GtsRange; + +struct _GtsRange { + gdouble min, max, sum, sum2, mean, stddev; + guint n; +}; + +void gts_range_init (GtsRange * r); +void gts_range_reset (GtsRange * r); +void gts_range_add_value (GtsRange * r, + gdouble val); +void gts_range_update (GtsRange * r); +void gts_range_print (GtsRange * r, + FILE * fptr); + +/* Points: point.c */ + +#define GTS_IS_POINT(obj) (gts_object_is_from_class (obj,\ + gts_point_class ())) +#define GTS_POINT(obj) GTS_OBJECT_CAST (obj,\ + GtsPoint,\ + gts_point_class ()) +#define GTS_POINT_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsPointClass,\ + gts_point_class ()) + +struct _GtsPoint { + GtsObject object; + + gdouble x, y, z; /* must be contiguous (cast to robust functions) */ +}; + +struct _GtsPointClass { + GtsObjectClass parent_class; + gboolean binary; +}; + +GtsPointClass * gts_point_class (void); +GtsPoint * gts_point_new (GtsPointClass * klass, + gdouble x, + gdouble y, + gdouble z); +void gts_point_set (GtsPoint * p, + gdouble x, + gdouble y, + gdouble z); +#define gts_point_is_in_rectangle(p, p1, p2) ((p)->x >= (p1)->x &&\ + (p)->x <= (p2)->x &&\ + (p)->y >= (p1)->y &&\ + (p)->y <= (p2)->y &&\ + (p)->z >= (p1)->z &&\ + (p)->z <= (p2)->z) +GtsPoint * gts_segment_triangle_intersection (GtsSegment * s, + GtsTriangle * t, + gboolean boundary, + GtsPointClass * klass); +void gts_point_transform (GtsPoint * p, + GtsMatrix * m); +gdouble gts_point_distance (GtsPoint * p1, + GtsPoint * p2); +gdouble gts_point_distance2 (GtsPoint * p1, + GtsPoint * p2); +gdouble gts_point_orientation_3d (GtsPoint * p1, + GtsPoint * p2, + GtsPoint * p3, + GtsPoint * p4); +gint gts_point_orientation_3d_sos (GtsPoint * p1, + GtsPoint * p2, + GtsPoint * p3, + GtsPoint * p4); +GtsIntersect gts_point_is_in_triangle (GtsPoint * p, + GtsTriangle * t); +gdouble gts_point_in_circle (GtsPoint * p, + GtsPoint * p1, + GtsPoint * p2, + GtsPoint * p3); +gdouble gts_point_in_sphere (GtsPoint * p, + GtsPoint * p1, + GtsPoint * p2, + GtsPoint * p3, + GtsPoint * p4); +gdouble gts_point_in_triangle_circle (GtsPoint * p, + GtsTriangle * t); +gdouble gts_point_orientation (GtsPoint * p1, + GtsPoint * p2, + GtsPoint * p3); +gint gts_point_orientation_sos (GtsPoint * p1, + GtsPoint * p2, + GtsPoint * p3); +gdouble gts_point_segment_distance2 (GtsPoint * p, + GtsSegment * s); +gdouble gts_point_segment_distance (GtsPoint * p, + GtsSegment * s); +void gts_point_segment_closest (GtsPoint * p, + GtsSegment * s, + GtsPoint * closest); +gdouble gts_point_triangle_distance2 (GtsPoint * p, + GtsTriangle * t); +gdouble gts_point_triangle_distance (GtsPoint * p, + GtsTriangle * t); +void gts_point_triangle_closest (GtsPoint * p, + GtsTriangle * t, + GtsPoint * closest); +gboolean gts_point_is_inside_surface (GtsPoint * p, + GNode * tree, + gboolean is_open); + +/* Vertices: vertex.c */ + +#define GTS_IS_VERTEX(obj) (gts_object_is_from_class (obj,\ + gts_vertex_class ())) +#define GTS_VERTEX(obj) GTS_OBJECT_CAST (obj,\ + GtsVertex,\ + gts_vertex_class ()) +#define GTS_VERTEX_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsVertexClass,\ + gts_vertex_class ()) +struct _GtsVertex { + GtsPoint p; + + GSList * segments; +}; + +struct _GtsVertexClass { + GtsPointClass parent_class; + + void (* intersection_attributes) (GtsVertex *, + GtsObject *, + GtsObject *); +}; + +GTS_C_VAR +gboolean gts_allow_floating_vertices; + +GtsVertexClass * gts_vertex_class (void); +GtsVertex * gts_vertex_new (GtsVertexClass * klass, + gdouble x, + gdouble y, + gdouble z); +void gts_vertex_replace (GtsVertex * v, + GtsVertex * with); +gboolean gts_vertex_is_unattached (GtsVertex * v); +GtsSegment * gts_vertices_are_connected (GtsVertex * v1, + GtsVertex * v2); +GSList * gts_vertex_triangles (GtsVertex * v, + GSList * list); +GSList * gts_vertex_faces (GtsVertex * v, + GtsSurface * surface, + GSList * list); +GSList * gts_vertex_neighbors (GtsVertex * v, + GSList * list, + GtsSurface * surface); +GSList * gts_vertices_from_segments (GSList * segments); +gboolean gts_vertex_is_boundary (GtsVertex * v, + GtsSurface * surface); +GList * gts_vertices_merge (GList * vertices, + gdouble epsilon, + gboolean (* check) (GtsVertex *, GtsVertex *)); +GSList * gts_vertex_fan_oriented (GtsVertex * v, + GtsSurface * surface); +guint gts_vertex_is_contact (GtsVertex * v, gboolean sever); + +/* GtsVertexNormal: Header */ + +typedef struct _GtsVertexNormal GtsVertexNormal; + +struct _GtsVertexNormal { + /*< private >*/ + GtsVertex parent; + + /*< public >*/ + GtsVector n; +}; + +#define GTS_VERTEX_NORMAL(obj) GTS_OBJECT_CAST (obj,\ + GtsVertexNormal,\ + gts_vertex_normal_class ()) +#define GTS_IS_VERTEX_NORMAL(obj) (gts_object_is_from_class (obj,\ + gts_vertex_normal_class ())) + +GtsVertexClass * gts_vertex_normal_class (void); + +/* GtsColorVertex: Header */ + +typedef struct _GtsColorVertex GtsColorVertex; + +struct _GtsColorVertex { + /*< private >*/ + GtsVertex parent; + + /*< public >*/ + GtsColor c; +}; + +#define GTS_COLOR_VERTEX(obj) GTS_OBJECT_CAST (obj,\ + GtsColorVertex,\ + gts_color_vertex_class ()) +#define GTS_IS_COLOR_VERTEX(obj) (gts_object_is_from_class (obj,\ + gts_color_vertex_class ())) + +GtsVertexClass * gts_color_vertex_class (void); + +/* Segments: segment.c */ + +#define GTS_IS_SEGMENT(obj) (gts_object_is_from_class (obj,\ + gts_segment_class ())) +#define GTS_SEGMENT(obj) GTS_OBJECT_CAST (obj,\ + GtsSegment,\ + gts_segment_class ()) +#define GTS_SEGMENT_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsSegmentClass,\ + gts_segment_class ()) + +struct _GtsSegment { + GtsObject object; + + GtsVertex * v1; + GtsVertex * v2; +}; + +struct _GtsSegmentClass { + GtsObjectClass parent_class; +}; + +GtsSegmentClass * gts_segment_class (void); +GtsSegment * gts_segment_new (GtsSegmentClass * klass, + GtsVertex * v1, + GtsVertex * v2); +#define gts_segment_connect(s, e1, e2) (((s)->v1 == e1 &&\ + (s)->v2 == e2) || \ + ((s)->v1 == e2 &&\ + (s)->v2 == e1)) +#define gts_segments_are_identical(s1, s2) (((s1)->v1 == (s2)->v1 &&\ + (s1)->v2 == (s2)->v2)\ + ||\ + ((s1)->v1 == (s2)->v2 &&\ + (s1)->v2 == (s2)->v1)) +#define gts_segments_touch(s1, s2) ((s1)->v1 == (s2)->v1 ||\ + (s1)->v1 == (s2)->v2 ||\ + (s1)->v2 == (s2)->v1 ||\ + (s1)->v2 == (s2)->v2) +GtsIntersect gts_segments_are_intersecting (GtsSegment * s1, + GtsSegment * s2); +GtsSegment * gts_segment_is_duplicate (GtsSegment * s); +GtsVertex * gts_segment_midvertex (GtsSegment * s, + GtsVertexClass * klass); +GSList * gts_segments_from_vertices (GSList * vertices); +gboolean gts_segment_is_ok (GtsSegment * s); + +/* Edges: edge.c */ + +#define GTS_IS_EDGE(obj) (gts_object_is_from_class (obj,\ + gts_edge_class ())) +#define GTS_EDGE(obj) GTS_OBJECT_CAST (obj,\ + GtsEdge,\ + gts_edge_class ()) +#define GTS_EDGE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsEdgeClass,\ + gts_edge_class ()) + +struct _GtsEdge { + GtsSegment segment; + + GSList * triangles; +}; + +struct _GtsEdgeClass { + GtsSegmentClass parent_class; +}; + +GTS_C_VAR +gboolean gts_allow_floating_edges; + +GtsEdgeClass * gts_edge_class (void); +GtsEdge * gts_edge_new (GtsEdgeClass * klass, + GtsVertex * v1, + GtsVertex * v2); +/** + * gts_edge_is_unattached: + * @s: a #GtsEdge. + * + * Evaluates to %TRUE if no triangles uses @s as an edge, %FALSE otherwise. + */ +#define gts_edge_is_unattached(s) ((s)->triangles == NULL ? TRUE : FALSE) +GtsFace * gts_edge_has_parent_surface (GtsEdge * e, + GtsSurface * surface); +GtsFace * gts_edge_has_any_parent_surface (GtsEdge * e); +GtsFace * gts_edge_is_boundary (GtsEdge * e, + GtsSurface * surface); +void gts_edge_replace (GtsEdge * e, + GtsEdge * with); +GSList * gts_edges_from_vertices (GSList * vertices, + GtsSurface * parent); +guint gts_edge_face_number (GtsEdge * e, + GtsSurface * s); +gboolean gts_edge_collapse_is_valid (GtsEdge * e); +gboolean gts_edge_collapse_creates_fold (GtsEdge * e, + GtsVertex * v, + gdouble max); +GtsEdge * gts_edge_is_duplicate (GtsEdge * e); +GList * gts_edges_merge (GList * edges); +gboolean gts_edge_belongs_to_tetrahedron (GtsEdge * e); +guint gts_edge_is_contact (GtsEdge * e); +void gts_edge_swap (GtsEdge * e, + GtsSurface * s); +gboolean gts_edge_manifold_faces (GtsEdge * e, + GtsSurface * s, + GtsFace ** f1, + GtsFace ** f2); + +/* Triangles: triangle.c */ + +#define GTS_IS_TRIANGLE(obj) (gts_object_is_from_class (obj,\ + gts_triangle_class ())) +#define GTS_TRIANGLE(obj) GTS_OBJECT_CAST (obj,\ + GtsTriangle,\ + gts_triangle_class ()) +#define GTS_TRIANGLE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsTriangleClass,\ + gts_triangle_class ()) + +struct _GtsTriangle { + GtsObject object; + + GtsEdge * e1; + GtsEdge * e2; + GtsEdge * e3; +}; + +struct _GtsTriangleClass { + GtsObjectClass parent_class; +}; + +GtsTriangleClass * gts_triangle_class (void); +void gts_triangle_set (GtsTriangle * triangle, + GtsEdge * e1, + GtsEdge * e2, + GtsEdge * e3); +GtsTriangle * gts_triangle_new (GtsTriangleClass * klass, + GtsEdge * e1, + GtsEdge * e2, + GtsEdge * e3); +#define gts_triangle_vertex(t) (GTS_SEGMENT (GTS_TRIANGLE (t)->e1)->v1 ==\ + GTS_SEGMENT (GTS_TRIANGLE (t)->e2)->v1 || \ + GTS_SEGMENT (GTS_TRIANGLE (t)->e1)->v2 ==\ + GTS_SEGMENT (GTS_TRIANGLE (t)->e2)->v1 ? \ + GTS_SEGMENT (GTS_TRIANGLE (t)->e2)->v2 :\ + GTS_SEGMENT (GTS_TRIANGLE (t)->e2)->v1) +GtsVertex * gts_triangle_vertex_opposite (GtsTriangle * t, + GtsEdge * e); +GtsEdge * gts_triangle_edge_opposite (GtsTriangle * t, + GtsVertex * v); +gdouble gts_triangles_angle (GtsTriangle * t1, + GtsTriangle * t2); +gboolean gts_triangles_are_compatible (GtsTriangle * t1, + GtsTriangle * t2, + GtsEdge * e); +gdouble gts_triangle_area (GtsTriangle * t); +gdouble gts_triangle_perimeter (GtsTriangle * t); +gdouble gts_triangle_quality (GtsTriangle * t); +void gts_triangle_normal (GtsTriangle * t, + gdouble * x, + gdouble * y, + gdouble * z); +gdouble gts_triangle_orientation (GtsTriangle * t); +void gts_triangle_revert (GtsTriangle * t); +GSList * gts_triangles_from_edges (GSList * edges); +void gts_triangle_vertices_edges (GtsTriangle * t, + GtsEdge * e, + GtsVertex ** v1, + GtsVertex ** v2, + GtsVertex ** v3, + GtsEdge ** e1, + GtsEdge ** e2, + GtsEdge ** e3); +GtsTriangle * gts_triangle_enclosing (GtsTriangleClass * klass, + GSList * points, + gdouble scale); +guint gts_triangle_neighbor_number (GtsTriangle * t); +GSList * gts_triangle_neighbors (GtsTriangle * t); +GtsEdge * gts_triangles_common_edge (GtsTriangle * t1, + GtsTriangle * t2); +GtsTriangle * gts_triangle_is_duplicate (GtsTriangle * t); +GtsTriangle * gts_triangle_use_edges (GtsEdge * e1, + GtsEdge * e2, + GtsEdge * e3); +gboolean gts_triangle_is_ok (GtsTriangle * t); +void gts_triangle_vertices (GtsTriangle * t, + GtsVertex ** v1, + GtsVertex ** v2, + GtsVertex ** v3); +GtsPoint * gts_triangle_circumcircle_center (GtsTriangle * t, + GtsPointClass * point_class); +gboolean gts_triangles_are_folded (GSList * triangles, + GtsVertex * A, GtsVertex * B, + gdouble max); +GtsObject * gts_triangle_is_stabbed (GtsTriangle * t, + GtsPoint * p, + gdouble * orientation); +void gts_triangle_interpolate_height (GtsTriangle * t, + GtsPoint * p); + +/* Faces: face.c */ + +#define GTS_IS_FACE(obj) (gts_object_is_from_class (obj,\ + gts_face_class ())) +#define GTS_FACE(obj) GTS_OBJECT_CAST (obj,\ + GtsFace,\ + gts_face_class ()) +#define GTS_FACE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsFaceClass,\ + gts_face_class ()) + +struct _GtsFace { + GtsTriangle triangle; + + GSList * surfaces; +}; + +struct _GtsFaceClass { + GtsTriangleClass parent_class; +}; + +GTS_C_VAR +gboolean gts_allow_floating_faces; + +GtsFaceClass * gts_face_class (void); +GtsFace * gts_face_new (GtsFaceClass * klass, + GtsEdge * e1, + GtsEdge * e2, + GtsEdge * e3); +gboolean gts_face_has_parent_surface (GtsFace * f, + GtsSurface * s); +GSList * gts_faces_from_edges (GSList * edges, + GtsSurface * s); +guint gts_face_neighbor_number (GtsFace * f, + GtsSurface * s); +GSList * gts_face_neighbors (GtsFace * f, + GtsSurface * s); +void gts_face_foreach_neighbor (GtsFace * f, + GtsSurface * s, + GtsFunc func, + gpointer data); +gboolean gts_face_is_compatible (GtsFace * f, + GtsSurface * s); + +/* Matrices: matrix.c */ + +#define gts_vector_cross(C,A,B) ((C)[0] = (A)[1]*(B)[2] - (A)[2]*(B)[1],\ + (C)[1] = (A)[2]*(B)[0] - (A)[0]*(B)[2],\ + (C)[2] = (A)[0]*(B)[1] - (A)[1]*(B)[0]) + +#define gts_vector_init(v, p1, p2) ((v)[0] = (p2)->x - (p1)->x,\ + (v)[1] = (p2)->y - (p1)->y,\ + (v)[2] = (p2)->z - (p1)->z) +#define gts_vector_scalar(v1, v2) ((v1)[0]*(v2)[0] +\ + (v1)[1]*(v2)[1] +\ + (v1)[2]*(v2)[2]) +#define gts_vector_norm(v) (sqrt ((v)[0]*(v)[0] +\ + (v)[1]*(v)[1] +\ + (v)[2]*(v)[2])) +#define gts_vector_normalize(v) {\ + gdouble __gts_n = gts_vector_norm (v);\ + if (__gts_n > 0.) {\ + (v)[0] /= __gts_n;\ + (v)[1] /= __gts_n;\ + (v)[2] /= __gts_n;\ + }\ +} +GtsMatrix * gts_matrix_new (gdouble a00, gdouble a01, gdouble a02, gdouble a03, + gdouble a10, gdouble a11, gdouble a12, gdouble a13, + gdouble a20, gdouble a21, gdouble a22, gdouble a23, + gdouble a30, gdouble a31, gdouble a32, gdouble a33); +void gts_matrix_assign (GtsMatrix * m, + gdouble a00, gdouble a01, gdouble a02, gdouble a03, + gdouble a10, gdouble a11, gdouble a12, gdouble a13, + gdouble a20, gdouble a21, gdouble a22, gdouble a23, + gdouble a30, gdouble a31, gdouble a32, gdouble a33); +GtsMatrix * gts_matrix_projection (GtsTriangle * t); +GtsMatrix * gts_matrix_transpose (GtsMatrix * m); +gdouble gts_matrix_determinant (GtsMatrix * m); +GtsMatrix * gts_matrix_inverse (GtsMatrix * m); +GtsMatrix * gts_matrix3_inverse (GtsMatrix * m); +void gts_matrix_print (GtsMatrix * m, + FILE * fptr); +guint gts_matrix_compatible_row (GtsMatrix * A, + GtsVector b, + guint n, + GtsVector A1, + gdouble b1); +guint gts_matrix_quadratic_optimization (GtsMatrix * A, + GtsVector b, + guint n, + GtsMatrix * H, + GtsVector c); +GtsMatrix * gts_matrix_product (GtsMatrix * m1, + GtsMatrix * m2); +GtsMatrix * gts_matrix_zero (GtsMatrix * m); +GtsMatrix * gts_matrix_identity (GtsMatrix * m); +GtsMatrix * gts_matrix_scale (GtsMatrix * m, + GtsVector s); +GtsMatrix * gts_matrix_translate (GtsMatrix * m, + GtsVector t); +GtsMatrix * gts_matrix_rotate (GtsMatrix * m, + GtsVector r, + gdouble angle); +void gts_matrix_destroy (GtsMatrix * m); +void gts_vector_print (GtsVector v, + FILE * fptr); +void gts_vector4_print (GtsVector4 v, + FILE * fptr); + +/* Kdtrees: kdtree.c */ + +#define gts_kdtree_destroy(tree) g_node_destroy(tree) + +GNode * gts_kdtree_new (GPtrArray * points, + int (*compare) + (const void *, + const void *)); +GSList * gts_kdtree_range (GNode * tree, + GtsBBox * bbox, + int (*compare) + (const void *, + const void *)); + +/* Bboxtrees: bbtree.c */ + +/** + * GtsBBTreeTraverseFunc: + * @bb1: a #GtsBBox. + * @bb2: another #GtsBBox. + * @data: user data passed to the function. + * + * User function called for each pair of overlapping bounding + * boxes. See gts_bb_tree_traverse_overlapping(). + */ +typedef void (*GtsBBTreeTraverseFunc) (GtsBBox * bb1, + GtsBBox * bb2, + gpointer data); +/** + * GtsBBoxDistFunc: + * @p: a #GtsPoint. + * @bounded: an object bounded by a #GtsBBox. + * + * User function returning the (minimum) distance between the object + * defined by @bounded and point @p. + * + * Returns: the distance between @p and @bounded. + */ +typedef gdouble (*GtsBBoxDistFunc) (GtsPoint * p, + gpointer bounded); +/** + * GtsBBoxClosestFunc: + * @p: a #GtsPoint. + * @bounded: an object bounded by a #GtsBBox. + * + * User function returning a #GtsPoint belonging to the object defined + * by @bounded and closest to @p. + * + * Returns: a #GtsPoint. + */ +typedef GtsPoint * (*GtsBBoxClosestFunc) (GtsPoint * p, + gpointer bounded); + +/** + * GTS_IS_BBOX: + * @obj: a #GtsObject. + * + * Evaluates to %TRUE if @obj is a #GtsBBox, %FALSE otherwise. + */ +#define GTS_IS_BBOX(obj) (gts_object_is_from_class (obj,\ + gts_bbox_class ())) +/** + * GTS_BBOX: + * @obj: a #GtsObject. + * + * Casts @obj to #GtsBBox. + */ +#define GTS_BBOX(obj) GTS_OBJECT_CAST (obj,\ + GtsBBox,\ + gts_bbox_class ()) +/** + * GTS_BBOX_CLASS: + * @klass: a descendant of #GtsBBoxClass. + * + * Casts @klass to #GtsBBoxClass. + */ +#define GTS_BBOX_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsBBoxClass,\ + gts_bbox_class ()) + +struct _GtsBBox { + GtsObject object; + gpointer bounded; + gdouble x1, y1, z1; + gdouble x2, y2, z2; +}; + +struct _GtsBBoxClass { + GtsObjectClass parent_class; +}; + +GtsBBoxClass * gts_bbox_class (void); +GtsBBox * gts_bbox_new (GtsBBoxClass * klass, + gpointer bounded, + gdouble x1, + gdouble y1, + gdouble z1, + gdouble x2, + gdouble y2, + gdouble z2); +void gts_bbox_set (GtsBBox * bbox, + gpointer bounded, + gdouble x1, + gdouble y1, + gdouble z1, + gdouble x2, + gdouble y2, + gdouble z2); +GtsBBox * gts_bbox_segment (GtsBBoxClass * klass, + GtsSegment * s); +GtsBBox * gts_bbox_triangle (GtsBBoxClass * klass, + GtsTriangle * t); +GtsBBox * gts_bbox_surface (GtsBBoxClass * klass, + GtsSurface * surface); +GtsBBox * gts_bbox_bboxes (GtsBBoxClass * klass, + GSList * bboxes); +GtsBBox * gts_bbox_points (GtsBBoxClass * klass, + GSList * points); +/** + * gts_bbox_point_is_inside: + * @bbox: a #GtsBBox. + * @p: a #GtsPoint. + * + * Evaluates to %TRUE if @p is inside (or on the boundary) of @bbox, %FALSE otherwise. + */ +#define gts_bbox_point_is_inside(bbox, p) ((p)->x >= (bbox)->x1 &&\ + (p)->y >= (bbox)->y1 &&\ + (p)->z >= (bbox)->z1 &&\ + (p)->x <= (bbox)->x2 &&\ + (p)->y <= (bbox)->y2 &&\ + (p)->z <= (bbox)->z2) +gboolean gts_bboxes_are_overlapping (GtsBBox * bb1, + GtsBBox * bb2); +void gts_bbox_draw (GtsBBox * bb, + FILE * fptr); +gdouble gts_bbox_diagonal2 (GtsBBox * bb); +void gts_bbox_point_distance2 (GtsBBox * bb, + GtsPoint * p, + gdouble * min, + gdouble * max); +gboolean gts_bbox_is_stabbed (GtsBBox * bb, + GtsPoint * p); +gboolean gts_bbox_overlaps_triangle (GtsBBox * bb, + GtsTriangle * t); +gboolean gts_bbox_overlaps_segment (GtsBBox * bb, + GtsSegment * s); + +GNode * gts_bb_tree_new (GSList * bboxes); +GNode * gts_bb_tree_surface (GtsSurface * s); +GSList * gts_bb_tree_stabbed (GNode * tree, + GtsPoint * p); +GSList * gts_bb_tree_overlap (GNode * tree, + GtsBBox * bbox); +gboolean gts_bb_tree_is_overlapping (GNode * tree, + GtsBBox * bbox); +void gts_bb_tree_traverse_overlapping (GNode * tree1, + GNode * tree2, + GtsBBTreeTraverseFunc func, + gpointer data); +void gts_bb_tree_draw (GNode * tree, + guint depth, + FILE * fptr); +GSList * gts_bb_tree_point_closest_bboxes (GNode * tree, + GtsPoint * p); +gdouble gts_bb_tree_point_distance (GNode * tree, + GtsPoint * p, + GtsBBoxDistFunc distance, + GtsBBox ** bbox); +GtsPoint * gts_bb_tree_point_closest (GNode * tree, + GtsPoint * p, + GtsBBoxClosestFunc closest, + gdouble * distance); +void gts_bb_tree_segment_distance (GNode * tree, + GtsSegment * s, + GtsBBoxDistFunc distance, + gdouble delta, + GtsRange * range); +void gts_bb_tree_triangle_distance (GNode * tree, + GtsTriangle * t, + GtsBBoxDistFunc distance, + gdouble delta, + GtsRange * range); +void gts_bb_tree_surface_distance (GNode * tree, + GtsSurface * s, + GtsBBoxDistFunc distance, + gdouble delta, + GtsRange * range); +void gts_bb_tree_surface_boundary_distance + (GNode * tree, + GtsSurface * s, + GtsBBoxDistFunc distance, + gdouble delta, + GtsRange * range); +void gts_bb_tree_destroy (GNode * tree, + gboolean free_leaves); + +/* Surfaces: surface.c */ + +typedef struct _GtsSurfaceStats GtsSurfaceStats; +typedef struct _GtsSurfaceQualityStats GtsSurfaceQualityStats; +typedef GtsVertex * (*GtsRefineFunc) (GtsEdge * e, + GtsVertexClass * klass, + gpointer data); +typedef GtsVertex * (*GtsCoarsenFunc) (GtsEdge * e, + GtsVertexClass * klass, + gpointer data); +typedef gboolean (*GtsStopFunc) (gdouble cost, + guint nedge, + gpointer data); + +struct _GtsSurfaceStats { + guint n_faces; + guint n_incompatible_faces; + guint n_duplicate_faces; + guint n_duplicate_edges; + guint n_boundary_edges; + guint n_non_manifold_edges; + GtsRange edges_per_vertex, faces_per_edge; + GtsSurface * parent; +}; + +struct _GtsSurfaceQualityStats { + GtsRange face_quality; + GtsRange face_area; + GtsRange edge_length; + GtsRange edge_angle; + GtsSurface * parent; +}; + +struct _GtsSurface { + GtsObject object; + +#ifdef USE_SURFACE_BTREE + GTree * faces; +#else /* not USE_SURFACE_BTREE */ + GHashTable * faces; +#endif /* not USE_SURFACE_BTREE */ + GtsFaceClass * face_class; + GtsEdgeClass * edge_class; + GtsVertexClass * vertex_class; + gboolean keep_faces; +}; + +struct _GtsSurfaceClass { + GtsObjectClass parent_class; + + void (* add_face) (GtsSurface *, GtsFace *); + void (* remove_face) (GtsSurface *, GtsFace *); +}; + +#define GTS_IS_SURFACE(obj) (gts_object_is_from_class (obj,\ + gts_surface_class ())) +#define GTS_SURFACE(obj) GTS_OBJECT_CAST (obj,\ + GtsSurface,\ + gts_surface_class ()) +#define GTS_SURFACE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsSurfaceClass,\ + gts_surface_class ()) + +GtsSurfaceClass * gts_surface_class (void); +GtsSurface * gts_surface_new (GtsSurfaceClass * klass, + GtsFaceClass * face_class, + GtsEdgeClass * edge_class, + GtsVertexClass * vertex_class); +void gts_surface_add_face (GtsSurface * s, + GtsFace * f); +void gts_surface_remove_face (GtsSurface * s, + GtsFace * f); +guint gts_surface_read (GtsSurface * surface, + GtsFile * f); +gdouble gts_surface_area (GtsSurface * s); +void gts_surface_stats (GtsSurface * s, + GtsSurfaceStats * stats); +void gts_surface_quality_stats (GtsSurface * s, + GtsSurfaceQualityStats * stats); +void gts_surface_print_stats (GtsSurface * s, + FILE * fptr); +void gts_surface_write (GtsSurface * s, + FILE * fptr); +void gts_surface_write_oogl (GtsSurface * s, + FILE * fptr); +void gts_surface_write_vtk (GtsSurface * s, + FILE * fptr); +void gts_surface_write_oogl_boundary (GtsSurface * s, + FILE * fptr); +void gts_surface_foreach_vertex (GtsSurface * s, + GtsFunc func, + gpointer data); +void gts_surface_foreach_edge (GtsSurface * s, + GtsFunc func, + gpointer data); +void gts_surface_foreach_face (GtsSurface * s, + GtsFunc func, + gpointer data); +guint gts_surface_foreach_face_remove (GtsSurface * s, + GtsFunc func, + gpointer data); +typedef struct _GtsSurfaceTraverse GtsSurfaceTraverse; +GtsSurfaceTraverse * gts_surface_traverse_new (GtsSurface * s, + GtsFace * f); +GtsFace * gts_surface_traverse_next (GtsSurfaceTraverse * t, + guint * level); +void gts_surface_traverse_destroy (GtsSurfaceTraverse * t); +void gts_surface_refine (GtsSurface * surface, + GtsKeyFunc cost_func, + gpointer cost_data, + GtsRefineFunc refine_func, + gpointer refine_data, + GtsStopFunc stop_func, + gpointer stop_data); +gboolean gts_edge_collapse_is_valid (GtsEdge * e); +void gts_surface_coarsen (GtsSurface * surface, + GtsKeyFunc cost_func, + gpointer cost_data, + GtsCoarsenFunc coarsen_func, + gpointer coarsen_data, + GtsStopFunc stop_func, + gpointer stop_data, + gdouble minangle); +gboolean gts_coarsen_stop_number (gdouble cost, + guint nedge, + guint * min_number); +gboolean gts_coarsen_stop_cost (gdouble cost, + guint nedge, + gdouble * max_cost); +void gts_surface_tessellate (GtsSurface * s, + GtsRefineFunc refine_func, + gpointer refine_data); +GtsSurface * gts_surface_generate_sphere (GtsSurface * s, + guint geodesation_order); +GtsSurface * gts_surface_copy (GtsSurface * s1, + GtsSurface * s2); +void gts_surface_merge (GtsSurface * s, + GtsSurface * with); +gboolean gts_surface_is_manifold (GtsSurface * s); +gboolean gts_surface_is_closed (GtsSurface * s); +gboolean gts_surface_is_orientable (GtsSurface * s); +gdouble gts_surface_volume (GtsSurface * s); +gdouble gts_surface_center_of_mass (GtsSurface * s, + GtsVector cm); +gdouble gts_surface_center_of_area (GtsSurface * s, + GtsVector cm); +guint gts_surface_vertex_number (GtsSurface * s); +guint gts_surface_edge_number (GtsSurface * s); +guint gts_surface_face_number (GtsSurface * s); +void gts_surface_distance (GtsSurface * s1, + GtsSurface * s2, + gdouble delta, + GtsRange * face_range, + GtsRange * boundary_range); +GSList * gts_surface_boundary (GtsSurface * surface); +GSList * gts_surface_split (GtsSurface * s); + +/* Discrete differential operators: curvature.c */ + +gboolean gts_vertex_mean_curvature_normal (GtsVertex * v, + GtsSurface * s, + GtsVector Kh); +gboolean gts_vertex_gaussian_curvature (GtsVertex * v, + GtsSurface * s, + gdouble * Kg); +void gts_vertex_principal_curvatures (gdouble Kh, + gdouble Kg, + gdouble * K1, + gdouble * K2); +void gts_vertex_principal_directions (GtsVertex * v, + GtsSurface * s, + GtsVector Kh, + gdouble Kg, + GtsVector e1, + GtsVector e2); + +/* Volume optimization: vopt.c */ +typedef struct _GtsVolumeOptimizedParams GtsVolumeOptimizedParams; + +struct _GtsVolumeOptimizedParams { + gdouble volume_weight; + gdouble boundary_weight; + gdouble shape_weight; +}; + +GtsVertex * gts_volume_optimized_vertex (GtsEdge * edge, + GtsVertexClass * klass, + GtsVolumeOptimizedParams * params); +gdouble gts_volume_optimized_cost (GtsEdge * e, + GtsVolumeOptimizedParams * params); + +/* bool operations: boolean.c */ + +GSList * gts_surface_intersection (GtsSurface * s1, + GtsSurface * s2, + GNode * faces_tree1, + GNode * faces_tree2); + +typedef struct _GtsSurfaceInter GtsSurfaceInter; +typedef struct _GtsSurfaceInterClass GtsSurfaceInterClass; +/** + * GtsBooleanOperation: + * @GTS_1_OUT_2: identifies the part of the first surface which lies + * outside the second surface. + * @GTS_1_IN_2: identifies the part of the first surface which lies + * inside the second surface. + * @GTS_2_OUT_1: identifies the part of the second surface which lies + * outside the first surface. + * @GTS_2_IN_1: identifies the part of the second surface which lies + * inside the first surface. + */ +typedef enum { GTS_1_OUT_2, + GTS_1_IN_2, + GTS_2_OUT_1, + GTS_2_IN_1 } GtsBooleanOperation; + +/** + * GTS_IS_SURFACE_INTER: + * @obj: a #GtsObject. + * + * Evaluates to %TRUE if @obj is a #GtsSurfaceInter, %FALSE otherwise. + */ +#define GTS_IS_SURFACE_INTER(obj) (gts_object_is_from_class (obj,\ + gts_surface_inter_class ())) +/** + * GTS_SURFACE_INTER: + * @obj: a descendant of #GtsSurfaceInter. + * + * Casts @obj to #GtsSurfaceInter. + */ +#define GTS_SURFACE_INTER(obj) GTS_OBJECT_CAST (obj,\ + GtsSurfaceInter,\ + gts_surface_inter_class ()) +/** + * GTS_SURFACE_INTER_CLASS: + * @klass: a descendant of #GtsSurfaceInterClass. + * + * Casts @klass to #GtsSurfaceInterClass. + */ +#define GTS_SURFACE_INTER_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsSurfaceInterClass,\ + gts_surface_inter_class ()) + +struct _GtsSurfaceInter { + GtsObject object; + + GtsSurface * s1; + GtsSurface * s2; + GSList * edges; +}; + +struct _GtsSurfaceInterClass { + GtsObjectClass parent_class; +}; + +GtsSurfaceInterClass * +gts_surface_inter_class (void); +GtsSurfaceInter * +gts_surface_inter_new (GtsSurfaceInterClass * klass, + GtsSurface * s1, + GtsSurface * s2, + GNode * faces_tree1, + GNode * faces_tree2, + gboolean is_open1, + gboolean is_open2); +gboolean +gts_surface_inter_check (GtsSurfaceInter * si, + gboolean * closed); +void +gts_surface_inter_boolean (GtsSurfaceInter * si, + GtsSurface * surface, + GtsBooleanOperation op); +gboolean gts_surface_foreach_intersecting_face (GtsSurface * s, + GtsBBTreeTraverseFunc func, + gpointer data); +GtsSurface * +gts_surface_is_self_intersecting (GtsSurface * s); + +/* Binary Heap: heap.c */ + +typedef struct _GtsHeap GtsHeap; + +GtsHeap * gts_heap_new (GCompareFunc compare_func); +void gts_heap_insert (GtsHeap * heap, gpointer p); +gpointer gts_heap_remove_top (GtsHeap * heap); +gpointer gts_heap_top (GtsHeap * heap); +void gts_heap_thaw (GtsHeap * heap); +void gts_heap_foreach (GtsHeap * heap, + GFunc func, + gpointer user_data); +void gts_heap_freeze (GtsHeap * heap); +guint gts_heap_size (GtsHeap * heap); +void gts_heap_destroy (GtsHeap * heap); + +/* Extended Binary Heap: eheap.c */ + +typedef struct _GtsEHeap GtsEHeap; +typedef struct _GtsEHeapPair GtsEHeapPair; + +struct _GtsEHeap { + GPtrArray * elts; + GtsKeyFunc func; + gpointer data; + gboolean frozen, randomized; +}; + +/** + * _GtsEHeapPair: + * @data: Points to the item stored in the heap. + * @key: Value of the key for this item. + * @pos: Private field. + */ +struct _GtsEHeapPair { + gpointer data; + gdouble key; + guint pos; +}; + +GtsEHeap * gts_eheap_new (GtsKeyFunc key_func, + gpointer data); +GtsEHeapPair * gts_eheap_insert (GtsEHeap * heap, + gpointer p); +GtsEHeapPair * gts_eheap_insert_with_key (GtsEHeap * heap, + gpointer p, + gdouble key); +gpointer gts_eheap_remove_top (GtsEHeap * heap, + gdouble * key); +gpointer gts_eheap_top (GtsEHeap * heap, + gdouble * key); +void gts_eheap_thaw (GtsEHeap * heap); +void gts_eheap_foreach (GtsEHeap * heap, + GFunc func, + gpointer data); +gpointer gts_eheap_remove (GtsEHeap * heap, + GtsEHeapPair * p); +void gts_eheap_decrease_key (GtsEHeap * heap, + GtsEHeapPair * p, + gdouble new_key); +void gts_eheap_freeze (GtsEHeap * heap); +guint gts_eheap_size (GtsEHeap * heap); +void gts_eheap_update (GtsEHeap * heap); +gdouble gts_eheap_key (GtsEHeap * heap, + gpointer p); +void gts_eheap_randomized (GtsEHeap * heap, + gboolean randomized); +void gts_eheap_destroy (GtsEHeap * heap); + +/* FIFO queues: fifo.c */ + +typedef struct _GtsFifo GtsFifo; + +GtsFifo * gts_fifo_new (void); +void gts_fifo_write (GtsFifo * fifo, + FILE * fp); +void gts_fifo_push (GtsFifo * fifo, + gpointer data); +gpointer gts_fifo_pop (GtsFifo * fifo); +gpointer gts_fifo_top (GtsFifo * fifo); +guint gts_fifo_size (GtsFifo * fifo); +gboolean gts_fifo_is_empty (GtsFifo * fifo); +void gts_fifo_foreach (GtsFifo * fifo, + GtsFunc func, + gpointer data); +void gts_fifo_reverse (GtsFifo * fifo); +void gts_fifo_destroy (GtsFifo * fifo); + +/* Progressive surfaces */ + +/* split.c */ + +typedef struct _GtsSplit GtsSplit; +typedef struct _GtsSplitClass GtsSplitClass; +typedef struct _GtsSplitCFace GtsSplitCFace; + +struct _GtsSplit { + GtsObject object; + + GtsVertex * v; + GtsObject * v1; + GtsObject * v2; + GtsSplitCFace * cfaces; + guint ncf; +}; + +struct _GtsSplitClass { + GtsObjectClass parent_class; +}; + +#define GTS_IS_SPLIT(obj) (gts_object_is_from_class (obj,\ + gts_split_class ())) +#define GTS_SPLIT(obj) GTS_OBJECT_CAST (obj,\ + GtsSplit,\ + gts_split_class ()) +#define GTS_SPLIT_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsSplitClass,\ + gts_split_class ()) +#define GTS_SPLIT_V1(vs) (GTS_IS_SPLIT ((vs)->v1) ?\ + GTS_SPLIT ((vs)->v1)->v :\ + GTS_VERTEX ((vs)->v1)) +#define GTS_SPLIT_V2(vs) (GTS_IS_SPLIT ((vs)->v2) ?\ + GTS_SPLIT ((vs)->v2)->v :\ + GTS_VERTEX ((vs)->v2)) + +GtsSplitClass * gts_split_class (void); +GtsSplit * gts_split_new (GtsSplitClass * klass, + GtsVertex * v, + GtsObject * o1, + GtsObject * o2); +void gts_split_collapse (GtsSplit * vs, + GtsEdgeClass * klass, + GtsEHeap * heap); +void gts_split_expand (GtsSplit * vs, + GtsSurface * s, + GtsEdgeClass * klass); +typedef gboolean (*GtsSplitTraverseFunc) (GtsSplit * vs, + gpointer data); +void gts_split_traverse (GtsSplit * root, + GTraverseType order, + gint depth, + GtsSplitTraverseFunc func, + gpointer data); +guint gts_split_height (GtsSplit * root); + +/* psurface.c */ + +typedef struct _GtsPSurface GtsPSurface; +typedef struct _GtsPSurfaceClass GtsPSurfaceClass; + +struct _GtsPSurface { + GtsObject object; + + GtsSurface * s; + GPtrArray * split; + GtsSplitClass * split_class; + guint pos, min; + + GPtrArray * vertices; + GPtrArray * faces; +}; + +struct _GtsPSurfaceClass { + GtsObjectClass parent_class; +}; + +#define GTS_IS_PSURFACE(obj) (gts_object_is_from_class (obj,\ + gts_psurface_class ())) +#define GTS_PSURFACE(obj) GTS_OBJECT_CAST (obj,\ + GtsPSurface,\ + gts_psurface_class ()) +#define GTS_PSURFACE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsPSurfaceClass,\ + gts_psurface_class ()) +#define GTS_PSURFACE_IS_CLOSED(ps) (!(ps)->vertices) + +GtsPSurfaceClass * gts_psurface_class (void); +GtsPSurface * gts_psurface_new (GtsPSurfaceClass * klass, + GtsSurface * surface, + GtsSplitClass * split_class, + GtsKeyFunc cost_func, + gpointer cost_data, + GtsCoarsenFunc coarsen_func, + gpointer coarsen_data, + GtsStopFunc stop_func, + gpointer stop_data, + gdouble minangle); +GtsSplit * gts_psurface_add_vertex (GtsPSurface * ps); +GtsSplit * gts_psurface_remove_vertex (GtsPSurface * ps); +guint gts_psurface_max_vertex_number (GtsPSurface * ps); +guint gts_psurface_min_vertex_number (GtsPSurface * ps); +void gts_psurface_set_vertex_number (GtsPSurface * ps, + guint n); +guint gts_psurface_get_vertex_number (GtsPSurface * ps); +void gts_psurface_write (GtsPSurface * ps, + FILE * fptr); +GtsPSurface * gts_psurface_open (GtsPSurfaceClass * klass, + GtsSurface * s, + GtsSplitClass * split_class, + GtsFile * f); +GtsSplit * gts_psurface_read_vertex (GtsPSurface * ps, + GtsFile * fp); +void gts_psurface_close (GtsPSurface * ps); +void gts_psurface_foreach_vertex (GtsPSurface * ps, + GtsFunc func, + gpointer data); + +/* hsurface.c */ + +typedef struct _GtsHSplit GtsHSplit; +typedef struct _GtsHSplitClass GtsHSplitClass; +typedef struct _GtsHSurface GtsHSurface; +typedef struct _GtsHSurfaceClass GtsHSurfaceClass; + +struct _GtsHSplit { + GtsSplit split; + + GtsEHeapPair * index; + GtsHSplit * parent; + guint nchild; +}; + +struct _GtsHSplitClass { + GtsSplitClass parent_class; +}; + +#define GTS_IS_HSPLIT(obj) (gts_object_is_from_class (obj,\ + gts_hsplit_class ())) +#define GTS_HSPLIT(obj) GTS_OBJECT_CAST (obj,\ + GtsHSplit,\ + gts_hsplit_class ()) +#define GTS_HSPLIT_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsHSplitClass,\ + gts_hsplit_class ()) + +GtsHSplitClass * gts_hsplit_class (void); +GtsHSplit * gts_hsplit_new (GtsHSplitClass * klass, + GtsSplit * vs); +void gts_hsplit_collapse (GtsHSplit * hs, + GtsHSurface * hsurface); +void gts_hsplit_expand (GtsHSplit * hs, + GtsHSurface * hsurface); +void gts_hsplit_force_expand (GtsHSplit * hs, + GtsHSurface * hsurface); + +struct _GtsHSurface { + GtsObject object; + + GtsSurface * s; + GSList * roots; + GtsEHeap * expandable; + GtsEHeap * collapsable; + GPtrArray * split; + guint nvertex; +}; + +struct _GtsHSurfaceClass { + GtsObjectClass parent_class; +}; + +#define GTS_IS_HSURFACE(obj) (gts_object_is_from_class (obj,\ + gts_hsurface_class ())) +#define GTS_HSURFACE(obj) GTS_OBJECT_CAST (obj,\ + GtsHSurface,\ + gts_hsurface_class ()) +#define GTS_HSURFACE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsHSurfaceClass,\ + gts_hsurface_class ()) + +GtsHSurfaceClass * gts_hsurface_class (void); +GtsHSurface * gts_hsurface_new (GtsHSurfaceClass * klass, + GtsHSplitClass * hsplit_class, + GtsPSurface * psurface, + GtsKeyFunc expand_key, + gpointer expand_data, + GtsKeyFunc collapse_key, + gpointer collapse_data); +void gts_hsurface_traverse (GtsHSurface * hsurface, + GTraverseType order, + gint depth, + GtsSplitTraverseFunc func, + gpointer data); +void gts_hsurface_foreach (GtsHSurface * hsurface, + GTraverseType order, + GtsFunc func, + gpointer data); +guint gts_hsurface_height (GtsHSurface * hsurface); + +/* Constrained Delaunay triangulation: cdt.c */ + +/** + * GTS_IS_CONSTRAINT: + * @obj: a #GtsObject. + * + * Evaluates to %TRUE if @obj is a #GtsConstraint, %FALSE otherwise. + */ +#define GTS_IS_CONSTRAINT(obj) (gts_object_is_from_class (obj,\ + gts_constraint_class ())) +/** + * GTS_CONSTRAINT: + * @obj: a descendant of #GtsConstraint. + * + * Casts @obj to #GtsConstraint. + */ +#define GTS_CONSTRAINT(obj) GTS_OBJECT_CAST (obj,\ + GtsConstraint,\ + gts_constraint_class ()) +/** + * GTS_CONSTRAINT_CLASS: + * @klass: a desscendant of #GtsConstraintClass. + * + * Casts @klass to #GtsConstraintClass. + */ +#define GTS_CONSTRAINT_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsConstraintClass,\ + gts_constraint_class ()) + +struct _GtsConstraint { + GtsEdge edge; +}; + +struct _GtsConstraintClass { + GtsEdgeClass parent_class; +}; + +typedef struct _GtsConstraint GtsConstraint; +typedef struct _GtsConstraintClass GtsConstraintClass; + +GtsConstraintClass * gts_constraint_class (void); + +GtsFace * gts_point_locate (GtsPoint * p, + GtsSurface * surface, + GtsFace * guess); +GtsVertex * gts_delaunay_add_vertex_to_face (GtsSurface * surface, + GtsVertex * v, + GtsFace * f); +GtsVertex * gts_delaunay_add_vertex (GtsSurface * surface, + GtsVertex * v, + GtsFace * guess); +void gts_delaunay_remove_vertex (GtsSurface * surface, + GtsVertex * v); +GtsFace * gts_delaunay_check (GtsSurface * surface); +GSList * gts_delaunay_add_constraint (GtsSurface * surface, + GtsConstraint * c); +void gts_delaunay_remove_hull (GtsSurface * surface); + +/* GtsListFace: Header */ + +typedef struct _GtsListFace GtsListFace; + +struct _GtsListFace { + /*< private >*/ + GtsFace parent; + + /*< public >*/ + GSList * points; +}; + +#define GTS_LIST_FACE(obj) GTS_OBJECT_CAST (obj,\ + GtsListFace,\ + gts_list_face_class ()) +#define GTS_IS_LIST_FACE(obj) (gts_object_is_from_class (obj,\ + gts_list_face_class ())) + +GtsFaceClass * gts_list_face_class (void); + +/* Constrained Delaunay refinement: refine.c */ + +typedef gboolean (* GtsEncroachFunc) (GtsVertex * v, + GtsEdge * e, + GtsSurface * s, + gpointer data); + +gboolean gts_vertex_encroaches_edge (GtsVertex * v, + GtsEdge * e); +GtsVertex * gts_edge_is_encroached (GtsEdge * e, + GtsSurface * s, + GtsEncroachFunc encroaches, + gpointer data); +guint gts_delaunay_conform (GtsSurface * surface, + gint steiner_max, + GtsEncroachFunc encroaches, + gpointer data); +guint gts_delaunay_refine (GtsSurface * surface, + gint steiner_max, + GtsEncroachFunc encroaches, + gpointer encroach_data, + GtsKeyFunc cost, + gpointer cost_data); + +/* Isosurfaces (marching cubes): iso.c */ + +typedef struct _GtsGridPlane GtsGridPlane; +typedef struct _GtsIsoSlice GtsIsoSlice; +typedef struct _GtsCartesianGrid GtsCartesianGrid; + +struct _GtsGridPlane { + GtsPoint ** p; + guint nx, ny; +}; + +struct _GtsCartesianGrid { + guint nx, ny, nz; + gdouble x, dx, y, dy, z, dz; +}; + +typedef void (*GtsIsoCartesianFunc) (gdouble ** a, + GtsCartesianGrid g, + guint i, + gpointer data); + +GtsGridPlane * gts_grid_plane_new (guint nx, + guint ny); +void gts_grid_plane_destroy (GtsGridPlane * g); +GtsIsoSlice * gts_iso_slice_new (guint nx, guint ny); +void gts_iso_slice_fill (GtsIsoSlice * slice, + GtsGridPlane * plane1, + GtsGridPlane * plane2, + gdouble ** f1, + gdouble ** f2, + gdouble iso, + GtsVertexClass * klass); +void gts_iso_slice_fill_cartesian (GtsIsoSlice * slice, + GtsCartesianGrid g, + gdouble ** f1, + gdouble ** f2, + gdouble iso, + GtsVertexClass * klass); +void gts_iso_slice_destroy (GtsIsoSlice * slice); +void gts_isosurface_slice (GtsIsoSlice * slice1, + GtsIsoSlice * slice2, + GtsSurface * surface); +void gts_isosurface_cartesian (GtsSurface * surface, + GtsCartesianGrid g, + GtsIsoCartesianFunc f, + gpointer data, + gdouble iso); + +/* Isosurfaces (marching tetrahedra): isotetra.c */ + +void gts_isosurface_tetra (GtsSurface * surface, + GtsCartesianGrid g, + GtsIsoCartesianFunc f, + gpointer data, + gdouble iso); +void gts_isosurface_tetra_bcl (GtsSurface * surface, + GtsCartesianGrid g, + GtsIsoCartesianFunc f, + gpointer data, + gdouble iso); +void gts_isosurface_tetra_bounded (GtsSurface * surface, + GtsCartesianGrid g, + GtsIsoCartesianFunc f, + gpointer data, + gdouble iso); + +/* Named vertices, edges and triangles: named.c */ + +#define GTS_NAME_LENGTH 40 + +#define GTS_NVERTEX(obj) GTS_OBJECT_CAST (obj,\ + GtsNVertex,\ + gts_nvertex_class ()) +#define GTS_NVERTEX_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsNVertexClass,\ + gts_nvertex_class()) +#define GTS_IS_NVERTEX(obj) (gts_object_is_from_class (obj,\ + gts_nvertex_class ())) + +typedef struct _GtsNVertex GtsNVertex; +typedef struct _GtsNVertexClass GtsNVertexClass; + +struct _GtsNVertex { + GtsVertex parent; + char name[GTS_NAME_LENGTH]; +}; + +struct _GtsNVertexClass { + GtsVertexClass parent_class; +}; + +GtsNVertexClass * gts_nvertex_class (void); + +#define GTS_NEDGE(obj) GTS_OBJECT_CAST (obj,\ + GtsNEdge,\ + gts_nedge_class ()) +#define GTS_NEDGE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsNEdgeClass,\ + gts_nedge_class()) +#define GTS_IS_NEDGE(obj) (gts_object_is_from_class (obj,\ + gts_nedge_class ())) + +typedef struct _GtsNEdge GtsNEdge; +typedef struct _GtsNEdgeClass GtsNEdgeClass; + +struct _GtsNEdge { + GtsEdge parent; + char name[GTS_NAME_LENGTH]; +}; + +struct _GtsNEdgeClass { + GtsEdgeClass parent_class; +}; + +GtsNEdgeClass * gts_nedge_class (void); + +#define GTS_NFACE(obj) GTS_OBJECT_CAST (obj,\ + GtsNFace,\ + gts_nface_class ()) +#define GTS_NFACE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsNFaceClass,\ + gts_nface_class()) +#define GTS_IS_NFACE(obj) (gts_object_is_from_class (obj,\ + gts_nface_class ())) + +typedef struct _GtsNFace GtsNFace; +typedef struct _GtsNFaceClass GtsNFaceClass; + +struct _GtsNFace { + GtsFace parent; + char name[GTS_NAME_LENGTH]; +}; + +struct _GtsNFaceClass { + GtsFaceClass parent_class; +}; + +GtsNFaceClass * gts_nface_class (void); + +/* Cluster object for out-of-core simplification: oocs.c */ + +#define GTS_CLUSTER(obj) GTS_OBJECT_CAST (obj,\ + GtsCluster,\ + gts_cluster_class ()) +#define GTS_CLUSTER_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsClusterClass,\ + gts_cluster_class()) +#define GTS_IS_CLUSTER(obj) (gts_object_is_from_class (obj,\ + gts_cluster_class ())) + +typedef struct _GtsCluster GtsCluster; +typedef struct _GtsClusterClass GtsClusterClass; +typedef struct _GtsClusterId GtsClusterId; + +struct _GtsClusterId { + guint x, y, z; +}; + +struct _GtsCluster { + GtsObject parent; + + GtsClusterId id; + GtsVertex * v; + guint n; +}; + +struct _GtsClusterClass { + GtsObjectClass parent_class; + + void (* add) (GtsCluster * c, GtsPoint * p, gpointer data); + void (* update) (GtsCluster * c); +}; + +GtsClusterClass * gts_cluster_class (void); +GtsCluster * gts_cluster_new (GtsClusterClass * klass, + GtsClusterId id, + GtsVertexClass * vklass); +void gts_cluster_add (GtsCluster * c, + GtsPoint * p, + gpointer data); +void gts_cluster_update (GtsCluster * c); + +/* Cluster group object for out-of-core simplification: oocs.c */ + +#define GTS_CLUSTER_GRID(obj) GTS_OBJECT_CAST (obj,\ + GtsClusterGrid,\ + gts_cluster_grid_class ()) +#define GTS_CLUSTER_GRID_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsClusterGridClass,\ + gts_cluster_grid_class()) +#define GTS_IS_CLUSTER_GRID(obj) (gts_object_is_from_class (obj,\ + gts_cluster_grid_class ())) + +typedef struct _GtsClusterGrid GtsClusterGrid; +typedef struct _GtsClusterGridClass GtsClusterGridClass; + +struct _GtsClusterGrid { + GtsObject parent; + + GtsSurface * surface; + GtsBBox * bbox; + GtsVector size; + + GtsClusterClass * cluster_class; + GHashTable * clusters; +}; + +struct _GtsClusterGridClass { + GtsObjectClass parent_class; +}; + +GtsClusterGridClass * gts_cluster_grid_class (void); +GtsClusterGrid * gts_cluster_grid_new (GtsClusterGridClass * klass, + GtsClusterClass * cluster_class, + GtsSurface * s, + GtsBBox * bbox, + gdouble delta); +void gts_cluster_grid_add_triangle (GtsClusterGrid * cluster_grid, + GtsPoint * p1, + GtsPoint * p2, + GtsPoint * p3, + gpointer data); +GtsRange gts_cluster_grid_update (GtsClusterGrid * cluster_grid); + +/* Triangle strip generation: stripe.c */ +GSList * gts_surface_strip (GtsSurface * s); + +/* GtsContainee: container.c */ + +typedef struct _GtsContainee GtsContainee; +typedef struct _GtsContaineeClass GtsContaineeClass; +typedef struct _GtsContainer GtsContainer; +typedef struct _GtsContainerClass GtsContainerClass; + +struct _GtsContainee { + GtsObject object; +}; + +struct _GtsContaineeClass { + GtsObjectClass parent_class; + + void (* add_container) (GtsContainee *, GtsContainer *); + void (* remove_container) (GtsContainee *, GtsContainer *); + void (* foreach) (GtsContainee *, GtsFunc, gpointer); + gboolean (* is_contained) (GtsContainee *, GtsContainer *); + void (* replace) (GtsContainee *, GtsContainee *); +}; + +#define GTS_CONTAINEE(obj) GTS_OBJECT_CAST (obj,\ + GtsContainee,\ + gts_containee_class ()) +#define GTS_CONTAINEE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsContaineeClass,\ + gts_containee_class()) +#define GTS_IS_CONTAINEE(obj) (gts_object_is_from_class (obj,\ + gts_containee_class ())) + +GtsContaineeClass * gts_containee_class (void); +GtsContainee * gts_containee_new (GtsContaineeClass * klass); +gboolean gts_containee_is_contained (GtsContainee * item, + GtsContainer * c); +void gts_containee_replace (GtsContainee * item, + GtsContainee * with); + +/* GtsSListContainee: container.c */ + +typedef struct _GtsSListContainee GtsSListContainee; +typedef struct _GtsSListContaineeClass GtsSListContaineeClass; + +struct _GtsSListContainee { + GtsContainee containee; + + GSList * containers; +}; + +struct _GtsSListContaineeClass { + GtsContaineeClass parent_class; +}; + +#define GTS_SLIST_CONTAINEE(obj) GTS_OBJECT_CAST (obj,\ + GtsSListContainee,\ + gts_slist_containee_class ()) +#define GTS_SLIST_CONTAINEE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsSListContaineeClass,\ + gts_slist_containee_class()) +#define GTS_IS_SLIST_CONTAINEE(obj) (gts_object_is_from_class (obj,\ + gts_slist_containee_class ())) + +GtsSListContaineeClass * gts_slist_containee_class (void); + +/* GtsContainer: container.c */ + +struct _GtsContainer { + GtsSListContainee object; +}; + +struct _GtsContainerClass { + GtsSListContaineeClass parent_class; + + void (* add) (GtsContainer *, GtsContainee *); + void (* remove) (GtsContainer *, GtsContainee *); + void (* foreach) (GtsContainer *, GtsFunc, gpointer); + guint (* size) (GtsContainer *); +}; + +#define GTS_CONTAINER(obj) GTS_OBJECT_CAST (obj,\ + GtsContainer,\ + gts_container_class ()) +#define GTS_CONTAINER_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsContainerClass,\ + gts_container_class()) +#define GTS_IS_CONTAINER(obj) (gts_object_is_from_class (obj,\ + gts_container_class ())) + +GtsContainerClass * gts_container_class (void); +GtsContainer * gts_container_new (GtsContainerClass * klass); +void gts_container_add (GtsContainer * c, + GtsContainee * item); +void gts_container_remove (GtsContainer * c, + GtsContainee * item); +void gts_container_foreach (GtsContainer * c, + GtsFunc func, + gpointer data); +guint gts_container_size (GtsContainer * c); + +/* GtsHashContainer: container.c */ + +typedef struct _GtsHashContainer GtsHashContainer; +typedef struct _GtsHashContainerClass GtsHashContainerClass; + +struct _GtsHashContainer { + GtsContainer c; + + GHashTable * items; + gboolean frozen; +}; + +struct _GtsHashContainerClass { + GtsContainerClass parent_class; +}; + +#define GTS_HASH_CONTAINER(obj) GTS_OBJECT_CAST (obj,\ + GtsHashContainer,\ + gts_hash_container_class ()) +#define GTS_HASH_CONTAINER_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsHashContainerClass,\ + gts_hash_container_class()) +#define GTS_IS_HASH_CONTAINER(obj) (gts_object_is_from_class (obj,\ + gts_hash_container_class ())) + +GtsHashContainerClass * gts_hash_container_class (void); + +/* GtsSListContainer: container.c */ + +typedef struct _GtsSListContainer GtsSListContainer; +typedef struct _GtsSListContainerClass GtsSListContainerClass; + +struct _GtsSListContainer { + GtsContainer c; + + GSList * items; + gboolean frozen; +}; + +struct _GtsSListContainerClass { + GtsContainerClass parent_class; +}; + +#define GTS_SLIST_CONTAINER(obj) GTS_OBJECT_CAST (obj,\ + GtsSListContainer,\ + gts_slist_container_class ()) +#define GTS_SLIST_CONTAINER_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsSListContainerClass,\ + gts_slist_container_class()) +#define GTS_IS_SLIST_CONTAINER(obj) (gts_object_is_from_class (obj,\ + gts_slist_container_class ())) + +GtsSListContainerClass * gts_slist_container_class (void); + +/* GtsGNode: graph.c */ + +typedef struct _GtsGNode GtsGNode; +typedef struct _GtsGNodeClass GtsGNodeClass; +typedef struct _GtsGraph GtsGraph; +typedef struct _GtsGraphClass GtsGraphClass; + +struct _GtsGNode { + GtsSListContainer container; + + guint level; +}; + +struct _GtsGNodeClass { + GtsSListContainerClass parent_class; + + gfloat (* weight) (GtsGNode *); + void (* write) (GtsGNode *, FILE *); +}; + +#define GTS_GNODE(obj) GTS_OBJECT_CAST (obj,\ + GtsGNode,\ + gts_gnode_class ()) +#define GTS_GNODE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsGNodeClass,\ + gts_gnode_class()) +#define GTS_IS_GNODE(obj) (gts_object_is_from_class (obj,\ + gts_gnode_class ())) +#define GTS_GNODE_NEIGHBOR(n,e) (GTS_GEDGE (e)->n1 == n ? GTS_GEDGE (e)->n2 : GTS_GEDGE (e)->n2 == n ? GTS_GEDGE (e)->n1 : NULL) + +GtsGNodeClass * gts_gnode_class (void); +GtsGNode * gts_gnode_new (GtsGNodeClass * klass); +void gts_gnode_foreach_neighbor (GtsGNode * n, + GtsGraph * g, + GtsFunc func, + gpointer data); +void gts_gnode_foreach_edge (GtsGNode * n, + GtsGraph * g, + GtsFunc func, + gpointer data); +guint gts_gnode_degree (GtsGNode * n, + GtsGraph * g); +gfloat gts_gnode_move_cost (GtsGNode * n, + GtsGraph * src, + GtsGraph * dst); +gfloat gts_gnode_weight (GtsGNode * n); + +GTS_C_VAR +gboolean gts_allow_floating_gnodes; + +/* GtsNGNode: graph.c */ + +typedef struct _GtsNGNode GtsNGNode; +typedef struct _GtsNGNodeClass GtsNGNodeClass; + +struct _GtsNGNode { + GtsGNode node; + + guint id; +}; + +struct _GtsNGNodeClass { + GtsGNodeClass parent_class; +}; + +#define GTS_NGNODE(obj) GTS_OBJECT_CAST (obj,\ + GtsNGNode,\ + gts_ngnode_class ()) +#define GTS_NGNODE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsNGNodeClass,\ + gts_ngnode_class()) +#define GTS_IS_NGNODE(obj) (gts_object_is_from_class (obj,\ + gts_ngnode_class ())) + +GtsNGNodeClass * gts_ngnode_class (void); +GtsNGNode * gts_ngnode_new (GtsNGNodeClass * klass, + guint id); + +/* GtsWGNode: graph.c */ + +typedef struct _GtsWGNode GtsWGNode; +typedef struct _GtsWGNodeClass GtsWGNodeClass; + +struct _GtsWGNode { + GtsGNode node; + + gfloat weight; +}; + +struct _GtsWGNodeClass { + GtsGNodeClass parent_class; +}; + +#define GTS_WGNODE(obj) GTS_OBJECT_CAST (obj,\ + GtsWGNode,\ + gts_wgnode_class ()) +#define GTS_WGNODE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsWGNodeClass,\ + gts_wgnode_class()) +#define GTS_IS_WGNODE(obj) (gts_object_is_from_class (obj,\ + gts_wgnode_class ())) + +GtsWGNodeClass * gts_wgnode_class (void); +GtsWGNode * gts_wgnode_new (GtsWGNodeClass * klass, + gfloat weight); + +/* GtsPNode */ + +typedef struct _GtsPNode GtsPNode; +typedef struct _GtsPNodeClass GtsPNodeClass; + +struct _GtsPNode { + GtsGNode node; + + gpointer data; +}; + +struct _GtsPNodeClass { + GtsGNodeClass parent_class; +}; + +#define GTS_PNODE(obj) GTS_OBJECT_CAST (obj,\ + GtsPNode,\ + gts_pnode_class ()) +#define GTS_PNODE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsPNodeClass,\ + gts_pnode_class()) +#define GTS_IS_PNODE(obj) (gts_object_is_from_class (obj,\ + gts_pnode_class ())) + +GtsPNodeClass * gts_pnode_class (void); +GtsPNode * gts_pnode_new (GtsPNodeClass * klass, + gpointer data); + +/* GtsFNode */ + +typedef struct _GtsFNode GtsFNode; +typedef struct _GtsFNodeClass GtsFNodeClass; + +struct _GtsFNode { + GtsGNode node; + + GtsFace * f; +}; + +struct _GtsFNodeClass { + GtsGNodeClass parent_class; +}; + +#define GTS_FNODE(obj) GTS_OBJECT_CAST (obj,\ + GtsFNode,\ + gts_fnode_class ()) +#define GTS_FNODE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsFNodeClass,\ + gts_fnode_class()) +#define GTS_IS_FNODE(obj) (gts_object_is_from_class (obj,\ + gts_fnode_class ())) + +GtsFNodeClass * gts_fnode_class (void); +GtsFNode * gts_fnode_new (GtsFNodeClass * klass, + GtsFace * f); + +/* GtsGEdge: graph.c */ + +typedef struct _GtsGEdge GtsGEdge; +typedef struct _GtsGEdgeClass GtsGEdgeClass; + +struct _GtsGEdge { + GtsContainee containee; + + GtsGNode * n1; + GtsGNode * n2; +}; + +struct _GtsGEdgeClass { + GtsContaineeClass parent_class; + + GtsGEdge * (* link) (GtsGEdge * e, GtsGNode * n1, GtsGNode * n2); + gfloat (* weight) (GtsGEdge * e); + void (* write) (GtsGEdge * e, FILE * fp); +}; + +#define GTS_GEDGE(obj) GTS_OBJECT_CAST (obj,\ + GtsGEdge,\ + gts_gedge_class ()) +#define GTS_GEDGE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsGEdgeClass,\ + gts_gedge_class()) +#define GTS_IS_GEDGE(obj) (gts_object_is_from_class (obj,\ + gts_gedge_class ())) + +GtsGEdgeClass * gts_gedge_class (void); +GtsGEdge * gts_gedge_new (GtsGEdgeClass * klass, + GtsGNode * n1, + GtsGNode * n2); +gfloat gts_gedge_weight (GtsGEdge * e); +#define gts_gedge_connects(e, a1, a2)\ + (((e)->n1 == a1 && (e)->n2 == a2) || ((e)->n1 == a2 && (e)->n2 == a1)) + +/* GtsPGEdge: graph.c */ + +typedef struct _GtsPGEdge GtsPGEdge; +typedef struct _GtsPGEdgeClass GtsPGEdgeClass; + +struct _GtsPGEdge { + GtsGEdge gedge; + + gpointer data; +}; + +struct _GtsPGEdgeClass { + GtsGEdgeClass parent_class; +}; + +#define GTS_PGEDGE(obj) GTS_OBJECT_CAST (obj,\ + GtsPGEdge,\ + gts_pgedge_class ()) +#define GTS_PGEDGE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsPGEdgeClass,\ + gts_pgedge_class()) +#define GTS_IS_PGEDGE(obj) (gts_object_is_from_class (obj,\ + gts_pgedge_class ())) + +GtsPGEdgeClass * gts_pgedge_class (void); +GtsPGEdge * gts_pgedge_new (GtsPGEdgeClass * klass, + GtsGNode * n1, + GtsGNode * n2, + gpointer data); + +/* GtsWGEdge: graph.c */ + +typedef struct _GtsWGEdge GtsWGEdge; +typedef struct _GtsWGEdgeClass GtsWGEdgeClass; + +struct _GtsWGEdge { + GtsGEdge gedge; + + gfloat weight; +}; + +struct _GtsWGEdgeClass { + GtsGEdgeClass parent_class; +}; + +#define GTS_WGEDGE(obj) GTS_OBJECT_CAST (obj,\ + GtsWGEdge,\ + gts_wgedge_class ()) +#define GTS_WGEDGE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsWGEdgeClass,\ + gts_wgedge_class()) +#define GTS_IS_WGEDGE(obj) (gts_object_is_from_class (obj,\ + gts_wgedge_class ())) + +GtsWGEdgeClass * gts_wgedge_class (void); +GtsWGEdge * gts_wgedge_new (GtsWGEdgeClass * klass, + GtsGNode * n1, + GtsGNode * n2, + gfloat weight); + +/* GtsGraph: graph.c */ + +struct _GtsGraph { + GtsHashContainer object; + + GtsGraphClass * graph_class; + GtsGNodeClass * node_class; + GtsGEdgeClass * edge_class; +}; + +struct _GtsGraphClass { + GtsHashContainerClass parent_class; + + gfloat (* weight) (GtsGraph *); +}; + +#define GTS_GRAPH(obj) GTS_OBJECT_CAST (obj,\ + GtsGraph,\ + gts_graph_class ()) +#define GTS_GRAPH_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsGraphClass,\ + gts_graph_class()) +#define GTS_IS_GRAPH(obj) (gts_object_is_from_class (obj,\ + gts_graph_class ())) + +GtsGraphClass * gts_graph_class (void); +GtsGraph * gts_graph_new (GtsGraphClass * klass, + GtsGNodeClass * node_class, + GtsGEdgeClass * edge_class); +void gts_graph_print_stats (GtsGraph * g, + FILE * fp); +typedef struct _GtsGraphTraverse GtsGraphTraverse; +typedef enum { GTS_BREADTH_FIRST + } GtsTraverseType; +GtsGraphTraverse * gts_graph_traverse_new (GtsGraph * g, + GtsGNode * n, + GtsTraverseType type, + gboolean reinit); +GtsGNode * gts_graph_traverse_next (GtsGraphTraverse * t); +GtsGNode * gts_graph_traverse_what_next (GtsGraphTraverse * t); +void gts_graph_traverse_destroy (GtsGraphTraverse * t); +void gts_graph_foreach_edge (GtsGraph * g, + GtsFunc func, + gpointer data); +gfloat gts_graph_weight (GtsGraph * g); +guint gts_graph_distance_sum (GtsGraph * g, + GtsGNode * center); +GtsGNode * gts_graph_farthest (GtsGraph * g, + GSList * gnodes); +guint gts_graph_edges_cut (GtsGraph * g); +gfloat gts_graph_edges_cut_weight (GtsGraph * g); +void gts_graph_write (GtsGraph * g, + FILE * fp); +void gts_graph_write_dot (GtsGraph * g, + FILE * fp); +GtsGraph * gts_graph_read (GtsFile * fp); +guint gts_graph_read_jostle (GtsGraph * g, + GtsFile * fp); + +/* GtsWGraph: graph.c */ + +typedef struct _GtsWGraph GtsWGraph; +typedef struct _GtsWGraphClass GtsWGraphClass; + +struct _GtsWGraph { + GtsGraph graph; + + gfloat weight; +}; + +struct _GtsWGraphClass { + GtsGraphClass parent_class; +}; + +#define GTS_WGRAPH(obj) GTS_OBJECT_CAST (obj,\ + GtsWGraph,\ + gts_wgraph_class ()) +#define GTS_WGRAPH_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsWGraphClass,\ + gts_wgraph_class()) +#define GTS_IS_WGRAPH(obj) (gts_object_is_from_class (obj,\ + gts_wgraph_class ())) + +GtsWGraphClass * gts_wgraph_class (void); +gfloat gts_wgraph_weight_max (GtsWGraph * wg); + +/* Surface graph: graph.c */ + +GtsGraph * gts_surface_graph_new (GtsGraphClass * klass, + GtsSurface * s); +GtsSurface * gts_surface_graph_surface (GtsGraph * surface_graph, + GtsSurface * s); + +/* Segments graph: graph.c */ + +GtsGraph * gts_segments_graph_new (GtsGraphClass * klass, + GSList * segments); + +/* GtsGNodeSplit: pgraph.c */ + +typedef struct _GtsGNodeSplit GtsGNodeSplit; +typedef struct _GtsGNodeSplitClass GtsGNodeSplitClass; + +struct _GtsGNodeSplit { + GtsObject object; + + GtsGNode * n; + GtsObject * n1; + GtsObject * n2; +}; + +struct _GtsGNodeSplitClass { + GtsObjectClass parent_class; +}; + +#define GTS_GNODE_SPLIT(obj) GTS_OBJECT_CAST (obj,\ + GtsGNodeSplit,\ + gts_gnode_split_class ()) +#define GTS_GNODE_SPLIT_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsGNodeSplitClass,\ + gts_gnode_split_class()) +#define GTS_IS_GNODE_SPLIT(obj) (gts_object_is_from_class (obj,\ + gts_gnode_split_class ())) +#define GTS_GNODE_SPLIT_N1(ns) (GTS_IS_GNODE_SPLIT ((ns)->n1) ? GTS_GNODE_SPLIT ((ns)->n1)->n : GTS_GNODE ((ns)->n1)) +#define GTS_GNODE_SPLIT_N2(ns) (GTS_IS_GNODE_SPLIT ((ns)->n2) ? GTS_GNODE_SPLIT ((ns)->n2)->n : GTS_GNODE ((ns)->n2)) + +GtsGNodeSplitClass * gts_gnode_split_class (void); +GtsGNodeSplit * gts_gnode_split_new (GtsGNodeSplitClass * klass, + GtsGNode * n, + GtsObject * n1, + GtsObject * n2); +void gts_gnode_split_collapse (GtsGNodeSplit * ns, + GtsGraph * g, + GtsWGEdgeClass * klass); +void gts_gnode_split_expand (GtsGNodeSplit * ns, + GtsGraph * g); + +/* GtsPGraph: pgraph.c */ + +typedef struct _GtsPGraph GtsPGraph; +typedef struct _GtsPGraphClass GtsPGraphClass; + +struct _GtsPGraph { + GtsObject object; + + GtsGraph * g; + GPtrArray * split; + GArray * levels; + GtsGNodeSplitClass * split_class; + GtsWGEdgeClass * edge_class; + guint pos, min, level; +}; + +struct _GtsPGraphClass { + GtsObjectClass parent_class; +}; + +#define GTS_PGRAPH(obj) GTS_OBJECT_CAST (obj,\ + GtsPGraph,\ + gts_pgraph_class ()) +#define GTS_PGRAPH_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\ + GtsPGraphClass,\ + gts_pgraph_class()) +#define GTS_IS_PGRAPH(obj) (gts_object_is_from_class (obj,\ + gts_pgraph_class ())) + +GtsPGraphClass * gts_pgraph_class (void); +GtsPGraph * gts_pgraph_new (GtsPGraphClass * klass, + GtsGraph * g, + GtsGNodeSplitClass * split_class, + GtsWGNodeClass * node_class, + GtsWGEdgeClass * edge_class, + guint min); +GtsGNodeSplit * gts_pgraph_add_node (GtsPGraph * pg); +GtsGNodeSplit * gts_pgraph_remove_node (GtsPGraph * pg); +void gts_pgraph_set_node_number (GtsPGraph *pg, + guint n); +guint gts_pgraph_get_node_number (GtsPGraph *pg); +guint gts_pgraph_min_node_number (GtsPGraph *pg); +guint gts_pgraph_max_node_number (GtsPGraph *pg); +void gts_pgraph_foreach_node (GtsPGraph *pg, + GtsFunc func, + gpointer data); +gboolean gts_pgraph_down (GtsPGraph * pg, + GtsFunc func, + gpointer data); +/* Graph partition: partition.c */ + +GSList * gts_graph_bubble_partition (GtsGraph * g, + guint np, + guint niter, + GtsFunc step_info, + gpointer data); +guint gts_graph_partition_edges_cut (GSList * partition); +gfloat gts_graph_partition_edges_cut_weight (GSList * partition); +void gts_graph_partition_print_stats (GSList * partition, + FILE * fp); +gfloat gts_graph_partition_balance (GSList * partition); +GSList * gts_graph_partition_clone (GSList * partition); +GSList * gts_graph_recursive_bisection (GtsWGraph * wg, + guint n, + guint ntry, + guint mmax, + guint nmin, + gfloat imbalance); +void gts_graph_partition_destroy (GSList * partition); + +/* Graph bisection: partition.c */ + +typedef struct _GtsGraphBisection GtsGraphBisection; + +struct _GtsGraphBisection { + GtsGraph * g; + GtsGraph * g1; + GtsGraph * g2; + GHashTable * bg1; + GHashTable * bg2; +}; + +gboolean gts_graph_bisection_check (GtsGraphBisection * bg); +GtsGraphBisection * gts_graph_ggg_bisection (GtsGraph * g, + guint ntry); +GtsGraphBisection * gts_graph_bfgg_bisection (GtsGraph * g, + guint ntry); +gdouble gts_graph_bisection_kl_refine (GtsGraphBisection * bg, + guint mmax); +gdouble gts_graph_bisection_bkl_refine (GtsGraphBisection * bg, + guint mmax, + gfloat imbalance); +GtsGraphBisection * gts_graph_bisection_new (GtsWGraph * wg, + guint ntry, + guint mmax, + guint nmin, + gfloat imbalance); +void gts_graph_bisection_destroy (GtsGraphBisection * bg, + gboolean destroy_graphs); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __GTS_H__ */ Index: toporouter/src_3rd/gts/heap.c =================================================================== --- toporouter/src_3rd/gts/heap.c (nonexistent) +++ toporouter/src_3rd/gts/heap.c (revision 6803) @@ -0,0 +1,258 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "gts.h" + +#define PARENT(i) ((i) >= 2 ? (i)/2 : 0) +#define LEFT_CHILD(i) (2*(i)) +#define RIGHT_CHILD(i) (2*(i) + 1) + +struct _GtsHeap { + GPtrArray * elts; + GCompareFunc func; + gboolean frozen; +}; + +/** + * gts_heap_new: + * @compare_func: a GCompareFunc. + * + * Returns: a new #GtsHeap using @compare_func as a sorting function. + */ +GtsHeap * gts_heap_new (GCompareFunc compare_func) +{ + GtsHeap * heap; + + g_return_val_if_fail (compare_func != NULL, NULL); + + heap = g_malloc (sizeof(GtsHeap)); + heap->elts = g_ptr_array_new (); + heap->func = compare_func; + heap->frozen = FALSE; + return heap; +} + +static void sift_up (GtsHeap * heap, guint i) +{ + gpointer parent, child; + guint p; + gpointer * pdata = heap->elts->pdata; + GCompareFunc func = heap->func; + + child = pdata[i - 1]; + while ((p = PARENT (i))) { + parent = pdata[p - 1]; + if ((*func) (parent, child) > 0) { + pdata[p - 1] = child; + pdata[i - 1] = parent; + i = p; + } + else + i = 0; + } +} + +/** + * gts_heap_insert: + * @heap: a #GtsHeap. + * @p: a pointer to add to the heap. + * + * Inserts a new element @p in the heap. + */ +void gts_heap_insert (GtsHeap * heap, gpointer p) +{ + g_return_if_fail (heap != NULL); + + g_ptr_array_add (heap->elts, p); + if (!heap->frozen) + sift_up (heap, heap->elts->len); +} + +static void sift_down (GtsHeap * heap, guint i) +{ + gpointer left_child, right_child, child, parent; + guint lc, rc, c; + gpointer * pdata = heap->elts->pdata; + guint len = heap->elts->len; + GCompareFunc func = heap->func; + + lc = LEFT_CHILD (i); + rc = RIGHT_CHILD (i); + left_child = lc <= len ? pdata[lc - 1] : NULL; + right_child = rc <= len ? pdata[rc - 1] : NULL; + + parent = pdata[i - 1]; + while (left_child != NULL) { + if (right_child == NULL || + (*func) (left_child, right_child) < 0) { + child = left_child; + c = lc; + } + else { + child = right_child; + c = rc; + } + if ((*func) (parent, child) > 0) { + pdata[i - 1] = child; + pdata[c - 1] = parent; + i = c; + lc = LEFT_CHILD (i); + rc = RIGHT_CHILD (i); + left_child = lc <= len ? pdata[lc - 1] : NULL; + right_child = rc <= len ? pdata[rc - 1] : NULL; + } + else + left_child = NULL; + } +} + +/** + * gts_heap_remove_top: + * @heap: a #GtsHeap. + * + * Removes the element at the top of the heap. + * + * Returns: the element at the top of the heap. + */ +gpointer gts_heap_remove_top (GtsHeap * heap) +{ + gpointer root; + GPtrArray * elts; + guint len; + + g_return_val_if_fail (heap != NULL, NULL); + + elts = heap->elts; len = elts->len; + + if (len == 0) + return NULL; + if (len == 1) + return g_ptr_array_remove_index (elts, 0); + + root = elts->pdata[0]; + elts->pdata[0] = g_ptr_array_remove_index (elts, len - 1); + sift_down (heap, 1); + return root; +} + +/** + * gts_heap_top: + * @heap: a #GtsHeap. + * + * Returns: the element at the top of the heap. + */ +gpointer gts_heap_top (GtsHeap * heap) +{ + GPtrArray * elts; + guint len; + + g_return_val_if_fail (heap != NULL, NULL); + + elts = heap->elts; + len = elts->len; + if (len == 0) + return NULL; + return elts->pdata[0]; +} + +/** + * gts_heap_destroy: + * @heap: a #GtsHeap. + * + * Free all the memory allocated for @heap. + */ +void gts_heap_destroy (GtsHeap * heap) +{ + g_return_if_fail (heap != NULL); + + g_ptr_array_free (heap->elts, TRUE); + g_free (heap); +} + +/** + * gts_heap_thaw: + * @heap: a #GtsHeap. + * + * If @heap has been frozen previously using gts_heap_freeze(), reorder it + * in O(n) time and unfreeze it. + */ +void gts_heap_thaw (GtsHeap * heap) +{ + guint i; + + g_return_if_fail (heap != NULL); + + if (!heap->frozen) + return; + + for (i = heap->elts->len/2; i > 0; i--) + sift_down (heap, i); + + heap->frozen = FALSE; +} + +/** + * gts_heap_foreach: + * @heap: a #GtsHeap. + * @func: the function to call for each element in the heap. + * @user_data: to pass to @func. + */ +void gts_heap_foreach (GtsHeap * heap, + GFunc func, + gpointer user_data) +{ + guint i; + GPtrArray * elts; + + g_return_if_fail (heap != NULL); + g_return_if_fail (func != NULL); + + elts = heap->elts; + for (i = 0; i < elts->len; i++) + (*func) (elts->pdata[i], user_data); +} + +/** + * gts_heap_freeze: + * @heap: a #GtsHeap. + * + * Freezes the heap. Any subsequent operation will not preserve the heap + * property. Used in conjunction with gts_heap_insert() and gts_heap_thaw() + * to create a heap in O(n) time. + */ +void gts_heap_freeze (GtsHeap * heap) +{ + g_return_if_fail (heap != NULL); + + heap->frozen = TRUE; +} + +/** + * gts_heap_size: + * @heap: a #GtsHeap. + * + * Returns: the number of items in @heap. + */ +guint gts_heap_size (GtsHeap * heap) +{ + g_return_val_if_fail (heap != NULL, 0); + + return heap->elts->len; +} Index: toporouter/src_3rd/gts/hsurface.c =================================================================== --- toporouter/src_3rd/gts/hsurface.c (nonexistent) +++ toporouter/src_3rd/gts/hsurface.c (revision 6803) @@ -0,0 +1,405 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include +#include "gts.h" + +#define HEAP_INSERT_HSPLIT(h, e) ((e)->index = gts_eheap_insert (h, e)) +#define HEAP_REMOVE_HSPLIT(h, e) (gts_eheap_remove (h, (e)->index),\ + (e)->index = NULL) + +static void hsplit_init (GtsHSplit * hsplit) +{ + hsplit->index = NULL; + hsplit->parent = NULL; + hsplit->nchild = 0; +} + +/** + * gts_hsplit_class: + * + * Returns: the #GtsHSplitClass. + */ +GtsHSplitClass * gts_hsplit_class (void) +{ + static GtsHSplitClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo hsplit_info = { + "GtsHSplit", + sizeof (GtsHSplit), + sizeof (GtsHSplitClass), + (GtsObjectClassInitFunc) NULL, + (GtsObjectInitFunc) hsplit_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_split_class ()), + &hsplit_info); + } + + return klass; +} + +/** + * gts_hsplit_new: + * @klass: a #GtsHSplitClass. + * @vs: a #GtsSplit. + * + * Returns: a new #GtsHSplit, hierarchical extension of @vs. + */ +GtsHSplit * gts_hsplit_new (GtsHSplitClass * klass, GtsSplit * vs) +{ + GtsHSplit * hs; + + g_return_val_if_fail (vs != NULL, NULL); + + hs = GTS_HSPLIT (gts_object_new (GTS_OBJECT_CLASS (klass))); + memcpy (hs, vs, sizeof (GtsSplit)); + GTS_OBJECT (hs)->reserved = NULL; + + return hs; +} + +/** + * gts_hsplit_collapse: + * @hs: a #GtsHSplit. + * @hsurface: a #GtsHSurface. + * + * Collapses the #GtsSplit defined by @hs, updates the expandable and + * collapsable priority heaps of @hsurface. + */ +void gts_hsplit_collapse (GtsHSplit * hs, + GtsHSurface * hsurface) +{ + GtsHSplit * parent; + GtsSplit * vs; + + g_return_if_fail (hs != NULL); + g_return_if_fail (hs->nchild == 2); + g_return_if_fail (hsurface != NULL); + + gts_split_collapse (GTS_SPLIT (hs), hsurface->s->edge_class, NULL); + + hsurface->nvertex--; + hs->nchild = 0; + HEAP_REMOVE_HSPLIT (hsurface->collapsable, hs); + HEAP_INSERT_HSPLIT (hsurface->expandable, hs); + + vs = GTS_SPLIT (hs); + if (GTS_IS_HSPLIT (vs->v1)) + HEAP_REMOVE_HSPLIT (hsurface->expandable, GTS_HSPLIT (vs->v1)); + if (GTS_IS_HSPLIT (vs->v2)) + HEAP_REMOVE_HSPLIT (hsurface->expandable, GTS_HSPLIT (vs->v2)); + + parent = hs->parent; + if (parent && ++parent->nchild == 2) + HEAP_INSERT_HSPLIT (hsurface->collapsable, parent); +} + +/** + * gts_hsplit_expand: + * @hs: a #GtsHSplit. + * @hsurface: a #GtsHSurface. + * + * Expands the #GtsSplit defined by @hs (which must be expandable) + * and updates the priority heaps of @hsurface. + */ +void gts_hsplit_expand (GtsHSplit * hs, + GtsHSurface * hsurface) +{ + GtsHSplit * parent; + GtsSplit * vs; + + g_return_if_fail (hs != NULL); + g_return_if_fail (hsurface != NULL); + g_return_if_fail (hs->nchild == 0); + + gts_split_expand (GTS_SPLIT (hs), hsurface->s, hsurface->s->edge_class); + hsurface->nvertex++; + hs->nchild = 2; + HEAP_REMOVE_HSPLIT (hsurface->expandable, hs); + HEAP_INSERT_HSPLIT (hsurface->collapsable, hs); + + vs = GTS_SPLIT (hs); + if (GTS_IS_HSPLIT (vs->v1)) + HEAP_INSERT_HSPLIT (hsurface->expandable, GTS_HSPLIT (vs->v1)); + if (GTS_IS_HSPLIT (vs->v2)) + HEAP_INSERT_HSPLIT (hsurface->expandable, GTS_HSPLIT (vs->v2)); + + parent = hs->parent; + if (parent && parent->nchild-- == 2) + HEAP_REMOVE_HSPLIT (hsurface->collapsable, parent); +} + +static void hsurface_destroy (GtsObject * object) +{ + GtsHSurface * hs = GTS_HSURFACE (object); + + gts_hsurface_traverse (hs, G_POST_ORDER, -1, + (GtsSplitTraverseFunc) gts_object_destroy, + NULL); + g_slist_free (hs->roots); + if (hs->expandable) + gts_eheap_destroy (hs->expandable); + if (hs->collapsable) + gts_eheap_destroy (hs->collapsable); + g_ptr_array_free (hs->split, TRUE); + + (* GTS_OBJECT_CLASS (gts_hsurface_class ())->parent_class->destroy) (object); +} + +static void hsurface_class_init (GtsObjectClass * klass) +{ + klass->destroy = hsurface_destroy; +} + +static void hsurface_init (GtsHSurface * hsurface) +{ + hsurface->s = NULL; + hsurface->roots = NULL; + hsurface->expandable = hsurface->collapsable = NULL; + hsurface->split = g_ptr_array_new (); + hsurface->nvertex = 0; +} + +/** + * gts_hsurface_class: + * + * Returns: the #GtsHSurfaceClass. + */ +GtsHSurfaceClass * gts_hsurface_class (void) +{ + static GtsHSurfaceClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo hsurface_info = { + "GtsHSurface", + sizeof (GtsHSurface), + sizeof (GtsHSurfaceClass), + (GtsObjectClassInitFunc) hsurface_class_init, + (GtsObjectInitFunc) hsurface_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (gts_object_class (), + &hsurface_info); + } + + return klass; +} + +/** + * gts_hsurface_new: + * @klass: a #GtsHSurfaceClass. + * @hsplit_class: a #GtsHSplitClass. + * @psurface: a #GtsPSurface. + * @expand_key: a #GtsKeyFunc used to order the priority heap of expandable + * #GtsHSplit. + * @expand_data: data to be passed to @expand_key. + * @collapse_key: a #GtsKeyFunc used to order the priority heap of collapsable + * #GtsHSplit. + * @collapse_data: data to be passed to @collapsed_key. + * + * Returns: a new #GtsHSurface, hierarchical extension of @psurface + * and using #GtsHSplit of class @hsplit_class. Note that @psurface is + * destroyed in the process. + */ +GtsHSurface * gts_hsurface_new (GtsHSurfaceClass * klass, + GtsHSplitClass * hsplit_class, + GtsPSurface * psurface, + GtsKeyFunc expand_key, + gpointer expand_data, + GtsKeyFunc collapse_key, + gpointer collapse_data) +{ + GtsHSurface * hsurface; + + g_return_val_if_fail (klass != NULL, NULL); + g_return_val_if_fail (hsplit_class != NULL, NULL); + g_return_val_if_fail (psurface != NULL, NULL); + g_return_val_if_fail (expand_key != NULL, NULL); + g_return_val_if_fail (collapse_key != NULL, NULL); + + hsurface = GTS_HSURFACE (gts_object_new (GTS_OBJECT_CLASS (klass))); + hsurface->s = psurface->s; + hsurface->expandable = gts_eheap_new (expand_key, expand_data); + hsurface->collapsable = gts_eheap_new (collapse_key, collapse_data); + g_ptr_array_set_size (hsurface->split, psurface->split->len); + + while (gts_psurface_remove_vertex (psurface)) + ; + while (psurface->pos) { + GtsSplit * vs = g_ptr_array_index (psurface->split, psurface->pos - 1); + GtsHSplit * hs = gts_hsplit_new (hsplit_class, vs); + + g_ptr_array_index (hsurface->split, psurface->pos - 1) = hs; + psurface->pos--; + + hs->parent = GTS_OBJECT (vs)->reserved; + if (hs->parent) { + GtsSplit * vsp = GTS_SPLIT (hs->parent); + + if (vsp->v1 == GTS_OBJECT (vs)) { + g_assert (vsp->v2 != GTS_OBJECT (vs)); + vsp->v1 = GTS_OBJECT (hs); + } + else { + g_assert (vsp->v2 == GTS_OBJECT (vs)); + vsp->v2 = GTS_OBJECT (hs); + } + } + else + hsurface->roots = g_slist_prepend (hsurface->roots, hs); + + hs->nchild = 0; + if (GTS_IS_SPLIT (vs->v1)) + GTS_OBJECT (vs->v1)->reserved = hs; + else + hs->nchild++; + if (GTS_IS_SPLIT (vs->v2)) + GTS_OBJECT (vs->v2)->reserved = hs; + else + hs->nchild++; + + gts_split_expand (vs, psurface->s, psurface->s->edge_class); + + if (hs->nchild == 2) + HEAP_INSERT_HSPLIT (hsurface->collapsable, hs); + } + + hsurface->nvertex = gts_surface_vertex_number (hsurface->s); + gts_object_destroy (GTS_OBJECT (psurface)); + + return hsurface; +} + +/** + * gts_hsurface_traverse: + * @hsurface: a #GtsHSurface. + * @order: the order in which nodes are visited - G_PRE_ORDER or G_POST_ORDER. + * @depth: the maximum depth of the traversal. Nodes below this depth + * will not be visited. If max_depth is -1 all nodes in the tree are + * visited. If depth is 1, only the root is visited. If depth is 2, + * the root and its children are visited. And so on. + * @func: the function to call for each visited #GtsHSplit. + * @data: user data to pass to the function. + * + * Traverses a hierarchical surface starting from its roots. It calls + * the given function for each #GtsHSplit visited. + * See also gts_split_traverse(). + */ +void gts_hsurface_traverse (GtsHSurface * hsurface, + GTraverseType order, + gint depth, + GtsSplitTraverseFunc func, + gpointer data) +{ + GSList * i; + + g_return_if_fail (hsurface != NULL); + g_return_if_fail (func != NULL); + g_return_if_fail (order < G_LEVEL_ORDER); + g_return_if_fail (depth == -1 || depth > 0); + + i = hsurface->roots; + while (i) { + gts_split_traverse (i->data, order, depth, func, data); + i = i->next; + } +} + +/** + * gts_hsurface_foreach: + * @hsurface: a #GtsHSurface. + * @order: the order in which #GtsHSplit are visited - G_PRE_ORDER or + * G_POST_ORDER. + * @func: the function to call for each visited #GtsHSplit. + * @data: user data to pass to the function. + * + * Starts by expanding all the #GtsHSplit of @hsurface. If @order is + * G_PRE_ORDER, calls @func for each #GtsHSplit and collapses it. If + * order is G_POST_ORDER, collapses each #GtsHSplit first and then + * calls @func. The traversal can be halted at any point by returning + * TRUE from func. + */ +void gts_hsurface_foreach (GtsHSurface * hsurface, + GTraverseType order, + GtsFunc func, + gpointer data) +{ + GtsHSplit * hs; + guint i = 0, len; + gboolean stop = FALSE; + + g_return_if_fail (hsurface != NULL); + g_return_if_fail (func != NULL); + g_return_if_fail (order == G_PRE_ORDER || order == G_POST_ORDER); + + while ((hs = gts_eheap_top (hsurface->expandable, NULL))) + gts_hsplit_expand (hs, hsurface); + + len = hsurface->split->len; + switch (order) { + case G_PRE_ORDER: + while (i < len && !stop) { + GtsHSplit * hs = g_ptr_array_index (hsurface->split, i); + stop = (*func) (hs, data); + if (!stop) + gts_hsplit_collapse (hs, hsurface); + i++; + } + break; + case G_POST_ORDER: + while (i < len && !stop) { + GtsHSplit * hs = g_ptr_array_index (hsurface->split, i); + gts_hsplit_collapse (hs, hsurface); + stop = (*func) (hs, data); + i++; + } + break; + default: + g_assert_not_reached (); + } +} + +/** + * gts_hsurface_height: + * @hsurface: a #GtsHSurface. + * + * Returns: the maximum height of the tree described by @hsurface. + */ +guint gts_hsurface_height (GtsHSurface * hsurface) +{ + GSList * i; + guint height = 0; + + g_return_val_if_fail (hsurface != NULL, 0); + + i = hsurface->roots; + while (i) { + guint tmp_height = gts_split_height (i->data); + if (tmp_height > height) + height = tmp_height; + i = i->next; + } + + return height; +} Index: toporouter/src_3rd/gts/iso.c =================================================================== --- toporouter/src_3rd/gts/iso.c (nonexistent) +++ toporouter/src_3rd/gts/iso.c (revision 6803) @@ -0,0 +1,455 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "gts.h" + +typedef enum { LEFT = 0, RIGHT = 1 } Orientation; + +typedef struct { + GtsVertex * v; + Orientation orientation; +} OrientedVertex; + +struct _GtsIsoSlice { + OrientedVertex *** vertices; + guint nx, ny; +}; + +/* coordinates of the edges of the cube (see doc/isocube.fig) */ +static guint c[12][4] = { + {0, 0, 0, 0}, {0, 0, 0, 1}, {0, 0, 1, 1}, {0, 0, 1, 0}, + {1, 0, 0, 0}, {1, 0, 0, 1}, {1, 1, 0, 1}, {1, 1, 0, 0}, + {2, 0, 0, 0}, {2, 1, 0, 0}, {2, 1, 1, 0}, {2, 0, 1, 0}}; + +/* first index is the edge number, second index is the edge orientation + (RIGHT or LEFT), third index are the edges which this edge may connect to + in order */ +static guint edge[12][2][3] = { + {{9, 1, 8}, {4, 3, 7}}, /* 0 */ + {{6, 2, 5}, {8, 0, 9}}, /* 1 */ + {{10, 3, 11}, {5, 1, 6}}, /* 2 */ + {{7, 0, 4}, {11, 2, 10}}, /* 3 */ + {{3, 7, 0}, {8, 5, 11}}, /* 4 */ + {{11, 4, 8}, {1, 6, 2}}, /* 5 */ + {{2, 5, 1}, {9, 7, 10}}, /* 6 */ + {{10, 6, 9}, {0, 4, 3}}, /* 7 */ + {{5, 11, 4}, {0, 9, 1}}, /* 8 */ + {{1, 8, 0}, {7, 10, 6}}, /* 9 */ + {{6, 9, 7}, {3, 11, 2}}, /* 10 */ + {{2, 10, 3}, {4, 8, 5}} /* 11 */ +}; + +static void ** malloc2D (guint nx, guint ny, gulong size) +{ + void ** m = g_malloc (nx*sizeof (void *)); + guint i; + + for (i = 0; i < nx; i++) + m[i] = g_malloc0 (ny*size); + + return m; +} + +static void free2D (void ** m, guint nx) +{ + guint i; + + g_return_if_fail (m != NULL); + + for (i = 0; i < nx; i++) + g_free (m[i]); + g_free (m); +} + +/** + * gts_grid_plane_new: + * @nx: + * @ny: + * + * Returns: + */ +GtsGridPlane * gts_grid_plane_new (guint nx, guint ny) +{ + GtsGridPlane * g = g_malloc (sizeof (GtsGridPlane)); + + g->p = (GtsPoint **) malloc2D (nx, ny, sizeof (GtsPoint)); + g->nx = nx; + g->ny = ny; + + return g; +} + +/** + * gts_grid_plane_destroy: + * @g: + * + */ +void gts_grid_plane_destroy (GtsGridPlane * g) +{ + g_return_if_fail (g != NULL); + + free2D ((void **) g->p, g->nx); + g_free (g); +} + +/** + * gts_iso_slice_new: + * @nx: number of vertices in the x direction. + * @ny: number of vertices in the y direction. + * + * Returns: a new #GtsIsoSlice. + */ +GtsIsoSlice * gts_iso_slice_new (guint nx, guint ny) +{ + GtsIsoSlice * slice; + + g_return_val_if_fail (nx > 1, NULL); + g_return_val_if_fail (ny > 1, NULL); + + slice = g_malloc (sizeof (GtsIsoSlice)); + + slice->vertices = g_malloc (3*sizeof (OrientedVertex **)); + slice->vertices[0] = + (OrientedVertex **) malloc2D (nx, ny, sizeof (OrientedVertex)); + slice->vertices[1] = + (OrientedVertex **) malloc2D (nx - 1, ny, sizeof (OrientedVertex)); + slice->vertices[2] = + (OrientedVertex **) malloc2D (nx, ny - 1, sizeof (OrientedVertex)); + slice->nx = nx; + slice->ny = ny; + + return slice; +} + +/** + * gts_iso_slice_fill: + * @slice: a #GtsIsoSlice. + * @plane1: a #GtsGridPlane. + * @plane2: another #GtsGridPlane. + * @f1: values of the function corresponding to @plane1. + * @f2: values of the function corresponding to @plane2. + * @iso: isosurface value. + * @klass: a #GtsVertexClass or one of its descendant to be used for the + * new vertices. + * + * Fill @slice with the coordinates of the vertices defined by + * f1 (x,y,z) = @iso and f2 (x, y, z) = @iso. + */ +void gts_iso_slice_fill (GtsIsoSlice * slice, + GtsGridPlane * plane1, + GtsGridPlane * plane2, + gdouble ** f1, + gdouble ** f2, + gdouble iso, + GtsVertexClass * klass) +{ + OrientedVertex *** vertices; + GtsPoint ** p1, ** p2 = NULL; + guint i, j, nx, ny; + + g_return_if_fail (slice != NULL); + g_return_if_fail (plane1 != NULL); + g_return_if_fail (f1 != NULL); + g_return_if_fail (f2 == NULL || plane2 != NULL); + + p1 = plane1->p; + if (plane2) + p2 = plane2->p; + vertices = slice->vertices; + nx = slice->nx; + ny = slice->ny; + + if (f2) + for (i = 0; i < nx; i++) + for (j = 0; j < ny; j++) { + gdouble v1 = f1[i][j] - iso; + gdouble v2 = f2[i][j] - iso; + if ((v1 >= 0. && v2 < 0.) || (v1 < 0. && v2 >= 0.)) { + gdouble c2 = v1/(v1 - v2), c1 = 1. - c2; + vertices[0][i][j].v = + gts_vertex_new (klass, + c1*p1[i][j].x + c2*p2[i][j].x, + c1*p1[i][j].y + c2*p2[i][j].y, + c1*p1[i][j].z + c2*p2[i][j].z); + vertices[0][i][j].orientation = v2 >= 0. ? RIGHT : LEFT; + } + else + vertices[0][i][j].v = NULL; + } + for (i = 0; i < nx - 1; i++) + for (j = 0; j < ny; j++) { + gdouble v1 = f1[i][j] - iso; + gdouble v2 = f1[i+1][j] - iso; + if ((v1 >= 0. && v2 < 0.) || (v1 < 0. && v2 >= 0.)) { + gdouble c2 = v1/(v1 - v2), c1 = 1. - c2; + vertices[1][i][j].v = + gts_vertex_new (klass, + c1*p1[i][j].x + c2*p1[i+1][j].x, + c1*p1[i][j].y + c2*p1[i+1][j].y, + c1*p1[i][j].z + c2*p1[i+1][j].z); + vertices[1][i][j].orientation = v2 >= 0. ? RIGHT : LEFT; + } + else + vertices[1][i][j].v = NULL; + } + for (i = 0; i < nx; i++) + for (j = 0; j < ny - 1; j++) { + gdouble v1 = f1[i][j] - iso; + gdouble v2 = f1[i][j+1] - iso; + if ((v1 >= 0. && v2 < 0.) || (v1 < 0. && v2 >= 0.)) { + gdouble c2 = v1/(v1 - v2), c1 = 1. - c2; + vertices[2][i][j].v = + gts_vertex_new (klass, + c1*p1[i][j].x + c2*p1[i][j+1].x, + c1*p1[i][j].y + c2*p1[i][j+1].y, + c1*p1[i][j].z + c2*p1[i][j+1].z); + vertices[2][i][j].orientation = v2 >= 0. ? RIGHT : LEFT; + } + else + vertices[2][i][j].v = NULL; + } +} + +/** + * gts_iso_slice_fill_cartesian: + * @slice: a #GtsIsoSlice. + * @g: a #GtsCartesianGrid. + * @f1: values of the function for plane z = @g.z. + * @f2: values of the function for plane z = @g.z + @g.dz. + * @iso: isosurface value. + * @klass: a #GtsVertexClass. + * + * Fill @slice with the coordinates of the vertices defined by + * f1 (x,y,z) = @iso and f2 (x, y, z) = @iso. + */ +void gts_iso_slice_fill_cartesian (GtsIsoSlice * slice, + GtsCartesianGrid g, + gdouble ** f1, + gdouble ** f2, + gdouble iso, + GtsVertexClass * klass) +{ + OrientedVertex *** vertices; + guint i, j; + gdouble x, y; + + g_return_if_fail (slice != NULL); + g_return_if_fail (f1 != NULL); + + vertices = slice->vertices; + + if (f2) + for (i = 0, x = g.x; i < g.nx; i++, x += g.dx) + for (j = 0, y = g.y; j < g.ny; j++, y += g.dy) { + gdouble v1 = f1[i][j] - iso; + gdouble v2 = f2[i][j] - iso; + if ((v1 >= 0. && v2 < 0.) || (v1 < 0. && v2 >= 0.)) { + vertices[0][i][j].v = + gts_vertex_new (klass, + x, y, g.z + g.dz*v1/(v1 - v2)); + vertices[0][i][j].orientation = v2 >= 0. ? RIGHT : LEFT; + } + else + vertices[0][i][j].v = NULL; + } + for (i = 0, x = g.x; i < g.nx - 1; i++, x += g.dx) + for (j = 0, y = g.y; j < g.ny; j++, y += g.dy) { + gdouble v1 = f1[i][j] - iso; + gdouble v2 = f1[i+1][j] - iso; + if ((v1 >= 0. && v2 < 0.) || (v1 < 0. && v2 >= 0.)) { + vertices[1][i][j].v = + gts_vertex_new (klass, x + g.dx*v1/(v1 - v2), y, g.z); + vertices[1][i][j].orientation = v2 >= 0. ? RIGHT : LEFT; + } + else + vertices[1][i][j].v = NULL; + } + for (i = 0, x = g.x; i < g.nx; i++, x += g.dx) + for (j = 0, y = g.y; j < g.ny - 1; j++, y += g.dy) { + gdouble v1 = f1[i][j] - iso; + gdouble v2 = f1[i][j+1] - iso; + if ((v1 >= 0. && v2 < 0.) || (v1 < 0. && v2 >= 0.)) { + vertices[2][i][j].v = + gts_vertex_new (klass, x, y + g.dy*v1/(v1 - v2), g.z); + vertices[2][i][j].orientation = v2 >= 0. ? RIGHT : LEFT; + } + else + vertices[2][i][j].v = NULL; + } +} + +/** + * gts_iso_slice_destroy: + * @slice: a #GtsIsoSlice. + * + * Free all memory allocated for @slice. + */ +void gts_iso_slice_destroy (GtsIsoSlice * slice) +{ + g_return_if_fail (slice != NULL); + + free2D ((void **) slice->vertices[0], slice->nx); + free2D ((void **) slice->vertices[1], slice->nx - 1); + free2D ((void **) slice->vertices[2], slice->nx); + g_free (slice->vertices); + g_free (slice); +} + +/** + * gts_isosurface_slice: + * @slice1: a #GtsIsoSlice. + * @slice2: another #GtsIsoSlice. + * @surface: a #GtsSurface. + * + * Given two successive slices @slice1 and @slice2 link their vertices with + * segments and triangles which are added to @surface. + */ +void gts_isosurface_slice (GtsIsoSlice * slice1, + GtsIsoSlice * slice2, + GtsSurface * surface) +{ + guint j, k, l, nx, ny; + OrientedVertex *** vertices[2]; + GtsVertex * va[12]; + + g_return_if_fail (slice1 != NULL); + g_return_if_fail (slice2 != NULL); + g_return_if_fail (surface != NULL); + g_return_if_fail (slice1->nx == slice2->nx && slice1->ny == slice2->ny); + + vertices[0] = slice1->vertices; + vertices[1] = slice2->vertices; + nx = slice1->nx; + ny = slice1->ny; + + /* link vertices with segments and triangles */ + for (j = 0; j < nx - 1; j++) + for (k = 0; k < ny - 1; k++) { + gboolean cube_is_cut = FALSE; + for (l = 0; l < 12; l++) { + guint nv = 0, e = l; + OrientedVertex ov = + vertices[c[e][1]][c[e][0]][j + c[e][2]][k + c[e][3]]; + while (ov.v && !GTS_OBJECT (ov.v)->reserved) { + guint m = 0, * ne = edge[e][ov.orientation]; + va[nv++] = ov.v; + GTS_OBJECT (ov.v)->reserved = surface; + ov.v = NULL; + while (m < 3 && !ov.v) { + e = ne[m++]; + ov = vertices[c[e][1]][c[e][0]][j + c[e][2]][k + c[e][3]]; + } + } + /* create edges and faces */ + if (nv > 2) { + GtsEdge * e1, * e2, * e3; + guint m; + if (!(e1 = GTS_EDGE (gts_vertices_are_connected (va[0], va[1])))) + e1 = gts_edge_new (surface->edge_class, va[0], va[1]); + for (m = 1; m < nv - 1; m++) { + if (!(e2 = GTS_EDGE (gts_vertices_are_connected (va[m], va[m+1])))) + e2 = gts_edge_new (surface->edge_class, va[m], va[m+1]); + if (!(e3 = GTS_EDGE (gts_vertices_are_connected (va[m+1], va[0])))) + e3 = gts_edge_new (surface->edge_class, va[m+1], va[0]); + gts_surface_add_face (surface, + gts_face_new (surface->face_class, + e1, e2, e3)); + e1 = e3; + } + } + if (nv > 0) + cube_is_cut = TRUE; + } + if (cube_is_cut) + for (l = 0; l < 12; l++) { + GtsVertex * v = + vertices[c[l][1]][c[l][0]][j + c[l][2]][k + c[l][3]].v; + if (v) + GTS_OBJECT (v)->reserved = NULL; + } + } +} + +#define SWAP(s1, s2, tmp) (tmp = s1, s1 = s2, s2 = tmp) + +/** + * gts_isosurface_cartesian: + * @surface: a #GtsSurface. + * @g: a #GtsCartesianGrid. + * @f: a #GtsIsoCartesianFunc. + * @data: user data to be passed to @f. + * @iso: isosurface value. + * + * Adds to @surface new faces defining the isosurface f(x,y,z) = @iso. By + * convention, the normals to the surface are pointing toward the positive + * values of f(x,y,z) - @iso. + * + * The user function @f is called successively for each value of the z + * coordinate defined by @g. It must fill the corresponding (x,y) plane with + * the values of the function for which the isosurface is to be computed. + */ +void gts_isosurface_cartesian (GtsSurface * surface, + GtsCartesianGrid g, + GtsIsoCartesianFunc f, + gpointer data, + gdouble iso) +{ + void * tmp; + gdouble ** f1, ** f2; + GtsIsoSlice * slice1, * slice2; + guint i; + + g_return_if_fail (surface != NULL); + g_return_if_fail (f != NULL); + g_return_if_fail (g.nx > 1); + g_return_if_fail (g.ny > 1); + g_return_if_fail (g.nz > 1); + + slice1 = gts_iso_slice_new (g.nx, g.ny); + slice2 = gts_iso_slice_new (g.nx, g.ny); + f1 = (gdouble **) malloc2D (g.nx, g.ny, sizeof (gdouble)); + f2 = (gdouble **) malloc2D (g.nx, g.ny, sizeof (gdouble)); + + (*f) (f1, g, 0, data); + g.z += g.dz; + (*f) (f2, g, 1, data); + g.z -= g.dz; + gts_iso_slice_fill_cartesian (slice1, g, f1, f2, iso, + surface->vertex_class); + g.z += g.dz; + for (i = 2; i < g.nz; i++) { + g.z += g.dz; + (*f) (f1, g, i, data); + SWAP (f1, f2, tmp); + g.z -= g.dz; + gts_iso_slice_fill_cartesian (slice2, g, f1, f2, iso, + surface->vertex_class); + g.z += g.dz; + gts_isosurface_slice (slice1, slice2, surface); + SWAP (slice1, slice2, tmp); + } + gts_iso_slice_fill_cartesian (slice2, g, f2, NULL, iso, + surface->vertex_class); + gts_isosurface_slice (slice1, slice2, surface); + + gts_iso_slice_destroy (slice1); + gts_iso_slice_destroy (slice2); + free2D ((void **) f1, g.nx); + free2D ((void **) f2, g.nx); +} Index: toporouter/src_3rd/gts/isotetra.c =================================================================== --- toporouter/src_3rd/gts/isotetra.c (nonexistent) +++ toporouter/src_3rd/gts/isotetra.c (revision 6803) @@ -0,0 +1,840 @@ +/* GTS-Library conform marching tetrahedra algorithm + * Copyright (C) 2002 Gert Wollny + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#ifdef NATIVE_WIN32 +# include +# define M_SQRT2 1.41421356237309504880 +#endif /* NATIVE_WIN32 */ + +typedef struct { + gint nx, ny; + gdouble ** data; +} slice_t; + +typedef struct { + gint x, y, z; + gboolean mid; + gdouble d; +} tetra_vertex_t; + +/* this helper is a lookup table for vertices */ +typedef struct { + gint nx, ny; + GtsVertex ** vtop, ** vmid, **vbot; +} helper_t ; + +typedef struct { + GHashTable * vbot, * vtop; +} helper_bcl ; + + +static helper_t * init_helper (int nx, int ny) +{ + gint nxy = 4*nx*ny; + helper_t *retval = g_malloc0 (sizeof (helper_t)); + + retval->nx = nx; + retval->ny = ny; + retval->vtop = g_malloc0 (sizeof (GtsVertex *)*nxy); + retval->vmid = g_malloc0 (sizeof (GtsVertex *)*nxy); + retval->vbot = g_malloc0 (sizeof (GtsVertex *)*nxy); + return retval; +} + +static helper_bcl * init_helper_bcl (void) +{ + helper_bcl *retval = g_malloc0 (sizeof (helper_bcl)); + + retval->vtop = g_hash_table_new (g_str_hash, g_str_equal); + retval->vbot = g_hash_table_new (g_str_hash, g_str_equal); + return retval; +} + +static void free_helper (helper_t * h) +{ + g_free (h->vtop); + g_free (h->vmid); + g_free (h->vbot); + g_free (h); +} + +static void free_helper_bcl (helper_bcl * h) +{ + g_hash_table_destroy (h->vtop); + g_hash_table_destroy (h->vbot); + g_free (h); +} + +/* move the vertices in the bottom slice to the top, and clear the + other slices in the lookup tables */ +static void helper_advance (helper_t * h) +{ + GtsVertex ** help = h->vbot; + h->vbot = h->vtop; + h->vtop = help; + + memset (h->vmid, 0, 4*sizeof(GtsVertex *) * h->nx * h->ny); + memset (h->vbot, 0, 4*sizeof(GtsVertex *) * h->nx * h->ny); +} + +static void helper_advance_bcl (helper_bcl * h) +{ + GHashTable * help = g_hash_table_new (g_str_hash, g_str_equal); + + g_hash_table_destroy (h->vbot); + h->vbot = h->vtop; + h->vtop = help; +} + +/* find the zero-crossing of line through v1 and v2 and return the + corresponding GtsVertex */ +static GtsVertex * get_vertex (gint mz, + const tetra_vertex_t * v1, + const tetra_vertex_t * v2, + helper_t * help, + GtsCartesianGrid * g, + GtsVertexClass * klass) +{ + GtsVertex ** vertex; + gint x, y, index, idx2, z; + gdouble dx, dy, dz, d; + + g_assert (v1->d - v2->d != 0.); + + dx = dy = dz = 0.0; + d = v1->d/(v1->d - v2->d); + + index = 0; + + if (v1->x != v2->x) { + index |= 1; + dx = d; + } + + if (v1->y != v2->y) { + index |= 2; + dy = d; + } + + if (v1->z != v2->z) { + dz = d; + } + + x = v1->x; + if (v1->x > v2->x) { x = v2->x; dx = 1.0 - dx; } + + y = v1->y; + if (v1->y > v2->y) { y = v2->y; dy = 1.0 - dy;} + + z = v1->z; + if (v1->z > v2->z) { z = v2->z; dz = 1.0 - dz;} + + idx2 = 4 * ( x + y * help->nx ) + index; + + if (v1->z == v2->z) + vertex = (mz == z) ? &help->vtop[idx2] : &help->vbot[idx2]; + else + vertex = &help->vmid[idx2]; + + if (mz != z && dz != 0.0) { + fprintf(stderr, "%f \n", dz); + } + + /* if vertex is not yet created, do it now */ + if (!*vertex) + *vertex = gts_vertex_new (klass, + g->dx * ( x + dx) + g->x, + g->dy * ( y + dy) + g->y, + g->dz * ( z + dz) + g->z); + + return *vertex; +} + +static GtsVertex * get_vertex_bcl (gint mz, + const tetra_vertex_t * v1, + const tetra_vertex_t * v2, + helper_bcl * help, + GtsCartesianGrid * g, + GtsVertexClass * klass) +{ + GtsVertex * v; + GHashTable * table; + gchar * s1, * s2, * hash; + gdouble x1, x2, y1, y2, z1, z2, d; + + g_assert (v1->d - v2->d != 0.); + + /* first find correct hash table */ + if ((v1->z > mz) && (v2->z > mz)) + table = help->vtop; + else + table = help->vbot; + + d = v1->d / (v1->d - v2->d); + + /* sort vertices */ + s1 = g_strdup_printf ("%d %d %d %d", v1->x, v1->y, v1->z, v1->mid); + s2 = g_strdup_printf ("%d %d %d %d", v2->x, v2->y, v2->z, v2->mid); + + hash = (d == 0.0) ? g_strdup (s1) : + (d == 1.0) ? g_strdup (s2) : + (strcmp (s1, s2) < 0) ? g_strjoin (" ", s1, s2, NULL) : + g_strjoin (" ", s2, s1, NULL); + + /* return existing vertex or make a new one */ + v = g_hash_table_lookup (table, hash); + if (!v){ + + x1 = g->dx * (v1->x + (v1->mid / 2.0)) + g->x; + x2 = g->dx * (v2->x + (v2->mid / 2.0)) + g->x; + y1 = g->dy * (v1->y + (v1->mid / 2.0)) + g->y; + y2 = g->dy * (v2->y + (v2->mid / 2.0)) + g->y; + z1 = g->dz * (v1->z + (v1->mid / 2.0)) + g->z; + z2 = g->dz * (v2->z + (v2->mid / 2.0)) + g->z; + + v = gts_vertex_new (klass, x1 * (1.0 - d) + d * x2, + y1 * (1.0 - d) + d * y2, + z1 * (1.0 - d) + d * z2); + + g_hash_table_insert (table, g_strdup(hash), v); + } + g_free (s1); + g_free (s2); + g_free (hash); + + return v; +} + +/* create an edge connecting the zero crossings of lines through a + pair of vertices, or return an existing one */ +static GtsEdge * get_edge (GtsVertex * v1, GtsVertex * v2, + GtsEdgeClass * klass) +{ + GtsSegment *s; + GtsEdge *edge; + + g_assert (v1); + g_assert (v2); + + s = gts_vertices_are_connected (v1, v2); + + if (GTS_IS_EDGE (s)) + edge = GTS_EDGE(s); + else + edge = gts_edge_new (klass, v1, v2); + return edge; +} + +static void add_face (GtsSurface * surface, + const tetra_vertex_t * a1, const tetra_vertex_t * a2, + const tetra_vertex_t * b1, const tetra_vertex_t * b2, + const tetra_vertex_t * c1, const tetra_vertex_t * c2, + gint rev, helper_t * help, + gint z, GtsCartesianGrid * g) +{ + GtsFace * t; + GtsEdge * e1, * e2, * e3; + GtsVertex * v1 = get_vertex (z, a1, a2, help, g, surface->vertex_class); + GtsVertex * v2 = get_vertex (z, b1, b2, help, g, surface->vertex_class); + GtsVertex * v3 = get_vertex (z, c1, c2, help, g, surface->vertex_class); + + g_assert (v1 != v2); + g_assert (v2 != v3); + g_assert (v1 != v3); + + if (!rev) { + e1 = get_edge (v1, v2, surface->edge_class); + e2 = get_edge (v2, v3, surface->edge_class); + e3 = get_edge (v1, v3, surface->edge_class); + } else { + e1 = get_edge (v1, v3, surface->edge_class); + e2 = get_edge (v2, v3, surface->edge_class); + e3 = get_edge (v1, v2, surface->edge_class); + } + + t = gts_face_new (surface->face_class, e1, e2, e3); + gts_surface_add_face (surface, t); +} + +static void add_face_bcl (GtsSurface * surface, + const tetra_vertex_t * a1, + const tetra_vertex_t * a2, + const tetra_vertex_t * b1, + const tetra_vertex_t * b2, + const tetra_vertex_t * c1, + const tetra_vertex_t * c2, + gint rev, helper_bcl * help, + gint z, GtsCartesianGrid * g) +{ + GtsFace * t; + GtsEdge * e1, * e2, * e3; + GtsVertex * v1 = get_vertex_bcl (z, a1, a2, help, g, surface->vertex_class); + GtsVertex * v2 = get_vertex_bcl (z, b1, b2, help, g, surface->vertex_class); + GtsVertex * v3 = get_vertex_bcl (z, c1, c2, help, g, surface->vertex_class); + + if (v1 == v2 || v2 == v3 || v1 == v3) + return; + + if (!rev) { + e1 = get_edge (v1, v2, surface->edge_class); + e2 = get_edge (v2, v3, surface->edge_class); + e3 = get_edge (v1, v3, surface->edge_class); + } else { + e1 = get_edge (v1, v3, surface->edge_class); + e2 = get_edge (v2, v3, surface->edge_class); + e3 = get_edge (v1, v2, surface->edge_class); + } + + t = gts_face_new (surface->face_class, e1, e2, e3); + gts_surface_add_face (surface, t); +} + +/* create a new slice of site nx \times ny */ +static slice_t * new_slice (gint nx, gint ny) +{ + gint x; + slice_t * retval = g_malloc (sizeof (slice_t)); + + retval->data = g_malloc (nx*sizeof(gdouble *)); + retval->nx = nx; + retval->ny = ny; + for (x = 0; x < nx; x++) + retval->data[x] = g_malloc (ny*sizeof (gdouble)); + return retval; +} + +/* initialize a slice with inival */ +static void slice_init (slice_t * slice, gdouble inival) +{ + gint x, y; + + g_assert (slice); + + for (x = 0; x < slice->nx; x++) + for (y = 0; y < slice->ny; y++) + slice->data[x][y] = inival; +} + +/* free the memory of a slice */ +static void free_slice (slice_t * slice) +{ + gint x; + + g_return_if_fail (slice != NULL); + + for (x = 0; x < slice->nx; x++) + g_free (slice->data[x]); + g_free (slice->data); + g_free (slice); +} + +static void analyze_tetrahedra (const tetra_vertex_t * a, + const tetra_vertex_t * b, + const tetra_vertex_t * c, + const tetra_vertex_t * d, + gint parity, GtsSurface * surface, + helper_t * help, + gint z, GtsCartesianGrid * g) +{ + gint rev = parity; + gint code = 0; + + if (a->d >= 0.) code |= 1; + if (b->d >= 0.) code |= 2; + if (c->d >= 0.) code |= 4; + if (d->d >= 0.) code |= 8; + + switch (code) { + case 15: + case 0: return; /* all inside or outside */ + + case 14:rev = !parity; + case 1:add_face (surface, a, b, a, d, a, c, rev, help, z, g); + break; + case 13:rev = ! parity; + case 2:add_face (surface, a, b, b, c, b, d, rev, help, z, g); + break; + case 12:rev = !parity; + case 3:add_face (surface, a, d, a, c, b, c, rev, help, z, g); + add_face (surface, a, d, b, c, b, d, rev, help, z, g); + break; + case 11:rev = !parity; + case 4:add_face (surface, a, c, c, d, b, c, rev, help, z, g); + break; + case 10:rev = !parity; + case 5: add_face (surface, a, b, a, d, c, d, rev, help, z, g); + add_face (surface, a, b, c, d, b, c, rev, help, z, g); + break; + case 9:rev = !parity; + case 6:add_face (surface, a, b, a, c, c, d, rev, help, z, g); + add_face (surface, a, b, c, d, b, d, rev, help, z, g); + break; + case 7:rev = !parity; + case 8:add_face (surface, a, d, b, d, c, d, rev, help, z, g); + break; + } +} + +static void analyze_tetrahedra_bcl (const tetra_vertex_t * a, + const tetra_vertex_t * b, + const tetra_vertex_t * c, + const tetra_vertex_t * d, + GtsSurface * surface, + helper_bcl * help, + gint z, GtsCartesianGrid * g) +{ + gint rev = 0; + gint code = 0; + + if (a->d >= 0.) code |= 1; + if (b->d >= 0.) code |= 2; + if (c->d >= 0.) code |= 4; + if (d->d >= 0.) code |= 8; + + switch (code) { + case 15: + case 0: return; /* all inside or outside */ + + case 14:rev = !rev; + case 1:add_face_bcl (surface, a, b, a, d, a, c, rev, help, z, g); + break; + case 13:rev = !rev; + case 2:add_face_bcl (surface, a, b, b, c, b, d, rev, help, z, g); + break; + case 12:rev = !rev; + case 3:add_face_bcl (surface, a, d, a, c, b, c, rev, help, z, g); + add_face_bcl (surface, a, d, b, c, b, d, rev, help, z, g); + break; + case 11:rev = !rev; + case 4:add_face_bcl (surface, a, c, c, d, b, c, rev, help, z, g); + break; + case 10:rev = !rev; + case 5: add_face_bcl (surface, a, b, a, d, c, d, rev, help, z, g); + add_face_bcl (surface, a, b, c, d, b, c, rev, help, z, g); + break; + case 9:rev = !rev; + case 6:add_face_bcl (surface, a, b, a, c, c, d, rev, help, z, g); + add_face_bcl (surface, a, b, c, d, b, d, rev, help, z, g); + break; + case 7:rev = !rev; + case 8:add_face_bcl (surface, a, d, b, d, c, d, rev, help, z, g); + break; + } +} + +static void iso_slice_evaluate (slice_t * s1, slice_t * s2, + GtsCartesianGrid g, + gint z, GtsSurface * surface, helper_t * help) +{ + gint x,y; + tetra_vertex_t v0, v1, v2, v3, v4, v5, v6, v7; + gdouble ** s1p = s1->data; + gdouble ** s2p = s2->data; + + for (y = 0; y < g.ny-1; y++) + for (x = 0; x < g.nx-1; x++) { + gint parity = (((x ^ y) ^ z) & 1); + + v0.x = x ; v0.y = y ; v0.z = z ; v0.mid = FALSE; v0.d = s1p[x ][y ]; + v1.x = x ; v1.y = y+1; v1.z = z ; v1.mid = FALSE; v1.d = s1p[x ][y+1]; + v2.x = x+1; v2.y = y ; v2.z = z ; v2.mid = FALSE; v2.d = s1p[x+1][y ]; + v3.x = x+1; v3.y = y+1; v3.z = z ; v3.mid = FALSE; v3.d = s1p[x+1][y+1]; + v4.x = x ; v4.y = y ; v4.z = z+1; v4.mid = FALSE; v4.d = s2p[x ][y ]; + v5.x = x ; v5.y = y+1; v5.z = z+1; v5.mid = FALSE; v5.d = s2p[x ][y+1]; + v6.x = x+1; v6.y = y ; v6.z = z+1; v6.mid = FALSE; v6.d = s2p[x+1][y ]; + v7.x = x+1; v7.y = y+1; v7.z = z+1; v7.mid = FALSE; v7.d = s2p[x+1][y+1]; + + if (parity == 0) { + analyze_tetrahedra (&v0, &v1, &v2, &v4, parity, surface, help, z, &g); + analyze_tetrahedra (&v7, &v1, &v4, &v2, parity, surface, help, z, &g); + analyze_tetrahedra (&v1, &v7, &v3, &v2, parity, surface, help, z, &g); + analyze_tetrahedra (&v1, &v7, &v4, &v5, parity, surface, help, z, &g); + analyze_tetrahedra (&v2, &v6, &v4, &v7, parity, surface, help, z, &g); + }else{ + analyze_tetrahedra (&v4, &v5, &v6, &v0, parity, surface, help, z, &g); + analyze_tetrahedra (&v3, &v5, &v0, &v6, parity, surface, help, z, &g); + analyze_tetrahedra (&v5, &v3, &v7, &v6, parity, surface, help, z, &g); + analyze_tetrahedra (&v5, &v3, &v0, &v1, parity, surface, help, z, &g); + analyze_tetrahedra (&v6, &v2, &v0, &v3, parity, surface, help, z, &g); + } + } +} + +static void iso_slice_evaluate_bcl (slice_t * s1, slice_t * s2, slice_t * s3, + GtsCartesianGrid g, + gint z, GtsSurface * surface, + helper_bcl * help) +{ + gint x,y; + tetra_vertex_t v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, w0; + gdouble ** s1p = s1->data; + gdouble ** s2p = s2->data; + gdouble ** s3p = s3->data; + + for (y = 0; y < g.ny-2; y++) + for (x = 0; x < g.nx-2; x++) { + v0.x = x ; v0.y = y ; v0.z = z ; v0.mid = TRUE; + v0.d = (s1p[x ][y ] + s2p[x ][y ] + + s1p[x ][y+1] + s2p[x ][y+1] + + s1p[x+1][y ] + s2p[x+1][y ] + + s1p[x+1][y+1] + s2p[x+1][y+1])/8.0; + + v1.x = x+1; v1.y = y ; v1.z = z ; v1.mid = TRUE; + v1.d = (s1p[x+1][y ] + s2p[x+1][y ] + + s1p[x+1][y+1] + s2p[x+1][y+1] + + s1p[x+2][y ] + s2p[x+2][y ] + + s1p[x+2][y+1] + s2p[x+2][y+1])/8.0; + + v2.x = x ; v2.y = y+1; v2.z = z ; v2.mid = TRUE; + v2.d = (s1p[x ][y+1] + s2p[x ][y+1] + + s1p[x ][y+2] + s2p[x ][y+2] + + s1p[x+1][y+1] + s2p[x+1][y+1] + + s1p[x+1][y+2] + s2p[x+1][y+2])/8.0; + + v3.x = x ; v3.y = y ; v3.z = z+1; v3.mid = TRUE; + v3.d = (s2p[x ][y ] + s3p[x ][y ] + + s2p[x ][y+1] + s3p[x ][y+1] + + s2p[x+1][y ] + s3p[x+1][y ] + + s2p[x+1][y+1] + s3p[x+1][y+1])/8.0; + + v4.x = x+1; v4.y = y ; v4.z = z ; v4.mid = FALSE; v4.d = s1p[x+1][y ]; + v5.x = x ; v5.y = y+1; v5.z = z ; v5.mid = FALSE; v5.d = s1p[x ][y+1]; + v6.x = x+1; v6.y = y+1; v6.z = z ; v6.mid = FALSE; v6.d = s1p[x+1][y+1]; + v7.x = x+1; v7.y = y ; v7.z = z+1; v7.mid = FALSE; v7.d = s2p[x+1][y ]; + v8.x = x ; v8.y = y+1; v8.z = z+1; v8.mid = FALSE; v8.d = s2p[x ][y+1]; + v9.x = x+1; v9.y = y+1; v9.z = z+1; v9.mid = FALSE; v9.d = s2p[x+1][y+1]; + w0.x = x ; w0.y = y ; w0.z = z+1; w0.mid = FALSE; w0.d = s2p[x ][y ]; + + analyze_tetrahedra_bcl (&v0, &v9, &v6, &v1, surface, help, z, &g); + analyze_tetrahedra_bcl (&v0, &v6, &v4, &v1, surface, help, z, &g); + analyze_tetrahedra_bcl (&v0, &v4, &v7, &v1, surface, help, z, &g); + analyze_tetrahedra_bcl (&v0, &v7, &v9, &v1, surface, help, z, &g); + analyze_tetrahedra_bcl (&v0, &v5, &v6, &v2, surface, help, z, &g); + analyze_tetrahedra_bcl (&v0, &v6, &v9, &v2, surface, help, z, &g); + analyze_tetrahedra_bcl (&v0, &v9, &v8, &v2, surface, help, z, &g); + analyze_tetrahedra_bcl (&v0, &v8, &v5, &v2, surface, help, z, &g); + analyze_tetrahedra_bcl (&v0, &v8, &v9, &v3, surface, help, z, &g); + analyze_tetrahedra_bcl (&v0, &v9, &v7, &v3, surface, help, z, &g); + analyze_tetrahedra_bcl (&v0, &v7, &w0, &v3, surface, help, z, &g); + analyze_tetrahedra_bcl (&v0, &w0, &v8, &v3, surface, help, z, &g); + } +} + +/* copy src into dest by stripping off the iso value and leave out + the boundary (which should be G_MINDOUBLE) */ +static void copy_to_bounded (slice_t * dest, slice_t * src, + gdouble iso, gdouble fill) +{ + gint x,y; + gdouble * src_ptr; + gdouble * dest_ptr = dest->data[0]; + + g_assert(dest->ny == src->ny + 2); + g_assert(dest->nx == src->nx + 2); + + for (y = 0; y < dest->ny; ++y, ++dest_ptr) + *dest_ptr = fill; + + for (x = 1; x < src->nx - 1; ++x) { + dest_ptr = dest->data[x]; + src_ptr = src->data[x-1]; + *dest_ptr++ = fill; + for (y = 0; y < src->ny; ++y, ++dest_ptr, ++src_ptr) + *dest_ptr = *src_ptr - iso; + *dest_ptr++ = fill; + } + + dest_ptr = dest->data[y]; + + for (y = 0; y < dest->ny; ++y, ++dest_ptr) + *dest_ptr = fill; +} + +static void iso_sub (slice_t * s, gdouble iso) +{ + gint x,y; + + for (x = 0; x < s->nx; ++x) { + gdouble *ptr = s->data[x]; + + for (y = 0; y < s->ny; ++y, ++ptr) + *ptr -= iso; + } +} + + +/** + * gts_isosurface_tetra_bounded: + * @surface: a #GtsSurface. + * @g: a #GtsCartesianGrid. + * @f: a #GtsIsoCartesianFunc. + * @data: user data to be passed to @f. + * @iso: isosurface value. + * + * Adds to @surface new faces defining the isosurface f(x,y,z) = + * @iso. By convention, the normals to the surface are pointing toward + * the positive values of f(x,y,z) - @iso. To ensure a closed object, + * a boundary of G_MINDOUBLE is added around the domain + * + * The user function @f is called successively for each value of the z + * coordinate defined by @g. It must fill the corresponding (x,y) + * plane with the values of the function for which the isosurface is + * to be computed. + */ +void gts_isosurface_tetra_bounded (GtsSurface * surface, + GtsCartesianGrid g, + GtsIsoCartesianFunc f, + gpointer data, + gdouble iso) +{ + slice_t *slice1, *slice2, *transfer_slice; + GtsCartesianGrid g_intern = g; + helper_t *helper; + gint z; + + g_return_if_fail (surface != NULL); + g_return_if_fail (f != NULL); + g_return_if_fail (g.nx > 1); + g_return_if_fail (g.ny > 1); + g_return_if_fail (g.nz > 1); + + /* create the helper slices */ + slice1 = new_slice (g.nx + 2, g.ny + 2); + slice2 = new_slice (g.nx + 2, g.ny + 2); + + /* initialize the first slice as OUTSIDE */ + slice_init (slice1, -1.0); + + /* create a slice of the original image size */ + transfer_slice = new_slice (g.nx, g.ny); + + /* adapt the parameters to our enlarged image */ + g_intern.x -= g.dx; + g_intern.y -= g.dy; + g_intern.z -= g.dz; + g_intern.nx = g.nx + 2; + g_intern.ny = g.ny + 2; + g_intern.nz = g.nz; + + /* create the helper for vertex-lookup */ + helper = init_helper (g_intern.nx, g_intern.ny); + + /* go slicewise through the data */ + z = 0; + while (z < g.nz) { + slice_t * hs; + + /* request slice */ + f (transfer_slice->data, g, z, data); + g.z += g.dz; + + /* copy slice in enlarged image and mark the border as OUTSIDE */ + copy_to_bounded (slice2, transfer_slice, iso, -1.); + + /* triangulate */ + iso_slice_evaluate (slice1, slice2, g_intern, z, surface, helper); + + /* switch the input slices */ + hs = slice1; slice1 = slice2; slice2 = hs; + + /* switch the vertex lookup tables */ + helper_advance(helper); + ++z; + } + + /* initialize the last slice as OUTSIDE */ + slice_init (slice2, - 1.0); + + /* close the object */ + iso_slice_evaluate(slice1, slice2, g_intern, z, surface, helper); + + free_helper (helper); + free_slice (slice1); + free_slice (slice2); + free_slice (transfer_slice); +} + +/** + * gts_isosurface_tetra: + * @surface: a #GtsSurface. + * @g: a #GtsCartesianGrid. + * @f: a #GtsIsoCartesianFunc. + * @data: user data to be passed to @f. + * @iso: isosurface value. + * + * Adds to @surface new faces defining the isosurface f(x,y,z) = + * @iso. By convention, the normals to the surface are pointing toward + * the positive values of f(x,y,z) - @iso. + * + * The user function @f is called successively for each value of the z + * coordinate defined by @g. It must fill the corresponding (x,y) + * plane with the values of the function for which the isosurface is + * to be computed. + */ +void gts_isosurface_tetra (GtsSurface * surface, + GtsCartesianGrid g, + GtsIsoCartesianFunc f, + gpointer data, + gdouble iso) +{ + slice_t *slice1, *slice2; + helper_t *helper; + gint z; + GtsCartesianGrid g_internal; + + g_return_if_fail (surface != NULL); + g_return_if_fail (f != NULL); + g_return_if_fail (g.nx > 1); + g_return_if_fail (g.ny > 1); + g_return_if_fail (g.nz > 1); + + memcpy (&g_internal, &g, sizeof (GtsCartesianGrid)); + + /* create the helper slices */ + slice1 = new_slice (g.nx, g.ny); + slice2 = new_slice (g.nx, g.ny); + + /* create the helper for vertex-lookup */ + helper = init_helper (g.nx, g.ny); + + z = 0; + f (slice1->data, g, z, data); + iso_sub (slice1, iso); + + z++; + g.z += g.dz; + + /* go slicewise through the data */ + while (z < g.nz) { + slice_t * hs; + + /* request slice */ + f (slice2->data, g, z, data); + iso_sub (slice2, iso); + + g.z += g.dz; + + /* triangulate */ + iso_slice_evaluate (slice1, slice2, g_internal, z-1, surface, helper); + + /* switch the input slices */ + hs = slice1; slice1 = slice2; slice2 = hs; + + /* switch the vertex lookup tables */ + helper_advance (helper); + + ++z; + } + + free_helper(helper); + free_slice(slice1); + free_slice(slice2); +} + +/** + * gts_isosurface_tetra_bcl: + * @surface: a #GtsSurface. + * @g: a #GtsCartesianGrid. + * @f: a #GtsIsoCartesianFunc. + * @data: user data to be passed to @f. + * @iso: isosurface value. + * + * Adds to @surface new faces defining the isosurface f(x,y,z) = + * @iso. By convention, the normals to the surface are pointing toward + * the positive values of f(x,y,z) - @iso. + * + * The user function @f is called successively for each value of the z + * coordinate defined by @g. It must fill the corresponding (x,y) + * plane with the values of the function for which the isosurface is + * to be computed. + * + * This version produces the dual "body-centered" faces relative to + * the faces produced by gts_isosurface_tetra(). + */ +void gts_isosurface_tetra_bcl (GtsSurface * surface, + GtsCartesianGrid g, + GtsIsoCartesianFunc f, + gpointer data, + gdouble iso) +{ + slice_t *slice1, *slice2, *slice3; + helper_bcl *helper; + gint z; + GtsCartesianGrid g_internal; + + g_return_if_fail (surface != NULL); + g_return_if_fail (f != NULL); + g_return_if_fail (g.nx > 1); + g_return_if_fail (g.ny > 1); + g_return_if_fail (g.nz > 1); + + memcpy (&g_internal, &g, sizeof (GtsCartesianGrid)); + + /* create the helper slices */ + slice1 = new_slice (g.nx, g.ny); + slice2 = new_slice (g.nx, g.ny); + slice3 = new_slice (g.nx, g.ny); + + /* create the helper for vertex-lookup */ + helper = init_helper_bcl (); + + z = 0; + f (slice1->data, g, z, data); + iso_sub (slice1, iso); + + z++; + g.z += g.dz; + + f (slice2->data, g, z, data); + iso_sub (slice1, iso); + + z++; + g.z += g.dz; + + /* go slicewise through the data */ + while (z < g.nz) { + slice_t * hs; + + /* request slice */ + f (slice3->data, g, z, data); + iso_sub (slice3, iso); + + g.z += g.dz; + + /* triangulate */ + iso_slice_evaluate_bcl (slice1, slice2, slice3, g_internal, z-2, + surface, helper); + + /* switch the input slices */ + hs = slice1; slice1 = slice2; slice2 = slice3; slice3 = hs; + + /* switch the vertex lookup tables */ + helper_advance_bcl (helper); + + ++z; + } + + free_helper_bcl(helper); + free_slice(slice1); + free_slice(slice2); + free_slice(slice3); +} Index: toporouter/src_3rd/gts/kdtree.c =================================================================== --- toporouter/src_3rd/gts/kdtree.c (nonexistent) +++ toporouter/src_3rd/gts/kdtree.c (revision 6803) @@ -0,0 +1,152 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "gts.h" + + +static int compare_x (const void * p1, const void * p2) { + GtsPoint + * pp1 = *((gpointer *) p1), + * pp2 = *((gpointer *) p2); + if (pp1->x > pp2->x) + return 1; + return -1; +} + +static int compare_y (const void * p1, const void * p2) { + GtsPoint + * pp1 = *((gpointer *) p1), + * pp2 = *((gpointer *) p2); + if (pp1->y > pp2->y) + return 1; + return -1; +} + +static int compare_z (const void * p1, const void * p2) { + GtsPoint + * pp1 = *((gpointer *) p1), + * pp2 = *((gpointer *) p2); + if (pp1->z > pp2->z) + return 1; + return -1; +} + +/** + * gts_kdtree_new: + * @points: an array of #GtsPoint. + * @compare: always %NULL. + * + * Note that the order of the points in array @points is modified by this + * function. + * + * Returns: a new 3D tree for @points. + */ +GNode * gts_kdtree_new (GPtrArray * points, + int (*compare) (const void *, const void *)) +{ + guint middle; + GPtrArray array; + GNode * node; + GtsPoint * point; + + g_return_val_if_fail (points != NULL, NULL); + g_return_val_if_fail (points->len > 0, NULL); + + /* sort the points */ + if (compare == compare_x) compare = compare_y; + else if (compare == compare_y) compare = compare_z; + else compare = compare_x; + qsort (points->pdata, points->len, sizeof (gpointer), compare); + + middle = (points->len - 1)/2; + point = points->pdata[middle]; + node = g_node_new (point); + + if (points->len > 1) { + array.len = middle; + if (array.len > 0) { + array.pdata = points->pdata; + g_node_prepend (node, gts_kdtree_new (&array, compare)); + } + else + g_node_prepend (node, g_node_new (NULL)); + + array.len = points->len - middle - 1; + if (array.len > 0) { + array.pdata = &(points->pdata[middle + 1]); + g_node_prepend (node, gts_kdtree_new (&array, compare)); + } + else + g_node_prepend (node, g_node_new (NULL)); + } + + return node; +} + +/** + * gts_kdtree_range: + * @tree: a 3D tree. + * @bbox: a #GtsBBox. + * @compare: always %NULL. + * + * Returns: a list of #GtsPoint belonging to @tree which are inside @bbox. + */ +GSList * gts_kdtree_range (GNode * tree_3d, + GtsBBox * bbox, + int (*compare) (const void *, const void *)) +{ + GSList * list = NULL; + GtsPoint * p; + gdouble left, right, v; + GNode * node; + + g_return_val_if_fail (tree_3d != NULL, NULL); + g_return_val_if_fail (bbox != NULL, NULL); + + p = tree_3d->data; + if (p == NULL) + return NULL; + + if (gts_bbox_point_is_inside (bbox, p)) + list = g_slist_prepend (list, p); + + if (compare == compare_x) { + left = bbox->y1; right = bbox->y2; v = p->y; + compare = compare_y; + } + else if (compare == compare_y) { + left = bbox->z1; right = bbox->z2; v = p->z; + compare = compare_z; + } + else { + left = bbox->x1; right = bbox->x2; v = p->x; + compare = compare_x; + } + + if ((node = tree_3d->children)) { + if (right >= v) + list = g_slist_concat (list, gts_kdtree_range (node, bbox, compare)); + node = node->next; + if (left <= v) + list = g_slist_concat (list, gts_kdtree_range (node, bbox, compare)); + } + return list; +} + Index: toporouter/src_3rd/gts/matrix.c =================================================================== --- toporouter/src_3rd/gts/matrix.c (nonexistent) +++ toporouter/src_3rd/gts/matrix.c (revision 6803) @@ -0,0 +1,725 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "gts.h" + +/** + * gts_matrix_new: + * @a00: element [0][0]. + * @a01: element [0][1]. + * @a02: element [0][2]. + * @a03: element [0][3]. + * @a10: element [1][0]. + * @a11: element [1][1]. + * @a12: element [1][2]. + * @a13: element [1][3]. + * @a20: element [2][0]. + * @a21: element [2][1]. + * @a22: element [2][2]. + * @a23: element [2][3]. + * @a30: element [3][0]. + * @a31: element [3][1]. + * @a32: element [3][2]. + * @a33: element [3][3]. + * + * Allocates memory and initializes a new #GtsMatrix. + * + * Returns: a pointer to the newly created #GtsMatrix. + */ +GtsMatrix * gts_matrix_new (gdouble a00, gdouble a01, gdouble a02, gdouble a03, + gdouble a10, gdouble a11, gdouble a12, gdouble a13, + gdouble a20, gdouble a21, gdouble a22, gdouble a23, + gdouble a30, gdouble a31, gdouble a32, gdouble a33) +{ + GtsMatrix * m; + + m = g_malloc (4*sizeof (GtsVector4)); + + m[0][0] = a00; m[1][0] = a10; m[2][0] = a20; m[3][0] = a30; + m[0][1] = a01; m[1][1] = a11; m[2][1] = a21; m[3][1] = a31; + m[0][2] = a02; m[1][2] = a12; m[2][2] = a22; m[3][2] = a32; + m[0][3] = a03; m[1][3] = a13; m[2][3] = a23; m[3][3] = a33; + + return m; +} + +/** + * gts_matrix_assign: + * @m: a #GtsMatrix. + * @a00: element [0][0]. + * @a01: element [0][1]. + * @a02: element [0][2]. + * @a03: element [0][3]. + * @a10: element [1][0]. + * @a11: element [1][1]. + * @a12: element [1][2]. + * @a13: element [1][3]. + * @a20: element [2][0]. + * @a21: element [2][1]. + * @a22: element [2][2]. + * @a23: element [2][3]. + * @a30: element [3][0]. + * @a31: element [3][1]. + * @a32: element [3][2]. + * @a33: element [3][3]. + * + * Set values of matrix elements. + */ +void gts_matrix_assign (GtsMatrix * m, + gdouble a00, gdouble a01, gdouble a02, gdouble a03, + gdouble a10, gdouble a11, gdouble a12, gdouble a13, + gdouble a20, gdouble a21, gdouble a22, gdouble a23, + gdouble a30, gdouble a31, gdouble a32, gdouble a33) +{ + g_return_if_fail (m != NULL); + + m[0][0] = a00; m[1][0] = a10; m[2][0] = a20; m[3][0] = a30; + m[0][1] = a01; m[1][1] = a11; m[2][1] = a21; m[3][1] = a31; + m[0][2] = a02; m[1][2] = a12; m[2][2] = a22; m[3][2] = a32; + m[0][3] = a03; m[1][3] = a13; m[2][3] = a23; m[3][3] = a33; +} + +/** + * gts_matrix_projection: + * @t: a #GtsTriangle. + * + * Creates a new #GtsMatrix representing the projection onto a plane of normal + * given by @t. + * + * Returns: a pointer to the newly created #GtsMatrix. + */ +GtsMatrix * gts_matrix_projection (GtsTriangle * t) +{ + GtsVertex * v1, * v2, * v3; + GtsEdge * e1, * e2, * e3; + GtsMatrix * m; + gdouble x1, y1, z1, x2, y2, z2, x3, y3, z3, l; + + g_return_val_if_fail (t != NULL, NULL); + + m = g_malloc (4*sizeof (GtsVector4)); + gts_triangle_vertices_edges (t, NULL, &v1, &v2, &v3, &e1, &e2, &e3); + + x1 = GTS_POINT (v2)->x - GTS_POINT (v1)->x; + y1 = GTS_POINT (v2)->y - GTS_POINT (v1)->y; + z1 = GTS_POINT (v2)->z - GTS_POINT (v1)->z; + x2 = GTS_POINT (v3)->x - GTS_POINT (v1)->x; + y2 = GTS_POINT (v3)->y - GTS_POINT (v1)->y; + z2 = GTS_POINT (v3)->z - GTS_POINT (v1)->z; + x3 = y1*z2 - z1*y2; y3 = z1*x2 - x1*z2; z3 = x1*y2 - y1*x2; + x2 = y3*z1 - z3*y1; y2 = z3*x1 - x3*z1; z2 = x3*y1 - y3*x1; + + g_assert ((l = sqrt (x1*x1 + y1*y1 + z1*z1)) > 0.0); + m[0][0] = x1/l; m[1][0] = y1/l; m[2][0] = z1/l; m[3][0] = 0.; + g_assert ((l = sqrt (x2*x2 + y2*y2 + z2*z2)) > 0.0); + m[0][1] = x2/l; m[1][1] = y2/l; m[2][1] = z2/l; m[3][1] = 0.; + g_assert ((l = sqrt (x3*x3 + y3*y3 + z3*z3)) > 0.0); + m[0][2] = x3/l; m[1][2] = y3/l; m[2][2] = z3/l; m[3][2] = 0.; + m[0][3] = 0; m[1][3] = 0.; m[2][3] = 0.; m[3][3] = 1.; + + return m; +} + +/** + * gts_matrix_transpose: + * @m: a #GtsMatrix. + * + * Returns: a pointer to a newly created #GtsMatrix transposed of @m. + */ +GtsMatrix * gts_matrix_transpose (GtsMatrix * m) +{ + GtsMatrix * mi; + + g_return_val_if_fail (m != NULL, NULL); + + mi = g_malloc (4*sizeof (GtsVector4)); + + mi[0][0] = m[0][0]; mi[1][0] = m[0][1]; + mi[2][0] = m[0][2]; mi[3][0] = m[0][3]; + mi[0][1] = m[1][0]; mi[1][1] = m[1][1]; + mi[2][1] = m[1][2]; mi[3][1] = m[1][3]; + mi[0][2] = m[2][0]; mi[1][2] = m[2][1]; + mi[2][2] = m[2][2]; mi[3][2] = m[2][3]; + mi[0][3] = m[3][0]; mi[1][3] = m[3][1]; + mi[2][3] = m[3][2]; mi[3][3] = m[3][3]; + + return mi; +} + +/* + * calculate the determinant of a 2x2 matrix. + * + * Adapted from: + * Matrix Inversion + * by Richard Carling + * from "Graphics Gems", Academic Press, 1990 + */ +static gdouble det2x2 (gdouble a, gdouble b, gdouble c, gdouble d) +{ + gdouble ans2; + + ans2 = a*d - b*c; + return ans2; +} + +/* + * calculate the determinant of a 3x3 matrix + * in the form + * + * | a1, b1, c1 | + * | a2, b2, c2 | + * | a3, b3, c3 | + * + * Adapted from: + * Matrix Inversion + * by Richard Carling + * from "Graphics Gems", Academic Press, 1990 + */ +static gdouble det3x3 (gdouble a1, gdouble a2, gdouble a3, + gdouble b1, gdouble b2, gdouble b3, + gdouble c1, gdouble c2, gdouble c3) +{ + gdouble ans3; + + ans3 = a1 * det2x2( b2, b3, c2, c3 ) + - b1 * det2x2( a2, a3, c2, c3 ) + + c1 * det2x2( a2, a3, b2, b3 ); + return ans3; +} + +/** + * gts_matrix_determinant: + * @m: a #GtsMatrix. + * + * Returns: the value of det(@m). + */ +gdouble gts_matrix_determinant (GtsMatrix * m) +{ + gdouble ans4; + gdouble a1, a2, a3, a4, b1, b2, b3, b4, c1, c2, c3, c4, d1, d2, d3, d4; + + g_return_val_if_fail (m != NULL, 0.0); + + a1 = m[0][0]; b1 = m[0][1]; + c1 = m[0][2]; d1 = m[0][3]; + + a2 = m[1][0]; b2 = m[1][1]; + c2 = m[1][2]; d2 = m[1][3]; + + a3 = m[2][0]; b3 = m[2][1]; + c3 = m[2][2]; d3 = m[2][3]; + + a4 = m[3][0]; b4 = m[3][1]; + c4 = m[3][2]; d4 = m[3][3]; + + ans4 = a1 * det3x3 (b2, b3, b4, c2, c3, c4, d2, d3, d4) + - b1 * det3x3 (a2, a3, a4, c2, c3, c4, d2, d3, d4) + + c1 * det3x3 (a2, a3, a4, b2, b3, b4, d2, d3, d4) + - d1 * det3x3 (a2, a3, a4, b2, b3, b4, c2, c3, c4); + + return ans4; +} + +/* + * adjoint( original_matrix, inverse_matrix ) + * + * calculate the adjoint of a 4x4 matrix + * + * Let a denote the minor determinant of matrix A obtained by + * ij + * + * deleting the ith row and jth column from A. + * + * i+j + * Let b = (-1) a + * ij ji + * + * The matrix B = (b ) is the adjoint of A + * ij + */ +static GtsMatrix * adjoint (GtsMatrix * m) +{ + gdouble a1, a2, a3, a4, b1, b2, b3, b4; + gdouble c1, c2, c3, c4, d1, d2, d3, d4; + GtsMatrix * ma; + + a1 = m[0][0]; b1 = m[0][1]; + c1 = m[0][2]; d1 = m[0][3]; + + a2 = m[1][0]; b2 = m[1][1]; + c2 = m[1][2]; d2 = m[1][3]; + + a3 = m[2][0]; b3 = m[2][1]; + c3 = m[2][2]; d3 = m[2][3]; + + a4 = m[3][0]; b4 = m[3][1]; + c4 = m[3][2]; d4 = m[3][3]; + + ma = g_malloc (4*sizeof (GtsVector4)); + + /* row column labeling reversed since we transpose rows & columns */ + + ma[0][0] = det3x3 (b2, b3, b4, c2, c3, c4, d2, d3, d4); + ma[1][0] = - det3x3 (a2, a3, a4, c2, c3, c4, d2, d3, d4); + ma[2][0] = det3x3 (a2, a3, a4, b2, b3, b4, d2, d3, d4); + ma[3][0] = - det3x3 (a2, a3, a4, b2, b3, b4, c2, c3, c4); + + ma[0][1] = - det3x3 (b1, b3, b4, c1, c3, c4, d1, d3, d4); + ma[1][1] = det3x3 (a1, a3, a4, c1, c3, c4, d1, d3, d4); + ma[2][1] = - det3x3 (a1, a3, a4, b1, b3, b4, d1, d3, d4); + ma[3][1] = det3x3 (a1, a3, a4, b1, b3, b4, c1, c3, c4); + + ma[0][2] = det3x3 (b1, b2, b4, c1, c2, c4, d1, d2, d4); + ma[1][2] = - det3x3 (a1, a2, a4, c1, c2, c4, d1, d2, d4); + ma[2][2] = det3x3 (a1, a2, a4, b1, b2, b4, d1, d2, d4); + ma[3][2] = - det3x3 (a1, a2, a4, b1, b2, b4, c1, c2, c4); + + ma[0][3] = - det3x3 (b1, b2, b3, c1, c2, c3, d1, d2, d3); + ma[1][3] = det3x3 (a1, a2, a3, c1, c2, c3, d1, d2, d3); + ma[2][3] = - det3x3 (a1, a2, a3, b1, b2, b3, d1, d2, d3); + ma[3][3] = det3x3 (a1, a2, a3, b1, b2, b3, c1, c2, c3); + + return ma; +} + + +/** + * gts_matrix_inverse: + * @m: a #GtsMatrix. + * + * Returns: a pointer to a newly created #GtsMatrix inverse of @m or %NULL + * if @m is not invertible. + */ +GtsMatrix * gts_matrix_inverse (GtsMatrix * m) +{ + GtsMatrix * madj; + gdouble det; + gint i, j; + + g_return_val_if_fail (m != NULL, NULL); + + det = gts_matrix_determinant (m); + if (det == 0.) + return NULL; + + madj = adjoint (m); + for (i = 0; i < 4; i++) + for(j = 0; j < 4; j++) + madj[i][j] /= det; + + return madj; +} + +/** + * gts_matrix3_inverse: + * @m: a 3x3 #GtsMatrix. + * + * Returns: a pointer to a newly created 3x3 #GtsMatrix inverse of @m or %NULL + * if @m is not invertible. + */ +GtsMatrix * gts_matrix3_inverse (GtsMatrix * m) +{ + GtsMatrix * mi; + gdouble det; + + g_return_val_if_fail (m != NULL, NULL); + + det = (m[0][0]*(m[1][1]*m[2][2] - m[2][1]*m[1][2]) - + m[0][1]*(m[1][0]*m[2][2] - m[2][0]*m[1][2]) + + m[0][2]*(m[1][0]*m[2][1] - m[2][0]*m[1][1])); + if (det == 0.0) + return NULL; + + mi = g_malloc0 (4*sizeof (GtsVector)); + + mi[0][0] = (m[1][1]*m[2][2] - m[1][2]*m[2][1])/det; + mi[0][1] = (m[2][1]*m[0][2] - m[0][1]*m[2][2])/det; + mi[0][2] = (m[0][1]*m[1][2] - m[1][1]*m[0][2])/det; + mi[1][0] = (m[1][2]*m[2][0] - m[1][0]*m[2][2])/det; + mi[1][1] = (m[0][0]*m[2][2] - m[2][0]*m[0][2])/det; + mi[1][2] = (m[1][0]*m[0][2] - m[0][0]*m[1][2])/det; + mi[2][0] = (m[1][0]*m[2][1] - m[2][0]*m[1][1])/det; + mi[2][1] = (m[2][0]*m[0][1] - m[0][0]*m[2][1])/det; + mi[2][2] = (m[0][0]*m[1][1] - m[0][1]*m[1][0])/det; + + return mi; +} + +/** + * gts_matrix_print: + * @m: a #GtsMatrix. + * @fptr: a file descriptor. + * + * Print @m to file @fptr. + */ +void gts_matrix_print (GtsMatrix * m, FILE * fptr) +{ + g_return_if_fail (m != NULL); + g_return_if_fail (fptr != NULL); + + fprintf (fptr, + "[[%15.7g %15.7g %15.7g %15.7g]\n" + " [%15.7g %15.7g %15.7g %15.7g]\n" + " [%15.7g %15.7g %15.7g %15.7g]\n" + " [%15.7g %15.7g %15.7g %15.7g]]\n", + m[0][0], m[0][1], m[0][2], m[0][3], + m[1][0], m[1][1], m[1][2], m[1][3], + m[2][0], m[2][1], m[2][2], m[2][3], + m[3][0], m[3][1], m[3][2], m[3][3]); +} + +/** + * gts_vector_print: + * @v: a #GtsVector. + * @fptr: a file descriptor. + * + * Print @s to file @fptr. + */ +void gts_vector_print (GtsVector v, FILE * fptr) +{ + g_return_if_fail (fptr != NULL); + + fprintf (fptr, + "[%15.7g %15.7g %15.7g ]\n", + v[0], v[1], v[2]); +} + +/** + * gts_vector4_print: + * @v: a #GtsVector4. + * @fptr: a file descriptor. + * + * Print @v to file @fptr. + */ +void gts_vector4_print (GtsVector4 v, FILE * fptr) +{ + g_return_if_fail (fptr != NULL); + + fprintf (fptr, + "[%15.7g %15.7g %15.7g %15.7g]\n", + v[0], v[1], v[2], v[3]); +} + +/* [cos(alpha)]^2 */ +#define COSALPHA2 0.999695413509 /* alpha = 1 degree */ +/* [sin(alpha)]^2 */ +#define SINALPHA2 3.04586490453e-4 /* alpha = 1 degree */ + +/** + * gts_matrix_compatible_row: + * @A: a #GtsMatrix. + * @b: a #GtsVector. + * @n: the number of previous constraints of @A.x=@b. + * @A1: a #GtsMatrix. + * @b1: a #GtsVector. + * + * Given a system of @n constraints @A.x=@b adds to it the compatible + * constraints defined by @A1.x=@b1. The compatibility is determined + * by insuring that the resulting system is well-conditioned (see + * Lindstrom and Turk (1998, 1999)). + * + * Returns: the number of constraints of the resulting system. + */ +guint gts_matrix_compatible_row (GtsMatrix * A, + GtsVector b, + guint n, + GtsVector A1, + gdouble b1) +{ + gdouble na1; + + g_return_val_if_fail (A != NULL, 0); + + na1 = gts_vector_scalar (A1, A1); + if (na1 == 0.0) + return n; + + /* normalize row */ + na1 = sqrt (na1); + A1[0] /= na1; A1[1] /= na1; A1[2] /= na1; b1 /= na1; + + if (n == 1) { + gdouble a0a1 = gts_vector_scalar (A[0], A1); + if (a0a1*a0a1 >= COSALPHA2) + return 1; + } + else if (n == 2) { + GtsVector V; + gdouble s; + + gts_vector_cross (V, A[0], A[1]); + s = gts_vector_scalar (V, A1); + if (s*s <= gts_vector_scalar (V, V)*SINALPHA2) + return 2; + } + + A[n][0] = A1[0]; A[n][1] = A1[1]; A[n][2] = A1[2]; b[n] = b1; + return n + 1; +} + +/** + * gts_matrix_quadratic_optimization: + * @A: a #GtsMatrix. + * @b: a #GtsVector. + * @n: the number of constraints (must be smaller than 3). + * @H: a symmetric positive definite Hessian. + * @c: a #GtsVector. + * + * Solve a quadratic optimization problem: Given a quadratic objective function + * f which can be written as: f(x) = x^t.@H.x + @c^t.x + k, where @H is the + * symmetric positive definite Hessian of f and k is a constant, find the + * minimum of f subject to the set of @n prior linear constraints, defined by + * the first @n rows of @A and @b (@A.x = @b). The new constraints given by + * the minimization are added to @A and @b only if they are linearly + * independent as determined by gts_matrix_compatible_row(). + * + * Returns: the new number of constraints defined by @A and @b. + */ +guint gts_matrix_quadratic_optimization (GtsMatrix * A, + GtsVector b, + guint n, + GtsMatrix * H, + GtsVector c) +{ + g_return_val_if_fail (A != NULL, 0); + g_return_val_if_fail (b != NULL, 0); + g_return_val_if_fail (n < 3, 0); + g_return_val_if_fail (H != NULL, 0); + + switch (n) { + case 0: { + n = gts_matrix_compatible_row (A, b, n, H[0], - c[0]); + n = gts_matrix_compatible_row (A, b, n, H[1], - c[1]); + n = gts_matrix_compatible_row (A, b, n, H[2], - c[2]); + return n; + } + case 1: { + GtsVector Q0 = {0., 0., 0.}; + GtsVector Q1 = {0., 0., 0.}; + GtsVector A1; + gdouble max = A[0][0]*A[0][0]; + guint d = 0; + + /* build a vector orthogonal to the constraint */ + if (A[0][1]*A[0][1] > max) { max = A[0][1]*A[0][1]; d = 1; } + if (A[0][2]*A[0][2] > max) { max = A[0][2]*A[0][2]; d = 2; } + switch (d) { + case 0: Q0[0] = - A[0][2]/A[0][0]; Q0[2] = 1.0; break; + case 1: Q0[1] = - A[0][2]/A[0][1]; Q0[2] = 1.0; break; + case 2: Q0[2] = - A[0][0]/A[0][2]; Q0[0] = 1.0; break; + } + + /* build a second vector orthogonal to the first and to the constraint */ + gts_vector_cross (Q1, A[0], Q0); + + A1[0] = gts_vector_scalar (Q0, H[0]); + A1[1] = gts_vector_scalar (Q0, H[1]); + A1[2] = gts_vector_scalar (Q0, H[2]); + + n = gts_matrix_compatible_row (A, b, n, A1, - gts_vector_scalar (Q0, c)); + + A1[0] = gts_vector_scalar (Q1, H[0]); + A1[1] = gts_vector_scalar (Q1, H[1]); + A1[2] = gts_vector_scalar (Q1, H[2]); + + n = gts_matrix_compatible_row (A, b, n, A1, - gts_vector_scalar (Q1, c)); + + return n; + } + case 2: { + /* build a vector orthogonal to the two constraints */ + GtsVector A1, Q; + + gts_vector_cross (Q, A[0], A[1]); + A1[0] = gts_vector_scalar (Q, H[0]); + A1[1] = gts_vector_scalar (Q, H[1]); + A1[2] = gts_vector_scalar (Q, H[2]); + + n = gts_matrix_compatible_row (A, b, n, A1, - gts_vector_scalar (Q, c)); + + return n; + } + default: + g_assert_not_reached (); + } + return 0; +} + +/** + * gts_matrix_destroy: + * @m: a #GtsMatrix. + * + * Free all the memory allocated for @m. + */ +void gts_matrix_destroy (GtsMatrix * m) +{ + g_free (m); +} + +/** + * gts_matrix_product: + * @m1: a #GtsMatrix. + * @m2: another #GtsMatrix. + * + * Returns: a new #GtsMatrix, product of @m1 and @m2. + */ +GtsMatrix * gts_matrix_product (GtsMatrix * m1, GtsMatrix * m2) +{ + guint i, j; + GtsMatrix * m; + + g_return_val_if_fail (m1 != NULL, NULL); + g_return_val_if_fail (m2 != NULL, NULL); + g_return_val_if_fail (m1 != m2, NULL); + + m = g_malloc (4*sizeof (GtsVector4)); + + for (i = 0; i < 4; i++) + for (j = 0; j < 4; j++) + m[i][j] = m1[i][0]*m2[0][j] + m1[i][1]*m2[1][j] + + m1[i][2]*m2[2][j] + m1[i][3]*m2[3][j]; + return m; +} + +/** + * gts_matrix_zero: + * @m: a #GtsMatrix or $NULL. + * + * Initializes @m to zeros. Allocates a matrix if @m is %NULL. + * + * Returns: the zero'ed matrix. + */ +GtsMatrix * gts_matrix_zero (GtsMatrix * m) +{ + if (m == NULL) + m = g_malloc0 (4*sizeof (GtsVector4)); + else { + m[0][0] = m[1][0] = m[2][0] = m[3][0] = 0.; + m[0][1] = m[1][1] = m[2][1] = m[3][1] = 0.; + m[0][2] = m[1][2] = m[2][2] = m[3][2] = 0.; + m[0][3] = m[1][3] = m[2][3] = m[3][3] = 0.; + } + return m; +} + +/** + * gts_matrix_identity: + * @m: a #GtsMatrix or %NULL. + * + * Initializes @m to an identity matrix. Allocates a matrix if @m is %NULL. + * + * Returns: the identity matrix. + */ +GtsMatrix * gts_matrix_identity (GtsMatrix * m) +{ + m = gts_matrix_zero (m); + m[0][0] = m[1][1] = m[2][2] = m[3][3] = 1.; + return m; +} + +/** + * gts_matrix_scale: + * @m: a #GtsMatrix or %NULL. + * @s: the scaling vector. + * + * Initializes @m to a scaling matrix for @s. Allocates a matrix if @m + * is %NULL. + * + * Returns: the scaling matrix. + */ +GtsMatrix * gts_matrix_scale (GtsMatrix * m, GtsVector s) +{ + m = gts_matrix_zero (m); + m[0][0] = s[0]; + m[1][1] = s[1]; + m[2][2] = s[2]; + m[3][3] = 1.; + return m; +} + +/** + * gts_matrix_translate: + * @m: a #GtsMatrix or %NULL. + * @t: the translation vector. + * + * Initializes @m to a translation matrix for @t. Allocates a new + * matrix if @m is %NULL. + * + * Returns: the translation matix. + */ +GtsMatrix * gts_matrix_translate (GtsMatrix * m, GtsVector t) +{ + m = gts_matrix_zero (m); + m[0][3] = t[0]; + m[1][3] = t[1]; + m[2][3] = t[2]; + m[3][3] = 1.; + m[0][0] = m[1][1] = m[2][2] = 1.; + return m; +} + +/** + * gts_matrix_rotate: + * @m: a #GtsMatrix or %NULL. + * @r: the rotation axis. + * @angle: the angle (in radians) to rotate by. + * + * Initializes @m to a rotation matrix around @r by @angle. + * Allocates a new matrix if @m is %NULL. + * + * Returns: the rotation matrix. + */ +GtsMatrix * gts_matrix_rotate (GtsMatrix * m, + GtsVector r, + gdouble angle) +{ + gdouble c, c1, s; + + gts_vector_normalize (r); + + c = cos (angle); + c1 = 1. - c; + s = sin (angle); + + if (m == NULL) + m = g_malloc (4*sizeof (GtsVector4)); + + m[0][0] = r[0]*r[0]*c1 + c; + m[0][1] = r[0]*r[1]*c1 - r[2]*s; + m[0][2] = r[0]*r[2]*c1 + r[1]*s; + m[0][3] = 0.; + + m[1][0] = r[1]*r[0]*c1 + r[2]*s; + m[1][1] = r[1]*r[1]*c1 + c; + m[1][2] = r[1]*r[2]*c1 - r[0]*s; + m[1][3] = 0.; + + m[2][0] = r[2]*r[0]*c1 - r[1]*s; + m[2][1] = r[2]*r[1]*c1 + r[0]*s; + m[2][2] = r[2]*r[2]*c1 + c; + m[2][3] = 0.; + + m[3][0] = 0.; + m[3][1] = 0.; + m[3][2] = 0.; + m[3][3] = 1.; + + return m; +} Index: toporouter/src_3rd/gts/misc.c =================================================================== --- toporouter/src_3rd/gts/misc.c (nonexistent) +++ toporouter/src_3rd/gts/misc.c (revision 6803) @@ -0,0 +1,692 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include + +#include "gts.h" +#include "gts-private.h" +#include "config.h" + +const guint gts_major_version = GTS_MAJOR_VERSION; +const guint gts_minor_version = GTS_MINOR_VERSION; +const guint gts_micro_version = GTS_MICRO_VERSION; +const guint gts_interface_age = 1; +const guint gts_binary_age = 1; + +static gboolean char_in_string (char c, const char * s) +{ + while (*s != '\0') + if (*(s++) == c) + return TRUE; + return FALSE; +} + +static GtsFile * file_new (void) +{ + GtsFile * f; + + f = g_malloc (sizeof (GtsFile)); + f->fp = NULL; + f->s = f->s1 = NULL; + f->curline = 1; + f->curpos = 1; + f->token = g_string_new (""); + f->type = '\0'; + f->error = NULL; + f->next_token = '\0'; + + f->scope = f->scope_max = 0; + f->delimiters = g_strdup (" \t"); + f->comments = g_strdup (GTS_COMMENTS); + f->tokens = g_strdup ("\n{}()="); + + return f; +} + +/** + * gts_file_new: + * @fp: a file pointer. + * + * Returns: a new #GtsFile. + */ +GtsFile * gts_file_new (FILE * fp) +{ + GtsFile * f; + + g_return_val_if_fail (fp != NULL, NULL); + + f = file_new (); + f->fp = fp; + gts_file_next_token (f); + + return f; +} + +/** + * gts_file_new_from_string: + * @s: a string. + * + * Returns: a new #GtsFile. + */ +GtsFile * gts_file_new_from_string (const gchar * s) +{ + GtsFile * f; + + g_return_val_if_fail (s != NULL, NULL); + + f = file_new (); + f->s1 = f->s = g_strdup (s); + gts_file_next_token (f); + + return f; +} + +/** + * gts_file_destroy: + * @f: a #GtsFile. + * + * Frees all the memory allocated for @f. + */ +void gts_file_destroy (GtsFile * f) +{ + g_return_if_fail (f != NULL); + + g_free (f->delimiters); + g_free (f->comments); + g_free (f->tokens); + if (f->error) + g_free (f->error); + if (f->s1) + g_free (f->s1); + g_string_free (f->token, TRUE); + g_free (f); +} + +/** + * gts_file_verror: + * @f: a @GtsFile. + * @format: the standard sprintf() format string. + * @args: the list of parameters to insert into the format string. + * + * Sets the @error field of @f using g_strdup_vprintf(). + * + * This function can be called only once and disables any other + * operation on @f (gts_file_close() excepted). + */ +void gts_file_verror (GtsFile * f, + const gchar * format, + va_list args) +{ + g_return_if_fail (f != NULL); + g_return_if_fail (format != NULL); + + g_assert (f->type != GTS_ERROR); + f->error = g_strdup_vprintf (format, args); + f->type = GTS_ERROR; +} + +/** + * gts_file_error: + * @f: a @GtsFile. + * @format: the standard sprintf() format string. + * @...: the parameters to insert into the format string. + * + * Sets the @error field of @f using gts_file_verror(). + * + * This function can be called only once and disables any other + * operation on @f (gts_file_close() excepted). + */ +void gts_file_error (GtsFile * f, + const gchar * format, + ...) +{ + va_list args; + + g_return_if_fail (f != NULL); + g_return_if_fail (format != NULL); + + va_start (args, format); + gts_file_verror (f, format, args); + va_end (args); +} + +static gint next_char (GtsFile * f) +{ + if (f->fp) + return fgetc (f->fp); + else if (*f->s == '\0') + return EOF; + return *(f->s++); +} + +/** + * gts_file_getc : + * @f: a #GtsFile. + * + * Returns: the next character in @f or EOF if the end of the file is + * reached or if an error occured. + */ +gint gts_file_getc (GtsFile * f) +{ + gint c; + + g_return_val_if_fail (f != NULL, EOF); + + if (f->type == GTS_ERROR) + return EOF; + + c = next_char (f); + f->curpos++; + while (char_in_string (c, f->comments)) { + while (c != EOF && c != '\n') + c = next_char (f); + if (c == '\n') { + f->curline++; + f->curpos = 1; + c = next_char (f); + } + } + switch (c) { + case '\n': + f->curline++; + f->curpos = 1; + break; + case '{': + f->scope++; + break; + case '}': + if (f->scope == 0) { + f->line = f->curline; + f->pos = f->curpos - 1; + gts_file_error (f, "no matching opening brace"); + c = EOF; + } + else + f->scope--; + } + return c; +} + +/** + * gts_file_read: + * @f: a #GtsFile. + * @ptr: a pointer. + * @size: size of an element. + * @nmemb: number of elements. + * + * Reads @nmemb elements of data, each @size bytes long, from @f, + * storing them at the location given by @ptr. + * + * Returns: the number of elements read. + */ +guint gts_file_read (GtsFile * f, gpointer ptr, guint size, guint nmemb) +{ + guint i, n; + gchar * p; + + g_return_val_if_fail (f != NULL, 0); + g_return_val_if_fail (ptr != NULL, 0); + g_return_val_if_fail (f->fp != NULL, 0); + + if (f->type == GTS_ERROR) + return 0; + + n = fread (ptr, size, nmemb, f->fp); + for (i = 0, p = ptr; i < n*size; i++, p++) { + f->curpos++; + if (*p == '\n') { + f->curline++; + f->curpos = 1; + } + } + return n; +} + +/** + * gts_file_getc_scope : + * @f: a #GtsFile. + * + * Returns: the next character in @f in the scope defined by + * @f->scope_max or EOF if the end of the file is reached or if an + * error occured. + */ +gint gts_file_getc_scope (GtsFile * f) +{ + gint c; + + g_return_val_if_fail (f != NULL, EOF); + + if (f->type == GTS_ERROR) + return EOF; + + if (f->scope <= f->scope_max) + c = gts_file_getc (f); + else { + c = gts_file_getc (f); + while (c != EOF && f->scope > f->scope_max) + c = gts_file_getc (f); + } + return c; +} + +/** + * gts_file_next_token: + * @f: a #GtsFile. + * + * Reads next token from @f and updates its @token and @delim fields. + */ +void gts_file_next_token (GtsFile * f) +{ + gint c; + gboolean in_string = FALSE; + + g_return_if_fail (f != NULL); + + if (f->type == GTS_ERROR) + return; + f->token->str[0] = '\0'; + f->token->len = 0; + if (f->next_token != '\0') { + if (char_in_string (f->next_token, f->tokens)) { + f->line = f->curline; + f->pos = f->curpos - 1; + g_string_append_c (f->token, f->next_token); + f->type = f->next_token; + f->next_token = '\0'; + return; + } + else { + c = f->next_token; + f->next_token = '\0'; + } + } + else + c = gts_file_getc_scope (f); + f->type = GTS_NONE; + while (c != EOF && (!in_string || !char_in_string (c, f->delimiters))) { + if (in_string) { + if (char_in_string (c, f->tokens)) { + f->next_token = c; + break; + } + g_string_append_c (f->token, c); + } + else if (!char_in_string (c, f->delimiters)) { + in_string = TRUE; + f->line = f->curline; + f->pos = f->curpos - 1; + g_string_append_c (f->token, c); + if (char_in_string (c, f->tokens)) { + f->type = c; + break; + } + } + c = gts_file_getc_scope (f); + } + if (f->type == GTS_NONE && f->token->len > 0) { + gchar * a; + + a = f->token->str; + while (*a != '\0' && char_in_string (*a, "+-")) a++; + if (*a == '\0') { + f->type = GTS_STRING; + return; + } + a = f->token->str; + while (*a != '\0' && char_in_string (*a, "+-0123456789")) a++; + if (*a == '\0') { + f->type = GTS_INT; + return; + } + a = f->token->str; + while (*a != '\0' && char_in_string (*a, "+-eE.")) a++; + if (*a == '\0') { + f->type = GTS_STRING; + return; + } + a = f->token->str; + while (*a != '\0' && char_in_string (*a, "+-0123456789eE.")) a++; + if (*a == '\0') { + f->type = GTS_FLOAT; + return; + } + a = f->token->str; + if (!strncmp (a, "0x", 2) || + !strncmp (a, "-0x", 3) || + !strncmp (a, "+0x", 3)) { + while (*a != '\0' && char_in_string (*a, "+-0123456789abcdefx")) a++; + if (*a == '\0') { + f->type = GTS_INT; + return; + } + a = f->token->str; + while (*a != '\0' && char_in_string (*a, "+-0123456789abcdefx.p")) a++; + if (*a == '\0') { + f->type = GTS_FLOAT; + return; + } + } + f->type = GTS_STRING; + } +} + +/** + * gts_file_first_token_after: + * @f: a #GtsFile. + * @type: a #GtsTokenType. + * + * Finds and sets the first token of a type different from @type + * occuring after a token of type @type. + */ +void gts_file_first_token_after (GtsFile * f, GtsTokenType type) +{ + g_return_if_fail (f != NULL); + + while (f->type != GTS_ERROR && + f->type != GTS_NONE && + f->type != type) + gts_file_next_token (f); + while (f->type == type) + gts_file_next_token (f); +} + +/** + * gts_file_assign_start: + * @f: a #GtsFile. + * @vars: a %GTS_NONE terminated array of #GtsFileVariable. + * + * Opens a block delimited by braces to read a list of optional + * arguments specified by @vars. + * + * If an error is encountered the @error field of @f is set. + */ +void gts_file_assign_start (GtsFile * f, GtsFileVariable * vars) +{ + GtsFileVariable * var; + + g_return_if_fail (f != NULL); + g_return_if_fail (vars != NULL); + + var = vars; + while (var->type != GTS_NONE) + (var++)->set = FALSE; + + if (f->type != '{') { + gts_file_error (f, "expecting an opening brace"); + return; + } + + f->scope_max++; + gts_file_next_token (f); +} + +/** + * gts_file_assign_next: + * @f: a #GtsFile. + * @vars: a %GTS_NONE terminated array of #GtsFileVariable. + * + * Assigns the next optional argument of @vars read from @f. + * + * Returns: the variable of @vars which has been assigned or %NULL if + * no variable has been assigned (if an error has been encountered the + * @error field of @f is set). + */ +GtsFileVariable * gts_file_assign_next (GtsFile * f, GtsFileVariable * vars) +{ + GtsFileVariable * var; + gboolean found = FALSE; + + g_return_val_if_fail (f != NULL, NULL); + g_return_val_if_fail (vars != NULL, NULL); + + while (f->type == '\n') + gts_file_next_token (f); + if (f->type == '}') { + f->scope_max--; + gts_file_next_token (f); + return NULL; + } + if (f->type == GTS_ERROR) + return NULL; + + var = vars; + while (f->type != GTS_ERROR && var->type != GTS_NONE && !found) { + if (!strcmp (var->name, f->token->str)) { + found = TRUE; + if (var->unique && var->set) + gts_file_error (f, "variable `%s' was already set at line %d:%d", + var->name, var->line, var->pos); + else { + var->line = f->line; + var->pos = f->pos; + gts_file_next_token (f); + if (f->type != '=') + gts_file_error (f, "expecting `='"); + else { + var->set = TRUE; + switch (var->type) { + case GTS_FILE: + break; + case GTS_INT: + gts_file_next_token (f); + if (f->type != GTS_INT) { + gts_file_error (f, "expecting an integer"); + var->set = FALSE; + } + else if (var->data) + *((gint *) var->data) = atoi (f->token->str); + break; + case GTS_UINT: + gts_file_next_token (f); + if (f->type != GTS_INT) { + gts_file_error (f, "expecting an integer"); + var->set = FALSE; + } + else if (var->data) + *((guint *) var->data) = atoi (f->token->str); + break; + case GTS_FLOAT: + gts_file_next_token (f); + if (f->type != GTS_INT && f->type != GTS_FLOAT) { + gts_file_error (f, "expecting a number"); + var->set = FALSE; + } + else if (var->data) + *((gfloat *) var->data) = atof (f->token->str); + break; + case GTS_DOUBLE: + gts_file_next_token (f); + if (f->type != GTS_INT && f->type != GTS_FLOAT) { + gts_file_error (f, "expecting a number"); + var->set = FALSE; + } + else if (var->data) + *((gdouble *) var->data) = atof (f->token->str); + break; + case GTS_STRING: + gts_file_next_token (f); + if (f->type != GTS_INT && + f->type != GTS_FLOAT && + f->type != GTS_STRING) { + gts_file_error (f, "expecting a string"); + var->set = FALSE; + } + else if (var->data) + *((gchar **) var->data) = g_strdup (f->token->str); + break; + default: + g_assert_not_reached (); + } + } + } + } + else + var++; + } + if (!found) + gts_file_error (f, "unknown identifier `%s'", f->token->str); + else if (f->type != GTS_ERROR) { + g_assert (var->set); + gts_file_next_token (f); + return var; + } + return NULL; +} + +/** + * gts_file_assign_variables: + * @f: a #GtsFile. + * @vars: an array of #GtsFileVariable. + * + * Assigns all the variables belonging to @vars found in @f. + * + * If an error is encountered the @error field of @f is set. + */ +void gts_file_assign_variables (GtsFile * f, GtsFileVariable * vars) +{ + g_return_if_fail (f != NULL); + g_return_if_fail (vars != NULL); + + gts_file_assign_start (f, vars); + while (gts_file_assign_next (f, vars)) + ; +} + +/** + * gts_file_variable_error: + * @f: a #GtsFile. + * @vars: an array of #GtsFileVariable. + * @name: the name of a variable in @vars. + * @format: the standard sprintf() format string. + * @...: the parameters to insert into the format string. + * + * Sets the @error field of @f using gts_file_verror(). + * + * String @name must match one of the variable names in @vars. + * + * If variable @name has been assigned (using gts_file_assign_variables()) + * sets the @line and @pos fields of @f to the line and position where + * it has been assigned. + */ +void gts_file_variable_error (GtsFile * f, + GtsFileVariable * vars, + const gchar * name, + const gchar * format, + ...) +{ + va_list args; + GtsFileVariable * var; + + g_return_if_fail (f != NULL); + g_return_if_fail (vars != NULL); + g_return_if_fail (name != NULL); + g_return_if_fail (format != NULL); + + var = vars; + while (var->type != GTS_NONE && strcmp (var->name, name)) + var++; + + g_return_if_fail (var->type != GTS_NONE); /* @name not found in @vars */ + + if (var->set) { + f->line = var->line; + f->pos = var->pos; + } + + va_start (args, format); + gts_file_verror (f, format, args); + va_end (args); +} + +#ifdef DEBUG_FUNCTIONS +static GHashTable * ids = NULL; +static guint next_id = 1; + +guint id (gpointer p) +{ + g_return_val_if_fail (p != NULL, 0); + g_return_val_if_fail (ids != NULL, 0); + g_assert (g_hash_table_lookup (ids, p)); + return GPOINTER_TO_UINT (g_hash_table_lookup (ids, p)); +} + +void id_insert (gpointer p) +{ + g_return_if_fail (p != NULL); + if (ids == NULL) ids = g_hash_table_new (NULL, NULL); + g_assert (g_hash_table_lookup (ids, p) == NULL); + g_hash_table_insert (ids, p, GUINT_TO_POINTER (next_id++)); +} + +void id_remove (gpointer p) +{ + g_assert (g_hash_table_lookup (ids, p)); + g_hash_table_remove (ids, p); +} + +void gts_write_triangle (GtsTriangle * t, + GtsPoint * o, + FILE * fptr) +{ + gdouble xo = o ? o->x : 0.0; + gdouble yo = o ? o->y : 0.0; + gdouble zo = o ? o->z : 0.0; + + g_return_if_fail (t != NULL && fptr != NULL); + + fprintf (fptr, "(hdefine geometry \"t%d\" { =\n", id (t)); + fprintf (fptr, "OFF 3 1 0\n" + "%g %g %g\n%g %g %g\n%g %g %g\n3 0 1 2\n})\n" + "(geometry \"t%d\" { : \"t%d\"})\n" + "(normalization \"t%d\" none)\n", + GTS_POINT (GTS_SEGMENT (t->e1)->v1)->x - xo, + GTS_POINT (GTS_SEGMENT (t->e1)->v1)->y - yo, + GTS_POINT (GTS_SEGMENT (t->e1)->v1)->z - zo, + GTS_POINT (GTS_SEGMENT (t->e1)->v2)->x - xo, + GTS_POINT (GTS_SEGMENT (t->e1)->v2)->y - yo, + GTS_POINT (GTS_SEGMENT (t->e1)->v2)->z - zo, + GTS_POINT (gts_triangle_vertex (t))->x - xo, + GTS_POINT (gts_triangle_vertex (t))->y - yo, + GTS_POINT (gts_triangle_vertex (t))->z - zo, + id (t), id (t), id (t)); +} + +void gts_write_segment (GtsSegment * s, + GtsPoint * o, + FILE * fptr) +{ + gdouble xo = o ? o->x : 0.0; + gdouble yo = o ? o->y : 0.0; + gdouble zo = o ? o->z : 0.0; + + g_return_if_fail (s != NULL && fptr != NULL); + + fprintf (fptr, "(geometry \"s%d\" { =\n", id (s)); + fprintf (fptr, "VECT 1 2 0 2 0 %g %g %g %g %g %g })\n" + "(normalization \"s%d\" none)\n", + GTS_POINT (s->v1)->x - xo, + GTS_POINT (s->v1)->y - yo, + GTS_POINT (s->v1)->z - zo, + GTS_POINT (s->v2)->x - xo, + GTS_POINT (s->v2)->y - yo, + GTS_POINT (s->v2)->z - zo, + id (s)); +} +#endif /* DEBUG_FUNCTIONS */ Index: toporouter/src_3rd/gts/named.c =================================================================== --- toporouter/src_3rd/gts/named.c (nonexistent) +++ toporouter/src_3rd/gts/named.c (revision 6803) @@ -0,0 +1,188 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "gts.h" + +static void nvertex_read (GtsObject ** po, GtsFile * fp) +{ + if ((*po)->klass->parent_class->read) + (* (*po)->klass->parent_class->read) (po, fp); + + if (fp->type != '\n' && fp->type != GTS_ERROR) { + strncpy (GTS_NVERTEX (*po)->name, fp->token->str, GTS_NAME_LENGTH); + gts_file_next_token (fp); + } +} + +static void nvertex_write (GtsObject * o, FILE * fptr) +{ + GtsNVertex * nv = GTS_NVERTEX (o); + + (* o->klass->parent_class->write) (o, fptr); + if (nv->name[0] != '\0') + fprintf (fptr, " %s", nv->name); +} + +static void nvertex_class_init (GtsNVertexClass * klass) +{ + GTS_OBJECT_CLASS (klass)->read = nvertex_read; + GTS_OBJECT_CLASS (klass)->write = nvertex_write; +} + +static void nvertex_init (GtsNVertex * nvertex) +{ + nvertex->name[0] = '\0'; +} + +/** + * gts_nvertex_class: + * + * Returns: the #GtsNVertexClass. + */ +GtsNVertexClass * gts_nvertex_class (void) +{ + static GtsNVertexClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo nvertex_info = { + "GtsNVertex", + sizeof (GtsNVertex), + sizeof (GtsNVertexClass), + (GtsObjectClassInitFunc) nvertex_class_init, + (GtsObjectInitFunc) nvertex_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_vertex_class ()), + &nvertex_info); + } + + return klass; +} + +static void nedge_read (GtsObject ** po, GtsFile * fp) +{ + if (fp->type != GTS_STRING) { + gts_file_error (fp, "expecting a string (name)"); + return; + } + strncpy (GTS_NEDGE (*po)->name, fp->token->str, GTS_NAME_LENGTH); + gts_file_next_token (fp); +} + +static void nedge_write (GtsObject * o, FILE * fptr) +{ + GtsNEdge * ne = GTS_NEDGE (o); + + if (ne->name[0] != '\0') + fprintf (fptr, " %s", ne->name); +} + +static void nedge_class_init (GtsNEdgeClass * klass) +{ + GTS_OBJECT_CLASS (klass)->read = nedge_read; + GTS_OBJECT_CLASS (klass)->write = nedge_write; +} + +static void nedge_init (GtsNEdge * nedge) +{ + nedge->name[0] = '\0'; +} + +/** + * gts_nedge_class: + * + * Returns: the #GtsNEdgeClass. + */ +GtsNEdgeClass * gts_nedge_class (void) +{ + static GtsNEdgeClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo nedge_info = { + "GtsNEdge", + sizeof (GtsNEdge), + sizeof (GtsNEdgeClass), + (GtsObjectClassInitFunc) nedge_class_init, + (GtsObjectInitFunc) nedge_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_edge_class ()), + &nedge_info); + } + + return klass; +} + +static void nface_read (GtsObject ** po, GtsFile * fp) +{ + if (fp->type != GTS_STRING) { + gts_file_error (fp, "expecting a string (name)"); + return; + } + strncpy (GTS_NFACE (*po)->name, fp->token->str, GTS_NAME_LENGTH); + gts_file_next_token (fp); +} + +static void nface_write (GtsObject * o, FILE * fptr) +{ + GtsNFace * nf = GTS_NFACE (o); + + if (nf->name[0] != '\0') + fprintf (fptr, " %s", GTS_NFACE (o)->name); +} + +static void nface_class_init (GtsNFaceClass * klass) +{ + GTS_OBJECT_CLASS (klass)->read = nface_read; + GTS_OBJECT_CLASS (klass)->write = nface_write; +} + +static void nface_init (GtsNFace * nface) +{ + nface->name[0] = '\0'; +} + +/** + * gts_nface_class: + * + * Returns: the #GtsNFaceClass. + */ +GtsNFaceClass * gts_nface_class (void) +{ + static GtsNFaceClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo nface_info = { + "GtsNFace", + sizeof (GtsNFace), + sizeof (GtsNFaceClass), + (GtsObjectClassInitFunc) nface_class_init, + (GtsObjectInitFunc) nface_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_face_class ()), + &nface_info); + } + + return klass; +} Index: toporouter/src_3rd/gts/object.c =================================================================== --- toporouter/src_3rd/gts/object.c (nonexistent) +++ toporouter/src_3rd/gts/object.c (revision 6803) @@ -0,0 +1,345 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "gts.h" +#include "gts-private.h" + +static GHashTable * class_table = NULL; + +static void gts_object_class_init (GtsObjectClass * klass, + GtsObjectClass * parent_class) +{ + if (parent_class) { + gts_object_class_init (klass, parent_class->parent_class); + if (parent_class->info.class_init_func) + (*parent_class->info.class_init_func) (klass); + } +} + +/** + * gts_object_class_new: + * @parent_class: a #GtsObjectClass. + * @info: a #GtsObjectClassInfo, description of the new class to create. + * + * Returns: a new #GtsObjectClass derived from @parent_class and described by + * @info. + */ +gpointer gts_object_class_new (GtsObjectClass * parent_class, + GtsObjectClassInfo * info) +{ + GtsObjectClass * klass; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (parent_class == NULL || + info->object_size >= parent_class->info.object_size, + NULL); + g_return_val_if_fail (parent_class == NULL || + info->class_size >= parent_class->info.class_size, + NULL); + + klass = g_malloc0 (info->class_size); + klass->info = *info; + klass->parent_class = parent_class; + gts_object_class_init (klass, klass); + + if (!class_table) + class_table = g_hash_table_new (g_str_hash, g_str_equal); + g_hash_table_insert (class_table, klass->info.name, klass); + + return klass; +} + +/** + * gts_object_class_from_name: + * @name: the name of a #GtsObjectClass. + * + * Returns: the #GtsObjectClass with name @name or %NULL if it hasn't been + * instantiated yet. + */ +GtsObjectClass * gts_object_class_from_name (const gchar * name) +{ + g_return_val_if_fail (name != NULL, NULL); + + if (!class_table) + return NULL; + return g_hash_table_lookup (class_table, name); +} + +static void object_destroy (GtsObject * object) +{ +#ifdef DEBUG_IDENTITY +#ifdef DEBUG_LEAKS + fprintf (stderr, "destroy %s %p->%d\n", + object->klass->info.name, + object, + id (object)); +#endif + id_remove (object); +#endif + object->klass = NULL; + g_free (object); +} + +static void object_clone (GtsObject * clone, GtsObject * object) +{ + memcpy (clone, object, object->klass->info.object_size); + clone->reserved = NULL; +} + +static void object_class_init (GtsObjectClass * klass) +{ + klass->clone = object_clone; + klass->destroy = object_destroy; + klass->read = NULL; + klass->write = NULL; + klass->color = NULL; + klass->attributes = NULL; +} + +static void object_init (GtsObject * object) +{ + object->reserved = NULL; + object->flags = 0; +} + +/** + * gts_object_class: + * + * Returns: the #GtsObjectClass. + */ +GtsObjectClass * gts_object_class (void) +{ + static GtsObjectClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo object_info = { + "GtsObject", + sizeof (GtsObject), + sizeof (GtsObjectClass), + (GtsObjectClassInitFunc) object_class_init, + (GtsObjectInitFunc) object_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (NULL, &object_info); + } + + return klass; +} + +/** + * gts_object_check_cast: + * @object: a #GtsObject. + * @klass: a #GtsObjectClass. + * + * Returns: @object while emitting warnings if @object is not of class @klass. + */ +gpointer gts_object_check_cast (gpointer object, + gpointer klass) +{ + if (!object) { + g_warning ("invalid cast from (NULL) pointer to `%s'", + GTS_OBJECT_CLASS (klass)->info.name); + return object; + } + if (!((GtsObject *) object)->klass) { + g_warning ("invalid unclassed pointer in cast to `%s'", + GTS_OBJECT_CLASS (klass)->info.name); + return object; + } + if (!gts_object_is_from_class (object, klass)) { + g_warning ("invalid cast from `%s' to `%s'", + ((GtsObject *) object)->klass->info.name, + GTS_OBJECT_CLASS (klass)->info.name); + return object; + } + return object; +} + +/** + * gts_object_class_check_cast: + * @klass: a #GtsObjectClass. + * @from: a #GtsObjectClass. + * + * Returns: @klass while emitting warnings if @klass is not derived from + * @from. + */ +gpointer gts_object_class_check_cast (gpointer klass, + gpointer from) +{ + if (!klass) { + g_warning ("invalid cast from (NULL) pointer to `%s'", + GTS_OBJECT_CLASS (from)->info.name); + return klass; + } + if (!gts_object_class_is_from_class (klass, from)) { + g_warning ("invalid cast from `%s' to `%s'", + GTS_OBJECT_CLASS (klass)->info.name, + GTS_OBJECT_CLASS (from)->info.name); + return klass; + } + return klass; +} + +/** + * gts_object_init: + * @object: a #GtsObject. + * @klass: a #GtsObjectClass. + * + * Calls the init method of @klass with @object as argument. This is done + * recursively in the correct order (from the base class to the top). You + * should rarely need this function as it is called automatically by the + * constructor for each class. + */ +void gts_object_init (GtsObject * object, GtsObjectClass * klass) +{ + GtsObjectClass * parent_class; + + g_return_if_fail (object != NULL); + g_return_if_fail (klass != NULL); + + parent_class = klass->parent_class; + if (parent_class) + gts_object_init (object, parent_class); + if (klass->info.object_init_func) + (*klass->info.object_init_func) (object); +} + +/** + * gts_object_new: + * @klass: a #GtsObjectClass. + * + * Returns: a new initialized object of class @klass. + */ +GtsObject * gts_object_new (GtsObjectClass * klass) +{ + GtsObject * object; + + g_return_val_if_fail (klass != NULL, NULL); + + object = g_malloc0 (klass->info.object_size); + object->klass = klass; + gts_object_init (object, klass); + +#ifdef DEBUG_IDENTITY + id_insert (object); +#ifdef DEBUG_LEAKS + fprintf (stderr, "new %s %p->%d\n", klass->info.name, + object, + id (object)); +#endif +#endif + + return object; +} + +/** + * gts_object_clone: + * @object: a #GtsObject. + * + * Calls the clone method of @object. The call to this function will fail + * if no clone method exists for the given object. + * + * Returns: a new object clone of @object. + */ +GtsObject * gts_object_clone (GtsObject * object) +{ + GtsObject * clone; + + g_return_val_if_fail (object != NULL, NULL); + g_return_val_if_fail (object->klass->clone, NULL); + + clone = g_malloc0 (object->klass->info.object_size); + clone->klass = object->klass; + object_init (clone); + (* object->klass->clone) (clone, object); + +#ifdef DEBUG_IDENTITY + id_insert (clone); +#ifdef DEBUG_LEAKS + fprintf (stderr, "clone %s %p->%d\n", clone->klass->info.name, + clone, + id (clone)); +#endif +#endif + + return clone; +} + +/** + * gts_object_destroy: + * @object: a #GtsObject. + * + * Calls the destroy method of @object, freeing all memory allocated for it. + */ +void gts_object_destroy (GtsObject * object) +{ + g_assert (object->klass->destroy); + GTS_OBJECT_SET_FLAGS (object, GTS_DESTROYED); + (* object->klass->destroy) (object); +} + +/** + * gts_object_reset_reserved: + * @object: a #GtsObject. + * + * Reset the reserved field of @object. + */ +void gts_object_reset_reserved (GtsObject * object) +{ + g_return_if_fail (object != NULL); + + object->reserved = NULL; +} + +/** + * gts_object_attributes: + * @object: a #GtsObject. + * @from: a #GtsObject. + * + * Calls the attributes() method of @object using @from as source. + */ +void gts_object_attributes (GtsObject * object, GtsObject * from) +{ + g_return_if_fail (object != NULL); + + if (object->klass->attributes) + (* object->klass->attributes) (object, from); +} + +static void free_class (gchar * name, GtsObjectClass * klass) +{ + g_free (klass); +} + +/** + * gts_finalize: + * + * Free all the memory allocated by the object system of GTS. No other + * GTS function can be called after this function has been called. + */ +void gts_finalize (void) +{ + if (class_table) { + g_hash_table_foreach (class_table, (GHFunc) free_class, NULL); + g_hash_table_destroy (class_table); + class_table = NULL; + } +} Index: toporouter/src_3rd/gts/oocs.c =================================================================== --- toporouter/src_3rd/gts/oocs.c (nonexistent) +++ toporouter/src_3rd/gts/oocs.c (revision 6803) @@ -0,0 +1,387 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "gts.h" + +static void cluster_destroy (GtsObject * object) +{ + GtsCluster * c = GTS_CLUSTER (object); + + if (c->v && gts_vertex_is_unattached (c->v)) + gts_object_destroy (GTS_OBJECT (c->v)); + + /* do not forget to call destroy method of the parent */ + (* GTS_OBJECT_CLASS (gts_cluster_class ())->parent_class->destroy) (object); +} + +static void cluster_add (GtsCluster * c, GtsPoint * p, gpointer data) +{ + GtsPoint * cp; + + g_return_if_fail (c != NULL); + g_return_if_fail (c->v != NULL); + g_return_if_fail (p != NULL); + + cp = GTS_POINT (c->v); + + cp->x += p->x; + cp->y += p->y; + cp->z += p->z; + c->n++; +} + +static void cluster_update (GtsCluster * c) +{ + GtsPoint * p; + + g_return_if_fail (c != NULL); + g_return_if_fail (c->v != NULL); + + if (c->n > 1) { + p = GTS_POINT (c->v); + p->x /= c->n; + p->y /= c->n; + p->z /= c->n; + } +} + +static void cluster_class_init (GtsClusterClass * klass) +{ + klass->add = cluster_add; + klass->update = cluster_update; + + GTS_OBJECT_CLASS (klass)->destroy = cluster_destroy; +} + +static void cluster_init (GtsCluster * c) +{ + c->v = NULL; + c->n = 0; +} + +/** + * gts_cluster_class: + * + * Returns: the #GtsClusterClass. + */ +GtsClusterClass * gts_cluster_class (void) +{ + static GtsClusterClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo cluster_info = { + "GtsCluster", + sizeof (GtsCluster), + sizeof (GtsClusterClass), + (GtsObjectClassInitFunc) cluster_class_init, + (GtsObjectInitFunc) cluster_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (gts_object_class (), &cluster_info); + } + + return klass; +} + +/** + * gts_cluster_new: + * @klass: a #GtsClusterClass. + * @id: the id of the new cluster. + * @vklass: a #GtsVertexClass for the representative vertex of the cluster. + * + * Returns: a new #GtsCluster. + */ +GtsCluster * gts_cluster_new (GtsClusterClass * klass, + GtsClusterId id, + GtsVertexClass * vklass) +{ + GtsCluster * c; + + c = GTS_CLUSTER (gts_object_new (GTS_OBJECT_CLASS (klass))); + c->id = id; + c->v = gts_vertex_new (vklass, 0., 0., 0.); + + return c; +} + +/** + * gts_cluster_add: + * @c: a #GtsCluster. + * @p: a #GtsPoint. + * @data: data to pass to the add() virtual method of #GtsClusterClass. + * + * Adds point @p to cluster @c. + */ +void gts_cluster_add (GtsCluster * c, GtsPoint * p, gpointer data) +{ + g_return_if_fail (c != NULL); + g_return_if_fail (p != NULL); + + (* GTS_CLUSTER_CLASS (GTS_OBJECT (c)->klass)->add) (c, p, data); +} + +/** + * gts_cluster_update: + * @c: a #GtsCluster. + * + * Updates the position of the vertex representative of all the + * vertices added to @c. + */ +void gts_cluster_update (GtsCluster * c) +{ + g_return_if_fail (c != NULL); + + (* GTS_CLUSTER_CLASS (GTS_OBJECT (c)->klass)->update) (c); +} + +static void destroy_cluster (GtsClusterId * id, GtsObject * cluster) +{ + gts_object_destroy (cluster); +} + +static void cluster_grid_destroy (GtsObject * object) +{ + GtsClusterGrid * cluster_grid = GTS_CLUSTER_GRID (object); + + g_hash_table_foreach (cluster_grid->clusters, + (GHFunc) destroy_cluster, NULL); + g_hash_table_destroy (cluster_grid->clusters); + + (* GTS_OBJECT_CLASS (gts_cluster_grid_class ())->parent_class->destroy) + (object); +} + +static void cluster_grid_class_init (GtsClusterGridClass * klass) +{ + GTS_OBJECT_CLASS (klass)->destroy = cluster_grid_destroy; +} + +static gint cluster_id_equal (gconstpointer v1, + gconstpointer v2) +{ + const GtsClusterId * id1 = (const GtsClusterId *) v1; + const GtsClusterId * id2 = (const GtsClusterId *) v2; + return ((id1->x == id2->x) && (id1->y == id2->y) && (id1->z == id2->z)); +} + +static guint cluster_id_hash (gconstpointer key) +{ + const GtsClusterId * id = (const GtsClusterId *) key; + return id->x + id->y + id->z; +} + +static void cluster_grid_init (GtsClusterGrid * cluster_grid) +{ + cluster_grid->surface = NULL; + cluster_grid->bbox = NULL; + cluster_grid->cluster_class = gts_cluster_class (); + cluster_grid->clusters = g_hash_table_new (cluster_id_hash, + cluster_id_equal); +} + +/** + * gts_cluster_grid_class: + * + * Returns: the #GtsClusterGridClass. + */ +GtsClusterGridClass * gts_cluster_grid_class (void) +{ + static GtsClusterGridClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo cluster_grid_info = { + "GtsClusterGrid", + sizeof (GtsClusterGrid), + sizeof (GtsClusterGridClass), + (GtsObjectClassInitFunc) cluster_grid_class_init, + (GtsObjectInitFunc) cluster_grid_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (gts_object_class (), &cluster_grid_info); + } + + return klass; +} + +/** + * gts_cluster_grid_new: + * @klass: a #GtsClusterGridClass. + * @cluster_class: the klass to be used for the vertex clusters. + * @s: the simplified surface. + * @bbox: bounding box of the surface to be simplified. + * @delta: the size of one grid cell of the simplification grid. + * + * Returns: a new #GtsClusterGrid. + */ +GtsClusterGrid * gts_cluster_grid_new (GtsClusterGridClass * klass, + GtsClusterClass * cluster_class, + GtsSurface * s, + GtsBBox * bbox, + gdouble delta) +{ + GtsClusterGrid * cluster_grid; + GtsVector size; + + g_return_val_if_fail (klass != NULL, NULL); + g_return_val_if_fail (cluster_class != NULL, NULL); + g_return_val_if_fail (s != NULL, NULL); + g_return_val_if_fail (bbox != NULL, NULL); + g_return_val_if_fail (delta > 0., NULL); + + size[0] = ceil ((bbox->x2 - bbox->x1)/delta); + size[1] = ceil ((bbox->y2 - bbox->y1)/delta); + size[2] = ceil ((bbox->z2 - bbox->z1)/delta); + g_return_val_if_fail (size[0] <= 2.*G_MAXINT + 2. && + size[1] <= 2.*G_MAXINT + 2. && + size[2] <= 2.*G_MAXINT + 2., NULL); + cluster_grid = + GTS_CLUSTER_GRID (gts_object_new (GTS_OBJECT_CLASS (klass))); + cluster_grid->cluster_class = cluster_class; + cluster_grid->surface = s; + cluster_grid->bbox = bbox; + cluster_grid->size[0] = size[0]; + cluster_grid->size[1] = size[1]; + cluster_grid->size[2] = size[2]; + + return cluster_grid; +} + +static GtsClusterId cluster_index (GtsPoint * p, + GtsBBox * bb, + GtsVector n) +{ + GtsClusterId id = {0, 0, 0}; + + g_return_val_if_fail (p->x >= bb->x1 && p->x <= bb->x2, id); + g_return_val_if_fail (p->y >= bb->y1 && p->y <= bb->y2, id); + g_return_val_if_fail (p->z >= bb->z1 && p->z <= bb->z2, id); + + id.x = (guint) (p->x == bb->x2 ? n[0] - 1. : n[0]*(p->x - bb->x1)/(bb->x2 - bb->x1)); + id.y = (guint) (p->y == bb->y2 ? n[1] - 1. : n[1]*(p->y - bb->y1)/(bb->y2 - bb->y1)); + id.z = (guint) (p->z == bb->z2 ? n[2] - 1. : n[2]*(p->z - bb->z1)/(bb->z2 - bb->z1)); + + return id; +} + +static GtsCluster * cluster_grid_add_point (GtsClusterGrid * cluster_grid, + GtsPoint * p, + gpointer data) +{ + GtsClusterId id = cluster_index (p, + cluster_grid->bbox, + cluster_grid->size); + GtsCluster * c = g_hash_table_lookup (cluster_grid->clusters, &id); + + if (c == NULL) { + c = gts_cluster_new (cluster_grid->cluster_class, id, + cluster_grid->surface->vertex_class); + g_hash_table_insert (cluster_grid->clusters, &c->id, c); + } + + gts_cluster_add (c, p, data); + + return c; +} + +/** + * gts_cluster_grid_add_triangle: + * @cluster_grid: a #GtsClusterGrid. + * @p1: a #GtsPoint. + * @p2: a #GtsPoint. + * @p3: a #GtsPoint. + * @data: user data to pass to the cluster add() method. + * + * Adds the triangle defined by @p1, @p2 and @p3 to the respective clusters + * of @cluster_grid. + */ +void gts_cluster_grid_add_triangle (GtsClusterGrid * cluster_grid, + GtsPoint * p1, + GtsPoint * p2, + GtsPoint * p3, + gpointer data) +{ + GtsCluster * c1, * c2, * c3; + + g_return_if_fail (cluster_grid != NULL); + g_return_if_fail (p1 != NULL); + g_return_if_fail (p2 != NULL); + g_return_if_fail (p3 != NULL); + g_return_if_fail (cluster_grid->surface != NULL); + + c1 = cluster_grid_add_point (cluster_grid, p1, data); + c2 = cluster_grid_add_point (cluster_grid, p2, data); + c3 = cluster_grid_add_point (cluster_grid, p3, data); + + if (c1 != c2 && c2 != c3 && c3 != c1) { + GtsVertex * v1, * v2, * v3; + GtsEdge * e1, * e2, * e3; + gboolean new_edge = FALSE; + + v1 = c1->v; v2 = c2->v; v3 = c3->v; + + if ((e1 = GTS_EDGE (gts_vertices_are_connected (v1, v2))) == NULL) { + e1 = gts_edge_new (cluster_grid->surface->edge_class, v1, v2); + new_edge = TRUE; + } + if ((e2 = GTS_EDGE (gts_vertices_are_connected (v2, v3))) == NULL) { + e2 = gts_edge_new (cluster_grid->surface->edge_class, v2, v3); + new_edge = TRUE; + } + if ((e3 = GTS_EDGE (gts_vertices_are_connected (v3, v1))) == NULL) { + e3 = gts_edge_new (cluster_grid->surface->edge_class, v3, v1); + new_edge = TRUE; + } + if (new_edge || !gts_triangle_use_edges (e1, e2, e3)) + gts_surface_add_face (cluster_grid->surface, + gts_face_new (cluster_grid->surface->face_class, + e1, e2, e3)); + } +} + +static void update_cluster (gint * id, GtsCluster * cluster, GtsRange * stats) +{ + gts_cluster_update (cluster); + gts_range_add_value (stats, cluster->n); +} + +/** + * gts_cluster_grid_update: + * @cluster_grid: a #GtsClusterGrid. + * + * Updates the representative vertices of all the clusters of @cluster_grid. + * + * Returns: a #GtsRange describing the statistics for the number of vertices + * added to each cluster of @cluster_grid. + */ +GtsRange gts_cluster_grid_update (GtsClusterGrid * cluster_grid) +{ + GtsRange stats; + + gts_range_init (&stats); + g_return_val_if_fail (cluster_grid != NULL, stats); + + g_hash_table_foreach (cluster_grid->clusters, + (GHFunc) update_cluster, &stats); + gts_range_update (&stats); + + return stats; +} Index: toporouter/src_3rd/gts/partition.c =================================================================== --- toporouter/src_3rd/gts/partition.c (nonexistent) +++ toporouter/src_3rd/gts/partition.c (revision 6803) @@ -0,0 +1,1219 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + +#include "gts.h" + +/* #define DEBUG */ + +/* Graph partition */ + +/** + * gts_graph_partition_edges_cut: + * @partition: a list of @GtsGraph representing a partition. + * + * Returns: the number of edges cut by the partition. + */ +guint gts_graph_partition_edges_cut (GSList * partition) +{ + guint cuts = 0; + + while (partition) { + cuts += gts_graph_edges_cut (partition->data); + partition = partition->next; + } + + return cuts/2; +} + +/** + * gts_graph_partition_edges_cut_weight: + * @partition: a list of @GtsGraph representing a partition. + * + * Returns: the total weight of the edges cut by the partition. + */ +gfloat gts_graph_partition_edges_cut_weight (GSList * partition) +{ + gfloat weight = 0.; + + while (partition) { + weight += gts_graph_edges_cut_weight (partition->data); + partition = partition->next; + } + + return weight/2.; +} + +/** + * gts_graph_partition_print_stats: + * @partition: a list of @GtsGraph representing a partition. + * @fp: a file pointer. + * + * Writes to @fp a summary of the properties of @partition. + */ +void gts_graph_partition_print_stats (GSList * partition, + FILE * fp) +{ + GtsRange weight; + GSList * i; + + g_return_if_fail (partition != NULL); + g_return_if_fail (fp != NULL); + + gts_range_init (&weight); + i = partition; + while (i) { + gts_range_add_value (&weight, gts_graph_weight (i->data)); + i = i->next; + } + gts_range_update (&weight); + + fprintf (fp, + "# parts: %d\n" + "# edge cuts: %5d edge cuts weight: %5g\n" + "# weight: ", + g_slist_length (partition), + gts_graph_partition_edges_cut (partition), + gts_graph_partition_edges_cut_weight (partition)); + gts_range_print (&weight, fp); + fputc ('\n', fp); +} + +/** + * gts_graph_partition_balance: + * @partition: a list of @GtsGraph representing a partition. + * + * Returns: the difference between the maximum and the minimum weight + * of the graphs in @partition. + */ +gfloat gts_graph_partition_balance (GSList * partition) +{ + gfloat wmin = G_MAXFLOAT; + gfloat wmax = - G_MAXFLOAT; + + g_return_val_if_fail (partition != NULL, 0.); + + while (partition) { + gfloat weight = gts_graph_weight (partition->data); + if (weight < wmin) + wmin = weight; + if (weight > wmax) + wmax = weight; + partition = partition->next; + } + return wmax - wmin; +} + +/** + * gts_graph_partition_clone: + * @partition: a list of @GtsGraph representing a partition. + * + * Returns: a new partition clone of @partition (i.e. a list of new + * graphs clones of the graphs in @partition). + */ +GSList * gts_graph_partition_clone (GSList * partition) +{ + GSList * cparts = NULL; + + while (partition) { + cparts = + g_slist_prepend (cparts, + gts_object_clone (GTS_OBJECT (partition->data))); + partition = partition->next; + } + return cparts; +} + +/** + * gts_graph_partition_destroy: + * @partition: a list of @GtsGraph representing a partition. + * + * Destroys all the graphs in @partition and frees @partition. + */ +void gts_graph_partition_destroy (GSList * partition) +{ + GSList * i = partition; + + while (i) { + gts_object_destroy (GTS_OBJECT (i->data)); + i = i->next; + } + g_slist_free (partition); +} + +static void find_smallest_degree (GtsGNode * n, gpointer * data) +{ + GtsGNode ** nmin = data[0]; + GtsGraph * g = data[1]; + guint * min = data[2]; + guint degree = gts_gnode_degree (n, g); + + if (degree < *min) { + *min = degree; + *nmin = n; + } +} + +static gint graph_comp_weight (GtsGraph * g1, GtsGraph * g2) +{ + if (gts_graph_weight (g1) > gts_graph_weight (g2)) + return 1; + return -1; +} + +static void partition_update (GSList * list, GtsGraph * g) +{ + GSList * i; + GtsGraph * g1; + GtsHeap * size_heap; + gboolean reinit = TRUE; + + /* initialize traversals */ + i = list; + while (i) { + GtsGNode * seed = GTS_OBJECT (i->data)->reserved; + GTS_OBJECT (seed)->reserved = + gts_graph_traverse_new (g, seed, GTS_BREADTH_FIRST, reinit); + reinit = FALSE; + i = i->next; + } + + size_heap = gts_heap_new ((GCompareFunc) graph_comp_weight); + i = list; + while (i) { + gts_heap_insert (size_heap, i->data); + i = i->next; + } + while ((g1 = gts_heap_remove_top (size_heap))) { + GtsGraphTraverse * t = GTS_OBJECT (GTS_OBJECT (g1)->reserved)->reserved; + GtsGNode * n = gts_graph_traverse_next (t); + if (n) { + gts_container_add (GTS_CONTAINER (g1), GTS_CONTAINEE (n)); + gts_heap_insert (size_heap, g1); + } + } + gts_heap_destroy (size_heap); + + /* destroy traversals */ + i = list; + while (i) { + GtsGNode * seed = GTS_OBJECT (i->data)->reserved; + gts_graph_traverse_destroy (GTS_OBJECT (seed)->reserved); + GTS_OBJECT (seed)->reserved = NULL; + i = i->next; + } +} + +static void better_seed (GtsGNode * n, gpointer * data) +{ + guint * sum = data[0]; + GtsGNode ** seed = data[1]; + GtsGraph * g = data[2]; + guint sum1 = gts_graph_distance_sum (g, n); + + if (sum1 < *sum) { + *sum = sum1; + *seed = n; + } +} + +static GtsGNode * graph_new_seed (GtsGraph * g, GtsGNode * seed) +{ + guint sum = gts_graph_distance_sum (g, seed); + gpointer data[3]; + GtsGNode * new_seed = seed; + + data[0] = ∑ + data[1] = &new_seed; + data[2] = g; + gts_gnode_foreach_neighbor (seed, g, (GtsFunc) better_seed, data); + + return new_seed; +} + +/** + * gts_graph_bubble_partition: + * @g: a #GtsGraph. + * @np: number of partitions. + * @niter: the maximum number of iterations. + * @step_info: a #GtsFunc or %NULL. + * @data: user data to pass to @step_info. + * + * An implementation of the "bubble partitioning algorithm" of + * Diekmann, Preis, Schlimbach and Walshaw (2000). The maximum number + * of iteration on the positions of the graph growing seeds is + * controlled by @niter. + * + * If not %NULL @step_info is called after each iteration on the seeds + * positions passing the partition (a GSList) as argument. + * + * Returns: a list of @np new #GtsGraph representing the partition. + */ +GSList * gts_graph_bubble_partition (GtsGraph * g, + guint np, + guint niter, + GtsFunc step_info, + gpointer data) +{ + GSList * list = NULL, * seeds = NULL; + GtsGNode * seed = NULL; + guint min = G_MAXINT/2 - 1; + gpointer info[3]; + GtsGraph * g1; + gboolean changed = TRUE; + + g_return_val_if_fail (g != NULL, NULL); + g_return_val_if_fail (np > 0, NULL); + + info[0] = &seed; + info[1] = g; + info[2] = &min; + gts_container_foreach (GTS_CONTAINER (g), + (GtsFunc) find_smallest_degree, + info); + if (seed == NULL) + return NULL; + + g1 = GTS_GRAPH (gts_object_new (GTS_OBJECT (g)->klass)); + gts_container_add (GTS_CONTAINER (g1), GTS_CONTAINEE (seed)); + list = g_slist_prepend (list, g1); + GTS_OBJECT (g1)->reserved = seed; + seeds = g_slist_prepend (seeds, seed); + + while (--np && seed) + if ((seed = gts_graph_farthest (g, seeds))) { + g1 = GTS_GRAPH (gts_object_new (GTS_OBJECT (g)->klass)); + gts_container_add (GTS_CONTAINER (g1), GTS_CONTAINEE (seed)); + list = g_slist_prepend (list, g1); + GTS_OBJECT (g1)->reserved = seed; + seeds = g_slist_prepend (seeds, seed); + } + g_slist_free (seeds); + + partition_update (list, g); + + while (changed && niter--) { + GSList * i; + + changed = FALSE; + i = list; + while (i) { + GtsGraph * g1 = i->data; + GtsGNode * seed = GTS_OBJECT (g1)->reserved; + GtsGNode * new_seed = graph_new_seed (g1, seed); + if (new_seed != seed) { + changed = TRUE; + GTS_OBJECT (g1)->reserved = new_seed; + } + i = i->next; + } + + if (changed) { + i = list; + while (i) { + GtsGraph * g1 = i->data; + GtsGNode * seed = GTS_OBJECT (g1)->reserved; + + gts_object_destroy (GTS_OBJECT (g1)); + i->data = g1 = GTS_GRAPH (gts_object_new (GTS_OBJECT (g)->klass)); + gts_container_add (GTS_CONTAINER (g1), GTS_CONTAINEE (seed)); + GTS_OBJECT (g1)->reserved = seed; + i = i->next; + } + partition_update (list, g); + if (step_info) + (* step_info) (list, data); + } + } + g_slist_foreach (list, (GFunc) gts_object_reset_reserved, NULL); + return list; +} + +/* Graph bisection */ + +static gdouble node_cost (GtsGNode * n, gpointer * data) +{ + GtsGraph * g = data[0]; + GtsGraph * g1 = data[1]; + GSList * i = GTS_SLIST_CONTAINER (n)->items; + gdouble cost = 0.; + + while (i) { + GtsGEdge * e = i->data; + GtsGNode * n1 = GTS_GNODE_NEIGHBOR (n, e); + + if (gts_containee_is_contained (GTS_CONTAINEE (n1), GTS_CONTAINER (g))) { + if (gts_containee_is_contained (GTS_CONTAINEE (n1), GTS_CONTAINER (g1))) + cost -= gts_gedge_weight (e); + else + cost += gts_gedge_weight (e); + } + i = i->next; + } + + return cost; +} + +static void add_neighbor (GtsGNode * n, GtsEHeap * heap) +{ + if (GTS_OBJECT (n)->reserved == n) + return; + if (GTS_OBJECT (n)->reserved) + gts_eheap_remove (heap, GTS_OBJECT (n)->reserved); + GTS_OBJECT (n)->reserved = gts_eheap_insert (heap, n); +} + +static void add_unused (GtsGNode * n, GtsGraph * g2) +{ + if (GTS_OBJECT (n)->reserved) + GTS_OBJECT (n)->reserved = NULL; + else + gts_container_add (GTS_CONTAINER (g2), GTS_CONTAINEE (n)); +} + +static gdouble degree_cost (GtsGNode * n, GtsGraph * g) +{ + return gts_gnode_degree (n, g); +} + +static void add_seed (GtsGNode * n, GtsEHeap * heap) +{ + gts_eheap_insert (heap, n); +} + +static void boundary_node1 (GtsGNode * n, GtsGraphBisection * bg) +{ + GSList * i = GTS_SLIST_CONTAINER (n)->items; + + while (i) { + GtsGNode * n1 = GTS_GNODE_NEIGHBOR (n, i->data); + if (gts_containee_is_contained (GTS_CONTAINEE (n1), + GTS_CONTAINER (bg->g2))) { + g_hash_table_insert (bg->bg1, n, n1); + return; + } + i = i->next; + } +} + +static void boundary_node2 (GtsGNode * n, GtsGraphBisection * bg) +{ + GSList * i = GTS_SLIST_CONTAINER (n)->items; + + while (i) { + GtsGNode * n1 = GTS_GNODE_NEIGHBOR (n, i->data); + if (gts_containee_is_contained (GTS_CONTAINEE (n1), + GTS_CONTAINER (bg->g1))) { + g_hash_table_insert (bg->bg2, n, n1); + return; + } + i = i->next; + } +} + +static void check_bg (GtsGNode * n, gpointer * data) +{ + GHashTable * bg = data[0]; + GtsGraph * g = data[1]; + gboolean * ok = data[2]; + guint * nb = data[3]; + guint nn = gts_gnode_degree (n, g); + + if (nn > 0) + (*nb)++; + if ((nn > 0 && !g_hash_table_lookup (bg, n)) || + (nn == 0 && g_hash_table_lookup (bg, n))) { + g_warning ("nn: %d lookup: %p\n", + nn, g_hash_table_lookup (bg, n)); + *ok = FALSE; + } +} + +/** + * gts_graph_bisection_check: + * @bg: a #GtsGraphBisection. + * + * Checks that the boundary of @bg is correctly defined (used for + * debugging purposes). + * + * Returns: %TRUE if @bg is ok, %FALSE otherwise. + */ +gboolean gts_graph_bisection_check (GtsGraphBisection * bg) +{ + gboolean ok = TRUE; + guint nb; + gpointer data[4]; + + g_return_val_if_fail (bg != NULL, FALSE); + + nb = 0; + data[0] = bg->bg1; + data[1] = bg->g2; + data[2] = &ok; + data[3] = &nb; + gts_container_foreach (GTS_CONTAINER (bg->g1), (GtsFunc) check_bg, data); + g_return_val_if_fail (g_hash_table_size (bg->bg1) == nb, FALSE); + + nb = 0; + data[0] = bg->bg2; + data[1] = bg->g1; + gts_container_foreach (GTS_CONTAINER (bg->g2), (GtsFunc) check_bg, data); + g_return_val_if_fail (g_hash_table_size (bg->bg2) == nb, FALSE); + + return ok; +} + +/** + * gts_graph_ggg_bisection: + * @g: a #GtsGraph. + * @ntry: the number of randomly selected initial seeds. + * + * An implementation of the "Greedy Graph Growing" algorithm of + * Karypis and Kumar (1997). + * + * @ntry randomly chosen seeds are used and the best partition is retained. + * + * Returns: a new #GtsGraphBisection of @g. + */ +GtsGraphBisection * gts_graph_ggg_bisection (GtsGraph * g, guint ntry) +{ + gfloat size, bestcost = G_MAXFLOAT, smin; + GtsGraph * bestg1 = NULL, * bestg2 = NULL; + gboolean balanced = FALSE; + GtsEHeap * degree_heap; + GtsGNode * seed; + GtsGraphBisection * bg; + + g_return_val_if_fail (g != NULL, NULL); + + bg = g_malloc (sizeof (GtsGraphBisection)); + bg->g = g; + + size = gts_graph_weight (g)/2.; + smin = 0.9*size; + + degree_heap = gts_eheap_new ((GtsKeyFunc) degree_cost, g); + gts_eheap_freeze (degree_heap); + gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) add_seed, degree_heap); + gts_eheap_thaw (degree_heap); + + while (ntry && ((seed = gts_eheap_remove_top (degree_heap, NULL)))) { + GtsGraph * g1, * g2; + GtsGNode * n; + gdouble cost; + gpointer data[2]; + GtsEHeap * heap; + + g1 = gts_graph_new (GTS_GRAPH_CLASS (GTS_OBJECT (g)->klass), + g->node_class, g->edge_class); + g2 = gts_graph_new (GTS_GRAPH_CLASS (GTS_OBJECT (g)->klass), + g->node_class, g->edge_class); + + data[0] = g; + data[1] = g1; + heap = gts_eheap_new ((GtsKeyFunc) node_cost, data); + + gts_container_add (GTS_CONTAINER (g1), GTS_CONTAINEE (seed)); + GTS_OBJECT (seed)->reserved = seed; + gts_gnode_foreach_neighbor (seed, g, (GtsFunc) add_neighbor, heap); + + while ((n = gts_eheap_remove_top (heap, &cost))) + if (gts_graph_weight (g1) + gts_gnode_weight (n) <= size) { + gts_container_add (GTS_CONTAINER (g1), GTS_CONTAINEE (n)); + GTS_OBJECT (n)->reserved = n; + gts_gnode_foreach_neighbor (n, g, (GtsFunc) add_neighbor, heap); + } + else + GTS_OBJECT (n)->reserved = NULL; + gts_eheap_destroy (heap); + + gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) add_unused, g2); + + cost = gts_graph_edges_cut_weight (g1); + if (!bestg1 || + (!balanced && gts_graph_weight (g1) >= smin) || + (cost < bestcost && gts_graph_weight (g1) >= smin)) { + if (bestg1) + bestcost = cost; + if (bestg1) + gts_object_destroy (GTS_OBJECT (bestg1)); + if (bestg2) + gts_object_destroy (GTS_OBJECT (bestg2)); + bestg1 = g1; + bestg2 = g2; + if (gts_graph_weight (g1) >= smin) + balanced = TRUE; + } + else { + gts_object_destroy (GTS_OBJECT (g1)); + gts_object_destroy (GTS_OBJECT (g2)); + } + + ntry--; + } + gts_eheap_destroy (degree_heap); + +#ifdef DEBUG + fprintf (stderr, "bestcost: %5g g1: %5g|%5d g2: %5g|%5d\n", + bestcost, + gts_graph_weight (bestg1), + gts_container_size (GTS_CONTAINER (bestg1)), + gts_graph_weight (bestg2), + gts_container_size (GTS_CONTAINER (bestg2))); +#endif + + g_assert (bestg1 != NULL); + bg->g1 = bestg1; + g_assert (bestg2 != NULL); + bg->g2 = bestg2; + + /* boundary nodes */ + bg->bg1 = g_hash_table_new (NULL, NULL); + gts_container_foreach (GTS_CONTAINER (bg->g1), (GtsFunc) boundary_node1, bg); + bg->bg2 = g_hash_table_new (NULL, NULL); + gts_container_foreach (GTS_CONTAINER (bg->g2), (GtsFunc) boundary_node2, bg); + + return bg; +} + +/** + * gts_graph_bfgg_bisection: + * @g: a #GtsGraph. + * @ntry: the number of randomly selected initial seeds. + * + * An implementation of a "Breadth-First Graph Growing" algorithm. + * + * @ntry randomly chosen seeds are used and the best partition is retained. + * + * Returns: a new #GtsGraphBisection of @g. + */ +GtsGraphBisection * gts_graph_bfgg_bisection (GtsGraph * g, guint ntry) +{ + gfloat size, bestcost = G_MAXFLOAT, smin; + GtsGraph * bestg1 = NULL, * bestg2 = NULL; + GtsEHeap * degree_heap; + GtsGNode * seed; + GtsGraphBisection * bg; + + g_return_val_if_fail (g != NULL, NULL); + + bg = g_malloc (sizeof (GtsGraphBisection)); + bg->g = g; + + size = gts_graph_weight (g)/2.; + smin = 0.9*size; + + degree_heap = gts_eheap_new ((GtsKeyFunc) degree_cost, g); + gts_eheap_freeze (degree_heap); + gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) add_seed, degree_heap); + gts_eheap_thaw (degree_heap); + + while (ntry && ((seed = gts_eheap_remove_top (degree_heap, NULL)))) { + GtsGraph * g1, * g2; + GtsGNode * n; + gdouble cost; + GtsGraphTraverse * t = gts_graph_traverse_new (g, seed, + GTS_BREADTH_FIRST, TRUE); + + g1 = gts_graph_new (GTS_GRAPH_CLASS (GTS_OBJECT (g)->klass), + g->node_class, g->edge_class); + g2 = gts_graph_new (GTS_GRAPH_CLASS (GTS_OBJECT (g)->klass), + g->node_class, g->edge_class); + + while ((n = gts_graph_traverse_next (t))) + if (gts_graph_weight (g1) + gts_gnode_weight (n) <= size) { + gts_container_add (GTS_CONTAINER (g1), GTS_CONTAINEE (n)); + GTS_OBJECT (n)->reserved = n; + } + gts_graph_traverse_destroy (t); + + gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) add_unused, g2); + + cost = gts_graph_edges_cut_weight (g1); + if (!bestg1 || (cost < bestcost && gts_graph_weight (g1) >= smin)) { + if (bestg1) + bestcost = cost; + if (bestg1) + gts_object_destroy (GTS_OBJECT (bestg1)); + if (bestg2) + gts_object_destroy (GTS_OBJECT (bestg2)); + bestg1 = g1; + bestg2 = g2; + } + else { + gts_object_destroy (GTS_OBJECT (g1)); + gts_object_destroy (GTS_OBJECT (g2)); + } + + ntry--; + } + gts_eheap_destroy (degree_heap); + +#ifdef DEBUG + fprintf (stderr, "bestcost: %5g g1: %5g|%5d g2: %5g|%5d\n", + bestcost, + gts_graph_weight (bestg1), + gts_container_size (GTS_CONTAINER (bestg1)), + gts_graph_weight (bestg2), + gts_container_size (GTS_CONTAINER (bestg2))); +#endif + + bg->g1 = bestg1; + bg->g2 = bestg2; + + /* boundary nodes */ + bg->bg1 = g_hash_table_new (NULL, NULL); + gts_container_foreach (GTS_CONTAINER (bg->g1), (GtsFunc) boundary_node1, bg); + bg->bg2 = g_hash_table_new (NULL, NULL); + gts_container_foreach (GTS_CONTAINER (bg->g2), (GtsFunc) boundary_node2, bg); + + return bg; +} + +static gdouble node_move_cost1 (GtsGNode * n, GtsGraphBisection * bg) +{ + return gts_gnode_move_cost (n, bg->g1, bg->g2); +} + +static gdouble node_move_cost2 (GtsGNode * n, GtsGraphBisection * bg) +{ + return gts_gnode_move_cost (n, bg->g2, bg->g1); +} + +static void build_heap (GtsGNode * n, GtsEHeap * heap) +{ + GTS_OBJECT (n)->reserved = gts_eheap_insert (heap, n); +} + +/** + * gts_graph_bisection_kl_refine: + * @bg: a #GtsGraphBisection. + * @mmax: the maximum number of unsuccessful successive moves. + * + * An implementation of the simplified Kernighan-Lin algorithm for + * graph bisection refinement as described in Karypis and Kumar + * (1997). + * + * The algorithm stops if @mmax consecutive modes do not lead to a + * decrease in the number of edges cut. This last @mmax moves are + * undone. + * + * Returns: the decrease in the weight of the edges cut by the bisection. + */ +gdouble gts_graph_bisection_kl_refine (GtsGraphBisection * bg, + guint mmax) +{ + GtsEHeap * h1, * h2; + GtsGNode * n; + guint nm = 0, i; + GtsGNode ** moves; + gdouble bestcost = 0., totalcost = 0., best_balance; + + g_return_val_if_fail (bg != NULL, 0.); + g_return_val_if_fail (mmax > 0, 0.); + + h1 = gts_eheap_new ((GtsKeyFunc) node_move_cost1, bg); + gts_eheap_freeze (h1); + gts_container_foreach (GTS_CONTAINER (bg->g1), (GtsFunc) build_heap, h1); + gts_eheap_thaw (h1); + + h2 = gts_eheap_new ((GtsKeyFunc) node_move_cost2, bg); + gts_eheap_freeze (h2); + gts_container_foreach (GTS_CONTAINER (bg->g2), (GtsFunc) build_heap, h2); + gts_eheap_thaw (h2); + + moves = g_malloc (sizeof (GtsGNode *)*mmax); + best_balance = fabs (gts_graph_weight (bg->g1) - gts_graph_weight (bg->g2)); + + do { + GtsGraph * g1, * g2; + gdouble cost; + + if (gts_graph_weight (bg->g1) > gts_graph_weight (bg->g2)) { + n = gts_eheap_remove_top (h1, &cost); + g1 = bg->g1; + g2 = bg->g2; + } + else { + n = gts_eheap_remove_top (h2, &cost); + g1 = bg->g2; + g2 = bg->g1; + } + if (n) { + GSList * i; + + GTS_OBJECT (n)->reserved = NULL; + gts_container_add (GTS_CONTAINER (g2), GTS_CONTAINEE (n)); + gts_container_remove (GTS_CONTAINER (g1), GTS_CONTAINEE (n)); + + totalcost += cost; + if (totalcost < bestcost) { + bestcost = totalcost; + nm = 0; + } + else if (totalcost == bestcost) { + gdouble balance = fabs (gts_graph_weight (g1) - gts_graph_weight (g2)); + + if (balance < best_balance) { + best_balance = balance; + nm = 0; + } + } + else + moves[nm++] = n; + + i = GTS_SLIST_CONTAINER (n)->items; + while (i) { + GtsGNode * n1 = GTS_GNODE_NEIGHBOR (n, i->data); + if (GTS_OBJECT (n1)->reserved && + gts_containee_is_contained (GTS_CONTAINEE (n1), + GTS_CONTAINER (bg->g))) { + GtsEHeap * h = + gts_containee_is_contained (GTS_CONTAINEE (n1), + GTS_CONTAINER (bg->g1)) ? h1 : h2; + gts_eheap_remove (h, GTS_OBJECT (n1)->reserved); + GTS_OBJECT (n1)->reserved = gts_eheap_insert (h, n1); + } + i = i->next; + } + } + } while (n && nm < mmax); + + gts_eheap_foreach (h1, (GFunc) gts_object_reset_reserved, NULL); + gts_eheap_foreach (h2, (GFunc) gts_object_reset_reserved, NULL); + gts_eheap_destroy (h1); + gts_eheap_destroy (h2); + + /* undo last nm moves */ + for (i = 0; i < nm; i++) { + GtsGNode * n = moves[i]; + GtsGraph * g1 = + gts_containee_is_contained (GTS_CONTAINEE (n), + GTS_CONTAINER (bg->g1)) ? bg->g1 : bg->g2; + GtsGraph * g2 = g1 == bg->g1 ? bg->g2 : bg->g1; + + gts_container_add (GTS_CONTAINER (g2), GTS_CONTAINEE (n)); + gts_container_remove (GTS_CONTAINER (g1), GTS_CONTAINEE (n)); + } + g_free (moves); + + return bestcost; +} + +static void build_bheap (GtsGNode * n, GtsGNode * n1, GtsEHeap * heap) +{ + GTS_OBJECT (n)->reserved = gts_eheap_insert (heap, n); +} + +static void update_neighbors (GtsGNode * n, GtsGraphBisection * bg, + GtsEHeap * h1, GtsEHeap * h2) +{ + GSList * i; + + i = GTS_SLIST_CONTAINER (n)->items; + while (i) { + GtsGNode * n1 = GTS_GNODE_NEIGHBOR (n, i->data); + if (gts_containee_is_contained (GTS_CONTAINEE (n1), + GTS_CONTAINER (bg->g))) { + GtsEHeap * h; + GtsGraph /* * g1,*/ * g2; + GHashTable * bg1; + + if (gts_containee_is_contained (GTS_CONTAINEE (n1), + GTS_CONTAINER (bg->g1))) { + h = h1; + //g1 = bg->g1; + g2 = bg->g2; + bg1 = bg->bg1; + } + else { + h = h2; + //g1 = bg->g2; + g2 = bg->g1; + bg1 = bg->bg2; + } + g_hash_table_remove (bg1, n1); + if (h && GTS_OBJECT (n1)->reserved && GTS_OBJECT (n1)->reserved != n1) { + gts_eheap_remove (h, GTS_OBJECT (n1)->reserved); + GTS_OBJECT (n1)->reserved = NULL; + } + if (gts_gnode_degree (n1, g2)) { + g_hash_table_insert (bg1, n1, n1); + if (h && GTS_OBJECT (n1)->reserved != n1) + GTS_OBJECT (n1)->reserved = gts_eheap_insert (h, n1); + } + } + i = i->next; + } +} + +/** + * gts_graph_bisection_bkl_refine: + * @bg: a #GtsGraphBisection. + * @mmax: the maximum number of unsuccessful successive moves. + * @imbalance: the maximum relative imbalance allowed between the + * weights of both halves of the partition. + * + * An implementation of the simplified boundary Kernighan-Lin + * algorithm for graph bisection refinement as described in Karypis + * and Kumar (1997). + * + * The algorithm stops if @mmax consecutive modes do not lead to a + * decrease in the number of edges cut. This last @mmax moves are + * undone. + * + * Returns: the decrease in the weight of the edges cut by the bisection. + */ +gdouble gts_graph_bisection_bkl_refine (GtsGraphBisection * bg, + guint mmax, + gfloat imbalance) +{ + GtsEHeap * h1, * h2; + GtsGNode * n; + guint nm = 0, i; + GtsGNode ** moves; + gdouble bestcost = 0., totalcost = 0., best_balance; + gboolean balanced = FALSE; + + g_return_val_if_fail (bg != NULL, 0.); + g_return_val_if_fail (mmax > 0, 0.); + g_return_val_if_fail (imbalance >= 0. && imbalance <= 1., 0.); + + h1 = gts_eheap_new ((GtsKeyFunc) node_move_cost1, bg); + gts_eheap_freeze (h1); + g_hash_table_foreach (bg->bg1, (GHFunc) build_bheap, h1); + gts_eheap_thaw (h1); + + h2 = gts_eheap_new ((GtsKeyFunc) node_move_cost2, bg); + gts_eheap_freeze (h2); + g_hash_table_foreach (bg->bg2, (GHFunc) build_bheap, h2); + gts_eheap_thaw (h2); + + moves = g_malloc (sizeof (GtsGNode *)*mmax); + imbalance *= gts_graph_weight (bg->g); + best_balance = fabs (gts_graph_weight (bg->g1) - gts_graph_weight (bg->g2)); + if (best_balance <= imbalance) + balanced = TRUE; + + do { + GtsGraph * g1, * g2; + GHashTable * bg1, * bg2; + gdouble cost; + + if (gts_graph_weight (bg->g1) > gts_graph_weight (bg->g2)) { + n = gts_eheap_remove_top (h1, &cost); + g1 = bg->g1; + g2 = bg->g2; + bg1 = bg->bg1; + bg2 = bg->bg2; + } + else { + n = gts_eheap_remove_top (h2, &cost); + g1 = bg->g2; + g2 = bg->g1; + bg1 = bg->bg2; + bg2 = bg->bg1; + } + if (n) { + gdouble balance; + + GTS_OBJECT (n)->reserved = n; + gts_container_add (GTS_CONTAINER (g2), GTS_CONTAINEE (n)); + gts_container_remove (GTS_CONTAINER (g1), GTS_CONTAINEE (n)); + g_hash_table_remove (bg1, n); + if (gts_gnode_degree (n, g1)) + g_hash_table_insert (bg2, n, n); + + update_neighbors (n, bg, h1, h2); + + totalcost += cost; + balance = fabs (gts_graph_weight (g1) - gts_graph_weight (g2)); + + if (!balanced && balance <= imbalance) { + bestcost = totalcost; + best_balance = balance; + balanced = TRUE; + nm = 0; + } + else if (totalcost < bestcost && + (balance < best_balance || balance <= imbalance)) { + bestcost = totalcost; + best_balance = balance; + nm = 0; + } + else if (totalcost == bestcost && balance < best_balance) { + best_balance = balance; + nm = 0; + } + else + moves[nm++] = n; + } + } while (n && nm < mmax); + + gts_container_foreach (GTS_CONTAINER (bg->g), + (GtsFunc) gts_object_reset_reserved, NULL); + gts_eheap_destroy (h1); + gts_eheap_destroy (h2); + + /* undo last nm moves */ + for (i = 0; i < nm; i++) { + GtsGNode * n = moves[i]; + GtsGraph * g1, * g2; + GHashTable * bg1, * bg2; + + if (gts_containee_is_contained (GTS_CONTAINEE (n), + GTS_CONTAINER (bg->g1))) { + g1 = bg->g1; + g2 = bg->g2; + bg1 = bg->bg1; + bg2 = bg->bg2; + } + else { + g1 = bg->g2; + g2 = bg->g1; + bg1 = bg->bg2; + bg2 = bg->bg1; + } + + gts_container_add (GTS_CONTAINER (g2), GTS_CONTAINEE (n)); + gts_container_remove (GTS_CONTAINER (g1), GTS_CONTAINEE (n)); + g_hash_table_remove (bg1, n); + if (gts_gnode_degree (n, g1)) + g_hash_table_insert (bg2, n, n); + + update_neighbors (n, bg, NULL, NULL); + } + g_free (moves); + + return bestcost; +} + +/* Multilevel partitioning */ + +static void bisection_children (GtsGNodeSplit * ns, GtsGraphBisection * bg) +{ + GtsGraph * g, * g1; + GHashTable * bbg; + GtsGNode * n1 = GTS_GNODE_SPLIT_N1 (ns); + GtsGNode * n2 = GTS_GNODE_SPLIT_N2 (ns); + + if (gts_containee_is_contained (GTS_CONTAINEE (ns->n), + GTS_CONTAINER (bg->g1))) { + g = bg->g1; + g1 = bg->g2; + bbg = bg->bg1; + } + else { + g = bg->g2; + g1 = bg->g1; + bbg = bg->bg2; + } + + gts_allow_floating_gnodes = TRUE; + gts_container_remove (GTS_CONTAINER (g), GTS_CONTAINEE (ns->n)); + gts_allow_floating_gnodes = FALSE; + gts_container_add (GTS_CONTAINER (g), GTS_CONTAINEE (n1)); + gts_container_add (GTS_CONTAINER (g), GTS_CONTAINEE (n2)); + + if (g_hash_table_lookup (bbg, ns->n)) { + g_hash_table_remove (bbg, ns->n); + if (gts_gnode_degree (n1, g1) > 0) + g_hash_table_insert (bbg, n1, n1); + if (gts_gnode_degree (n2, g1) > 0) + g_hash_table_insert (bbg, n2, n2); + } +} + +/** + * gts_graph_bisection_new: + * @wg: a #GtsWGraph. + * @ntry: the number of tries for the graph growing algorithm. + * @mmax: the number of unsucessful moves for the refinement algorithm. + * @nmin: the minimum number of nodes of the coarsest graph. + * @imbalance: the maximum relative imbalance allowed between the + * weights of both halves of the partition. + * + * An implementation of a multilevel bisection algorithm as presented + * in Karypis and Kumar (1997). A multilevel hierarchy of graphs is + * created using the #GtsPGraph object. The bisection of the coarsest + * graph is created using the gts_graph_ggg_bisection() function. The + * graph is then uncoarsened using gts_pgraph_down() and at each level + * the bisection is refined using gts_graph_bisection_bkl_refine(). + * + * Returns: a new #GtsGraphBisection of @wg. + */ +GtsGraphBisection * gts_graph_bisection_new (GtsWGraph * wg, + guint ntry, + guint mmax, + guint nmin, + gfloat imbalance) +{ + GtsGraph * g; + GtsPGraph * pg; + GtsGraphBisection * bg; + gdouble cost; + + g_return_val_if_fail (wg != NULL, NULL); + + g = GTS_GRAPH (wg); + pg = gts_pgraph_new (gts_pgraph_class (), g, + gts_gnode_split_class (), + gts_wgnode_class (), + gts_wgedge_class (), + nmin); + + bg = gts_graph_ggg_bisection (g, ntry); +#ifdef DEBUG + fprintf (stderr, "before size: %5d weight: %5g cuts: %5d cweight: %5g\n", + gts_container_size (GTS_CONTAINER (bg->g1)), + gts_graph_weight (bg->g1), + gts_graph_edges_cut (bg->g1), + gts_graph_edges_cut_weight (bg->g1)); + g_assert (gts_graph_bisection_check (bg)); +#endif + while ((cost = gts_graph_bisection_bkl_refine (bg, mmax, imbalance))) { +#ifdef DEBUG + fprintf (stderr, " cost: %g\n", cost); + g_assert (gts_graph_bisection_check (bg)); +#endif + } +#ifdef DEBUG + fprintf (stderr, "after size: %5d weight: %5g cuts: %5d cweight: %5g\n", + gts_container_size (GTS_CONTAINER (bg->g1)), + gts_graph_weight (bg->g1), + gts_graph_edges_cut (bg->g1), + gts_graph_edges_cut_weight (bg->g1)); +#endif + while (gts_pgraph_down (pg, (GtsFunc) bisection_children, bg)) { +#ifdef DEBUG + fprintf (stderr, "before size: %5d weight: %5g cuts: %5d cweight: %5g\n", + gts_container_size (GTS_CONTAINER (bg->g1)), + gts_graph_weight (bg->g1), + gts_graph_edges_cut (bg->g1), + gts_graph_edges_cut_weight (bg->g1)); +#endif + while ((cost = gts_graph_bisection_bkl_refine (bg, mmax, imbalance))) { +#ifdef DEBUG + fprintf (stderr, " cost: %g\n", cost); + g_assert (gts_graph_bisection_check (bg)); +#endif + } +#ifdef DEBUG + fprintf (stderr, "after size: %5d weight: %5g cuts: %5d cweight: %5g\n", + gts_container_size (GTS_CONTAINER (bg->g1)), + gts_graph_weight (bg->g1), + gts_graph_edges_cut (bg->g1), + gts_graph_edges_cut_weight (bg->g1)); +#endif + } + gts_object_destroy (GTS_OBJECT (pg)); + + return bg; +} + +/** + * gts_graph_bisection_destroy: + * @bg: a #GtsGraphBisection. + * @destroy_graphs: controls graph destruction. + * + * Frees all the memory allocated for @bg. If @destroy_graphs is %TRUE + * the graphs created by @bg are destroyed. + */ +void gts_graph_bisection_destroy (GtsGraphBisection * bg, + gboolean destroy_graphs) +{ + g_return_if_fail (bg != NULL); + + g_hash_table_destroy (bg->bg1); + g_hash_table_destroy (bg->bg2); + + if (destroy_graphs) { + gts_object_destroy (GTS_OBJECT (bg->g1)); + gts_object_destroy (GTS_OBJECT (bg->g2)); + } + + g_free (bg); +} + +static void recursive_bisection (GtsWGraph * wg, + guint np, + guint ntry, + guint mmax, + guint nmin, + gfloat imbalance, + GSList ** list) +{ + if (np == 0) + *list = g_slist_prepend (*list, wg); + else { + GtsGraphBisection * bg = + gts_graph_bisection_new (wg, ntry, mmax, nmin, imbalance); + GtsGraph * g1 = bg->g1; + GtsGraph * g2 = bg->g2; + + gts_object_destroy (GTS_OBJECT (wg)); + gts_graph_bisection_destroy (bg, FALSE); + recursive_bisection (GTS_WGRAPH (g1), np - 1, ntry, mmax, nmin, imbalance, + list); + recursive_bisection (GTS_WGRAPH (g2), np - 1, ntry, mmax, nmin, imbalance, + list); + } +} + +/** + * gts_graph_recursive_bisection: + * @wg: a #GtsWGraph. + * @n: the number of bisection levels. + * @ntry: the number of tries for the graph growing algorithm. + * @mmax: the number of unsucessful moves for the refinement algorithm. + * @nmin: the minimum number of nodes of the coarsest graph. + * @imbalance: the maximum relative imbalance allowed between the + * weights of both halves of the partition. + * + * Calls gts_graph_bisection_new() recursively in order to obtain a + * 2^@n partition of @wg. + * + * Returns: a list of 2^@n new #GtsGraph representing the partition. + */ +GSList * gts_graph_recursive_bisection (GtsWGraph * wg, + guint n, + guint ntry, + guint mmax, + guint nmin, + gfloat imbalance) +{ + GtsGraphBisection * bg; + GtsGraph * g1, * g2; + GSList * list = NULL; + + g_return_val_if_fail (wg != NULL, NULL); + g_return_val_if_fail (n > 0, NULL); + + bg = gts_graph_bisection_new (wg, ntry, mmax, nmin, imbalance); + g1 = bg->g1; + g2 = bg->g2; + gts_graph_bisection_destroy (bg, FALSE); + recursive_bisection (GTS_WGRAPH (g1), n - 1, ntry, mmax, nmin, imbalance, + &list); + recursive_bisection (GTS_WGRAPH (g2), n - 1, ntry, mmax, nmin, imbalance, + &list); + + return list; +} Index: toporouter/src_3rd/gts/pgraph.c =================================================================== --- toporouter/src_3rd/gts/pgraph.c (nonexistent) +++ toporouter/src_3rd/gts/pgraph.c (revision 6803) @@ -0,0 +1,584 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "gts.h" + +/* GtsGNodeSplit */ + +static void gnode_split_destroy (GtsObject * object) +{ + GtsGNodeSplit * ns = GTS_GNODE_SPLIT (object); + + if (gts_container_size (GTS_CONTAINER (ns->n)) == 0) { + g_assert (GTS_SLIST_CONTAINEE (ns->n)->containers == NULL); + gts_object_destroy (GTS_OBJECT (ns->n)); + } + else { + /* GtsGNode * n1 = GTS_GNODE_SPLIT_N1 (ns); */ + /* GtsGNode * n2 = GTS_GNODE_SPLIT_N2 (ns); */ + + g_warning ("Memory deallocation for GtsGNodeSplit not fully implemented yet: memory leak!"); + } + + (* GTS_OBJECT_CLASS (gts_gnode_split_class ())->parent_class->destroy) + (object); +} + +static void gnode_split_class_init (GtsGNodeSplitClass * klass) +{ + GTS_OBJECT_CLASS (klass)->destroy = gnode_split_destroy; +} + +static void gnode_split_init (GtsGNodeSplit * ns) +{ + ns->n = NULL; + ns->n1 = ns->n2 = NULL; +} + +/** + * gts_gnode_split_class: + * + * Returns: the #GtsGNodeSplitClass. + */ +GtsGNodeSplitClass * gts_gnode_split_class (void) +{ + static GtsGNodeSplitClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo gnode_split_info = { + "GtsGNodeSplit", + sizeof (GtsGNodeSplit), + sizeof (GtsGNodeSplitClass), + (GtsObjectClassInitFunc) gnode_split_class_init, + (GtsObjectInitFunc) gnode_split_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (gts_object_class (), &gnode_split_info); + } + + return klass; +} + +/** + * gts_gnode_split_new: + * @klass: a #GtsGNodeSplitClass. + * @n: a #GtsGNode. + * @n1: a #GtsGNodeSplit or #GtsGNode. + * @n2: a #GtsGNodeSplit or #GtsGNode. + * + * Creates a new #GtsGNodeSplit which would collapse @n1 and @n2 into + * @n. The collapse itself is not performed. + * + * Returns: the new #GtsGNodeSplit. + */ +GtsGNodeSplit * gts_gnode_split_new (GtsGNodeSplitClass * klass, + GtsGNode * n, + GtsObject * n1, + GtsObject * n2) +{ + GtsGNodeSplit * ns; + + g_return_val_if_fail (klass != NULL, NULL); + g_return_val_if_fail (n != NULL, NULL); + g_return_val_if_fail (GTS_IS_GNODE_SPLIT (n1) || GTS_IS_GNODE (n1), NULL); + g_return_val_if_fail (GTS_IS_GNODE_SPLIT (n2) || GTS_IS_GNODE (n2), NULL); + + ns = GTS_GNODE_SPLIT (gts_object_new (GTS_OBJECT_CLASS (klass))); + ns->n = n; + ns->n1 = n1; + ns->n2 = n2; + + return ns; +} + +static void connect_edge (GtsGEdge * e, gpointer * data) +{ + GtsGNode * n = data[0]; + GtsGNode * n1 = data[1]; + GtsGNode * n2 = data[2]; + + if (GTS_OBJECT (e)->reserved || /* edge is disconnected */ + gts_gedge_connects (e, n1, n2)) + return; + if (e->n1 == n1 || e->n1 == n2) + e->n1 = n; + else if (e->n2 == n1 || e->n2 == n2) + e->n2 = n; + else + g_assert_not_reached (); + gts_container_add (GTS_CONTAINER (n), GTS_CONTAINEE (e)); +} + +/** + * gts_gnode_split_collapse: + * @ns: a #GtsGNodeSplit. + * @g: a #GtsGraph. + * @klass: a #GtsWGEdgeClass. + * + * Collapses the node split @ns. Any new edge created during the + * process will be of class @klass. + */ +void gts_gnode_split_collapse (GtsGNodeSplit * ns, + GtsGraph * g, + GtsWGEdgeClass * klass) +{ + GtsGNode * n1, * n2; + GSList * i; + gpointer data[3]; + + g_return_if_fail (ns != NULL); + g_return_if_fail (g != NULL); + g_return_if_fail (gts_container_size (GTS_CONTAINER (ns->n)) == 0); + + n1 = GTS_GNODE_SPLIT_N1 (ns); + n2 = GTS_GNODE_SPLIT_N2 (ns); + + /* look for triangles */ + i = GTS_SLIST_CONTAINER (n1)->items; + while (i) { + GtsGEdge * e13 = i->data; + GtsGNode * n3 = GTS_GNODE_NEIGHBOR (n1, e13); + if (n3 != n2) { + GSList * j = GTS_SLIST_CONTAINER (n3)->items; + while (j) { + GtsGEdge * e32 = j->data; + GSList * next = j->next; + GtsGNode * n4 = GTS_GNODE_NEIGHBOR (n3, e32); + if (n4 == n2) { /* found triangle n1 (e13) n3 (e32) n2 */ + gts_wgedge_new (klass, ns->n, n3, + gts_gedge_weight (e13) + gts_gedge_weight (e32)); + GTS_OBJECT (e13)->reserved = n3; + GTS_OBJECT (e32)->reserved = n3; + GTS_SLIST_CONTAINER (n3)->items = + g_slist_remove (GTS_SLIST_CONTAINER (n3)->items, e32); + } + j = next; + } + if (GTS_OBJECT (e13)->reserved == n3) + GTS_SLIST_CONTAINER (n3)->items = + g_slist_remove (GTS_SLIST_CONTAINER (n3)->items, e13); + } + i = i->next; + } + + /* connect edges to new node */ + data[0] = ns->n; + data[1] = n1; + data[2] = n2; + gts_container_foreach (GTS_CONTAINER (n1), (GtsFunc) connect_edge, data); + gts_container_foreach (GTS_CONTAINER (n2), (GtsFunc) connect_edge, data); + + gts_allow_floating_gnodes = TRUE; + gts_container_remove (GTS_CONTAINER (g), GTS_CONTAINEE (n1)); + gts_container_remove (GTS_CONTAINER (g), GTS_CONTAINEE (n2)); + gts_allow_floating_gnodes = FALSE; + gts_container_add (GTS_CONTAINER (g), GTS_CONTAINEE (ns->n)); +} + +static void restore_edge (GtsGEdge * e, gpointer * data) +{ + GtsGNode * n = data[0]; + GtsGNode * n1 = data[1]; + GtsGNode * n2 = data[2]; + GtsGNode * n3 = GTS_OBJECT (e)->reserved; + + if (n3) { /* e is a disconnected edge */ + GTS_OBJECT (e)->reserved = NULL; + gts_container_add (GTS_CONTAINER (n3), GTS_CONTAINEE (e)); + return; + } + + if (gts_gedge_connects (e, n1, n2)) + return; + + if (e->n1 == n) + e->n1 = n1; + else if (e->n2 == n) + e->n2 = n1; + else + g_assert_not_reached (); + GTS_SLIST_CONTAINER (n)->items = + g_slist_remove (GTS_SLIST_CONTAINER (n)->items, e); +} + +/** + * gts_gnode_split_expand: + * @ns: a #GtsGNodeSplit. + * @g: a #GtsGraph. + * + * Expands the node split ns adding the new nodes to @g. + */ +void gts_gnode_split_expand (GtsGNodeSplit * ns, + GtsGraph * g) +{ + GtsGNode * n1, * n2; + gpointer data[3]; + GSList * i; + + g_return_if_fail (ns != NULL); + g_return_if_fail (g != NULL); + g_return_if_fail (gts_containee_is_contained (GTS_CONTAINEE (ns->n), + GTS_CONTAINER (g))); + + n1 = GTS_GNODE_SPLIT_N1 (ns); + n2 = GTS_GNODE_SPLIT_N2 (ns); + + data[0] = ns->n; + data[1] = n1; + data[2] = n2; + gts_container_foreach (GTS_CONTAINER (n1), (GtsFunc) restore_edge, data); + data[1] = n2; + data[2] = n1; + gts_container_foreach (GTS_CONTAINER (n2), (GtsFunc) restore_edge, data); + + i = GTS_SLIST_CONTAINER (ns->n)->items; + while (i) { + GSList * next = i->next; + gts_container_remove (GTS_CONTAINER (ns->n), GTS_CONTAINEE (i->data)); + i = next; + } + g_assert (gts_container_size (GTS_CONTAINER (ns->n)) == 0); + + gts_allow_floating_gnodes = TRUE; + gts_container_remove (GTS_CONTAINER (g), GTS_CONTAINEE (ns->n)); + gts_allow_floating_gnodes = FALSE; + + gts_container_add (GTS_CONTAINER (g), GTS_CONTAINEE (n1)); + gts_container_add (GTS_CONTAINER (g), GTS_CONTAINEE (n2)); +} + +/* GtsPGraph */ + +static void pgraph_destroy (GtsObject * object) +{ + GtsPGraph * pg = GTS_PGRAPH (object); + guint i; + + for (i = 0; i < pg->split->len; i++) + gts_object_destroy (GTS_OBJECT (g_ptr_array_index (pg->split, i))); + g_ptr_array_free (pg->split, TRUE); + g_array_free (pg->levels, TRUE); + + (* GTS_OBJECT_CLASS (gts_pgraph_class ())->parent_class->destroy) (object); +} + +static void pgraph_class_init (GtsPGraphClass * klass) +{ + GTS_OBJECT_CLASS (klass)->destroy = pgraph_destroy; +} + +static void pgraph_init (GtsPGraph * pg) +{ + pg->g = NULL; + pg->split = g_ptr_array_new (); + pg->levels = g_array_new (FALSE, FALSE, sizeof (guint)); + pg->level = 0; + pg->split_class = gts_gnode_split_class (); + pg->edge_class = gts_wgedge_class (); + pg->pos = pg->min = 0; +} + +/** + * gts_pgraph_class: + * + * Returns: the #GtsPGraphClass. + */ +GtsPGraphClass * gts_pgraph_class (void) +{ + static GtsPGraphClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo pgraph_info = { + "GtsPGraph", + sizeof (GtsPGraph), + sizeof (GtsPGraphClass), + (GtsObjectClassInitFunc) pgraph_class_init, + (GtsObjectInitFunc) pgraph_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (gts_object_class (), &pgraph_info); + } + + return klass; +} + +static void match_neighbor (GtsGNode * n, gpointer * data) +{ + if (!GTS_OBJECT (n)->reserved) { + GtsGraph * g = data[0]; + GSList ** list = data[1]; + GSList * i = GTS_SLIST_CONTAINER (n)->items; + gfloat wmax = - G_MAXFLOAT; + GtsGEdge * emax = NULL; + + while (i) { + GtsGNode * n1 = GTS_GNODE_NEIGHBOR (n, i->data); + if (!GTS_OBJECT (n1)->reserved && + gts_gedge_weight (i->data) > wmax && + gts_containee_is_contained (GTS_CONTAINEE (n1), GTS_CONTAINER (g))) { + emax = i->data; + wmax = gts_gedge_weight (emax); + } + i = i->next; + } + if (emax) { + GtsGNode * n1 = GTS_GNODE_NEIGHBOR (n, emax); + + GTS_OBJECT (n1)->reserved = n; + GTS_OBJECT (n)->reserved = n1; + *list = g_slist_prepend (*list, emax); + } + } +} + +static GSList * maximal_matching (GtsGraph * g) +{ + GSList * list = NULL; + gpointer data[2]; + + data[0] = g; + data[1] = &list; + gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) match_neighbor, data); + gts_container_foreach (GTS_CONTAINER (g), + (GtsFunc) gts_object_reset_reserved, + NULL); + + return list; +} + +/** + * gts_pgraph_new: + * @klass: a #GtsPGraphClass. + * @g: a #GtsGraph. + * @split_class: a #GtsGNodeSplitClass. + * @node_class: a #GtsWGNodeClass. + * @edge_class: a #GtsWGEdgeClass. + * @min: the minimum number of nodes. + * + * Creates a new multilevel approximation of graph @g. At each level a + * maximal matching is created using the Heavy Edge Matching (HEM) + * technique of Karypis and Kumar (1997). The newly created nodes are + * of type @node_class and their weight is set to the sum of the + * weights of their children. The newly created edges are of type + * @edge_class and their weight is set to the sum of the weight of the + * collapsed edges. The last level is reached when the maximal + * matching obtained would lead to a graph with less than @min nodes. + * + * Returns: the new #GtsPGraph containing the multilevel + * representation of @g. + */ +GtsPGraph * gts_pgraph_new (GtsPGraphClass * klass, + GtsGraph * g, + GtsGNodeSplitClass * split_class, + GtsWGNodeClass * node_class, + GtsWGEdgeClass * edge_class, + guint min) +{ + GtsPGraph * pg; + GSList * matching; + + g_return_val_if_fail (klass != NULL, NULL); + g_return_val_if_fail (g != NULL, NULL); + g_return_val_if_fail (split_class != NULL, NULL); + g_return_val_if_fail (node_class != NULL, NULL); + g_return_val_if_fail (edge_class != NULL, NULL); + + pg = GTS_PGRAPH (gts_object_new (GTS_OBJECT_CLASS (klass))); + pg->g = g; + pg->split_class = split_class; + pg->edge_class = edge_class; + + while (gts_container_size (GTS_CONTAINER (g)) > min && + (matching = maximal_matching (g))) { + GSList * i = matching; + guint size = gts_container_size (GTS_CONTAINER (g)); + + g_array_append_val (pg->levels, size); + + while (i && gts_container_size (GTS_CONTAINER (g)) > min) { + GtsGEdge * e = i->data; + GtsGNode * n = GTS_GNODE (gts_wgnode_new (node_class, + gts_gnode_weight (e->n1) + + gts_gnode_weight (e->n2))); + GtsGNodeSplit * ns = gts_gnode_split_new (split_class, n, + GTS_OBJECT (e->n1), + GTS_OBJECT (e->n2)); + gts_gnode_split_collapse (ns, g, edge_class); + g_ptr_array_add (pg->split, ns); + i = i->next; + } + g_slist_free (matching); + } + + pg->pos = pg->split->len; + pg->min = gts_container_size (GTS_CONTAINER (g)); + pg->level = pg->levels->len; + + return pg; +} + +/** + * gts_pgraph_add_node: + * @pg: a #GtsPGraph. + * + * Adds one node to the multilevel graph @pg by expanding the next + * available #GtsGNodeSplit. + * + * Returns: the expanded #GtsGNodeSplit or #NULL if all the + * #GtsGNodeSplit have already been expanded. + */ +GtsGNodeSplit * gts_pgraph_add_node (GtsPGraph * pg) +{ + GtsGNodeSplit * ns; + + g_return_val_if_fail (pg != NULL, NULL); + + if (pg->pos == 0) + return NULL; + + ns = g_ptr_array_index (pg->split, --pg->pos); + gts_gnode_split_expand (ns, pg->g); + + return ns; +} + +/** + * gts_pgraph_remove_node: + * @pg: a #GtsPGraph. + * + * Removes one node from the multilevel graph @pg by collapsing the + * first available #GtsGNodeSplit. + * + * Returns: the collapsed #GtsGNodeSplit or %NULL if all the + * #GtsGNodeSplit have already been collapsed. + */ +GtsGNodeSplit * gts_pgraph_remove_node (GtsPGraph * pg) +{ + GtsGNodeSplit * ns; + + g_return_val_if_fail (pg != NULL, NULL); + + if (pg->pos == pg->split->len) + return NULL; + + ns = g_ptr_array_index (pg->split, pg->pos++); + gts_gnode_split_collapse (ns, pg->g, pg->edge_class); + + return ns; +} + +/** + * gts_pgraph_max_node_number: + * @pg: a #GtsPGraph. + * + * Returns: the maximum number of nodes of @pg i.e. the number of + * nodes if all the #GtsGNodeSplit were expanded. + */ +guint gts_pgraph_max_node_number (GtsPGraph * pg) +{ + g_return_val_if_fail (pg != NULL, 0); + + return pg->min + pg->split->len; +} + +/** + * gts_pgraph_min_node_number: + * @pg: a #GtsPGraph. + * + * Returns: the minimum number of nodes of @pg i.e. the number of + * nodes if all the #GtsGNodeSplit were collapsed. + */ +guint gts_pgraph_min_node_number (GtsPGraph * pg) +{ + g_return_val_if_fail (pg != NULL, 0); + + return pg->min; +} + +/** + * gts_pgraph_set_node_number: + * @pg: a #GtsPGraph. + * @n: a number of nodes. + * + * Performs the required number of collapses or expansions to set the + * number of nodes of @pg to @n. + */ +void gts_pgraph_set_node_number (GtsPGraph * pg, guint n) +{ + g_return_if_fail (pg != NULL); + + n = pg->min + pg->split->len - n; + while (pg->pos > n && gts_pgraph_add_node (pg)) + ; + while (pg->pos < n && gts_pgraph_remove_node (pg)) + ; +} + +/** + * gts_pgraph_get_node_number: + * @pg: a #GtsPGraph. + * + * Returns: the current number of nodes of @pg. + */ +guint gts_pgraph_get_node_number (GtsPGraph * pg) +{ + g_return_val_if_fail (pg != NULL, 0); + + return pg->min + pg->split->len - pg->pos; +} + +/** + * gts_pgraph_down: + * @pg: a #GtsPGraph. + * @func: a #GtsFunc or %NULL. + * @data: user data to pass to @func. + * + * Performs the required number of expansions to go from the current + * level to the level immediately below. + * + * If @func is not %NULL, it is called after each #GtsGNodeSplit has + * been expanded. + * + * Returns: %FALSE if it is not possible to go down one level, %TRUE + * otherwise. + */ +gboolean gts_pgraph_down (GtsPGraph * pg, + GtsFunc func, + gpointer data) +{ + guint size; + + g_return_val_if_fail (pg != NULL, FALSE); + + if (pg->level == 0) + return FALSE; + + size = g_array_index (pg->levels, guint, --(pg->level)); + while (gts_container_size (GTS_CONTAINER (pg->g)) < size) { + GtsGNodeSplit * ns = gts_pgraph_add_node (pg); + + g_assert (ns); + if (func) + (* func) (ns, data); + } + return TRUE; +} + Index: toporouter/src_3rd/gts/point.c =================================================================== --- toporouter/src_3rd/gts/point.c (nonexistent) +++ toporouter/src_3rd/gts/point.c (revision 6803) @@ -0,0 +1,986 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include +#include "gts.h" +#include "gts-private.h" +#include "predicates.h" + +static void point_read (GtsObject ** o, GtsFile * f) +{ + GtsPoint * p = GTS_POINT (*o); + + if (GTS_POINT_CLASS ((*o)->klass)->binary) { + if (gts_file_read (f, &(p->x), sizeof (gdouble), 1) != 1) { + gts_file_error (f, "expecting a binary number (x coordinate)"); + return; + } + if (gts_file_read (f, &(p->y), sizeof (gdouble), 1) != 1) { + gts_file_error (f, "expecting a binary number (y coordinate)"); + return; + } + if (gts_file_read (f, &(p->z), sizeof (gdouble), 1) != 1) { + gts_file_error (f, "expecting a binary number (z coordinate)"); + return; + } + } + else { + if (f->type != GTS_INT && f->type != GTS_FLOAT) { + gts_file_error (f, "expecting a number (x coordinate)"); + return; + } + p->x = atof (f->token->str); + + gts_file_next_token (f); + if (f->type != GTS_INT && f->type != GTS_FLOAT) { + gts_file_error (f, "expecting a number (y coordinate)"); + return; + } + p->y = atof (f->token->str); + + gts_file_next_token (f); + if (f->type != GTS_INT && f->type != GTS_FLOAT) { + gts_file_error (f, "expecting a number (z coordinate)"); + return; + } + p->z = atof (f->token->str); + + gts_file_next_token (f); + } +} + +static void point_write (GtsObject * o, FILE * fptr) +{ + GtsPoint * p = GTS_POINT (o); + + if (GTS_POINT_CLASS ((o)->klass)->binary) { + fwrite (&(p->x), sizeof (gdouble), 1, fptr); + fwrite (&(p->y), sizeof (gdouble), 1, fptr); + fwrite (&(p->z), sizeof (gdouble), 1, fptr); + } + else + fprintf (fptr, "%.10g %.10g %.10g", p->x, p->y, p->z); +} + +static void point_class_init (GtsObjectClass * klass) +{ + klass->read = point_read; + klass->write = point_write; +} + +/** + * gts_point_class: + * + * Returns: the #GtsPointClass. + */ +GtsPointClass * gts_point_class (void) +{ + static GtsPointClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo point_info = { + "GtsPoint", + sizeof (GtsPoint), + sizeof (GtsPointClass), + (GtsObjectClassInitFunc) point_class_init, + (GtsObjectInitFunc) NULL, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (gts_object_class (), + &point_info); + } + + return klass; +} + +/** + * gts_point_new: + * @klass: a #GtsPointClass. + * @x: the x-coordinate. + * @y: the y-coordinate. + * @z: the z-coordinate. + * + * Returns: a new #GtsPoint. + */ +GtsPoint * gts_point_new (GtsPointClass * klass, + gdouble x, gdouble y, gdouble z) +{ + GtsPoint * p; + + p = GTS_POINT (gts_object_new (GTS_OBJECT_CLASS (klass))); + p->x = x; + p->y = y; + p->z = z; + + return p; +} + +/** + * gts_point_set: + * @p: a #GtsPoint. + * @x: the x-coordinate. + * @y: the y-coordinate. + * @z: the z-coordinate. + * + * Sets the coordinates of @p. + */ +void gts_point_set (GtsPoint * p, gdouble x, gdouble y, gdouble z) +{ + g_return_if_fail (p != NULL); + + p->x = x; + p->y = y; + p->z = z; +} + +/** + * gts_point_distance: + * @p1: a #GtsPoint. + * @p2: another #GtsPoint. + * + * Returns: the Euclidean distance between @p1 and @p2. + */ +gdouble gts_point_distance (GtsPoint * p1, GtsPoint * p2) +{ + g_return_val_if_fail (p1 != NULL && p2 != NULL, 0.0); + + return sqrt ((p1->x - p2->x)*(p1->x - p2->x) + + (p1->y - p2->y)*(p1->y - p2->y) + + (p1->z - p2->z)*(p1->z - p2->z)); +} + +/** + * gts_point_distance2: + * @p1: a #GtsPoint. + * @p2: another #GtsPoint. + * + * Returns: the square of the Euclidean distance between @p1 and @p2. + */ +gdouble gts_point_distance2 (GtsPoint * p1, GtsPoint * p2) +{ + g_return_val_if_fail (p1 != NULL && p2 != NULL, 0.0); + + return + (p1->x - p2->x)*(p1->x - p2->x) + + (p1->y - p2->y)*(p1->y - p2->y) + + (p1->z - p2->z)*(p1->z - p2->z); +} + +/** + * gts_point_orientation_3d: + * @p1: a #GtsPoint. + * @p2: a #GtsPoint. + * @p3: a #GtsPoint. + * @p4: a #GtsPoint. + * + * Checks if @p4 lies above, below or on the plane passing through the + * points @p1, @p2 and @p3. Below is defined so that @p1, @p2 and @p3 + * appear in counterclockwise order when viewed from above the + * plane. The returned value is an approximation of six times the + * signed volume of the tetrahedron defined by the four points. This + * function uses adaptive floating point arithmetic and is + * consequently geometrically robust. + * + * Returns: a positive value if @p4 lies below, a negative value if + * @p4 lies above the plane, zero if the four points are coplanar. + */ +gdouble gts_point_orientation_3d (GtsPoint * p1, + GtsPoint * p2, + GtsPoint * p3, + GtsPoint * p4) +{ + g_return_val_if_fail (p1 != NULL && p2 != NULL && + p3 != NULL && p4 != NULL, 0.0); + return orient3d ((gdouble *) &p1->x, + (gdouble *) &p2->x, + (gdouble *) &p3->x, + (gdouble *) &p4->x); +} + +/** + * gts_point_is_in_triangle: + * @p: a #GtsPoint. + * @t: a #GtsTriangle. + * + * Tests if the planar projection (x, y) of @p is inside, outside or + * on the boundary of the planar projection of @t. This function is + * geometrically robust. + * + * Returns: %GTS_IN if @p is inside @t, %GTS_ON if @p is on the boundary of + * @t, %GTS_OUT otherwise. + */ +GtsIntersect gts_point_is_in_triangle (GtsPoint * p, GtsTriangle * t) +{ + GtsVertex * v1, * v2, * v3; + gdouble d1, d2, d3; + + g_return_val_if_fail (p != NULL && t != NULL, FALSE); + + gts_triangle_vertices (t, &v1, &v2, &v3); + + d1 = gts_point_orientation (GTS_POINT (v1), GTS_POINT (v2), p); + if (d1 < 0.0) + return GTS_OUT; + d2 = gts_point_orientation (GTS_POINT (v2), GTS_POINT (v3), p); + if (d2 < 0.0) + return GTS_OUT; + d3 = gts_point_orientation (GTS_POINT (v3), GTS_POINT (v1), p); + if (d3 < 0.0) + return GTS_OUT; + if (d1 == 0.0 || d2 == 0.0 || d3 == 0.0) + return GTS_ON; + return GTS_IN; +} + +/** + * gts_point_in_triangle_circle: + * @p: a #GtsPoint. + * @t: a #GtsTriangle. + * + * Tests if the planar projection (x, y) of @p is inside or outside + * the circumcircle of the planar projection of @t. This function is + * geometrically robust. + * + * Returns: a positive number if @p lies inside, + * a negative number if @p lies outside and zero if @p lies on + * the circumcircle of @t. + */ +gdouble gts_point_in_triangle_circle (GtsPoint * p, GtsTriangle * t) +{ + GtsPoint * p1, * p2, * p3; + + g_return_val_if_fail (p != NULL && t != NULL, 0.0); + + gts_triangle_vertices (t, + (GtsVertex **) &p1, + (GtsVertex **) &p2, + (GtsVertex **) &p3); + + return incircle ((gdouble *) &p1->x, + (gdouble *) &p2->x, + (gdouble *) &p3->x, + (gdouble *) &p->x); +} + +/** + * gts_point_in_circle: + * @p: a #GtsPoint. + * @p1: a #GtsPoint. + * @p2: a #GtsPoint. + * @p3: a #GtsPoint. + * + * Tests if the planar projection (x, y) of @p is inside or outside the + * circle defined by the planar projection of @p1, @p2 and @p3. + * + * Returns: a positive number if @p lies inside, + * a negative number if @p lies outside and zero if @p lies on + * the circle. + */ +gdouble gts_point_in_circle (GtsPoint * p, + GtsPoint * p1, GtsPoint * p2, GtsPoint * p3) +{ + g_return_val_if_fail (p != NULL && p1 != NULL && p2 != NULL && p3 != NULL, + 0.0); + + return incircle ((gdouble *) &p1->x, + (gdouble *) &p2->x, + (gdouble *) &p3->x, + (gdouble *) &p->x); +} + +/** + * gts_point_in_sphere: + * @p: a #GtsPoint. + * @p1: a #GtsPoint. + * @p2: a #GtsPoint. + * @p3: a #GtsPoint. + * @p4: a #GtsPoint. + * + * Tests if @p is inside or outside the sphere defined by @p1, @p2, + * @p3 and @p4. + * + * Returns: a positive number if @p lies inside, + * a negative number if @p lies outside and zero if @p lies on + * the sphere. + */ +gdouble gts_point_in_sphere (GtsPoint * p, + GtsPoint * p1, GtsPoint * p2, GtsPoint * p3, GtsPoint * p4) +{ + g_return_val_if_fail (p != NULL && p1 != NULL && p2 != NULL && p3 != NULL && p4 != NULL, + 0.0); + + return insphere ((gdouble *) &p1->x, + (gdouble *) &p2->x, + (gdouble *) &p3->x, + (gdouble *) &p4->x, + (gdouble *) &p->x); +} + +/** + * gts_point_segment_distance2: + * @p: a #GtsPoint. + * @s: a #GtsSegment. + * + * Returns: the square of the minimun Euclidean distance between @p and @s. + */ +gdouble gts_point_segment_distance2 (GtsPoint * p, GtsSegment * s) +{ + gdouble t, ns2, x, y, z; + GtsPoint * p1, * p2; + + g_return_val_if_fail (p != NULL, 0.0); + g_return_val_if_fail (s != NULL, 0.0); + + p1 = GTS_POINT (s->v1); + p2 = GTS_POINT (s->v2); + ns2 = gts_point_distance2 (p1, p2); + if (ns2 == 0.0) + return gts_point_distance2 (p, p1); + t = ((p2->x - p1->x)*(p->x - p1->x) + + (p2->y - p1->y)*(p->y - p1->y) + + (p2->z - p1->z)*(p->z - p1->z))/ns2; + if (t > 1.0) + return gts_point_distance2 (p, p2); + if (t < 0.0) + return gts_point_distance2 (p, p1); + x = (1. - t)*p1->x + t*p2->x - p->x; + y = (1. - t)*p1->y + t*p2->y - p->y; + z = (1. - t)*p1->z + t*p2->z - p->z; + return x*x + y*y + z*z; +} + +/** + * gts_point_segment_distance: + * @p: a #GtsPoint. + * @s: a #GtsSegment. + * + * Returns: the minimun Euclidean distance between @p and @s. + */ +gdouble gts_point_segment_distance (GtsPoint * p, GtsSegment * s) +{ + g_return_val_if_fail (p != NULL, 0.0); + g_return_val_if_fail (s != NULL, 0.0); + + return sqrt (gts_point_segment_distance2 (p, s)); +} + +/** + * gts_point_segment_closest: + * @p: a #GtsPoint. + * @s: a #GtsSegment. + * @closest: a #GtsPoint. + * + * Set the coordinates of @closest to the coordinates of the point belonging + * to @s closest to @p. + */ +void gts_point_segment_closest (GtsPoint * p, + GtsSegment * s, + GtsPoint * closest) +{ + gdouble t, ns2; + GtsPoint * p1, * p2; + + g_return_if_fail (p != NULL); + g_return_if_fail (s != NULL); + g_return_if_fail (closest != NULL); + + p1 = GTS_POINT (s->v1); + p2 = GTS_POINT (s->v2); + ns2 = gts_point_distance2 (p1, p2); + + if (ns2 == 0.0) { + gts_point_set (closest, p1->x, p1->y, p1->z); + return; + } + + t = ((p2->x - p1->x)*(p->x - p1->x) + + (p2->y - p1->y)*(p->y - p1->y) + + (p2->z - p1->z)*(p->z - p1->z))/ns2; + + if (t > 1.0) + gts_point_set (closest, p2->x, p2->y, p2->z); + else if (t < 0.0) + gts_point_set (closest, p1->x, p1->y, p1->z); + else + gts_point_set (closest, + (1. - t)*p1->x + t*p2->x, + (1. - t)*p1->y + t*p2->y, + (1. - t)*p1->z + t*p2->z); +} + +/** + * gts_point_triangle_distance2: + * @p: a #GtsPoint. + * @t: a #GtsTriangle. + * + * Returns: the square of the minimun Euclidean distance between @p and @t. + */ +gdouble gts_point_triangle_distance2 (GtsPoint * p, GtsTriangle * t) +{ + GtsPoint * p1, * p2, * p3; + GtsEdge * e1, * e2, * e3; + GtsVector p1p2, p1p3, pp1; + gdouble A, B, C, D, E, det; + gdouble t1, t2; + gdouble x, y, z; + + g_return_val_if_fail (p != NULL, 0.0); + g_return_val_if_fail (t != NULL, 0.0); + + gts_triangle_vertices_edges (t, NULL, + (GtsVertex **) &p1, + (GtsVertex **) &p2, + (GtsVertex **) &p3, + &e1, &e2, &e3); + + gts_vector_init (p1p2, p1, p2); + gts_vector_init (p1p3, p1, p3); + gts_vector_init (pp1, p, p1); + + B = gts_vector_scalar (p1p3, p1p2); + E = gts_vector_scalar (p1p2, p1p2); + C = gts_vector_scalar (p1p3, p1p3); + + det = B*B - E*C; + if (det == 0.) { /* p1p2 and p1p3 are colinear */ + gdouble d1 = gts_point_segment_distance2 (p, GTS_SEGMENT (e1)); + gdouble d2 = gts_point_segment_distance2 (p, GTS_SEGMENT (e3)); + if (d1 < d2) + return d1; + return d2; + } + + A = gts_vector_scalar (p1p3, pp1); + D = gts_vector_scalar (p1p2, pp1); + + t1 = (D*C - A*B)/det; + t2 = (A*E - D*B)/det; + + if (t1 < 0.) + return gts_point_segment_distance2 (p, GTS_SEGMENT (e3)); + if (t2 < 0.) + return gts_point_segment_distance2 (p, GTS_SEGMENT (e1)); + if (t1 + t2 > 1.) + return gts_point_segment_distance2 (p, GTS_SEGMENT (e2)); + + x = pp1[0] + t1*p1p2[0] + t2*p1p3[0]; + y = pp1[1] + t1*p1p2[1] + t2*p1p3[1]; + z = pp1[2] + t1*p1p2[2] + t2*p1p3[2]; + + return x*x + y*y + z*z; +} + +/** + * gts_point_triangle_distance: + * @p: a #GtsPoint. + * @t: a #GtsTriangle. + * + * Returns: the minimun Euclidean distance between @p and @t. + */ +gdouble gts_point_triangle_distance (GtsPoint * p, GtsTriangle * t) +{ + g_return_val_if_fail (p != NULL, 0.0); + g_return_val_if_fail (t != NULL, 0.0); + + return sqrt (gts_point_triangle_distance2 (p, t)); +} + +/** + * gts_point_triangle_closest: + * @p: a #GtsPoint. + * @t: a #GtsTriangle. + * @closest: a #GtsPoint. + * + * Set the coordinates of @closest to those of the point belonging to @t and + * closest to @p. + */ +void gts_point_triangle_closest (GtsPoint * p, + GtsTriangle * t, + GtsPoint * closest) +{ + GtsPoint * p1, * p2, * p3; + GtsEdge * e1, * e2, * e3; + GtsVector p1p2, p1p3, pp1; + gdouble A, B, C, D, E, det; + gdouble t1, t2; + + g_return_if_fail (p != NULL); + g_return_if_fail (t != NULL); + g_return_if_fail (closest != NULL); + + gts_triangle_vertices_edges (t, NULL, + (GtsVertex **) &p1, + (GtsVertex **) &p2, + (GtsVertex **) &p3, + &e1, &e2, &e3); + + gts_vector_init (p1p2, p1, p2); + gts_vector_init (p1p3, p1, p3); + gts_vector_init (pp1, p, p1); + + B = gts_vector_scalar (p1p3, p1p2); + E = gts_vector_scalar (p1p2, p1p2); + C = gts_vector_scalar (p1p3, p1p3); + + det = B*B - E*C; + if (det == 0.) { /* p1p2 and p1p3 are colinear */ + GtsPoint * cp = + GTS_POINT (gts_object_new (GTS_OBJECT_CLASS (gts_point_class ()))); + gts_point_segment_closest (p, GTS_SEGMENT (e1), cp); + gts_point_segment_closest (p, GTS_SEGMENT (e3), closest); + + if (gts_point_distance2 (cp, p) < gts_point_distance2 (closest, p)) + gts_point_set (closest, cp->x, cp->y, cp->z); + gts_object_destroy (GTS_OBJECT (cp)); + return; + } + + A = gts_vector_scalar (p1p3, pp1); + D = gts_vector_scalar (p1p2, pp1); + + t1 = (D*C - A*B)/det; + t2 = (A*E - D*B)/det; + + if (t1 < 0.) + gts_point_segment_closest (p, GTS_SEGMENT (e3), closest); + else if (t2 < 0.) + gts_point_segment_closest (p, GTS_SEGMENT (e1), closest); + else if (t1 + t2 > 1.) + gts_point_segment_closest (p, GTS_SEGMENT (e2), closest); + else + gts_point_set (closest, + p1->x + t1*p1p2[0] + t2*p1p3[0], + p1->y + t1*p1p2[1] + t2*p1p3[1], + p1->z + t1*p1p2[2] + t2*p1p3[2]); +} + +/** + * gts_segment_triangle_intersection: + * @s: a #GtsSegment. + * @t: a #GtsTriangle. + * @boundary: if %TRUE, the boundary of @t is taken into account. + * @klass: a #GtsPointClass to be used for the new point. + * + * Checks if @s intersects @t. If this is the case, creates a new + * point pi intersection of @s with @t. + * + * This function is geometrically robust in the sense that it will not + * return a point if @s and @t do not intersect and will return a + * point if @s and @t do intersect. However, the point coordinates are + * subject to round-off errors. + * + * Note that this function will not return any point if @s is contained in + * the plane defined by @t. + * + * Returns: a summit of @t (if @boundary is set to %TRUE), one of the endpoints + * of @s or a new #GtsPoint, intersection of @s with @t or %NULL if @s + * and @t don't intersect. + */ +GtsPoint * gts_segment_triangle_intersection (GtsSegment * s, + GtsTriangle * t, + gboolean boundary, + GtsPointClass * klass) +{ + GtsPoint * A, * B, * C, * D, * E, * I; + gdouble ABCE, ABCD, ADCE, ABDE, BCDE; + gdouble c; + + g_return_val_if_fail (s != NULL, NULL); + g_return_val_if_fail (t != NULL, NULL); + g_return_val_if_fail (klass != NULL, NULL); + + A = GTS_POINT (GTS_SEGMENT (t->e1)->v1); + B = GTS_POINT (GTS_SEGMENT (t->e1)->v2); + C = GTS_POINT (gts_triangle_vertex (t)); + D = GTS_POINT (s->v1); + E = GTS_POINT (s->v2); + + ABCE = gts_point_orientation_3d (A, B, C, E); + ABCD = gts_point_orientation_3d (A, B, C, D); + if (ABCE < 0.0 || ABCD > 0.0) { + GtsPoint * tmpp; + gdouble tmp; + tmpp = E; E = D; D = tmpp; + tmp = ABCE; ABCE = ABCD; ABCD = tmp; + } + if (ABCE < 0.0 || ABCD > 0.0) + return NULL; + ADCE = gts_point_orientation_3d (A, D, C, E); + if ((boundary && ADCE < 0.) || (!boundary && ADCE <= 0.)) + return NULL; + ABDE = gts_point_orientation_3d (A, B, D, E); + if ((boundary && ABDE < 0.) || (!boundary && ABDE <= 0.)) + return NULL; + BCDE = gts_point_orientation_3d (B, C, D, E); + if ((boundary && BCDE < 0.) || (!boundary && BCDE <= 0.)) + return NULL; + if (ABCE == 0.0) { + if (ABCD == 0.0) + /* s is contained in the plane defined by t*/ + return NULL; + return E; + } + if (ABCD == 0.0) + return D; + if (boundary) { /* corners of @t */ + if (ABDE == 0.) { + if (ADCE == 0.) + return A; + if (BCDE == 0.) + return B; + } + else if (BCDE == 0. && ADCE == 0.) + return C; + } + c = ABCE/(ABCE - ABCD); + I = GTS_POINT (gts_object_new (GTS_OBJECT_CLASS (klass))); + gts_point_set (I, + E->x + c*(D->x - E->x), + E->y + c*(D->y - E->y), + E->z + c*(D->z - E->z)); + return I; +} + +/** + * gts_point_transform: + * @p: a #GtsPoint. + * @m: the #GtsMatrix representing the transformation to + * apply to the coordinates of @p. + * + * Transform the coordinates of @p according to @m. (p[] becomes m[][].p[]). + */ +void gts_point_transform (GtsPoint * p, GtsMatrix * m) +{ + gdouble x, y, z; + g_return_if_fail (p != NULL && m != NULL); + x = m[0][0]*p->x + m[0][1]*p->y + m[0][2]*p->z + m[0][3]; + y = m[1][0]*p->x + m[1][1]*p->y + m[1][2]*p->z + m[1][3]; + z = m[2][0]*p->x + m[2][1]*p->y + m[2][2]*p->z + m[2][3]; + p->x = x; p->y = y; p->z = z; +} + +/** + * gts_point_orientation: + * @p1: a #GtsPoint. + * @p2: a #GtsPoint. + * @p3: a #GtsPoint. + * + * Checks for orientation of the projection of three points on the + * (x,y) plane. The result is also an approximation of twice the + * signed area of the triangle defined by the three points. This + * function uses adaptive floating point arithmetic and is + * consequently geometrically robust. + * + * Returns: a positive value if @p1, @p2 and @p3 appear in + * counterclockwise order, a negative value if they appear in + * clockwise order and zero if they are colinear. + */ +gdouble gts_point_orientation (GtsPoint * p1, GtsPoint * p2, GtsPoint * p3) +{ + g_return_val_if_fail (p1 != NULL && p2 != NULL && p3 != NULL, 0.0); + + return orient2d ((gdouble *) &p1->x, + (gdouble *) &p2->x, + (gdouble *) &p3->x); +} + +static gboolean ray_intersects_triangle (GtsPoint * D, GtsPoint * E, + GtsTriangle * t) +{ + GtsPoint * A, * B, * C; + gint ABCE, ABCD, ADCE, ABDE, BCDE; + + gts_triangle_vertices (t, (GtsVertex **) &A, + (GtsVertex **) &B, + (GtsVertex **) &C); + + ABCE = gts_point_orientation_3d_sos (A, B, C, E); + ABCD = gts_point_orientation_3d_sos (A, B, C, D); + if (ABCE < 0 || ABCD > 0) { + GtsPoint * tmpp; + gint tmp; + + tmpp = E; E = D; D = tmpp; + tmp = ABCE; ABCE = ABCD; ABCD = tmp; + } + if (ABCE < 0 || ABCD > 0) + return FALSE; + ADCE = gts_point_orientation_3d_sos (A, D, C, E); + if (ADCE < 0) + return FALSE; + ABDE = gts_point_orientation_3d_sos (A, B, D, E); + if (ABDE < 0) + return FALSE; + BCDE = gts_point_orientation_3d_sos (B, C, D, E); + if (BCDE < 0) + return FALSE; + return TRUE; +} + +/** + * gts_point_is_inside_surface: + * @p: a #GtsPoint. + * @tree: a bounding box tree of the faces of a closed, orientable + * surface (see gts_bb_tree_surface()). + * @is_open: %TRUE if the surface defined by @tree is "open" i.e. its volume + * is negative, %FALSE otherwise. + * + * Returns: %TRUE if @p is inside the surface defined by @tree, %FALSE + * otherwise. + */ +gboolean gts_point_is_inside_surface (GtsPoint * p, + GNode * tree, + gboolean is_open) +{ + GSList * list, * i; + guint nc = 0; + GtsPoint * p1; + GtsBBox * bb; + + g_return_val_if_fail (p != NULL, FALSE); + g_return_val_if_fail (tree != NULL, FALSE); + + bb = tree->data; + p1 = gts_point_new (gts_point_class (), bb->x2 + fabs (bb->x2)/10., p->y, p->z); + i = list = gts_bb_tree_stabbed (tree, p); + while (i) { + GtsTriangle * t = GTS_TRIANGLE (GTS_BBOX (i->data)->bounded); + + if (ray_intersects_triangle (p, p1, t)) + nc++; + i = i->next; + } + g_slist_free (list); + gts_object_destroy (GTS_OBJECT (p1)); + + return is_open ? (nc % 2 == 0) : (nc % 2 != 0); +} + +#define SIGN(x) ((x) > 0. ? 1 : -1) +#define ORIENT1D(a,b) ((a) > (b) ? 1 : (a) < (b) ? -1 : 0) + +static gint sortp (gpointer * p, guint n) +{ + gint sign = 1; + guint i, j; + + for (i = 0; i < n - 1; i++) + for (j = 0; j < n - 1 - i; j++) + if (GPOINTER_TO_UINT (p[j+1]) < GPOINTER_TO_UINT (p[j])) { + gpointer tmp = p[j]; + + p[j] = p[j+1]; + p[j+1] = tmp; + sign = - sign; + } + return sign; +} + +/** + * gts_point_orientation_3d_sos: + * @p1: a #GtsPoint. + * @p2: a #GtsPoint. + * @p3: a #GtsPoint. + * @p4: a #GtsPoint. + * + * Checks if @p4 lies above or below the plane passing through the + * points @p1, @p2 and @p3. Below is defined so that @p1, @p2 and @p3 + * appear in counterclockwise order when viewed from above the + * plane. This function uses adaptive floating point arithmetic and is + * consequently geometrically robust. + * + * Simulation of Simplicity (SoS) is used to break ties when the + * orientation is degenerate (i.e. @p4 lies on the plane defined by + * @p1, @p2 and @p3). + * + * Returns: +1 if @p4 lies below, -1 if @p4 lies above the plane. + */ +gint gts_point_orientation_3d_sos (GtsPoint * p1, + GtsPoint * p2, + GtsPoint * p3, + GtsPoint * p4) +{ + gdouble o; + + g_return_val_if_fail (p1 != NULL && p2 != NULL && + p3 != NULL && p4 != NULL, 0); + + o = orient3d ((gdouble *) &p1->x, + (gdouble *) &p2->x, + (gdouble *) &p3->x, + (gdouble *) &p4->x); + if (o != 0.) + return SIGN (o); + else { + GtsPoint * p[4]; + gdouble a[2], b[2], c[2]; + gint sign; + + p[0] = p1; p[1] = p2; p[2] = p3; p[3] = p4; + sign = sortp ((gpointer *) p, 4); + + /* epsilon^1/8 */ + a[0] = p[1]->x; a[1] = p[1]->y; + b[0] = p[2]->x; b[1] = p[2]->y; + c[0] = p[3]->x; c[1] = p[3]->y; + o = orient2d (a, b, c); + if (o != 0.) + return SIGN (o)*sign; + + /* epsilon^1/4 */ + a[0] = p[1]->x; a[1] = p[1]->z; + b[0] = p[2]->x; b[1] = p[2]->z; + c[0] = p[3]->x; c[1] = p[3]->z; + o = orient2d (a, b, c); + if (o != 0.) + return - SIGN (o)*sign; + + /* epsilon^1/2 */ + a[0] = p[1]->y; a[1] = p[1]->z; + b[0] = p[2]->y; b[1] = p[2]->z; + c[0] = p[3]->y; c[1] = p[3]->z; + o = orient2d (a, b, c); + if (o != 0.) + return SIGN (o)*sign; + + /* epsilon */ + a[0] = p[0]->x; a[1] = p[0]->y; + b[0] = p[2]->x; b[1] = p[2]->y; + c[0] = p[3]->x; c[1] = p[3]->y; + o = orient2d (a, b, c); + if (o != 0.) + return - SIGN (o)*sign; + + /* epsilon^5/4 */ + o = ORIENT1D (p[2]->x, p[3]->x); + if (o != 0.) + return SIGN (o)*sign; + + /* epsilon^3/2 */ + o = ORIENT1D (p[2]->y, p[3]->y); + if (o != 0.) + return - SIGN (o)*sign; + + /* epsilon^2 */ + a[0] = p[0]->x; a[1] = p[0]->z; + b[0] = p[2]->x; b[1] = p[2]->z; + c[0] = p[3]->x; c[1] = p[3]->z; + o = orient2d (a, b, c); + if (o != 0.) + return SIGN (o)*sign; + + /* epsilon^5/2 */ + o = ORIENT1D (p[2]->z, p[3]->z); + if (o != 0.) + return SIGN (o)*sign; + + /* epsilon^4 */ + a[0] = p[0]->y; a[1] = p[0]->z; + b[0] = p[2]->y; b[1] = p[2]->z; + c[0] = p[3]->y; c[1] = p[3]->z; + o = orient2d (a, b, c); + if (o != 0.) + return - SIGN (o)*sign; + + /* epsilon^8 */ + a[0] = p[0]->x; a[1] = p[0]->y; + b[0] = p[1]->x; b[1] = p[1]->y; + c[0] = p[3]->x; c[1] = p[3]->y; + o = orient2d (a, b, c); + if (o != 0.) + return SIGN (o)*sign; + + /* epsilon^33/4 */ + o = ORIENT1D (p[1]->x, p[3]->x); + if (o != 0.) + return - SIGN (o)*sign; + + /* epsilon^17/2 */ + o = ORIENT1D (p[1]->y, p[3]->y); + if (o != 0.) + return SIGN (o)*sign; + + /* epsilon^10 */ + o = ORIENT1D (p[0]->x, p[3]->x); + if (o != 0.) + return SIGN (o)*sign; + + /* epsilon^21/2 */ + return sign; + } +} + +/** + * gts_point_orientation_sos: + * @p1: a #GtsPoint. + * @p2: a #GtsPoint. + * @p3: a #GtsPoint. + * + * Checks for orientation of the projection of three points on the + * (x,y) plane. + * + * Simulation of Simplicity (SoS) is used to break ties when the + * orientation is degenerate (i.e. @p3 lies on the line defined by + * @p1 and @p2). + * + * Returns: a positive value if @p1, @p2 and @p3 appear in + * counterclockwise order or a negative value if they appear in + * clockwise order. + */ +gint gts_point_orientation_sos (GtsPoint * p1, + GtsPoint * p2, + GtsPoint * p3) +{ + gdouble o; + + g_return_val_if_fail (p1 != NULL && p2 != NULL && p3 != NULL, 0); + + o = orient2d ((gdouble *) &p1->x, + (gdouble *) &p2->x, + (gdouble *) &p3->x); + if (o != 0.) + return SIGN (o); + else { + GtsPoint * p[3]; + gint sign; + + p[0] = p1; p[1] = p2; p[2] = p3; + sign = sortp ((gpointer *) p, 3); + + /* epsilon^1/4 */ + o = ORIENT1D (p[1]->x, p[2]->x); + if (o != 0.) + return - SIGN (o)*sign; + + /* epsilon^1/2 */ + o = ORIENT1D (p[1]->y, p[2]->y); + if (o != 0.) + return SIGN (o)*sign; + + /* epsilon */ + o = ORIENT1D (p[0]->x, p[2]->x); + if (o != 0.) + return SIGN (o)*sign; + + /* epsilon^3/2 */ + return sign; + } +} Index: toporouter/src_3rd/gts/predicates.c =================================================================== --- toporouter/src_3rd/gts/predicates.c (nonexistent) +++ toporouter/src_3rd/gts/predicates.c (revision 6803) @@ -0,0 +1,2742 @@ +/*****************************************************************************/ +/* */ +/* Routines for Arbitrary Precision Floating-point Arithmetic */ +/* and Fast Robust Geometric Predicates */ +/* (predicates.c) */ +/* */ +/* May 18, 1996 */ +/* */ +/* Placed in the public domain by */ +/* Jonathan Richard Shewchuk */ +/* School of Computer Science */ +/* Carnegie Mellon University */ +/* 5000 Forbes Avenue */ +/* Pittsburgh, Pennsylvania 15213-3891 */ +/* jrs@cs.cmu.edu */ +/* */ +/* This file contains C implementation of algorithms for exact addition */ +/* and multiplication of floating-point numbers, and predicates for */ +/* robustly performing the orientation and incircle tests used in */ +/* computational geometry. The algorithms and underlying theory are */ +/* described in Jonathan Richard Shewchuk. "Adaptive Precision Floating- */ +/* Point Arithmetic and Fast Robust Geometric Predicates." Technical */ +/* Report CMU-CS-96-140, School of Computer Science, Carnegie Mellon */ +/* University, Pittsburgh, Pennsylvania, May 1996. (Submitted to */ +/* Discrete & Computational Geometry.) */ +/* */ +/* This file, the paper listed above, and other information are available */ +/* from the Web page http://www.cs.cmu.edu/~quake/robust.html . */ +/* */ +/*****************************************************************************/ + +/*****************************************************************************/ +/* */ +/* Using this code: */ +/* */ +/* First, read the short or long version of the paper (from the Web page */ +/* above). */ +/* */ +/* Be sure to call exactinit() once, before calling any of the arithmetic */ +/* functions or geometric predicates. Also be sure to turn on the */ +/* optimizer when compiling this file. */ +/* */ +/* */ +/* Several geometric predicates are defined. Their parameters are all */ +/* points. Each point is an array of two or three floating-point */ +/* numbers. The geometric predicates, described in the papers, are */ +/* */ +/* orient2d(pa, pb, pc) */ +/* orient2dfast(pa, pb, pc) */ +/* orient3d(pa, pb, pc, pd) */ +/* orient3dfast(pa, pb, pc, pd) */ +/* incircle(pa, pb, pc, pd) */ +/* incirclefast(pa, pb, pc, pd) */ +/* insphere(pa, pb, pc, pd, pe) */ +/* inspherefast(pa, pb, pc, pd, pe) */ +/* */ +/* Those with suffix "fast" are approximate, non-robust versions. Those */ +/* without the suffix are adaptive precision, robust versions. There */ +/* are also versions with the suffices "exact" and "slow", which are */ +/* non-adaptive, exact arithmetic versions, which I use only for timings */ +/* in my arithmetic papers. */ +/* */ +/* */ +/* An expansion is represented by an array of floating-point numbers, */ +/* sorted from smallest to largest magnitude (possibly with interspersed */ +/* zeros). The length of each expansion is stored as a separate integer, */ +/* and each arithmetic function returns an integer which is the length */ +/* of the expansion it created. */ +/* */ +/* Several arithmetic functions are defined. Their parameters are */ +/* */ +/* e, f Input expansions */ +/* elen, flen Lengths of input expansions (must be >= 1) */ +/* h Output expansion */ +/* b Input scalar */ +/* */ +/* The arithmetic functions are */ +/* */ +/* grow_expansion(elen, e, b, h) */ +/* grow_expansion_zeroelim(elen, e, b, h) */ +/* expansion_sum(elen, e, flen, f, h) */ +/* expansion_sum_zeroelim1(elen, e, flen, f, h) */ +/* expansion_sum_zeroelim2(elen, e, flen, f, h) */ +/* fast_expansion_sum(elen, e, flen, f, h) */ +/* fast_expansion_sum_zeroelim(elen, e, flen, f, h) */ +/* linear_expansion_sum(elen, e, flen, f, h) */ +/* linear_expansion_sum_zeroelim(elen, e, flen, f, h) */ +/* scale_expansion(elen, e, b, h) */ +/* scale_expansion_zeroelim(elen, e, b, h) */ +/* compress(elen, e, h) */ +/* */ +/* All of these are described in the long version of the paper; some are */ +/* described in the short version. All return an integer that is the */ +/* length of h. Those with suffix _zeroelim perform zero elimination, */ +/* and are recommended over their counterparts. The procedure */ +/* fast_expansion_sum_zeroelim() (or linear_expansion_sum_zeroelim() on */ +/* processors that do not use the round-to-even tiebreaking rule) is */ +/* recommended over expansion_sum_zeroelim(). Each procedure has a */ +/* little note next to it (in the code below) that tells you whether or */ +/* not the output expansion may be the same array as one of the input */ +/* expansions. */ +/* */ +/* */ +/* If you look around below, you'll also find macros for a bunch of */ +/* simple unrolled arithmetic operations, and procedures for printing */ +/* expansions (commented out because they don't work with all C */ +/* compilers) and for generating random floating-point numbers whose */ +/* significand bits are all random. Most of the macros have undocumented */ +/* requirements that certain of their parameters should not be the same */ +/* variable; for safety, better to make sure all the parameters are */ +/* distinct variables. Feel free to send email to jrs@cs.cmu.edu if you */ +/* have questions. */ +/* */ +/*****************************************************************************/ + +#include +#include +#include +#include "predicates.h" + +/* Use header file generated automatically by predicates_init. */ +//#define USE_PREDICATES_INIT + +#ifdef USE_PREDICATES_INIT +#include "predicates_init.h" +#endif /* USE_PREDICATES_INIT */ + +/* FPU control. We MUST have only double precision (not extended precision) */ +#include "rounding.h" + +/* On some machines, the exact arithmetic routines might be defeated by the */ +/* use of internal extended precision floating-point registers. Sometimes */ +/* this problem can be fixed by defining certain values to be volatile, */ +/* thus forcing them to be stored to memory and rounded off. This isn't */ +/* a great solution, though, as it slows the arithmetic down. */ +/* */ +/* To try this out, write "#define INEXACT volatile" below. Normally, */ +/* however, INEXACT should be defined to be nothing. ("#define INEXACT".) */ + +#define INEXACT /* Nothing */ +/* #define INEXACT volatile */ + +#define REAL double /* float or double */ +#define REALPRINT doubleprint +#define REALRAND doublerand +#define NARROWRAND narrowdoublerand +#define UNIFORMRAND uniformdoublerand + +/* Which of the following two methods of finding the absolute values is */ +/* fastest is compiler-dependent. A few compilers can inline and optimize */ +/* the fabs() call; but most will incur the overhead of a function call, */ +/* which is disastrously slow. A faster way on IEEE machines might be to */ +/* mask the appropriate bit, but that's difficult to do in C. */ + +#define Absolute(a) ((a) >= 0.0 ? (a) : -(a)) +/* #define Absolute(a) fabs(a) */ + +/* Many of the operations are broken up into two pieces, a main part that */ +/* performs an approximate operation, and a "tail" that computes the */ +/* roundoff error of that operation. */ +/* */ +/* The operations Fast_Two_Sum(), Fast_Two_Diff(), Two_Sum(), Two_Diff(), */ +/* Split(), and Two_Product() are all implemented as described in the */ +/* reference. Each of these macros requires certain variables to be */ +/* defined in the calling routine. The variables `bvirt', `c', `abig', */ +/* `_i', `_j', `_k', `_l', `_m', and `_n' are declared `INEXACT' because */ +/* they store the result of an operation that may incur roundoff error. */ +/* The input parameter `x' (or the highest numbered `x_' parameter) must */ +/* also be declared `INEXACT'. */ + +#define Fast_Two_Sum_Tail(a, b, x, y) \ + bvirt = x - a; \ + y = b - bvirt + +#define Fast_Two_Sum(a, b, x, y) \ + x = (REAL) (a + b); \ + Fast_Two_Sum_Tail(a, b, x, y) + +#define Fast_Two_Diff_Tail(a, b, x, y) \ + bvirt = a - x; \ + y = bvirt - b + +#define Fast_Two_Diff(a, b, x, y) \ + x = (REAL) (a - b); \ + Fast_Two_Diff_Tail(a, b, x, y) + +#define Two_Sum_Tail(a, b, x, y) \ + bvirt = (REAL) (x - a); \ + avirt = x - bvirt; \ + bround = b - bvirt; \ + around = a - avirt; \ + y = around + bround + +#define Two_Sum(a, b, x, y) \ + x = (REAL) (a + b); \ + Two_Sum_Tail(a, b, x, y) + +#define Two_Diff_Tail(a, b, x, y) \ + bvirt = (REAL) (a - x); \ + avirt = x + bvirt; \ + bround = bvirt - b; \ + around = a - avirt; \ + y = around + bround + +#define Two_Diff(a, b, x, y) \ + x = (REAL) (a - b); \ + Two_Diff_Tail(a, b, x, y) + +#define Split(a, ahi, alo) \ + c = (REAL) (splitter * a); \ + abig = (REAL) (c - a); \ + ahi = c - abig; \ + alo = a - ahi + +#define Two_Product_Tail(a, b, x, y) \ + Split(a, ahi, alo); \ + Split(b, bhi, blo); \ + err1 = x - (ahi * bhi); \ + err2 = err1 - (alo * bhi); \ + err3 = err2 - (ahi * blo); \ + y = (alo * blo) - err3 + +#define Two_Product(a, b, x, y) \ + x = (REAL) (a * b); \ + Two_Product_Tail(a, b, x, y) + +/* Two_Product_Presplit() is Two_Product() where one of the inputs has */ +/* already been split. Avoids redundant splitting. */ + +#define Two_Product_Presplit(a, b, bhi, blo, x, y) \ + x = (REAL) (a * b); \ + Split(a, ahi, alo); \ + err1 = x - (ahi * bhi); \ + err2 = err1 - (alo * bhi); \ + err3 = err2 - (ahi * blo); \ + y = (alo * blo) - err3 + +/* Two_Product_2Presplit() is Two_Product() where both of the inputs have */ +/* already been split. Avoids redundant splitting. */ + +#define Two_Product_2Presplit(a, ahi, alo, b, bhi, blo, x, y) \ + x = (REAL) (a * b); \ + err1 = x - (ahi * bhi); \ + err2 = err1 - (alo * bhi); \ + err3 = err2 - (ahi * blo); \ + y = (alo * blo) - err3 + +/* Square() can be done more quickly than Two_Product(). */ + +#define Square_Tail(a, x, y) \ + Split(a, ahi, alo); \ + err1 = x - (ahi * ahi); \ + err3 = err1 - ((ahi + ahi) * alo); \ + y = (alo * alo) - err3 + +#define Square(a, x, y) \ + x = (REAL) (a * a); \ + Square_Tail(a, x, y) + +/* Macros for summing expansions of various fixed lengths. These are all */ +/* unrolled versions of Expansion_Sum(). */ + +#define Two_One_Sum(a1, a0, b, x2, x1, x0) \ + Two_Sum(a0, b , _i, x0); \ + Two_Sum(a1, _i, x2, x1) + +#define Two_One_Diff(a1, a0, b, x2, x1, x0) \ + Two_Diff(a0, b , _i, x0); \ + Two_Sum( a1, _i, x2, x1) + +#define Two_Two_Sum(a1, a0, b1, b0, x3, x2, x1, x0) \ + Two_One_Sum(a1, a0, b0, _j, _0, x0); \ + Two_One_Sum(_j, _0, b1, x3, x2, x1) + +#define Two_Two_Diff(a1, a0, b1, b0, x3, x2, x1, x0) \ + Two_One_Diff(a1, a0, b0, _j, _0, x0); \ + Two_One_Diff(_j, _0, b1, x3, x2, x1) + +#define Four_One_Sum(a3, a2, a1, a0, b, x4, x3, x2, x1, x0) \ + Two_One_Sum(a1, a0, b , _j, x1, x0); \ + Two_One_Sum(a3, a2, _j, x4, x3, x2) + +#define Four_Two_Sum(a3, a2, a1, a0, b1, b0, x5, x4, x3, x2, x1, x0) \ + Four_One_Sum(a3, a2, a1, a0, b0, _k, _2, _1, _0, x0); \ + Four_One_Sum(_k, _2, _1, _0, b1, x5, x4, x3, x2, x1) + +#define Four_Four_Sum(a3, a2, a1, a0, b4, b3, b1, b0, x7, x6, x5, x4, x3, x2, \ + x1, x0) \ + Four_Two_Sum(a3, a2, a1, a0, b1, b0, _l, _2, _1, _0, x1, x0); \ + Four_Two_Sum(_l, _2, _1, _0, b4, b3, x7, x6, x5, x4, x3, x2) + +#define Eight_One_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b, x8, x7, x6, x5, x4, \ + x3, x2, x1, x0) \ + Four_One_Sum(a3, a2, a1, a0, b , _j, x3, x2, x1, x0); \ + Four_One_Sum(a7, a6, a5, a4, _j, x8, x7, x6, x5, x4) + +#define Eight_Two_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b1, b0, x9, x8, x7, \ + x6, x5, x4, x3, x2, x1, x0) \ + Eight_One_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b0, _k, _6, _5, _4, _3, _2, \ + _1, _0, x0); \ + Eight_One_Sum(_k, _6, _5, _4, _3, _2, _1, _0, b1, x9, x8, x7, x6, x5, x4, \ + x3, x2, x1) + +#define Eight_Four_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b4, b3, b1, b0, x11, \ + x10, x9, x8, x7, x6, x5, x4, x3, x2, x1, x0) \ + Eight_Two_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b1, b0, _l, _6, _5, _4, _3, \ + _2, _1, _0, x1, x0); \ + Eight_Two_Sum(_l, _6, _5, _4, _3, _2, _1, _0, b4, b3, x11, x10, x9, x8, \ + x7, x6, x5, x4, x3, x2) + +/* Macros for multiplying expansions of various fixed lengths. */ + +#define Two_One_Product(a1, a0, b, x3, x2, x1, x0) \ + Split(b, bhi, blo); \ + Two_Product_Presplit(a0, b, bhi, blo, _i, x0); \ + Two_Product_Presplit(a1, b, bhi, blo, _j, _0); \ + Two_Sum(_i, _0, _k, x1); \ + Fast_Two_Sum(_j, _k, x3, x2) + +#define Four_One_Product(a3, a2, a1, a0, b, x7, x6, x5, x4, x3, x2, x1, x0) \ + Split(b, bhi, blo); \ + Two_Product_Presplit(a0, b, bhi, blo, _i, x0); \ + Two_Product_Presplit(a1, b, bhi, blo, _j, _0); \ + Two_Sum(_i, _0, _k, x1); \ + Fast_Two_Sum(_j, _k, _i, x2); \ + Two_Product_Presplit(a2, b, bhi, blo, _j, _0); \ + Two_Sum(_i, _0, _k, x3); \ + Fast_Two_Sum(_j, _k, _i, x4); \ + Two_Product_Presplit(a3, b, bhi, blo, _j, _0); \ + Two_Sum(_i, _0, _k, x5); \ + Fast_Two_Sum(_j, _k, x7, x6) + +#define Two_Two_Product(a1, a0, b1, b0, x7, x6, x5, x4, x3, x2, x1, x0) \ + Split(a0, a0hi, a0lo); \ + Split(b0, bhi, blo); \ + Two_Product_2Presplit(a0, a0hi, a0lo, b0, bhi, blo, _i, x0); \ + Split(a1, a1hi, a1lo); \ + Two_Product_2Presplit(a1, a1hi, a1lo, b0, bhi, blo, _j, _0); \ + Two_Sum(_i, _0, _k, _1); \ + Fast_Two_Sum(_j, _k, _l, _2); \ + Split(b1, bhi, blo); \ + Two_Product_2Presplit(a0, a0hi, a0lo, b1, bhi, blo, _i, _0); \ + Two_Sum(_1, _0, _k, x1); \ + Two_Sum(_2, _k, _j, _1); \ + Two_Sum(_l, _j, _m, _2); \ + Two_Product_2Presplit(a1, a1hi, a1lo, b1, bhi, blo, _j, _0); \ + Two_Sum(_i, _0, _n, _0); \ + Two_Sum(_1, _0, _i, x2); \ + Two_Sum(_2, _i, _k, _1); \ + Two_Sum(_m, _k, _l, _2); \ + Two_Sum(_j, _n, _k, _0); \ + Two_Sum(_1, _0, _j, x3); \ + Two_Sum(_2, _j, _i, _1); \ + Two_Sum(_l, _i, _m, _2); \ + Two_Sum(_1, _k, _i, x4); \ + Two_Sum(_2, _i, _k, x5); \ + Two_Sum(_m, _k, x7, x6) + +/* An expansion of length two can be squared more quickly than finding the */ +/* product of two different expansions of length two, and the result is */ +/* guaranteed to have no more than six (rather than eight) components. */ + +#define Two_Square(a1, a0, x5, x4, x3, x2, x1, x0) \ + Square(a0, _j, x0); \ + _0 = a0 + a0; \ + Two_Product(a1, _0, _k, _1); \ + Two_One_Sum(_k, _1, _j, _l, _2, x1); \ + Square(a1, _j, _1); \ + Two_Two_Sum(_j, _1, _l, _2, x5, x4, x3, x2) + +#ifndef USE_PREDICATES_INIT + +static REAL splitter; /* = 2^ceiling(p / 2) + 1. Used to split floats in half. */ +/* A set of coefficients used to calculate maximum roundoff errors. */ +static REAL resulterrbound; +static REAL ccwerrboundA, ccwerrboundB, ccwerrboundC; +static REAL o3derrboundA, o3derrboundB, o3derrboundC; +static REAL iccerrboundA, iccerrboundB, iccerrboundC; +static REAL isperrboundA, isperrboundB, isperrboundC; + +void +gts_predicates_init() +{ + double half = 0.5; + double check = 1.0, lastcheck; + int every_other = 1; + /* epsilon = 2^(-p). Used to estimate roundoff errors. */ + double epsilon = 1.0; + + FPU_ROUND_DOUBLE; + + splitter = 1.; + + /* Repeatedly divide `epsilon' by two until it is too small to add to */ + /* one without causing roundoff. (Also check if the sum is equal to */ + /* the previous sum, for machines that round up instead of using exact */ + /* rounding. Not that this library will work on such machines anyway). */ + do { + lastcheck = check; + epsilon *= half; + if (every_other) { + splitter *= 2.0; + } + every_other = !every_other; + check = 1.0 + epsilon; + } while ((check != 1.0) && (check != lastcheck)); + splitter += 1.0; + /* Error bounds for orientation and incircle tests. */ + resulterrbound = (3.0 + 8.0 * epsilon) * epsilon; + ccwerrboundA = (3.0 + 16.0 * epsilon) * epsilon; + ccwerrboundB = (2.0 + 12.0 * epsilon) * epsilon; + ccwerrboundC = (9.0 + 64.0 * epsilon) * epsilon * epsilon; + o3derrboundA = (7.0 + 56.0 * epsilon) * epsilon; + o3derrboundB = (3.0 + 28.0 * epsilon) * epsilon; + o3derrboundC = (26.0 + 288.0 * epsilon) * epsilon * epsilon; + iccerrboundA = (10.0 + 96.0 * epsilon) * epsilon; + iccerrboundB = (4.0 + 48.0 * epsilon) * epsilon; + iccerrboundC = (44.0 + 576.0 * epsilon) * epsilon * epsilon; + isperrboundA = (16.0 + 224.0 * epsilon) * epsilon; + isperrboundB = (5.0 + 72.0 * epsilon) * epsilon; + isperrboundC = (71.0 + 1408.0 * epsilon) * epsilon * epsilon; + + + FPU_RESTORE; +} + +#endif /* USE_PREDICATES_INIT */ + +/*****************************************************************************/ +/* */ +/* doubleprint() Print the bit representation of a double. */ +/* */ +/* Useful for debugging exact arithmetic routines. */ +/* */ +/*****************************************************************************/ + +/* +void doubleprint(number) +double number; +{ + unsigned long long no; + unsigned long long sign, expo; + int exponent; + int i, bottomi; + + no = *(unsigned long long *) &number; + sign = no & 0x8000000000000000ll; + expo = (no >> 52) & 0x7ffll; + exponent = (int) expo; + exponent = exponent - 1023; + if (sign) { + printf("-"); + } else { + printf(" "); + } + if (exponent == -1023) { + printf( + "0.0000000000000000000000000000000000000000000000000000_ ( )"); + } else { + printf("1."); + bottomi = -1; + for (i = 0; i < 52; i++) { + if (no & 0x0008000000000000ll) { + printf("1"); + bottomi = i; + } else { + printf("0"); + } + no <<= 1; + } + printf("_%d (%d)", exponent, exponent - 1 - bottomi); + } +} +*/ + +/*****************************************************************************/ +/* */ +/* floatprint() Print the bit representation of a float. */ +/* */ +/* Useful for debugging exact arithmetic routines. */ +/* */ +/*****************************************************************************/ + +/* +void floatprint(number) +float number; +{ + unsigned no; + unsigned sign, expo; + int exponent; + int i, bottomi; + + no = *(unsigned *) &number; + sign = no & 0x80000000; + expo = (no >> 23) & 0xff; + exponent = (int) expo; + exponent = exponent - 127; + if (sign) { + printf("-"); + } else { + printf(" "); + } + if (exponent == -127) { + printf("0.00000000000000000000000_ ( )"); + } else { + printf("1."); + bottomi = -1; + for (i = 0; i < 23; i++) { + if (no & 0x00400000) { + printf("1"); + bottomi = i; + } else { + printf("0"); + } + no <<= 1; + } + printf("_%3d (%3d)", exponent, exponent - 1 - bottomi); + } +} +*/ + +/*****************************************************************************/ +/* */ +/* expansion_print() Print the bit representation of an expansion. */ +/* */ +/* Useful for debugging exact arithmetic routines. */ +/* */ +/*****************************************************************************/ + +/* +void expansion_print(elen, e) +int elen; +REAL *e; +{ + int i; + + for (i = elen - 1; i >= 0; i--) { + REALPRINT(e[i]); + if (i > 0) { + printf(" +\n"); + } else { + printf("\n"); + } + } +} +*/ + +/*****************************************************************************/ +/* */ +/* doublerand() Generate a double with random 53-bit significand and a */ +/* random exponent in [0, 511]. */ +/* */ +/*****************************************************************************/ + +/* +static double doublerand() +{ + double result; + double expo; + long a, b, c; + long i; + + a = random(); + b = random(); + c = random(); + result = (double) (a - 1073741824) * 8388608.0 + (double) (b >> 8); + for (i = 512, expo = 2; i <= 131072; i *= 2, expo = expo * expo) { + if (c & i) { + result *= expo; + } + } + return result; +} +*/ + +/*****************************************************************************/ +/* */ +/* narrowdoublerand() Generate a double with random 53-bit significand */ +/* and a random exponent in [0, 7]. */ +/* */ +/*****************************************************************************/ + +/* +static double narrowdoublerand() +{ + double result; + double expo; + long a, b, c; + long i; + + a = random(); + b = random(); + c = random(); + result = (double) (a - 1073741824) * 8388608.0 + (double) (b >> 8); + for (i = 512, expo = 2; i <= 2048; i *= 2, expo = expo * expo) { + if (c & i) { + result *= expo; + } + } + return result; +} +*/ + +/*****************************************************************************/ +/* */ +/* uniformdoublerand() Generate a double with random 53-bit significand. */ +/* */ +/*****************************************************************************/ + +/* +static double uniformdoublerand() +{ + double result; + long a, b; + + a = random(); + b = random(); + result = (double) (a - 1073741824) * 8388608.0 + (double) (b >> 8); + return result; +} +*/ + +/*****************************************************************************/ +/* */ +/* floatrand() Generate a float with random 24-bit significand and a */ +/* random exponent in [0, 63]. */ +/* */ +/*****************************************************************************/ + +/* +static float floatrand() +{ + float result; + float expo; + long a, c; + long i; + + a = random(); + c = random(); + result = (float) ((a - 1073741824) >> 6); + for (i = 512, expo = 2; i <= 16384; i *= 2, expo = expo * expo) { + if (c & i) { + result *= expo; + } + } + return result; +} +*/ + +/*****************************************************************************/ +/* */ +/* narrowfloatrand() Generate a float with random 24-bit significand and */ +/* a random exponent in [0, 7]. */ +/* */ +/*****************************************************************************/ + +/* +static float narrowfloatrand() +{ + float result; + float expo; + long a, c; + long i; + + a = random(); + c = random(); + result = (float) ((a - 1073741824) >> 6); + for (i = 512, expo = 2; i <= 2048; i *= 2, expo = expo * expo) { + if (c & i) { + result *= expo; + } + } + return result; +} +*/ + +/*****************************************************************************/ +/* */ +/* uniformfloatrand() Generate a float with random 24-bit significand. */ +/* */ +/*****************************************************************************/ + +/* +static float uniformfloatrand() +{ + float result; + long a; + + a = random(); + result = (float) ((a - 1073741824) >> 6); + return result; +} +*/ + +/*****************************************************************************/ +/* */ +/* fast_expansion_sum_zeroelim() Sum two expansions, eliminating zero */ +/* components from the output expansion. */ +/* */ +/* Sets h = e + f. See the long version of my paper for details. */ +/* */ +/* If round-to-even is used (as with IEEE 754), maintains the strongly */ +/* nonoverlapping property. (That is, if e is strongly nonoverlapping, h */ +/* will be also.) Does NOT maintain the nonoverlapping or nonadjacent */ +/* properties. */ +/* */ +/*****************************************************************************/ + +static int fast_expansion_sum_zeroelim(int elen, REAL *e, + int flen, REAL *f, REAL *h) + /* h cannot be e or f. */ +{ + REAL Q; + INEXACT REAL Qnew; + INEXACT REAL hh; + INEXACT REAL bvirt; + REAL avirt, bround, around; + int eindex, findex, hindex; + REAL enow, fnow; + + enow = e[0]; + fnow = f[0]; + eindex = findex = 0; + if ((fnow > enow) == (fnow > -enow)) { + Q = enow; + enow = e[++eindex]; + } else { + Q = fnow; + fnow = f[++findex]; + } + hindex = 0; + if ((eindex < elen) && (findex < flen)) { + if ((fnow > enow) == (fnow > -enow)) { + Fast_Two_Sum(enow, Q, Qnew, hh); + enow = e[++eindex]; + } else { + Fast_Two_Sum(fnow, Q, Qnew, hh); + fnow = f[++findex]; + } + Q = Qnew; + if (hh != 0.0) { + h[hindex++] = hh; + } + while ((eindex < elen) && (findex < flen)) { + if ((fnow > enow) == (fnow > -enow)) { + Two_Sum(Q, enow, Qnew, hh); + enow = e[++eindex]; + } else { + Two_Sum(Q, fnow, Qnew, hh); + fnow = f[++findex]; + } + Q = Qnew; + if (hh != 0.0) { + h[hindex++] = hh; + } + } + } + while (eindex < elen) { + Two_Sum(Q, enow, Qnew, hh); + enow = e[++eindex]; + Q = Qnew; + if (hh != 0.0) { + h[hindex++] = hh; + } + } + while (findex < flen) { + Two_Sum(Q, fnow, Qnew, hh); + fnow = f[++findex]; + Q = Qnew; + if (hh != 0.0) { + h[hindex++] = hh; + } + } + if ((Q != 0.0) || (hindex == 0)) { + h[hindex++] = Q; + } + return hindex; +} + +/*****************************************************************************/ +/* */ +/* scale_expansion_zeroelim() Multiply an expansion by a scalar, */ +/* eliminating zero components from the */ +/* output expansion. */ +/* */ +/* Sets h = be. See either version of my paper for details. */ +/* */ +/* Maintains the nonoverlapping property. If round-to-even is used (as */ +/* with IEEE 754), maintains the strongly nonoverlapping and nonadjacent */ +/* properties as well. (That is, if e has one of these properties, so */ +/* will h.) */ +/* */ +/*****************************************************************************/ + +static int scale_expansion_zeroelim(int elen, REAL *e, REAL b, REAL *h) + /* e and h cannot be the same. */ +{ + INEXACT REAL Q, sum; + REAL hh; + INEXACT REAL product1; + REAL product0; + int eindex, hindex; + REAL enow; + INEXACT REAL bvirt; + REAL avirt, bround, around; + INEXACT REAL c; + INEXACT REAL abig; + REAL ahi, alo, bhi, blo; + REAL err1, err2, err3; + + Split(b, bhi, blo); + Two_Product_Presplit(e[0], b, bhi, blo, Q, hh); + hindex = 0; + if (hh != 0) { + h[hindex++] = hh; + } + for (eindex = 1; eindex < elen; eindex++) { + enow = e[eindex]; + Two_Product_Presplit(enow, b, bhi, blo, product1, product0); + Two_Sum(Q, product0, sum, hh); + if (hh != 0) { + h[hindex++] = hh; + } + Fast_Two_Sum(product1, sum, Q, hh); + if (hh != 0) { + h[hindex++] = hh; + } + } + if ((Q != 0.0) || (hindex == 0)) { + h[hindex++] = Q; + } + return hindex; +} + +/*****************************************************************************/ +/* */ +/* estimate() Produce a one-word estimate of an expansion's value. */ +/* */ +/* See either version of my paper for details. */ +/* */ +/*****************************************************************************/ + +static REAL estimate(int elen, REAL *e) +{ + REAL Q; + int eindex; + + Q = e[0]; + for (eindex = 1; eindex < elen; eindex++) { + Q += e[eindex]; + } + return Q; +} + +/*****************************************************************************/ +/* */ +/* orient2dfast() Approximate 2D orientation test. Nonrobust. */ +/* orient2dexact() Exact 2D orientation test. Robust. */ +/* orient2dslow() Another exact 2D orientation test. Robust. */ +/* orient2d() Adaptive exact 2D orientation test. Robust. */ +/* */ +/* Return a positive value if the points pa, pb, and pc occur */ +/* in counterclockwise order; a negative value if they occur */ +/* in clockwise order; and zero if they are collinear. The */ +/* result is also a rough approximation of twice the signed */ +/* area of the triangle defined by the three points. */ +/* */ +/* Only the first and last routine should be used; the middle two are for */ +/* timings. */ +/* */ +/* The last three use exact arithmetic to ensure a correct answer. The */ +/* result returned is the determinant of a matrix. In orient2d() only, */ +/* this determinant is computed adaptively, in the sense that exact */ +/* arithmetic is used only to the degree it is needed to ensure that the */ +/* returned value has the correct sign. Hence, orient2d() is usually quite */ +/* fast, but will run more slowly when the input points are collinear or */ +/* nearly so. */ +/* */ +/*****************************************************************************/ + +static REAL orient2dadapt(REAL *pa, REAL *pb, REAL *pc, REAL detsum) +{ + INEXACT REAL acx, acy, bcx, bcy; + REAL acxtail, acytail, bcxtail, bcytail; + INEXACT REAL detleft, detright; + REAL detlefttail, detrighttail; + REAL det, errbound; + REAL B[4], C1[8], C2[12], D[16]; + INEXACT REAL B3; + int C1length, C2length, Dlength; + REAL u[4]; + INEXACT REAL u3; + INEXACT REAL s1, t1; + REAL s0, t0; + + INEXACT REAL bvirt; + REAL avirt, bround, around; + INEXACT REAL c; + INEXACT REAL abig; + REAL ahi, alo, bhi, blo; + REAL err1, err2, err3; + INEXACT REAL _i, _j; + REAL _0; + + acx = (REAL) (pa[0] - pc[0]); + bcx = (REAL) (pb[0] - pc[0]); + acy = (REAL) (pa[1] - pc[1]); + bcy = (REAL) (pb[1] - pc[1]); + + Two_Product(acx, bcy, detleft, detlefttail); + Two_Product(acy, bcx, detright, detrighttail); + + Two_Two_Diff(detleft, detlefttail, detright, detrighttail, + B3, B[2], B[1], B[0]); + B[3] = B3; + + det = estimate(4, B); + errbound = ccwerrboundB * detsum; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Diff_Tail(pa[0], pc[0], acx, acxtail); + Two_Diff_Tail(pb[0], pc[0], bcx, bcxtail); + Two_Diff_Tail(pa[1], pc[1], acy, acytail); + Two_Diff_Tail(pb[1], pc[1], bcy, bcytail); + + if ((acxtail == 0.0) && (acytail == 0.0) + && (bcxtail == 0.0) && (bcytail == 0.0)) { + return det; + } + + errbound = ccwerrboundC * detsum + resulterrbound * Absolute(det); + det += (acx * bcytail + bcy * acxtail) + - (acy * bcxtail + bcx * acytail); + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Product(acxtail, bcy, s1, s0); + Two_Product(acytail, bcx, t1, t0); + Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]); + u[3] = u3; + C1length = fast_expansion_sum_zeroelim(4, B, 4, u, C1); + + Two_Product(acx, bcytail, s1, s0); + Two_Product(acy, bcxtail, t1, t0); + Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]); + u[3] = u3; + C2length = fast_expansion_sum_zeroelim(C1length, C1, 4, u, C2); + + Two_Product(acxtail, bcytail, s1, s0); + Two_Product(acytail, bcxtail, t1, t0); + Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]); + u[3] = u3; + Dlength = fast_expansion_sum_zeroelim(C2length, C2, 4, u, D); + + return(D[Dlength - 1]); +} + +REAL orient2d(pa, pb, pc) +REAL *pa; +REAL *pb; +REAL *pc; +{ + REAL detleft, detright, det; + REAL detsum, errbound; + REAL orient; + + FPU_ROUND_DOUBLE; + + detleft = (pa[0] - pc[0]) * (pb[1] - pc[1]); + detright = (pa[1] - pc[1]) * (pb[0] - pc[0]); + det = detleft - detright; + + if (detleft > 0.0) { + if (detright <= 0.0) { + FPU_RESTORE; + return det; + } else { + detsum = detleft + detright; + } + } else if (detleft < 0.0) { + if (detright >= 0.0) { + FPU_RESTORE; + return det; + } else { + detsum = -detleft - detright; + } + } else { + FPU_RESTORE; + return det; + } + + errbound = ccwerrboundA * detsum; + if ((det >= errbound) || (-det >= errbound)) { + FPU_RESTORE; + return det; + } + + orient = orient2dadapt(pa, pb, pc, detsum); + FPU_RESTORE; + return orient; +} + +/*****************************************************************************/ +/* */ +/* orient3dfast() Approximate 3D orientation test. Nonrobust. */ +/* orient3dexact() Exact 3D orientation test. Robust. */ +/* orient3dslow() Another exact 3D orientation test. Robust. */ +/* orient3d() Adaptive exact 3D orientation test. Robust. */ +/* */ +/* Return a positive value if the point pd lies below the */ +/* plane passing through pa, pb, and pc; "below" is defined so */ +/* that pa, pb, and pc appear in counterclockwise order when */ +/* viewed from above the plane. Returns a negative value if */ +/* pd lies above the plane. Returns zero if the points are */ +/* coplanar. The result is also a rough approximation of six */ +/* times the signed volume of the tetrahedron defined by the */ +/* four points. */ +/* */ +/* Only the first and last routine should be used; the middle two are for */ +/* timings. */ +/* */ +/* The last three use exact arithmetic to ensure a correct answer. The */ +/* result returned is the determinant of a matrix. In orient3d() only, */ +/* this determinant is computed adaptively, in the sense that exact */ +/* arithmetic is used only to the degree it is needed to ensure that the */ +/* returned value has the correct sign. Hence, orient3d() is usually quite */ +/* fast, but will run more slowly when the input points are coplanar or */ +/* nearly so. */ +/* */ +/*****************************************************************************/ + +static REAL orient3dadapt(REAL *pa, REAL *pb, REAL *pc, REAL *pd, + REAL permanent) +{ + INEXACT REAL adx, bdx, cdx, ady, bdy, cdy, adz, bdz, cdz; + REAL det, errbound; + + INEXACT REAL bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1; + REAL bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0; + REAL bc[4], ca[4], ab[4]; + INEXACT REAL bc3, ca3, ab3; + REAL adet[8], bdet[8], cdet[8]; + int alen, blen, clen; + REAL abdet[16]; + int ablen; + REAL *finnow, *finother, *finswap; + REAL fin1[192], fin2[192]; + int finlength; + + REAL adxtail, bdxtail, cdxtail; + REAL adytail, bdytail, cdytail; + REAL adztail, bdztail, cdztail; + INEXACT REAL at_blarge, at_clarge; + INEXACT REAL bt_clarge, bt_alarge; + INEXACT REAL ct_alarge, ct_blarge; + REAL at_b[4], at_c[4], bt_c[4], bt_a[4], ct_a[4], ct_b[4]; + int at_blen, at_clen, bt_clen, bt_alen, ct_alen, ct_blen; + INEXACT REAL bdxt_cdy1, cdxt_bdy1, cdxt_ady1; + INEXACT REAL adxt_cdy1, adxt_bdy1, bdxt_ady1; + REAL bdxt_cdy0, cdxt_bdy0, cdxt_ady0; + REAL adxt_cdy0, adxt_bdy0, bdxt_ady0; + INEXACT REAL bdyt_cdx1, cdyt_bdx1, cdyt_adx1; + INEXACT REAL adyt_cdx1, adyt_bdx1, bdyt_adx1; + REAL bdyt_cdx0, cdyt_bdx0, cdyt_adx0; + REAL adyt_cdx0, adyt_bdx0, bdyt_adx0; + REAL bct[8], cat[8], abt[8]; + int bctlen, catlen, abtlen; + INEXACT REAL bdxt_cdyt1, cdxt_bdyt1, cdxt_adyt1; + INEXACT REAL adxt_cdyt1, adxt_bdyt1, bdxt_adyt1; + REAL bdxt_cdyt0, cdxt_bdyt0, cdxt_adyt0; + REAL adxt_cdyt0, adxt_bdyt0, bdxt_adyt0; + REAL u[4], v[12], w[16]; + INEXACT REAL u3; + int vlength, wlength; + REAL negate; + + INEXACT REAL bvirt; + REAL avirt, bround, around; + INEXACT REAL c; + INEXACT REAL abig; + REAL ahi, alo, bhi, blo; + REAL err1, err2, err3; + INEXACT REAL _i, _j, _k; + REAL _0; + + adx = (REAL) (pa[0] - pd[0]); + bdx = (REAL) (pb[0] - pd[0]); + cdx = (REAL) (pc[0] - pd[0]); + ady = (REAL) (pa[1] - pd[1]); + bdy = (REAL) (pb[1] - pd[1]); + cdy = (REAL) (pc[1] - pd[1]); + adz = (REAL) (pa[2] - pd[2]); + bdz = (REAL) (pb[2] - pd[2]); + cdz = (REAL) (pc[2] - pd[2]); + + Two_Product(bdx, cdy, bdxcdy1, bdxcdy0); + Two_Product(cdx, bdy, cdxbdy1, cdxbdy0); + Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]); + bc[3] = bc3; + alen = scale_expansion_zeroelim(4, bc, adz, adet); + + Two_Product(cdx, ady, cdxady1, cdxady0); + Two_Product(adx, cdy, adxcdy1, adxcdy0); + Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]); + ca[3] = ca3; + blen = scale_expansion_zeroelim(4, ca, bdz, bdet); + + Two_Product(adx, bdy, adxbdy1, adxbdy0); + Two_Product(bdx, ady, bdxady1, bdxady0); + Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]); + ab[3] = ab3; + clen = scale_expansion_zeroelim(4, ab, cdz, cdet); + + ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet); + finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1); + + det = estimate(finlength, fin1); + errbound = o3derrboundB * permanent; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Diff_Tail(pa[0], pd[0], adx, adxtail); + Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail); + Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail); + Two_Diff_Tail(pa[1], pd[1], ady, adytail); + Two_Diff_Tail(pb[1], pd[1], bdy, bdytail); + Two_Diff_Tail(pc[1], pd[1], cdy, cdytail); + Two_Diff_Tail(pa[2], pd[2], adz, adztail); + Two_Diff_Tail(pb[2], pd[2], bdz, bdztail); + Two_Diff_Tail(pc[2], pd[2], cdz, cdztail); + + if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0) + && (adytail == 0.0) && (bdytail == 0.0) && (cdytail == 0.0) + && (adztail == 0.0) && (bdztail == 0.0) && (cdztail == 0.0)) { + return det; + } + + errbound = o3derrboundC * permanent + resulterrbound * Absolute(det); + det += (adz * ((bdx * cdytail + cdy * bdxtail) + - (bdy * cdxtail + cdx * bdytail)) + + adztail * (bdx * cdy - bdy * cdx)) + + (bdz * ((cdx * adytail + ady * cdxtail) + - (cdy * adxtail + adx * cdytail)) + + bdztail * (cdx * ady - cdy * adx)) + + (cdz * ((adx * bdytail + bdy * adxtail) + - (ady * bdxtail + bdx * adytail)) + + cdztail * (adx * bdy - ady * bdx)); + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + finnow = fin1; + finother = fin2; + + if (adxtail == 0.0) { + if (adytail == 0.0) { + at_b[0] = 0.0; + at_blen = 1; + at_c[0] = 0.0; + at_clen = 1; + } else { + negate = -adytail; + Two_Product(negate, bdx, at_blarge, at_b[0]); + at_b[1] = at_blarge; + at_blen = 2; + Two_Product(adytail, cdx, at_clarge, at_c[0]); + at_c[1] = at_clarge; + at_clen = 2; + } + } else { + if (adytail == 0.0) { + Two_Product(adxtail, bdy, at_blarge, at_b[0]); + at_b[1] = at_blarge; + at_blen = 2; + negate = -adxtail; + Two_Product(negate, cdy, at_clarge, at_c[0]); + at_c[1] = at_clarge; + at_clen = 2; + } else { + Two_Product(adxtail, bdy, adxt_bdy1, adxt_bdy0); + Two_Product(adytail, bdx, adyt_bdx1, adyt_bdx0); + Two_Two_Diff(adxt_bdy1, adxt_bdy0, adyt_bdx1, adyt_bdx0, + at_blarge, at_b[2], at_b[1], at_b[0]); + at_b[3] = at_blarge; + at_blen = 4; + Two_Product(adytail, cdx, adyt_cdx1, adyt_cdx0); + Two_Product(adxtail, cdy, adxt_cdy1, adxt_cdy0); + Two_Two_Diff(adyt_cdx1, adyt_cdx0, adxt_cdy1, adxt_cdy0, + at_clarge, at_c[2], at_c[1], at_c[0]); + at_c[3] = at_clarge; + at_clen = 4; + } + } + if (bdxtail == 0.0) { + if (bdytail == 0.0) { + bt_c[0] = 0.0; + bt_clen = 1; + bt_a[0] = 0.0; + bt_alen = 1; + } else { + negate = -bdytail; + Two_Product(negate, cdx, bt_clarge, bt_c[0]); + bt_c[1] = bt_clarge; + bt_clen = 2; + Two_Product(bdytail, adx, bt_alarge, bt_a[0]); + bt_a[1] = bt_alarge; + bt_alen = 2; + } + } else { + if (bdytail == 0.0) { + Two_Product(bdxtail, cdy, bt_clarge, bt_c[0]); + bt_c[1] = bt_clarge; + bt_clen = 2; + negate = -bdxtail; + Two_Product(negate, ady, bt_alarge, bt_a[0]); + bt_a[1] = bt_alarge; + bt_alen = 2; + } else { + Two_Product(bdxtail, cdy, bdxt_cdy1, bdxt_cdy0); + Two_Product(bdytail, cdx, bdyt_cdx1, bdyt_cdx0); + Two_Two_Diff(bdxt_cdy1, bdxt_cdy0, bdyt_cdx1, bdyt_cdx0, + bt_clarge, bt_c[2], bt_c[1], bt_c[0]); + bt_c[3] = bt_clarge; + bt_clen = 4; + Two_Product(bdytail, adx, bdyt_adx1, bdyt_adx0); + Two_Product(bdxtail, ady, bdxt_ady1, bdxt_ady0); + Two_Two_Diff(bdyt_adx1, bdyt_adx0, bdxt_ady1, bdxt_ady0, + bt_alarge, bt_a[2], bt_a[1], bt_a[0]); + bt_a[3] = bt_alarge; + bt_alen = 4; + } + } + if (cdxtail == 0.0) { + if (cdytail == 0.0) { + ct_a[0] = 0.0; + ct_alen = 1; + ct_b[0] = 0.0; + ct_blen = 1; + } else { + negate = -cdytail; + Two_Product(negate, adx, ct_alarge, ct_a[0]); + ct_a[1] = ct_alarge; + ct_alen = 2; + Two_Product(cdytail, bdx, ct_blarge, ct_b[0]); + ct_b[1] = ct_blarge; + ct_blen = 2; + } + } else { + if (cdytail == 0.0) { + Two_Product(cdxtail, ady, ct_alarge, ct_a[0]); + ct_a[1] = ct_alarge; + ct_alen = 2; + negate = -cdxtail; + Two_Product(negate, bdy, ct_blarge, ct_b[0]); + ct_b[1] = ct_blarge; + ct_blen = 2; + } else { + Two_Product(cdxtail, ady, cdxt_ady1, cdxt_ady0); + Two_Product(cdytail, adx, cdyt_adx1, cdyt_adx0); + Two_Two_Diff(cdxt_ady1, cdxt_ady0, cdyt_adx1, cdyt_adx0, + ct_alarge, ct_a[2], ct_a[1], ct_a[0]); + ct_a[3] = ct_alarge; + ct_alen = 4; + Two_Product(cdytail, bdx, cdyt_bdx1, cdyt_bdx0); + Two_Product(cdxtail, bdy, cdxt_bdy1, cdxt_bdy0); + Two_Two_Diff(cdyt_bdx1, cdyt_bdx0, cdxt_bdy1, cdxt_bdy0, + ct_blarge, ct_b[2], ct_b[1], ct_b[0]); + ct_b[3] = ct_blarge; + ct_blen = 4; + } + } + + bctlen = fast_expansion_sum_zeroelim(bt_clen, bt_c, ct_blen, ct_b, bct); + wlength = scale_expansion_zeroelim(bctlen, bct, adz, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, + finother); + finswap = finnow; finnow = finother; finother = finswap; + + catlen = fast_expansion_sum_zeroelim(ct_alen, ct_a, at_clen, at_c, cat); + wlength = scale_expansion_zeroelim(catlen, cat, bdz, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, + finother); + finswap = finnow; finnow = finother; finother = finswap; + + abtlen = fast_expansion_sum_zeroelim(at_blen, at_b, bt_alen, bt_a, abt); + wlength = scale_expansion_zeroelim(abtlen, abt, cdz, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, + finother); + finswap = finnow; finnow = finother; finother = finswap; + + if (adztail != 0.0) { + vlength = scale_expansion_zeroelim(4, bc, adztail, v); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (bdztail != 0.0) { + vlength = scale_expansion_zeroelim(4, ca, bdztail, v); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (cdztail != 0.0) { + vlength = scale_expansion_zeroelim(4, ab, cdztail, v); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + + if (adxtail != 0.0) { + if (bdytail != 0.0) { + Two_Product(adxtail, bdytail, adxt_bdyt1, adxt_bdyt0); + Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdz, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + if (cdztail != 0.0) { + Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdztail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + if (cdytail != 0.0) { + negate = -adxtail; + Two_Product(negate, cdytail, adxt_cdyt1, adxt_cdyt0); + Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdz, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + if (bdztail != 0.0) { + Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdztail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + } + if (bdxtail != 0.0) { + if (cdytail != 0.0) { + Two_Product(bdxtail, cdytail, bdxt_cdyt1, bdxt_cdyt0); + Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adz, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + if (adztail != 0.0) { + Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adztail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + if (adytail != 0.0) { + negate = -bdxtail; + Two_Product(negate, adytail, bdxt_adyt1, bdxt_adyt0); + Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdz, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + if (cdztail != 0.0) { + Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdztail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + } + if (cdxtail != 0.0) { + if (adytail != 0.0) { + Two_Product(cdxtail, adytail, cdxt_adyt1, cdxt_adyt0); + Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdz, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + if (bdztail != 0.0) { + Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdztail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + if (bdytail != 0.0) { + negate = -cdxtail; + Two_Product(negate, bdytail, cdxt_bdyt1, cdxt_bdyt0); + Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adz, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + if (adztail != 0.0) { + Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adztail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + } + + if (adztail != 0.0) { + wlength = scale_expansion_zeroelim(bctlen, bct, adztail, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (bdztail != 0.0) { + wlength = scale_expansion_zeroelim(catlen, cat, bdztail, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (cdztail != 0.0) { + wlength = scale_expansion_zeroelim(abtlen, abt, cdztail, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, + finother); + finswap = finnow; finnow = finother; finother = finswap; + } + + return finnow[finlength - 1]; +} + +REAL orient3d(pa, pb, pc, pd) +REAL *pa; +REAL *pb; +REAL *pc; +REAL *pd; +{ + REAL adx, bdx, cdx, ady, bdy, cdy, adz, bdz, cdz; + REAL bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady; + REAL det; + REAL permanent, errbound; + REAL orient; + + FPU_ROUND_DOUBLE; + + adx = pa[0] - pd[0]; + bdx = pb[0] - pd[0]; + cdx = pc[0] - pd[0]; + ady = pa[1] - pd[1]; + bdy = pb[1] - pd[1]; + cdy = pc[1] - pd[1]; + adz = pa[2] - pd[2]; + bdz = pb[2] - pd[2]; + cdz = pc[2] - pd[2]; + + bdxcdy = bdx * cdy; + cdxbdy = cdx * bdy; + + cdxady = cdx * ady; + adxcdy = adx * cdy; + + adxbdy = adx * bdy; + bdxady = bdx * ady; + + det = adz * (bdxcdy - cdxbdy) + + bdz * (cdxady - adxcdy) + + cdz * (adxbdy - bdxady); + + permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * Absolute(adz) + + (Absolute(cdxady) + Absolute(adxcdy)) * Absolute(bdz) + + (Absolute(adxbdy) + Absolute(bdxady)) * Absolute(cdz); + errbound = o3derrboundA * permanent; + if ((det > errbound) || (-det > errbound)) { + FPU_RESTORE; + return det; + } + + orient = orient3dadapt(pa, pb, pc, pd, permanent); + FPU_RESTORE; + return orient; +} + +/*****************************************************************************/ +/* */ +/* incirclefast() Approximate 2D incircle test. Nonrobust. */ +/* incircleexact() Exact 2D incircle test. Robust. */ +/* incircleslow() Another exact 2D incircle test. Robust. */ +/* incircle() Adaptive exact 2D incircle test. Robust. */ +/* */ +/* Return a positive value if the point pd lies inside the */ +/* circle passing through pa, pb, and pc; a negative value if */ +/* it lies outside; and zero if the four points are cocircular.*/ +/* The points pa, pb, and pc must be in counterclockwise */ +/* order, or the sign of the result will be reversed. */ +/* */ +/* Only the first and last routine should be used; the middle two are for */ +/* timings. */ +/* */ +/* The last three use exact arithmetic to ensure a correct answer. The */ +/* result returned is the determinant of a matrix. In incircle() only, */ +/* this determinant is computed adaptively, in the sense that exact */ +/* arithmetic is used only to the degree it is needed to ensure that the */ +/* returned value has the correct sign. Hence, incircle() is usually quite */ +/* fast, but will run more slowly when the input points are cocircular or */ +/* nearly so. */ +/* */ +/*****************************************************************************/ + +static REAL incircleadapt(REAL *pa, REAL *pb, REAL *pc, REAL *pd, + REAL permanent) +{ + INEXACT REAL adx, bdx, cdx, ady, bdy, cdy; + REAL det, errbound; + + INEXACT REAL bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1; + REAL bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0; + REAL bc[4], ca[4], ab[4]; + INEXACT REAL bc3, ca3, ab3; + REAL axbc[8], axxbc[16], aybc[8], ayybc[16], adet[32]; + int axbclen, axxbclen, aybclen, ayybclen, alen; + REAL bxca[8], bxxca[16], byca[8], byyca[16], bdet[32]; + int bxcalen, bxxcalen, bycalen, byycalen, blen; + REAL cxab[8], cxxab[16], cyab[8], cyyab[16], cdet[32]; + int cxablen, cxxablen, cyablen, cyyablen, clen; + REAL abdet[64]; + int ablen; + REAL fin1[1152], fin2[1152]; + REAL *finnow, *finother, *finswap; + int finlength; + + REAL adxtail, bdxtail, cdxtail, adytail, bdytail, cdytail; + INEXACT REAL adxadx1, adyady1, bdxbdx1, bdybdy1, cdxcdx1, cdycdy1; + REAL adxadx0, adyady0, bdxbdx0, bdybdy0, cdxcdx0, cdycdy0; + REAL aa[4], bb[4], cc[4]; + INEXACT REAL aa3, bb3, cc3; + INEXACT REAL ti1, tj1; + REAL ti0, tj0; + REAL u[4], v[4]; + INEXACT REAL u3, v3; + REAL temp8[8], temp16a[16], temp16b[16], temp16c[16]; + REAL temp32a[32], temp32b[32], temp48[48], temp64[64]; + int temp8len, temp16alen, temp16blen, temp16clen; + int temp32alen, temp32blen, temp48len, temp64len; + REAL axtbb[8], axtcc[8], aytbb[8], aytcc[8]; + int axtbblen, axtcclen, aytbblen, aytcclen; + REAL bxtaa[8], bxtcc[8], bytaa[8], bytcc[8]; + int bxtaalen, bxtcclen, bytaalen, bytcclen; + REAL cxtaa[8], cxtbb[8], cytaa[8], cytbb[8]; + int cxtaalen, cxtbblen, cytaalen, cytbblen; + REAL axtbc[8], aytbc[8], bxtca[8], bytca[8], cxtab[8], cytab[8]; + int axtbclen = 0, aytbclen = 0; + int bxtcalen = 0, bytcalen = 0; + int cxtablen = 0, cytablen = 0; + REAL axtbct[16], aytbct[16], bxtcat[16], bytcat[16], cxtabt[16], cytabt[16]; + int axtbctlen, aytbctlen, bxtcatlen, bytcatlen, cxtabtlen, cytabtlen; + REAL axtbctt[8], aytbctt[8], bxtcatt[8]; + REAL bytcatt[8], cxtabtt[8], cytabtt[8]; + int axtbcttlen, aytbcttlen, bxtcattlen, bytcattlen, cxtabttlen, cytabttlen; + REAL abt[8], bct[8], cat[8]; + int abtlen, bctlen, catlen; + REAL abtt[4], bctt[4], catt[4]; + int abttlen, bcttlen, cattlen; + INEXACT REAL abtt3, bctt3, catt3; + REAL negate; + + INEXACT REAL bvirt; + REAL avirt, bround, around; + INEXACT REAL c; + INEXACT REAL abig; + REAL ahi, alo, bhi, blo; + REAL err1, err2, err3; + INEXACT REAL _i, _j; + REAL _0; + + adx = (REAL) (pa[0] - pd[0]); + bdx = (REAL) (pb[0] - pd[0]); + cdx = (REAL) (pc[0] - pd[0]); + ady = (REAL) (pa[1] - pd[1]); + bdy = (REAL) (pb[1] - pd[1]); + cdy = (REAL) (pc[1] - pd[1]); + + Two_Product(bdx, cdy, bdxcdy1, bdxcdy0); + Two_Product(cdx, bdy, cdxbdy1, cdxbdy0); + Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]); + bc[3] = bc3; + axbclen = scale_expansion_zeroelim(4, bc, adx, axbc); + axxbclen = scale_expansion_zeroelim(axbclen, axbc, adx, axxbc); + aybclen = scale_expansion_zeroelim(4, bc, ady, aybc); + ayybclen = scale_expansion_zeroelim(aybclen, aybc, ady, ayybc); + alen = fast_expansion_sum_zeroelim(axxbclen, axxbc, ayybclen, ayybc, adet); + + Two_Product(cdx, ady, cdxady1, cdxady0); + Two_Product(adx, cdy, adxcdy1, adxcdy0); + Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]); + ca[3] = ca3; + bxcalen = scale_expansion_zeroelim(4, ca, bdx, bxca); + bxxcalen = scale_expansion_zeroelim(bxcalen, bxca, bdx, bxxca); + bycalen = scale_expansion_zeroelim(4, ca, bdy, byca); + byycalen = scale_expansion_zeroelim(bycalen, byca, bdy, byyca); + blen = fast_expansion_sum_zeroelim(bxxcalen, bxxca, byycalen, byyca, bdet); + + Two_Product(adx, bdy, adxbdy1, adxbdy0); + Two_Product(bdx, ady, bdxady1, bdxady0); + Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]); + ab[3] = ab3; + cxablen = scale_expansion_zeroelim(4, ab, cdx, cxab); + cxxablen = scale_expansion_zeroelim(cxablen, cxab, cdx, cxxab); + cyablen = scale_expansion_zeroelim(4, ab, cdy, cyab); + cyyablen = scale_expansion_zeroelim(cyablen, cyab, cdy, cyyab); + clen = fast_expansion_sum_zeroelim(cxxablen, cxxab, cyyablen, cyyab, cdet); + + ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet); + finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1); + + det = estimate(finlength, fin1); + errbound = iccerrboundB * permanent; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Diff_Tail(pa[0], pd[0], adx, adxtail); + Two_Diff_Tail(pa[1], pd[1], ady, adytail); + Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail); + Two_Diff_Tail(pb[1], pd[1], bdy, bdytail); + Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail); + Two_Diff_Tail(pc[1], pd[1], cdy, cdytail); + if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0) + && (adytail == 0.0) && (bdytail == 0.0) && (cdytail == 0.0)) { + return det; + } + + errbound = iccerrboundC * permanent + resulterrbound * Absolute(det); + det += ((adx * adx + ady * ady) * ((bdx * cdytail + cdy * bdxtail) + - (bdy * cdxtail + cdx * bdytail)) + + 2.0 * (adx * adxtail + ady * adytail) * (bdx * cdy - bdy * cdx)) + + ((bdx * bdx + bdy * bdy) * ((cdx * adytail + ady * cdxtail) + - (cdy * adxtail + adx * cdytail)) + + 2.0 * (bdx * bdxtail + bdy * bdytail) * (cdx * ady - cdy * adx)) + + ((cdx * cdx + cdy * cdy) * ((adx * bdytail + bdy * adxtail) + - (ady * bdxtail + bdx * adytail)) + + 2.0 * (cdx * cdxtail + cdy * cdytail) * (adx * bdy - ady * bdx)); + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + finnow = fin1; + finother = fin2; + + if ((bdxtail != 0.0) || (bdytail != 0.0) + || (cdxtail != 0.0) || (cdytail != 0.0)) { + Square(adx, adxadx1, adxadx0); + Square(ady, adyady1, adyady0); + Two_Two_Sum(adxadx1, adxadx0, adyady1, adyady0, aa3, aa[2], aa[1], aa[0]); + aa[3] = aa3; + } + if ((cdxtail != 0.0) || (cdytail != 0.0) + || (adxtail != 0.0) || (adytail != 0.0)) { + Square(bdx, bdxbdx1, bdxbdx0); + Square(bdy, bdybdy1, bdybdy0); + Two_Two_Sum(bdxbdx1, bdxbdx0, bdybdy1, bdybdy0, bb3, bb[2], bb[1], bb[0]); + bb[3] = bb3; + } + if ((adxtail != 0.0) || (adytail != 0.0) + || (bdxtail != 0.0) || (bdytail != 0.0)) { + Square(cdx, cdxcdx1, cdxcdx0); + Square(cdy, cdycdy1, cdycdy0); + Two_Two_Sum(cdxcdx1, cdxcdx0, cdycdy1, cdycdy0, cc3, cc[2], cc[1], cc[0]); + cc[3] = cc3; + } + + if (adxtail != 0.0) { + axtbclen = scale_expansion_zeroelim(4, bc, adxtail, axtbc); + temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, 2.0 * adx, + temp16a); + + axtcclen = scale_expansion_zeroelim(4, cc, adxtail, axtcc); + temp16blen = scale_expansion_zeroelim(axtcclen, axtcc, bdy, temp16b); + + axtbblen = scale_expansion_zeroelim(4, bb, adxtail, axtbb); + temp16clen = scale_expansion_zeroelim(axtbblen, axtbb, -cdy, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (adytail != 0.0) { + aytbclen = scale_expansion_zeroelim(4, bc, adytail, aytbc); + temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, 2.0 * ady, + temp16a); + + aytbblen = scale_expansion_zeroelim(4, bb, adytail, aytbb); + temp16blen = scale_expansion_zeroelim(aytbblen, aytbb, cdx, temp16b); + + aytcclen = scale_expansion_zeroelim(4, cc, adytail, aytcc); + temp16clen = scale_expansion_zeroelim(aytcclen, aytcc, -bdx, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (bdxtail != 0.0) { + bxtcalen = scale_expansion_zeroelim(4, ca, bdxtail, bxtca); + temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, 2.0 * bdx, + temp16a); + + bxtaalen = scale_expansion_zeroelim(4, aa, bdxtail, bxtaa); + temp16blen = scale_expansion_zeroelim(bxtaalen, bxtaa, cdy, temp16b); + + bxtcclen = scale_expansion_zeroelim(4, cc, bdxtail, bxtcc); + temp16clen = scale_expansion_zeroelim(bxtcclen, bxtcc, -ady, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (bdytail != 0.0) { + bytcalen = scale_expansion_zeroelim(4, ca, bdytail, bytca); + temp16alen = scale_expansion_zeroelim(bytcalen, bytca, 2.0 * bdy, + temp16a); + + bytcclen = scale_expansion_zeroelim(4, cc, bdytail, bytcc); + temp16blen = scale_expansion_zeroelim(bytcclen, bytcc, adx, temp16b); + + bytaalen = scale_expansion_zeroelim(4, aa, bdytail, bytaa); + temp16clen = scale_expansion_zeroelim(bytaalen, bytaa, -cdx, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (cdxtail != 0.0) { + cxtablen = scale_expansion_zeroelim(4, ab, cdxtail, cxtab); + temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, 2.0 * cdx, + temp16a); + + cxtbblen = scale_expansion_zeroelim(4, bb, cdxtail, cxtbb); + temp16blen = scale_expansion_zeroelim(cxtbblen, cxtbb, ady, temp16b); + + cxtaalen = scale_expansion_zeroelim(4, aa, cdxtail, cxtaa); + temp16clen = scale_expansion_zeroelim(cxtaalen, cxtaa, -bdy, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (cdytail != 0.0) { + cytablen = scale_expansion_zeroelim(4, ab, cdytail, cytab); + temp16alen = scale_expansion_zeroelim(cytablen, cytab, 2.0 * cdy, + temp16a); + + cytaalen = scale_expansion_zeroelim(4, aa, cdytail, cytaa); + temp16blen = scale_expansion_zeroelim(cytaalen, cytaa, bdx, temp16b); + + cytbblen = scale_expansion_zeroelim(4, bb, cdytail, cytbb); + temp16clen = scale_expansion_zeroelim(cytbblen, cytbb, -adx, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + + if ((adxtail != 0.0) || (adytail != 0.0)) { + if ((bdxtail != 0.0) || (bdytail != 0.0) + || (cdxtail != 0.0) || (cdytail != 0.0)) { + Two_Product(bdxtail, cdy, ti1, ti0); + Two_Product(bdx, cdytail, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]); + u[3] = u3; + negate = -bdy; + Two_Product(cdxtail, negate, ti1, ti0); + negate = -bdytail; + Two_Product(cdx, negate, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]); + v[3] = v3; + bctlen = fast_expansion_sum_zeroelim(4, u, 4, v, bct); + + Two_Product(bdxtail, cdytail, ti1, ti0); + Two_Product(cdxtail, bdytail, tj1, tj0); + Two_Two_Diff(ti1, ti0, tj1, tj0, bctt3, bctt[2], bctt[1], bctt[0]); + bctt[3] = bctt3; + bcttlen = 4; + } else { + bct[0] = 0.0; + bctlen = 1; + bctt[0] = 0.0; + bcttlen = 1; + } + + if (adxtail != 0.0) { + temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, adxtail, temp16a); + axtbctlen = scale_expansion_zeroelim(bctlen, bct, adxtail, axtbct); + temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, 2.0 * adx, + temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + if (bdytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, cc, adxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail, + temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, + temp16a, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (cdytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, bb, -adxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail, + temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, + temp16a, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + + temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, adxtail, + temp32a); + axtbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adxtail, axtbctt); + temp16alen = scale_expansion_zeroelim(axtbcttlen, axtbctt, 2.0 * adx, + temp16a); + temp16blen = scale_expansion_zeroelim(axtbcttlen, axtbctt, adxtail, + temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, + temp64, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (adytail != 0.0) { + temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, adytail, temp16a); + aytbctlen = scale_expansion_zeroelim(bctlen, bct, adytail, aytbct); + temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, 2.0 * ady, + temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + + + temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, adytail, + temp32a); + aytbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adytail, aytbctt); + temp16alen = scale_expansion_zeroelim(aytbcttlen, aytbctt, 2.0 * ady, + temp16a); + temp16blen = scale_expansion_zeroelim(aytbcttlen, aytbctt, adytail, + temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, + temp64, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + if ((bdxtail != 0.0) || (bdytail != 0.0)) { + if ((cdxtail != 0.0) || (cdytail != 0.0) + || (adxtail != 0.0) || (adytail != 0.0)) { + Two_Product(cdxtail, ady, ti1, ti0); + Two_Product(cdx, adytail, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]); + u[3] = u3; + negate = -cdy; + Two_Product(adxtail, negate, ti1, ti0); + negate = -cdytail; + Two_Product(adx, negate, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]); + v[3] = v3; + catlen = fast_expansion_sum_zeroelim(4, u, 4, v, cat); + + Two_Product(cdxtail, adytail, ti1, ti0); + Two_Product(adxtail, cdytail, tj1, tj0); + Two_Two_Diff(ti1, ti0, tj1, tj0, catt3, catt[2], catt[1], catt[0]); + catt[3] = catt3; + cattlen = 4; + } else { + cat[0] = 0.0; + catlen = 1; + catt[0] = 0.0; + cattlen = 1; + } + + if (bdxtail != 0.0) { + temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, bdxtail, temp16a); + bxtcatlen = scale_expansion_zeroelim(catlen, cat, bdxtail, bxtcat); + temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, 2.0 * bdx, + temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + if (cdytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, aa, bdxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail, + temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, + temp16a, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (adytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, cc, -bdxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail, + temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, + temp16a, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + + temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, bdxtail, + temp32a); + bxtcattlen = scale_expansion_zeroelim(cattlen, catt, bdxtail, bxtcatt); + temp16alen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, 2.0 * bdx, + temp16a); + temp16blen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, bdxtail, + temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, + temp64, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (bdytail != 0.0) { + temp16alen = scale_expansion_zeroelim(bytcalen, bytca, bdytail, temp16a); + bytcatlen = scale_expansion_zeroelim(catlen, cat, bdytail, bytcat); + temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, 2.0 * bdy, + temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + + + temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, bdytail, + temp32a); + bytcattlen = scale_expansion_zeroelim(cattlen, catt, bdytail, bytcatt); + temp16alen = scale_expansion_zeroelim(bytcattlen, bytcatt, 2.0 * bdy, + temp16a); + temp16blen = scale_expansion_zeroelim(bytcattlen, bytcatt, bdytail, + temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, + temp64, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + if ((cdxtail != 0.0) || (cdytail != 0.0)) { + if ((adxtail != 0.0) || (adytail != 0.0) + || (bdxtail != 0.0) || (bdytail != 0.0)) { + Two_Product(adxtail, bdy, ti1, ti0); + Two_Product(adx, bdytail, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]); + u[3] = u3; + negate = -ady; + Two_Product(bdxtail, negate, ti1, ti0); + negate = -adytail; + Two_Product(bdx, negate, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]); + v[3] = v3; + abtlen = fast_expansion_sum_zeroelim(4, u, 4, v, abt); + + Two_Product(adxtail, bdytail, ti1, ti0); + Two_Product(bdxtail, adytail, tj1, tj0); + Two_Two_Diff(ti1, ti0, tj1, tj0, abtt3, abtt[2], abtt[1], abtt[0]); + abtt[3] = abtt3; + abttlen = 4; + } else { + abt[0] = 0.0; + abtlen = 1; + abtt[0] = 0.0; + abttlen = 1; + } + + if (cdxtail != 0.0) { + temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, cdxtail, temp16a); + cxtabtlen = scale_expansion_zeroelim(abtlen, abt, cdxtail, cxtabt); + temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, 2.0 * cdx, + temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + if (adytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, bb, cdxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail, + temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, + temp16a, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (bdytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, aa, -cdxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail, + temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, + temp16a, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + + temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, cdxtail, + temp32a); + cxtabttlen = scale_expansion_zeroelim(abttlen, abtt, cdxtail, cxtabtt); + temp16alen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, 2.0 * cdx, + temp16a); + temp16blen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, cdxtail, + temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, + temp64, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + if (cdytail != 0.0) { + temp16alen = scale_expansion_zeroelim(cytablen, cytab, cdytail, temp16a); + cytabtlen = scale_expansion_zeroelim(abtlen, abt, cdytail, cytabt); + temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, 2.0 * cdy, + temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, + temp48, finother); + finswap = finnow; finnow = finother; finother = finswap; + + + temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, cdytail, + temp32a); + cytabttlen = scale_expansion_zeroelim(abttlen, abtt, cdytail, cytabtt); + temp16alen = scale_expansion_zeroelim(cytabttlen, cytabtt, 2.0 * cdy, + temp16a); + temp16blen = scale_expansion_zeroelim(cytabttlen, cytabtt, cdytail, + temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, + temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, + temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, + temp64, finother); + finswap = finnow; finnow = finother; finother = finswap; + } + } + + return finnow[finlength - 1]; +} + +REAL incircle(pa, pb, pc, pd) +REAL *pa; +REAL *pb; +REAL *pc; +REAL *pd; +{ + REAL adx, bdx, cdx, ady, bdy, cdy; + REAL bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady; + REAL alift, blift, clift; + REAL det; + REAL permanent, errbound; + REAL inc; + + FPU_ROUND_DOUBLE; + + adx = pa[0] - pd[0]; + bdx = pb[0] - pd[0]; + cdx = pc[0] - pd[0]; + ady = pa[1] - pd[1]; + bdy = pb[1] - pd[1]; + cdy = pc[1] - pd[1]; + + bdxcdy = bdx * cdy; + cdxbdy = cdx * bdy; + alift = adx * adx + ady * ady; + + cdxady = cdx * ady; + adxcdy = adx * cdy; + blift = bdx * bdx + bdy * bdy; + + adxbdy = adx * bdy; + bdxady = bdx * ady; + clift = cdx * cdx + cdy * cdy; + + det = alift * (bdxcdy - cdxbdy) + + blift * (cdxady - adxcdy) + + clift * (adxbdy - bdxady); + + permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * alift + + (Absolute(cdxady) + Absolute(adxcdy)) * blift + + (Absolute(adxbdy) + Absolute(bdxady)) * clift; + errbound = iccerrboundA * permanent; + if ((det > errbound) || (-det > errbound)) { + FPU_RESTORE; + return det; + } + + inc = incircleadapt(pa, pb, pc, pd, permanent); + FPU_RESTORE; + return inc; +} + +/*****************************************************************************/ +/* */ +/* inspherefast() Approximate 3D insphere test. Nonrobust. */ +/* insphereexact() Exact 3D insphere test. Robust. */ +/* insphereslow() Another exact 3D insphere test. Robust. */ +/* insphere() Adaptive exact 3D insphere test. Robust. */ +/* */ +/* Return a positive value if the point pe lies inside the */ +/* sphere passing through pa, pb, pc, and pd; a negative value */ +/* if it lies outside; and zero if the five points are */ +/* cospherical. The points pa, pb, pc, and pd must be ordered */ +/* so that they have a positive orientation (as defined by */ +/* orient3d()), or the sign of the result will be reversed. */ +/* */ +/* Only the first and last routine should be used; the middle two are for */ +/* timings. */ +/* */ +/* The last three use exact arithmetic to ensure a correct answer. The */ +/* result returned is the determinant of a matrix. In insphere() only, */ +/* this determinant is computed adaptively, in the sense that exact */ +/* arithmetic is used only to the degree it is needed to ensure that the */ +/* returned value has the correct sign. Hence, insphere() is usually quite */ +/* fast, but will run more slowly when the input points are cospherical or */ +/* nearly so. */ +/* */ +/*****************************************************************************/ + +static REAL insphereexact(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe) +{ + INEXACT REAL axby1, bxcy1, cxdy1, dxey1, exay1; + INEXACT REAL bxay1, cxby1, dxcy1, exdy1, axey1; + INEXACT REAL axcy1, bxdy1, cxey1, dxay1, exby1; + INEXACT REAL cxay1, dxby1, excy1, axdy1, bxey1; + REAL axby0, bxcy0, cxdy0, dxey0, exay0; + REAL bxay0, cxby0, dxcy0, exdy0, axey0; + REAL axcy0, bxdy0, cxey0, dxay0, exby0; + REAL cxay0, dxby0, excy0, axdy0, bxey0; + REAL ab[4], bc[4], cd[4], de[4], ea[4]; + REAL ac[4], bd[4], ce[4], da[4], eb[4]; + REAL temp8a[8], temp8b[8], temp16[16]; + int temp8alen, temp8blen, temp16len; + REAL abc[24], bcd[24], cde[24], dea[24], eab[24]; + REAL abd[24], bce[24], cda[24], deb[24], eac[24]; + int abclen, bcdlen, cdelen, dealen, eablen; + int abdlen, bcelen, cdalen, deblen, eaclen; + REAL temp48a[48], temp48b[48]; + int temp48alen, temp48blen; + REAL abcd[96], bcde[96], cdea[96], deab[96], eabc[96]; + int abcdlen, bcdelen, cdealen, deablen, eabclen; + REAL temp192[192]; + REAL det384x[384], det384y[384], det384z[384]; + int xlen, ylen, zlen; + REAL detxy[768]; + int xylen; + REAL adet[1152], bdet[1152], cdet[1152], ddet[1152], edet[1152]; + int alen, blen, clen, dlen, elen; + REAL abdet[2304], cddet[2304], cdedet[3456]; + int ablen, cdlen; + REAL deter[5760]; + int deterlen; + int i; + + INEXACT REAL bvirt; + REAL avirt, bround, around; + INEXACT REAL c; + INEXACT REAL abig; + REAL ahi, alo, bhi, blo; + REAL err1, err2, err3; + INEXACT REAL _i, _j; + REAL _0; + + Two_Product(pa[0], pb[1], axby1, axby0); + Two_Product(pb[0], pa[1], bxay1, bxay0); + Two_Two_Diff(axby1, axby0, bxay1, bxay0, ab[3], ab[2], ab[1], ab[0]); + + Two_Product(pb[0], pc[1], bxcy1, bxcy0); + Two_Product(pc[0], pb[1], cxby1, cxby0); + Two_Two_Diff(bxcy1, bxcy0, cxby1, cxby0, bc[3], bc[2], bc[1], bc[0]); + + Two_Product(pc[0], pd[1], cxdy1, cxdy0); + Two_Product(pd[0], pc[1], dxcy1, dxcy0); + Two_Two_Diff(cxdy1, cxdy0, dxcy1, dxcy0, cd[3], cd[2], cd[1], cd[0]); + + Two_Product(pd[0], pe[1], dxey1, dxey0); + Two_Product(pe[0], pd[1], exdy1, exdy0); + Two_Two_Diff(dxey1, dxey0, exdy1, exdy0, de[3], de[2], de[1], de[0]); + + Two_Product(pe[0], pa[1], exay1, exay0); + Two_Product(pa[0], pe[1], axey1, axey0); + Two_Two_Diff(exay1, exay0, axey1, axey0, ea[3], ea[2], ea[1], ea[0]); + + Two_Product(pa[0], pc[1], axcy1, axcy0); + Two_Product(pc[0], pa[1], cxay1, cxay0); + Two_Two_Diff(axcy1, axcy0, cxay1, cxay0, ac[3], ac[2], ac[1], ac[0]); + + Two_Product(pb[0], pd[1], bxdy1, bxdy0); + Two_Product(pd[0], pb[1], dxby1, dxby0); + Two_Two_Diff(bxdy1, bxdy0, dxby1, dxby0, bd[3], bd[2], bd[1], bd[0]); + + Two_Product(pc[0], pe[1], cxey1, cxey0); + Two_Product(pe[0], pc[1], excy1, excy0); + Two_Two_Diff(cxey1, cxey0, excy1, excy0, ce[3], ce[2], ce[1], ce[0]); + + Two_Product(pd[0], pa[1], dxay1, dxay0); + Two_Product(pa[0], pd[1], axdy1, axdy0); + Two_Two_Diff(dxay1, dxay0, axdy1, axdy0, da[3], da[2], da[1], da[0]); + + Two_Product(pe[0], pb[1], exby1, exby0); + Two_Product(pb[0], pe[1], bxey1, bxey0); + Two_Two_Diff(exby1, exby0, bxey1, bxey0, eb[3], eb[2], eb[1], eb[0]); + + temp8alen = scale_expansion_zeroelim(4, bc, pa[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, ac, -pb[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, + temp16); + temp8alen = scale_expansion_zeroelim(4, ab, pc[2], temp8a); + abclen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, + abc); + + temp8alen = scale_expansion_zeroelim(4, cd, pb[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, bd, -pc[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, + temp16); + temp8alen = scale_expansion_zeroelim(4, bc, pd[2], temp8a); + bcdlen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, + bcd); + + temp8alen = scale_expansion_zeroelim(4, de, pc[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, ce, -pd[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, + temp16); + temp8alen = scale_expansion_zeroelim(4, cd, pe[2], temp8a); + cdelen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, + cde); + + temp8alen = scale_expansion_zeroelim(4, ea, pd[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, da, -pe[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, + temp16); + temp8alen = scale_expansion_zeroelim(4, de, pa[2], temp8a); + dealen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, + dea); + + temp8alen = scale_expansion_zeroelim(4, ab, pe[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, eb, -pa[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, + temp16); + temp8alen = scale_expansion_zeroelim(4, ea, pb[2], temp8a); + eablen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, + eab); + + temp8alen = scale_expansion_zeroelim(4, bd, pa[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, da, pb[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, + temp16); + temp8alen = scale_expansion_zeroelim(4, ab, pd[2], temp8a); + abdlen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, + abd); + + temp8alen = scale_expansion_zeroelim(4, ce, pb[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, eb, pc[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, + temp16); + temp8alen = scale_expansion_zeroelim(4, bc, pe[2], temp8a); + bcelen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, + bce); + + temp8alen = scale_expansion_zeroelim(4, da, pc[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, ac, pd[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, + temp16); + temp8alen = scale_expansion_zeroelim(4, cd, pa[2], temp8a); + cdalen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, + cda); + + temp8alen = scale_expansion_zeroelim(4, eb, pd[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, bd, pe[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, + temp16); + temp8alen = scale_expansion_zeroelim(4, de, pb[2], temp8a); + deblen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, + deb); + + temp8alen = scale_expansion_zeroelim(4, ac, pe[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, ce, pa[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, + temp16); + temp8alen = scale_expansion_zeroelim(4, ea, pc[2], temp8a); + eaclen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, + eac); + + temp48alen = fast_expansion_sum_zeroelim(cdelen, cde, bcelen, bce, temp48a); + temp48blen = fast_expansion_sum_zeroelim(deblen, deb, bcdlen, bcd, temp48b); + for (i = 0; i < temp48blen; i++) { + temp48b[i] = -temp48b[i]; + } + bcdelen = fast_expansion_sum_zeroelim(temp48alen, temp48a, + temp48blen, temp48b, bcde); + xlen = scale_expansion_zeroelim(bcdelen, bcde, pa[0], temp192); + xlen = scale_expansion_zeroelim(xlen, temp192, pa[0], det384x); + ylen = scale_expansion_zeroelim(bcdelen, bcde, pa[1], temp192); + ylen = scale_expansion_zeroelim(ylen, temp192, pa[1], det384y); + zlen = scale_expansion_zeroelim(bcdelen, bcde, pa[2], temp192); + zlen = scale_expansion_zeroelim(zlen, temp192, pa[2], det384z); + xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy); + alen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, adet); + + temp48alen = fast_expansion_sum_zeroelim(dealen, dea, cdalen, cda, temp48a); + temp48blen = fast_expansion_sum_zeroelim(eaclen, eac, cdelen, cde, temp48b); + for (i = 0; i < temp48blen; i++) { + temp48b[i] = -temp48b[i]; + } + cdealen = fast_expansion_sum_zeroelim(temp48alen, temp48a, + temp48blen, temp48b, cdea); + xlen = scale_expansion_zeroelim(cdealen, cdea, pb[0], temp192); + xlen = scale_expansion_zeroelim(xlen, temp192, pb[0], det384x); + ylen = scale_expansion_zeroelim(cdealen, cdea, pb[1], temp192); + ylen = scale_expansion_zeroelim(ylen, temp192, pb[1], det384y); + zlen = scale_expansion_zeroelim(cdealen, cdea, pb[2], temp192); + zlen = scale_expansion_zeroelim(zlen, temp192, pb[2], det384z); + xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy); + blen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, bdet); + + temp48alen = fast_expansion_sum_zeroelim(eablen, eab, deblen, deb, temp48a); + temp48blen = fast_expansion_sum_zeroelim(abdlen, abd, dealen, dea, temp48b); + for (i = 0; i < temp48blen; i++) { + temp48b[i] = -temp48b[i]; + } + deablen = fast_expansion_sum_zeroelim(temp48alen, temp48a, + temp48blen, temp48b, deab); + xlen = scale_expansion_zeroelim(deablen, deab, pc[0], temp192); + xlen = scale_expansion_zeroelim(xlen, temp192, pc[0], det384x); + ylen = scale_expansion_zeroelim(deablen, deab, pc[1], temp192); + ylen = scale_expansion_zeroelim(ylen, temp192, pc[1], det384y); + zlen = scale_expansion_zeroelim(deablen, deab, pc[2], temp192); + zlen = scale_expansion_zeroelim(zlen, temp192, pc[2], det384z); + xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy); + clen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, cdet); + + temp48alen = fast_expansion_sum_zeroelim(abclen, abc, eaclen, eac, temp48a); + temp48blen = fast_expansion_sum_zeroelim(bcelen, bce, eablen, eab, temp48b); + for (i = 0; i < temp48blen; i++) { + temp48b[i] = -temp48b[i]; + } + eabclen = fast_expansion_sum_zeroelim(temp48alen, temp48a, + temp48blen, temp48b, eabc); + xlen = scale_expansion_zeroelim(eabclen, eabc, pd[0], temp192); + xlen = scale_expansion_zeroelim(xlen, temp192, pd[0], det384x); + ylen = scale_expansion_zeroelim(eabclen, eabc, pd[1], temp192); + ylen = scale_expansion_zeroelim(ylen, temp192, pd[1], det384y); + zlen = scale_expansion_zeroelim(eabclen, eabc, pd[2], temp192); + zlen = scale_expansion_zeroelim(zlen, temp192, pd[2], det384z); + xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy); + dlen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, ddet); + + temp48alen = fast_expansion_sum_zeroelim(bcdlen, bcd, abdlen, abd, temp48a); + temp48blen = fast_expansion_sum_zeroelim(cdalen, cda, abclen, abc, temp48b); + for (i = 0; i < temp48blen; i++) { + temp48b[i] = -temp48b[i]; + } + abcdlen = fast_expansion_sum_zeroelim(temp48alen, temp48a, + temp48blen, temp48b, abcd); + xlen = scale_expansion_zeroelim(abcdlen, abcd, pe[0], temp192); + xlen = scale_expansion_zeroelim(xlen, temp192, pe[0], det384x); + ylen = scale_expansion_zeroelim(abcdlen, abcd, pe[1], temp192); + ylen = scale_expansion_zeroelim(ylen, temp192, pe[1], det384y); + zlen = scale_expansion_zeroelim(abcdlen, abcd, pe[2], temp192); + zlen = scale_expansion_zeroelim(zlen, temp192, pe[2], det384z); + xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy); + elen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, edet); + + ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet); + cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet); + cdelen = fast_expansion_sum_zeroelim(cdlen, cddet, elen, edet, cdedet); + deterlen = fast_expansion_sum_zeroelim(ablen, abdet, cdelen, cdedet, deter); + + return deter[deterlen - 1]; +} + +static REAL insphereadapt(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe, + REAL permanent) +{ + INEXACT REAL aex, bex, cex, dex, aey, bey, cey, dey, aez, bez, cez, dez; + REAL det, errbound; + + INEXACT REAL aexbey1, bexaey1, bexcey1, cexbey1; + INEXACT REAL cexdey1, dexcey1, dexaey1, aexdey1; + INEXACT REAL aexcey1, cexaey1, bexdey1, dexbey1; + REAL aexbey0, bexaey0, bexcey0, cexbey0; + REAL cexdey0, dexcey0, dexaey0, aexdey0; + REAL aexcey0, cexaey0, bexdey0, dexbey0; + REAL ab[4], bc[4], cd[4], da[4], ac[4], bd[4]; + INEXACT REAL ab3, bc3, cd3, da3, ac3, bd3; + REAL abeps, bceps, cdeps, daeps, aceps, bdeps; + REAL temp8a[8], temp8b[8], temp8c[8], temp16[16], temp24[24], temp48[48]; + int temp8alen, temp8blen, temp8clen, temp16len, temp24len, temp48len; + REAL xdet[96], ydet[96], zdet[96], xydet[192]; + int xlen, ylen, zlen, xylen; + REAL adet[288], bdet[288], cdet[288], ddet[288]; + int alen, blen, clen, dlen; + REAL abdet[576], cddet[576]; + int ablen, cdlen; + REAL fin1[1152]; + int finlength; + + REAL aextail, bextail, cextail, dextail; + REAL aeytail, beytail, ceytail, deytail; + REAL aeztail, beztail, ceztail, deztail; + + INEXACT REAL bvirt; + REAL avirt, bround, around; + INEXACT REAL c; + INEXACT REAL abig; + REAL ahi, alo, bhi, blo; + REAL err1, err2, err3; + INEXACT REAL _i, _j; + REAL _0; + + aex = (REAL) (pa[0] - pe[0]); + bex = (REAL) (pb[0] - pe[0]); + cex = (REAL) (pc[0] - pe[0]); + dex = (REAL) (pd[0] - pe[0]); + aey = (REAL) (pa[1] - pe[1]); + bey = (REAL) (pb[1] - pe[1]); + cey = (REAL) (pc[1] - pe[1]); + dey = (REAL) (pd[1] - pe[1]); + aez = (REAL) (pa[2] - pe[2]); + bez = (REAL) (pb[2] - pe[2]); + cez = (REAL) (pc[2] - pe[2]); + dez = (REAL) (pd[2] - pe[2]); + + Two_Product(aex, bey, aexbey1, aexbey0); + Two_Product(bex, aey, bexaey1, bexaey0); + Two_Two_Diff(aexbey1, aexbey0, bexaey1, bexaey0, ab3, ab[2], ab[1], ab[0]); + ab[3] = ab3; + + Two_Product(bex, cey, bexcey1, bexcey0); + Two_Product(cex, bey, cexbey1, cexbey0); + Two_Two_Diff(bexcey1, bexcey0, cexbey1, cexbey0, bc3, bc[2], bc[1], bc[0]); + bc[3] = bc3; + + Two_Product(cex, dey, cexdey1, cexdey0); + Two_Product(dex, cey, dexcey1, dexcey0); + Two_Two_Diff(cexdey1, cexdey0, dexcey1, dexcey0, cd3, cd[2], cd[1], cd[0]); + cd[3] = cd3; + + Two_Product(dex, aey, dexaey1, dexaey0); + Two_Product(aex, dey, aexdey1, aexdey0); + Two_Two_Diff(dexaey1, dexaey0, aexdey1, aexdey0, da3, da[2], da[1], da[0]); + da[3] = da3; + + Two_Product(aex, cey, aexcey1, aexcey0); + Two_Product(cex, aey, cexaey1, cexaey0); + Two_Two_Diff(aexcey1, aexcey0, cexaey1, cexaey0, ac3, ac[2], ac[1], ac[0]); + ac[3] = ac3; + + Two_Product(bex, dey, bexdey1, bexdey0); + Two_Product(dex, bey, dexbey1, dexbey0); + Two_Two_Diff(bexdey1, bexdey0, dexbey1, dexbey0, bd3, bd[2], bd[1], bd[0]); + bd[3] = bd3; + + temp8alen = scale_expansion_zeroelim(4, cd, bez, temp8a); + temp8blen = scale_expansion_zeroelim(4, bd, -cez, temp8b); + temp8clen = scale_expansion_zeroelim(4, bc, dez, temp8c); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, + temp8blen, temp8b, temp16); + temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c, + temp16len, temp16, temp24); + temp48len = scale_expansion_zeroelim(temp24len, temp24, aex, temp48); + xlen = scale_expansion_zeroelim(temp48len, temp48, -aex, xdet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, aey, temp48); + ylen = scale_expansion_zeroelim(temp48len, temp48, -aey, ydet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, aez, temp48); + zlen = scale_expansion_zeroelim(temp48len, temp48, -aez, zdet); + xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet); + alen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, adet); + + temp8alen = scale_expansion_zeroelim(4, da, cez, temp8a); + temp8blen = scale_expansion_zeroelim(4, ac, dez, temp8b); + temp8clen = scale_expansion_zeroelim(4, cd, aez, temp8c); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, + temp8blen, temp8b, temp16); + temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c, + temp16len, temp16, temp24); + temp48len = scale_expansion_zeroelim(temp24len, temp24, bex, temp48); + xlen = scale_expansion_zeroelim(temp48len, temp48, bex, xdet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, bey, temp48); + ylen = scale_expansion_zeroelim(temp48len, temp48, bey, ydet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, bez, temp48); + zlen = scale_expansion_zeroelim(temp48len, temp48, bez, zdet); + xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet); + blen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, bdet); + + temp8alen = scale_expansion_zeroelim(4, ab, dez, temp8a); + temp8blen = scale_expansion_zeroelim(4, bd, aez, temp8b); + temp8clen = scale_expansion_zeroelim(4, da, bez, temp8c); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, + temp8blen, temp8b, temp16); + temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c, + temp16len, temp16, temp24); + temp48len = scale_expansion_zeroelim(temp24len, temp24, cex, temp48); + xlen = scale_expansion_zeroelim(temp48len, temp48, -cex, xdet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, cey, temp48); + ylen = scale_expansion_zeroelim(temp48len, temp48, -cey, ydet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, cez, temp48); + zlen = scale_expansion_zeroelim(temp48len, temp48, -cez, zdet); + xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet); + clen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, cdet); + + temp8alen = scale_expansion_zeroelim(4, bc, aez, temp8a); + temp8blen = scale_expansion_zeroelim(4, ac, -bez, temp8b); + temp8clen = scale_expansion_zeroelim(4, ab, cez, temp8c); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, + temp8blen, temp8b, temp16); + temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c, + temp16len, temp16, temp24); + temp48len = scale_expansion_zeroelim(temp24len, temp24, dex, temp48); + xlen = scale_expansion_zeroelim(temp48len, temp48, dex, xdet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, dey, temp48); + ylen = scale_expansion_zeroelim(temp48len, temp48, dey, ydet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, dez, temp48); + zlen = scale_expansion_zeroelim(temp48len, temp48, dez, zdet); + xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet); + dlen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, ddet); + + ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet); + cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet); + finlength = fast_expansion_sum_zeroelim(ablen, abdet, cdlen, cddet, fin1); + + det = estimate(finlength, fin1); + errbound = isperrboundB * permanent; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Diff_Tail(pa[0], pe[0], aex, aextail); + Two_Diff_Tail(pa[1], pe[1], aey, aeytail); + Two_Diff_Tail(pa[2], pe[2], aez, aeztail); + Two_Diff_Tail(pb[0], pe[0], bex, bextail); + Two_Diff_Tail(pb[1], pe[1], bey, beytail); + Two_Diff_Tail(pb[2], pe[2], bez, beztail); + Two_Diff_Tail(pc[0], pe[0], cex, cextail); + Two_Diff_Tail(pc[1], pe[1], cey, ceytail); + Two_Diff_Tail(pc[2], pe[2], cez, ceztail); + Two_Diff_Tail(pd[0], pe[0], dex, dextail); + Two_Diff_Tail(pd[1], pe[1], dey, deytail); + Two_Diff_Tail(pd[2], pe[2], dez, deztail); + if ((aextail == 0.0) && (aeytail == 0.0) && (aeztail == 0.0) + && (bextail == 0.0) && (beytail == 0.0) && (beztail == 0.0) + && (cextail == 0.0) && (ceytail == 0.0) && (ceztail == 0.0) + && (dextail == 0.0) && (deytail == 0.0) && (deztail == 0.0)) { + return det; + } + + errbound = isperrboundC * permanent + resulterrbound * Absolute(det); + abeps = (aex * beytail + bey * aextail) + - (aey * bextail + bex * aeytail); + bceps = (bex * ceytail + cey * bextail) + - (bey * cextail + cex * beytail); + cdeps = (cex * deytail + dey * cextail) + - (cey * dextail + dex * ceytail); + daeps = (dex * aeytail + aey * dextail) + - (dey * aextail + aex * deytail); + aceps = (aex * ceytail + cey * aextail) + - (aey * cextail + cex * aeytail); + bdeps = (bex * deytail + dey * bextail) + - (bey * dextail + dex * beytail); + det += (((bex * bex + bey * bey + bez * bez) + * ((cez * daeps + dez * aceps + aez * cdeps) + + (ceztail * da3 + deztail * ac3 + aeztail * cd3)) + + (dex * dex + dey * dey + dez * dez) + * ((aez * bceps - bez * aceps + cez * abeps) + + (aeztail * bc3 - beztail * ac3 + ceztail * ab3))) + - ((aex * aex + aey * aey + aez * aez) + * ((bez * cdeps - cez * bdeps + dez * bceps) + + (beztail * cd3 - ceztail * bd3 + deztail * bc3)) + + (cex * cex + cey * cey + cez * cez) + * ((dez * abeps + aez * bdeps + bez * daeps) + + (deztail * ab3 + aeztail * bd3 + beztail * da3)))) + + 2.0 * (((bex * bextail + bey * beytail + bez * beztail) + * (cez * da3 + dez * ac3 + aez * cd3) + + (dex * dextail + dey * deytail + dez * deztail) + * (aez * bc3 - bez * ac3 + cez * ab3)) + - ((aex * aextail + aey * aeytail + aez * aeztail) + * (bez * cd3 - cez * bd3 + dez * bc3) + + (cex * cextail + cey * ceytail + cez * ceztail) + * (dez * ab3 + aez * bd3 + bez * da3))); + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + return insphereexact(pa, pb, pc, pd, pe); +} + +REAL insphere(pa, pb, pc, pd, pe) +REAL *pa; +REAL *pb; +REAL *pc; +REAL *pd; +REAL *pe; +{ + REAL aex, bex, cex, dex; + REAL aey, bey, cey, dey; + REAL aez, bez, cez, dez; + REAL aexbey, bexaey, bexcey, cexbey, cexdey, dexcey, dexaey, aexdey; + REAL aexcey, cexaey, bexdey, dexbey; + REAL alift, blift, clift, dlift; + REAL ab, bc, cd, da, ac, bd; + REAL abc, bcd, cda, dab; + REAL aezplus, bezplus, cezplus, dezplus; + REAL aexbeyplus, bexaeyplus, bexceyplus, cexbeyplus; + REAL cexdeyplus, dexceyplus, dexaeyplus, aexdeyplus; + REAL aexceyplus, cexaeyplus, bexdeyplus, dexbeyplus; + REAL det; + REAL permanent, errbound; + REAL ins; + + FPU_ROUND_DOUBLE; + + aex = pa[0] - pe[0]; + bex = pb[0] - pe[0]; + cex = pc[0] - pe[0]; + dex = pd[0] - pe[0]; + aey = pa[1] - pe[1]; + bey = pb[1] - pe[1]; + cey = pc[1] - pe[1]; + dey = pd[1] - pe[1]; + aez = pa[2] - pe[2]; + bez = pb[2] - pe[2]; + cez = pc[2] - pe[2]; + dez = pd[2] - pe[2]; + + aexbey = aex * bey; + bexaey = bex * aey; + ab = aexbey - bexaey; + bexcey = bex * cey; + cexbey = cex * bey; + bc = bexcey - cexbey; + cexdey = cex * dey; + dexcey = dex * cey; + cd = cexdey - dexcey; + dexaey = dex * aey; + aexdey = aex * dey; + da = dexaey - aexdey; + + aexcey = aex * cey; + cexaey = cex * aey; + ac = aexcey - cexaey; + bexdey = bex * dey; + dexbey = dex * bey; + bd = bexdey - dexbey; + + abc = aez * bc - bez * ac + cez * ab; + bcd = bez * cd - cez * bd + dez * bc; + cda = cez * da + dez * ac + aez * cd; + dab = dez * ab + aez * bd + bez * da; + + alift = aex * aex + aey * aey + aez * aez; + blift = bex * bex + bey * bey + bez * bez; + clift = cex * cex + cey * cey + cez * cez; + dlift = dex * dex + dey * dey + dez * dez; + + det = (dlift * abc - clift * dab) + (blift * cda - alift * bcd); + + aezplus = Absolute(aez); + bezplus = Absolute(bez); + cezplus = Absolute(cez); + dezplus = Absolute(dez); + aexbeyplus = Absolute(aexbey); + bexaeyplus = Absolute(bexaey); + bexceyplus = Absolute(bexcey); + cexbeyplus = Absolute(cexbey); + cexdeyplus = Absolute(cexdey); + dexceyplus = Absolute(dexcey); + dexaeyplus = Absolute(dexaey); + aexdeyplus = Absolute(aexdey); + aexceyplus = Absolute(aexcey); + cexaeyplus = Absolute(cexaey); + bexdeyplus = Absolute(bexdey); + dexbeyplus = Absolute(dexbey); + permanent = ((cexdeyplus + dexceyplus) * bezplus + + (dexbeyplus + bexdeyplus) * cezplus + + (bexceyplus + cexbeyplus) * dezplus) + * alift + + ((dexaeyplus + aexdeyplus) * cezplus + + (aexceyplus + cexaeyplus) * dezplus + + (cexdeyplus + dexceyplus) * aezplus) + * blift + + ((aexbeyplus + bexaeyplus) * dezplus + + (bexdeyplus + dexbeyplus) * aezplus + + (dexaeyplus + aexdeyplus) * bezplus) + * clift + + ((bexceyplus + cexbeyplus) * aezplus + + (cexaeyplus + aexceyplus) * bezplus + + (aexbeyplus + bexaeyplus) * cezplus) + * dlift; + errbound = isperrboundA * permanent; + if ((det > errbound) || (-det > errbound)) { + FPU_RESTORE; + return det; + } + + ins = insphereadapt(pa, pb, pc, pd, pe, permanent); + FPU_RESTORE; + return ins; +} Index: toporouter/src_3rd/gts/predicates.h =================================================================== --- toporouter/src_3rd/gts/predicates.h (nonexistent) +++ toporouter/src_3rd/gts/predicates.h (revision 6803) @@ -0,0 +1,41 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/* Header file for robust predicates by Jonathan Richard Shewchuk */ + +#ifndef __PREDICATES_H__ +#define __PREDICATES_H__ + +double orient2d (double * pa, + double * pb, + double * pc); +double orient3d (double * pa, + double * pb, + double * pc, + double * pd); +double incircle (double * pa, + double * pb, + double * pc, + double * pd); +double insphere (double * pa, + double * pb, + double * pc, + double * pd, + double * pe); + +#endif /* __PREDICATES_H__ */ Index: toporouter/src_3rd/gts/psurface.c =================================================================== --- toporouter/src_3rd/gts/psurface.c (nonexistent) +++ toporouter/src_3rd/gts/psurface.c (revision 6803) @@ -0,0 +1,471 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include +#include "gts.h" + +#define HEAP_INSERT_OBJECT(h, e) (GTS_OBJECT (e)->reserved =\ + gts_eheap_insert (h, e)) +#define HEAP_REMOVE_OBJECT(h, e) (gts_eheap_remove (h, GTS_OBJECT (e)->reserved),\ + GTS_OBJECT (e)->reserved = NULL) + +static void psurface_destroy (GtsObject * object) +{ + GtsPSurface * ps = GTS_PSURFACE (object); + guint i; + + if (!GTS_PSURFACE_IS_CLOSED (ps)) + gts_psurface_close (ps); + + for (i = 0; i < ps->split->len; i++) + if (g_ptr_array_index (ps->split, i)) + gts_object_destroy (GTS_OBJECT (g_ptr_array_index (ps->split, i))); + g_ptr_array_free (ps->split, TRUE); + + (* GTS_OBJECT_CLASS (gts_psurface_class ())->parent_class->destroy) (object); +} + +static void psurface_class_init (GtsObjectClass * klass) +{ + klass->destroy = psurface_destroy; +} + +static void psurface_init (GtsPSurface * psurface) +{ + psurface->s = NULL; + psurface->split = g_ptr_array_new (); + psurface->split_class = gts_split_class (); + psurface->pos = psurface->min = 0; + psurface->vertices = psurface->faces = NULL; +} + +/** + * gts_psurface_class: + * + * Returns: the #GtsPSurfaceClass. + */ +GtsPSurfaceClass * gts_psurface_class (void) +{ + static GtsPSurfaceClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo psurface_info = { + "GtsPSurface", + sizeof (GtsPSurface), + sizeof (GtsPSurfaceClass), + (GtsObjectClassInitFunc) psurface_class_init, + (GtsObjectInitFunc) psurface_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (gts_object_class (), + &psurface_info); + } + + return klass; +} + +static GtsVertex * edge_collapse (GtsPSurface * ps, + GtsEdge * e, + GtsEHeap * heap, + GtsCoarsenFunc coarsen_func, + gpointer coarsen_data, + gdouble maxcosine2) +{ + GtsVertex * v1 = GTS_SEGMENT (e)->v1, * v2 = GTS_SEGMENT (e)->v2, * mid; + GtsSplit * vs; + GtsObject * o1, * o2; + + /* if the edge is degenerate (i.e. v1 == v2), destroy and return */ + if (v1 == v2) { + gts_object_destroy (GTS_OBJECT (e)); + return NULL; + } + + if (!gts_edge_collapse_is_valid (e) || + /* check that a non-manifold edge is not a contact edge */ + (g_slist_length (e->triangles) > 2 && gts_edge_is_contact (e) > 1)) { + GTS_OBJECT (e)->reserved = + gts_eheap_insert_with_key (heap, e, G_MAXDOUBLE); + return NULL; + } + + mid = (*coarsen_func) (e, ps->s->vertex_class, coarsen_data); + + if (gts_edge_collapse_creates_fold (e, mid, maxcosine2)) { + GTS_OBJECT (e)->reserved = + gts_eheap_insert_with_key (heap, e, G_MAXDOUBLE); + gts_object_destroy (GTS_OBJECT (mid)); + return NULL; + } + + if (GTS_OBJECT (v1)->reserved) + o1 = GTS_OBJECT (v1)->reserved; + else + o1 = GTS_OBJECT (v1); + if (GTS_OBJECT (v2)->reserved) + o2 = GTS_OBJECT (v2)->reserved; + else + o2 = GTS_OBJECT (v2); + vs = gts_split_new (ps->split_class, mid, o1, o2); + gts_split_collapse (vs, ps->s->edge_class, heap); + GTS_OBJECT (vs->v)->reserved = vs; + g_ptr_array_add (ps->split, vs); + + return mid; +} + +static void update_2nd_closest_neighbors (GtsVertex * v, GtsEHeap * heap) +{ + GSList * i = v->segments; + GSList * list = NULL; + + while (i) { + GtsSegment * s = i->data; + if (GTS_IS_EDGE (s)) { + GtsVertex * v1 = s->v1 == v ? s->v2 : s->v1; + GSList * j = v1->segments; + while (j) { + GtsSegment * s1 = j->data; + if (GTS_IS_EDGE (s1) && !g_slist_find (list, s1)) + list = g_slist_prepend (list, s1); + j = j->next; + } + } + i = i->next; + } + + i = list; + while (i) { + GtsEdge * e = i->data; + if (GTS_OBJECT (e)->reserved) + HEAP_REMOVE_OBJECT (heap, e); + HEAP_INSERT_OBJECT (heap, e); + i = i->next; + } + + g_slist_free (list); +} + +static gdouble edge_length2 (GtsEdge * e) +{ + return gts_point_distance2 (GTS_POINT (GTS_SEGMENT (e)->v1), + GTS_POINT (GTS_SEGMENT (e)->v2)); +} + +static void create_heap_coarsen (GtsEdge * e, GtsEHeap * heap) +{ + HEAP_INSERT_OBJECT (heap, e); +} + +/* #define DEBUG_FOLD */ +/* #define DEBUG_CONTACT_VERTEX */ + +#ifdef DEBUG_FOLD +static void check_fold (GtsTriangle * t, gdouble * maxcosine2) +{ + GtsEdge * e1 = t->e1, * e2 = t->e2, * e3 = t->e3; + + + if (gts_triangles_are_folded (e1->triangles, + GTS_SEGMENT (e1)->v1, + GTS_SEGMENT (e1)->v2, + *maxcosine2) || + gts_triangles_are_folded (e2->triangles, + GTS_SEGMENT (e2)->v1, + GTS_SEGMENT (e2)->v2, + *maxcosine2) || + gts_triangles_are_folded (e3->triangles, + GTS_SEGMENT (e3)->v1, + GTS_SEGMENT (e3)->v2, + *maxcosine2)) { + fprintf (stderr, "triangle %p:(%p,%p,%p) is folded\n", t, e1, e2, e3); + g_assert_not_reached (); + } +} +#endif + +/** + * gts_psurface_new: + * @klass: a #GtsPSurfaceClass. + * @surface: a #GtsSurface. + * @split_class: a #GtsSplitClass to use for the new progressive surface. + * @cost_func: cost function for the edge collapse algorithm. + * @cost_data: data to pass to @cost_func. + * @coarsen_func: the function returning the vertex replacement for the edge + * collapse. + * @coarsen_data: data to pass to @coarsen_func. + * @stop_func: the function to call to decide whether to stop the coarsening + * process. + * @stop_data: data to pass to @stop_func. + * @minangle: the minimum angle allowable between two neighboring triangles. + * This is used to avoid introducing folds in the mesh during simplification. + * + * This function works in exactly the same way as the + * gts_surface_coarsen() function, except that the history of edge + * collapse is saved in an array of #GtsSplit objects. This allows for + * dynamic continuous multiresolution control of the input @surface. + * + * Returns: a new progressive surface. + */ +GtsPSurface * gts_psurface_new (GtsPSurfaceClass * klass, + GtsSurface * surface, + GtsSplitClass * split_class, + GtsKeyFunc cost_func, + gpointer cost_data, + GtsCoarsenFunc coarsen_func, + gpointer coarsen_data, + GtsStopFunc stop_func, + gpointer stop_data, + gdouble minangle) +{ + GtsPSurface * psurface; + GtsEHeap * heap; + GtsEdge * e; + gdouble top_cost, maxcosine2; + guint i; + + g_return_val_if_fail (klass != NULL, NULL); + g_return_val_if_fail (surface != NULL, NULL); + g_return_val_if_fail (split_class != NULL, NULL); + g_return_val_if_fail (stop_func != NULL, NULL); + + psurface = GTS_PSURFACE (gts_object_new (GTS_OBJECT_CLASS (klass))); + psurface->s = surface; + psurface->split_class = split_class; + + if (cost_func == NULL) + cost_func = (GtsKeyFunc) edge_length2; + if (coarsen_func == NULL) + coarsen_func = (GtsCoarsenFunc) gts_segment_midvertex; + + heap = gts_eheap_new (cost_func, cost_data); + maxcosine2 = cos (minangle); maxcosine2 *= maxcosine2; + + gts_eheap_freeze (heap); + gts_surface_foreach_edge (surface, (GtsFunc) create_heap_coarsen, heap); + gts_eheap_thaw (heap); + /* we want to control edge destruction manually */ + gts_allow_floating_edges = TRUE; + while ((e = gts_eheap_remove_top (heap, &top_cost)) && + (top_cost < G_MAXDOUBLE) && + !(*stop_func) (top_cost, gts_eheap_size (heap) - + gts_edge_face_number (e, surface), stop_data)) { + GtsVertex * v = edge_collapse (psurface, e, heap, + coarsen_func, coarsen_data, maxcosine2); + if (v != NULL) { + update_2nd_closest_neighbors (v, heap); +#ifdef DEBUG_FOLD + { + GSList * triangles = gts_vertex_triangles (v, NULL), * i; + fprintf (stderr, "\n---- Check for folds ----\n%p: ", v); + i = triangles; + while (i) { + GtsTriangle * t = i->data; + fprintf (stderr, "%p:(%p,%p,%p) ", t, t->e1, t->e2, t->e3); + i = i->next; + } + fprintf (stderr, "\n"); + g_slist_free (triangles); + gts_surface_foreach_face (surface, (GtsFunc) check_fold, &maxcosine2); + } +#endif +#ifdef DEBUG_CONTACT_VERTEX + if (gts_vertex_is_contact (v, FALSE) != 1) { + FILE * fptr = fopen ("after", "wt"); + GSList * triangles = gts_vertex_triangles (v, NULL), * i; + + fprintf (stderr, "collapse of %p created a contact vertex\n", e); + + fprintf (fptr, + "(geometry \"sphere\" { = SPHERE 0.1 0. 0. 0. })\n" + "(normalization \"sphere\" none)\n"); + i = triangles; + while (i) { + gts_write_triangle (i->data, GTS_POINT (v), fptr); + i = i->next; + } + g_assert_not_reached (); + } +#endif + } + } + gts_allow_floating_edges = FALSE; + + /* set reserved field of remaining edges back to NULL */ + if (e) GTS_OBJECT (e)->reserved = NULL; + gts_eheap_foreach (heap, (GFunc) gts_object_reset_reserved, NULL); + + gts_eheap_destroy (heap); + + psurface->pos = psurface->split->len; + psurface->min = gts_surface_vertex_number (psurface->s); + + /* set reserved field of vertices (used to build the hierarchy) + back to NULL */ + for (i = 0; i < psurface->split->len; i++) { + GtsSplit * vs = g_ptr_array_index (psurface->split, i); + gts_object_reset_reserved (GTS_OBJECT (vs->v)); + } + + return psurface; +} + +/** + * gts_psurface_add_vertex: + * @ps: a #GtsPSurface. + * + * Adds a vertex to the progressive surface @ps by expanding the next + * available #GtsSplit. + * + * Returns: the expanded #GtsSplit or %NULL if all the #GtsSplit have already + * been expanded. + */ +GtsSplit * gts_psurface_add_vertex (GtsPSurface * ps) +{ + GtsSplit * vs; + + g_return_val_if_fail (ps != NULL, NULL); + g_return_val_if_fail (GTS_PSURFACE_IS_CLOSED (ps), NULL); + + if (ps->pos == 0) + return NULL; + + vs = g_ptr_array_index (ps->split, --ps->pos); + gts_split_expand (vs, ps->s, ps->s->edge_class); + + return vs; +} + +/** + * gts_psurface_remove_vertex: + * @ps: a #GtsPSurface. + * + * Removes one vertex from the progressive surface @ps by collapsing the first + * available #GtsSplit. + * + * Returns: the collapsed #GtsSplit or %NULL if all the #GtsSplit have already + * been collapsed. + */ +GtsSplit * gts_psurface_remove_vertex (GtsPSurface * ps) +{ + GtsSplit * vs; + + g_return_val_if_fail (ps != NULL, NULL); + g_return_val_if_fail (GTS_PSURFACE_IS_CLOSED (ps), NULL); + + if (ps->pos == ps->split->len) + return NULL; + + vs = g_ptr_array_index (ps->split, ps->pos++); + gts_split_collapse (vs, ps->s->edge_class, NULL); + + return vs; +} + +/** + * gts_psurface_max_vertex_number: + * @ps: a #GtsPSurface. + * + * Returns: the maximum number of vertices of @ps i.e. the number of vertices + * if all the #GtsSplit were expanded. + */ +guint gts_psurface_max_vertex_number (GtsPSurface * ps) +{ + g_return_val_if_fail (ps != NULL, 0); + + return ps->min + ps->split->len; +} + +/** + * gts_psurface_min_vertex_number: + * @ps: a #GtsPSurface. + * + * Returns: the minimum number of vertices of @ps i.e. the number of vertices + * if all the #GtsSplit were collapsed. + */ +guint gts_psurface_min_vertex_number (GtsPSurface * ps) +{ + g_return_val_if_fail (ps != NULL, 0); + + return ps->min; +} + +/** + * gts_psurface_set_vertex_number: + * @ps: a #GtsPSurface. + * @n: a number of vertices. + * + * Performs the required number of collapses or expansions to set the number + * of vertices of @ps to @n. + */ +void gts_psurface_set_vertex_number (GtsPSurface * ps, guint n) +{ + g_return_if_fail (ps != NULL); + g_return_if_fail (GTS_PSURFACE_IS_CLOSED (ps)); + + n = ps->min + ps->split->len - n; + while (ps->pos > n && gts_psurface_add_vertex (ps)) + ; + while (ps->pos < n && gts_psurface_remove_vertex (ps)) + ; +} + +/** + * gts_psurface_get_vertex_number: + * @ps: a #GtsPSurface. + * + * Returns: the current number of vertices of @ps. + */ +guint gts_psurface_get_vertex_number (GtsPSurface * ps) +{ + g_return_val_if_fail (ps != NULL, 0); + + if (!GTS_PSURFACE_IS_CLOSED (ps)) + return ps->min + ps->pos; + else + return ps->min + ps->split->len - ps->pos; +} + +/** + * gts_psurface_foreach_vertex: + * @ps: a #GtsPSurface. + * @func: a function to call for each vertex of @ps. + * @data: data to be passed to @func. + * + * Calls @func for each (potential) vertex of @ps, whether actually used + * or not. The vertices are called in the order they were created during the + * edge collapse operation. + */ +void gts_psurface_foreach_vertex (GtsPSurface * ps, + GtsFunc func, + gpointer data) +{ + guint i; + + g_return_if_fail (ps != NULL); + g_return_if_fail (func != NULL); + g_return_if_fail (GTS_PSURFACE_IS_CLOSED (ps)); + + for (i = 0; i < ps->split->len; i++) { + GtsSplit * vs = g_ptr_array_index (ps->split, i); + (*func) (vs->v, data); + } +} Index: toporouter/src_3rd/gts/refine.c =================================================================== --- toporouter/src_3rd/gts/refine.c (nonexistent) +++ toporouter/src_3rd/gts/refine.c (revision 6803) @@ -0,0 +1,418 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "gts.h" + +/** + * gts_vertex_encroaches_edge: + * @v: a #GtsVertex. + * @e: a #GtsEdge. + * + * Returns: %TRUE if @v is strictly contained in the diametral circle of @e, + * %FALSE otherwise. + */ +gboolean gts_vertex_encroaches_edge (GtsVertex * v, GtsEdge * e) +{ + GtsPoint * p, * p1, * p2; + + g_return_val_if_fail (v != NULL, FALSE); + g_return_val_if_fail (e != NULL, FALSE); + + p = GTS_POINT (v); + p1 = GTS_POINT (GTS_SEGMENT (e)->v1); + p2 = GTS_POINT (GTS_SEGMENT (e)->v2); + + if ((p1->x - p->x)*(p2->x - p->x) + (p1->y - p->y)*(p2->y - p->y) < 0.0) + return TRUE; + return FALSE; +} + +/** + * gts_edge_is_encroached: + * @e: a #GtsEdge. + * @s: a #GtsSurface describing a (constrained) Delaunay triangulation. + * @encroaches: a #GtsEncroachFunc. + * @data: user data to be passed to @encroaches. + * + * Returns: a #GtsVertex belonging to @s and encroaching upon @e + * (as defined by @encroaches) or %NULL if there is none. + */ +GtsVertex * gts_edge_is_encroached (GtsEdge * e, + GtsSurface * s, + GtsEncroachFunc encroaches, + gpointer data) +{ + GSList * i; + + g_return_val_if_fail (e != NULL, NULL); + g_return_val_if_fail (s != NULL, NULL); + g_return_val_if_fail (encroaches != NULL, NULL); + + i = e->triangles; + while (i) { + GtsFace * f = i->data; + if (GTS_IS_FACE (f) && gts_face_has_parent_surface (f, s)) { + GtsVertex * v = gts_triangle_vertex_opposite (GTS_TRIANGLE (f), e); + if ((* encroaches) (v, e, s, data)) + return v; + } + i = i->next; + } + + return NULL; +} + +#define ALREADY_ENCROACHED(c) (GTS_OBJECT (c)->reserved) + +static void vertex_encroaches (GtsVertex * v, + GtsSurface * surface, + GtsFifo * encroached, + GtsEncroachFunc encroaches, + gpointer data) +{ + GSList * triangles, * i; + + g_return_if_fail (v != NULL); + g_return_if_fail (surface != NULL); + g_return_if_fail (encroached != NULL); + g_return_if_fail (encroaches != NULL); + + i = triangles = gts_vertex_triangles (v, NULL); + while (i) { + GtsFace * f = i->data; + if (GTS_IS_FACE (f) && gts_face_has_parent_surface (f, surface)) { + GtsEdge * e = gts_triangle_edge_opposite (i->data, v); + if (!ALREADY_ENCROACHED (e) && + GTS_IS_CONSTRAINT (e) && + (* encroaches) (v, e, surface, data)) { + gts_fifo_push (encroached, e); + ALREADY_ENCROACHED (e) = encroached; + } + } + i = i->next; + } + g_slist_free (triangles); +} + +static void make_encroached_fifo (GtsEdge * e, gpointer * datas) +{ + GtsFifo * fifo = datas[0]; + GtsSurface * s = datas[1]; + GtsEncroachFunc encroaches = (GtsEncroachFunc) datas[2]; + gpointer data = datas[3]; + + if (GTS_IS_CONSTRAINT (e) && + gts_edge_is_encroached (e, s, encroaches, data)) { + gts_fifo_push (fifo, e); + ALREADY_ENCROACHED (e) = fifo; + } +} + +#define SQUARE_ROOT_TWO 1.41421356237309504880168872420969807856967187 +#define DISTANCE_2D(v1, v2) (sqrt ((GTS_POINT (v2)->x - GTS_POINT (v1)->x)*\ + (GTS_POINT (v2)->x - GTS_POINT (v1)->x) +\ + (GTS_POINT (v2)->y - GTS_POINT (v1)->y)*\ + (GTS_POINT (v2)->y - GTS_POINT (v1)->y))) + +/* finds where to split the given edge to avoid infinite cycles. (see + Shewchuk's thesis for details */ +static GtsVertex * split_edge (GtsEdge * e, + GtsSurface * surface) +{ + GSList * i = e->triangles; + GtsEdge * c = NULL; + + /* look for constraints touching e */ + while (i && !c) { + GtsTriangle * t = i->data; + if (GTS_IS_FACE (t) && + gts_face_has_parent_surface (GTS_FACE (t), surface)) { + GtsEdge * e1, * e2; + if (t->e1 == e) { e1 = t->e2; e2 = t->e3; } + else if (t->e2 == e) { e1 = t->e1; e2 = t->e3; } + else { e1 = t->e1; e2 = t->e2; } + if (GTS_IS_CONSTRAINT (e1) && !GTS_IS_CONSTRAINT (e2)) + c = e1; + else if (GTS_IS_CONSTRAINT (e2) && !GTS_IS_CONSTRAINT (e1)) + c = e2; + } + i = i->next; + } + if (c) { + /* use power of two concentric shells */ + GtsVertex * v1 = GTS_SEGMENT (e)->v1; + GtsVertex * v2 = GTS_SEGMENT (e)->v2; + gdouble l = DISTANCE_2D (v1, v2); + gdouble nearestpower = 1., split; + + while (l > SQUARE_ROOT_TWO*nearestpower) + nearestpower *= 2.; + while (l < SQUARE_ROOT_TWO*nearestpower/2.) + nearestpower /= 2.; + split = nearestpower/l/2.; + + if (GTS_SEGMENT (c)->v1 == v2 || GTS_SEGMENT (c)->v2 == v2) + split = 1. - split; + return gts_vertex_new (surface->vertex_class, + (1. - split)*GTS_POINT (v1)->x + + split*GTS_POINT (v2)->x, + (1. - split)*GTS_POINT (v1)->y + + split*GTS_POINT (v2)->y, + (1. - split)*GTS_POINT (v1)->z + + split*GTS_POINT (v2)->z); + } + else + return gts_segment_midvertex (GTS_SEGMENT (e), surface->vertex_class); +} + +static gint split_encroached (GtsSurface * surface, + GtsFifo * encroached, + gint steiner_max, + GtsEncroachFunc encroaches, + gpointer data) +{ + GtsSegment * s; + + while (steiner_max-- != 0 && (s = gts_fifo_pop (encroached))) { + GtsVertex * v = split_edge (GTS_EDGE (s), surface); + GtsFace * boundary = gts_edge_is_boundary (GTS_EDGE (s), surface); + GtsFace * f = boundary; +#if 1 + GtsEdge * e1 = GTS_EDGE (gts_object_clone (GTS_OBJECT (s))); + GtsEdge * e2 = GTS_EDGE (gts_object_clone (GTS_OBJECT (s))); + + GTS_SEGMENT (e1)->v1 = s->v1; + s->v1->segments = g_slist_prepend (s->v1->segments, e1); + GTS_SEGMENT (e1)->v2 = v; + v->segments = g_slist_prepend (v->segments, e1); + + GTS_SEGMENT (e2)->v1 = v; + v->segments = g_slist_prepend (v->segments, e2); + GTS_SEGMENT (e2)->v2 = s->v2; + s->v2->segments = g_slist_prepend (s->v2->segments, e2); +#else + GtsEdge * e1 = gts_edge_new (GTS_EDGE_CLASS (GTS_OBJECT (s)->klass), + s->v1, v); + GtsEdge * e2 = gts_edge_new (GTS_EDGE_CLASS (GTS_OBJECT (s)->klass), + v, s->v2); +#endif + + GTS_OBJECT (s)->klass = GTS_OBJECT_CLASS (surface->edge_class); + + if (f == NULL) + g_assert ((f = gts_edge_has_parent_surface (GTS_EDGE (s), surface))); + g_assert (gts_delaunay_add_vertex_to_face (surface, v, f) == NULL); + + if (boundary) + gts_object_destroy (GTS_OBJECT (s)); + + vertex_encroaches (v, surface, encroached, encroaches, data); + + if (gts_edge_is_encroached (e1, surface, encroaches, data)) { + gts_fifo_push (encroached, e1); + ALREADY_ENCROACHED (e1) = encroached; + } + if (gts_edge_is_encroached (e2, surface, encroaches, data)) { + gts_fifo_push (encroached, e2); + ALREADY_ENCROACHED (e2) = encroached; + } + } + + return steiner_max; +} + +/** + * gts_delaunay_conform: + * @surface: a #GtsSurface describing a constrained Delaunay triangulation. + * @steiner_max: maximum number of Steiner points. + * @encroaches: a #GtsEncroachFunc. + * @data: user-data to pass to @encroaches. + * + * Recursively split constraints of @surface which are encroached by + * vertices of @surface (see Shewchuk 96 for details). The split + * constraints are destroyed and replaced by a set of new constraints + * of the same class. If gts_vertex_encroaches_edge() is used for + * @encroaches, the resulting surface will be Delaunay conforming. + * + * If @steiner_max is positive or nul, the recursive splitting + * procedure will stop when this maximum number of Steiner points is + * reached. In that case the resulting surface will not necessarily be + * Delaunay conforming. + * + * Returns: the number of remaining encroached edges. If @steiner_max + * is set to a negative value and gts_vertex_encroaches_edge() is used + * for @encroaches this should always be zero. + */ +guint gts_delaunay_conform (GtsSurface * surface, + gint steiner_max, + GtsEncroachFunc encroaches, + gpointer data) +{ + GtsFifo * encroached; + gpointer datas[4]; + guint encroached_number; + + g_return_val_if_fail (surface != NULL, 0); + g_return_val_if_fail (surface != NULL, 0); + g_return_val_if_fail (encroaches != NULL, 0); + + datas[0] = encroached = gts_fifo_new (); + datas[1] = surface; + datas[2] = encroaches; + datas[3] = data; + gts_surface_foreach_edge (surface, (GtsFunc) make_encroached_fifo, datas); + + split_encroached (surface, + encroached, + steiner_max, + encroaches, data); + gts_fifo_foreach (encroached, (GtsFunc) gts_object_reset_reserved, NULL); + encroached_number = gts_fifo_size (encroached); + gts_fifo_destroy (encroached); + return encroached_number; +} + +#define EHEAP_PAIR(f) (GTS_OBJECT (f)->reserved) + +static void heap_surface_add_face (GtsSurface * s, GtsFace * f) +{ + GtsEHeap * heap = GTS_OBJECT (s)->reserved; + gdouble key = gts_eheap_key (heap, f); + + if (key != 0.) + EHEAP_PAIR (f) = gts_eheap_insert_with_key (heap, f, key); + + if (GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass->parent_class)->add_face) + (* GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass->parent_class)->add_face) + (s, f); +} + +static void heap_surface_remove_face (GtsSurface * s, GtsFace * f) +{ + GtsEHeap * heap = GTS_OBJECT (s)->reserved; + + if (EHEAP_PAIR (f)) + gts_eheap_remove (heap, EHEAP_PAIR (f)); + + if (GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass->parent_class)->remove_face) + (* GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass->parent_class)->remove_face) + (s, f); +} + +static void heap_surface_class_init (GtsSurfaceClass * klass) +{ + klass->add_face = heap_surface_add_face; + klass->remove_face = heap_surface_remove_face; +} + +static GtsObjectClass * heap_surface_class_new (GtsObjectClass * parent_class) +{ + GtsObjectClassInfo heap_surface_info; + + heap_surface_info = parent_class->info; + heap_surface_info.class_init_func = (GtsObjectClassInitFunc) + heap_surface_class_init; + return gts_object_class_new (parent_class, + &heap_surface_info); +} + +static void make_face_heap (GtsFace * f, GtsEHeap * heap) +{ + gdouble key = gts_eheap_key (heap, f); + + if (key != 0.) + EHEAP_PAIR (f) = gts_eheap_insert_with_key (heap, f, key); +} + +/** + * gts_delaunay_refine: + * @surface: a #GtsSurface describing a conforming Delaunay triangulation. + * @steiner_max: maximum number of Steiner points. + * @encroaches: a #GtsEncroachFunc. + * @encroach_data: user-data to pass to @encroaches. + * @cost: a #GtsKeyFunc used to sort the faces during refinement. + * @cost_data: user-data to pass to @cost. + * + * An implementation of the refinement algorithm described in Ruppert + * (1995) and Shewchuk (1996). + * + * Returns: the number of unrefined faces of @surface left. Should be zero + * if @steiner_max is set to a negative value. + */ +guint gts_delaunay_refine (GtsSurface * surface, + gint steiner_max, + GtsEncroachFunc encroaches, + gpointer encroach_data, + GtsKeyFunc cost, + gpointer cost_data) +{ + GtsObjectClass * heap_surface_class; + GtsObjectClass * original_class; + GtsEHeap * heap; + GtsFifo * encroached; + GtsFace * f; + guint unrefined_number; + + g_return_val_if_fail (surface != NULL, 0); + g_return_val_if_fail (encroaches != NULL, 0); + g_return_val_if_fail (cost != NULL, 0); + + original_class = GTS_OBJECT (surface)->klass; + heap_surface_class = heap_surface_class_new (original_class); + GTS_OBJECT (surface)->klass = heap_surface_class; + + heap = gts_eheap_new (cost, cost_data); + gts_surface_foreach_face (surface, (GtsFunc) make_face_heap, heap); + encroached = gts_fifo_new (); + + GTS_OBJECT (surface)->reserved = heap; + + while (steiner_max-- != 0 && (f = gts_eheap_remove_top (heap, NULL))) { + GtsVertex * c = + GTS_VERTEX (gts_triangle_circumcircle_center (GTS_TRIANGLE (f), + GTS_POINT_CLASS (surface->vertex_class))); + EHEAP_PAIR (f) = NULL; + g_assert (c != NULL); + g_assert (gts_delaunay_add_vertex (surface, c, f) == NULL); + + vertex_encroaches (c, surface, encroached, encroaches, encroach_data); + if (!gts_fifo_is_empty (encroached)) { + gts_delaunay_remove_vertex (surface, c); + steiner_max = split_encroached (surface, + encroached, + steiner_max, + encroaches, + encroach_data); + } + } + + unrefined_number = gts_eheap_size (heap); + gts_eheap_foreach (heap, (GFunc) gts_object_reset_reserved, NULL); + gts_eheap_destroy (heap); + + gts_fifo_foreach (encroached, (GtsFunc) gts_object_reset_reserved, NULL); + gts_fifo_destroy (encroached); + + GTS_OBJECT (surface)->klass = original_class; + GTS_OBJECT (surface)->reserved = NULL; + g_free (heap_surface_class); + + return unrefined_number; +} Index: toporouter/src_3rd/gts/rounding.h =================================================================== --- toporouter/src_3rd/gts/rounding.h (nonexistent) +++ toporouter/src_3rd/gts/rounding.h (revision 6803) @@ -0,0 +1,85 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#ifdef HAVE_FPU_CONTROL_H +# include +# ifdef _FPU_EXTENDED +# if !defined(__alpha__) || !defined(__GLIBC__) +# if defined(__arm__) + static fpu_control_t fpu_round_double = _FPU_DEFAULT; +# else + static fpu_control_t fpu_round_double = + (_FPU_DEFAULT & ~ _FPU_EXTENDED)|_FPU_DOUBLE; +# endif + static fpu_control_t fpu_init; +# define FPU_ROUND_DOUBLE { _FPU_GETCW(fpu_init);\ + _FPU_SETCW(fpu_round_double); } +# define FPU_RESTORE {_FPU_SETCW(fpu_init);} +# else /* __alpha__ && __GLIBC__ */ +# define FPU_ROUND_DOUBLE +# define FPU_RESTORE +# endif /* __alpha__ && __GLIBC__ */ +# else /* not FPU_EXTENDED */ +# define FPU_ROUND_DOUBLE +# define FPU_RESTORE +# endif /* not FPU_EXTENDED */ +#else /* not HAVE_FPU_CONTROL_H */ +# ifdef __FreeBSD__ +# include +# define FPU_ROUND_DOUBLE (fpsetprec(FP_PD)) +# define FPU_RESTORE (fpsetprec(FP_PE)) +# else /* not __FreeBSD__ */ +# ifdef WIN32 +# ifdef _MSC_VER +# include + static unsigned int fpu_init; +# define FPU_ROUND_DOUBLE (fpu_init = _controlfp (0, 0),\ + _controlfp (_PC_53, MCW_PC)) +# define FPU_RESTORE (_controlfp (fpu_init, 0xfffff)) +# elif __MINGW32__ +# include + static unsigned int fpu_init; +# define FPU_ROUND_DOUBLE (fpu_init = _controlfp (0, 0),\ + _controlfp (_PC_53, _MCW_PC)) +# define FPU_RESTORE (_controlfp (fpu_init, 0xfffff)) +# else /* not _MSC_VER or __MINGW32__ */ +# error "You need MSVC or MinGW for the Win32 version" +# endif /* not _MSC_VER or __MINGW32__ */ +# else /* not WIN32 */ +# ifdef __CYGWIN__ + typedef unsigned int fpu_control_t __attribute__ ((__mode__ (__HI__))); + static fpu_control_t fpu_round_double = 0x027f; + static fpu_control_t fpu_init; +# define _FPU_GETCW(cw) __asm__ ("fnstcw %0" : "=m" (*&cw)) +# define _FPU_SETCW(cw) __asm__ ("fldcw %0" : : "m" (*&cw)) +# define FPU_ROUND_DOUBLE { _FPU_GETCW(fpu_init);\ + _FPU_SETCW(fpu_round_double); } +# define FPU_RESTORE { _FPU_SETCW(fpu_init);} +# else /* not __CYGWIN__ */ +# ifdef CPP_HAS_WARNING +# warning "Unknown CPU: assuming default double precision rounding" +# endif /* CPP_HAS_WARNING */ +# define FPU_ROUND_DOUBLE +# define FPU_RESTORE +# endif /* not __CYGWIN__ */ +# endif /* not WIN32 */ +# endif /* not __FreeBSD__ */ +#endif /* not HAVE_FPU_CONTROL_H */ Index: toporouter/src_3rd/gts/segment.c =================================================================== --- toporouter/src_3rd/gts/segment.c (nonexistent) +++ toporouter/src_3rd/gts/segment.c (revision 6803) @@ -0,0 +1,233 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "gts.h" + +static void segment_destroy (GtsObject * object) +{ + GtsSegment * segment = GTS_SEGMENT (object); + GtsVertex * v1 = segment->v1; + GtsVertex * v2 = segment->v2; + + v1->segments = g_slist_remove (v1->segments, segment); + if (!GTS_OBJECT_DESTROYED (v1) && + !gts_allow_floating_vertices && v1->segments == NULL) + gts_object_destroy (GTS_OBJECT (v1)); + + v2->segments = g_slist_remove (v2->segments, segment); + if (!GTS_OBJECT_DESTROYED (v2) && + !gts_allow_floating_vertices && v2->segments == NULL) + gts_object_destroy (GTS_OBJECT (v2)); + + (* GTS_OBJECT_CLASS (gts_segment_class ())->parent_class->destroy) (object); +} + +static void segment_class_init (GtsObjectClass * klass) +{ + klass->destroy = segment_destroy; +} + +static void segment_init (GtsSegment * segment) +{ + segment->v1 = segment->v2 = NULL; +} + +/** + * gts_segment_class: + * + * Returns: the #GtsSegmentClass. + */ +GtsSegmentClass * gts_segment_class (void) +{ + static GtsSegmentClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo segment_info = { + "GtsSegment", + sizeof (GtsSegment), + sizeof (GtsSegmentClass), + (GtsObjectClassInitFunc) segment_class_init, + (GtsObjectInitFunc) segment_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (gts_object_class (), + &segment_info); + } + + return klass; +} + +/** + * gts_segment_new: + * @klass: a #GtsSegmentClass. + * @v1: a #GtsVertex. + * @v2: another #GtsVertex different from @v1. + * + * Returns: a new #GtsSegment linking @v1 and @v2. + */ +GtsSegment * gts_segment_new (GtsSegmentClass * klass, + GtsVertex * v1, GtsVertex * v2) +{ + GtsSegment * s; + + g_return_val_if_fail (v1 != NULL, NULL); + g_return_val_if_fail (v2 != NULL, NULL); + g_return_val_if_fail (v1 != v2, NULL); + + s = GTS_SEGMENT (gts_object_new (GTS_OBJECT_CLASS (klass))); + s->v1 = v1; + s->v2 = v2; + v1->segments = g_slist_prepend (v1->segments, s); + v2->segments = g_slist_prepend (v2->segments, s); + + return s; +} + +/** + * gts_segment_is_duplicate: + * @s: a #GtsSegment. + * + * Returns: the first #GtsSegment different from @s which shares the + * same endpoints or %NULL if there is none. + */ +GtsSegment * gts_segment_is_duplicate (GtsSegment * s) +{ + GSList * i; + GtsVertex * v2; + + g_return_val_if_fail (s != NULL, NULL); + + v2 = s->v2; + i = s->v1->segments; + if (s->v1 == v2) /* s is degenerate: special treatment */ + while (i) { + GtsSegment * s1 = i->data; + if (s1 != s && s1->v1 == v2 && s1->v2 == v2) + return s1; + i = i->next; + } + else /* s is not degenerate */ + while (i) { + GtsSegment * s1 = i->data; + if (s1 != s && (s1->v1 == v2 || s1->v2 == v2)) + return s1; + i = i->next; + } + return NULL; +} + +/** + * gts_segments_are_intersecting: + * @s1: a #GtsSegment. + * @s2: a #GtsSegment. + * + * Returns: %GTS_IN if @s1 and @s2 are intersecting, %GTS_ON if one of the + * endpoints of @s1 (resp. @s2) lies on @s2 (resp. @s1), %GTS_OUT otherwise. + */ +GtsIntersect gts_segments_are_intersecting (GtsSegment * s1, GtsSegment * s2) +{ + GtsPoint * p1, * p2, * p3, * p4; + gdouble d1, d2, d3, d4; + + g_return_val_if_fail (s1 != NULL && s2 != NULL, FALSE); + + p1 = GTS_POINT (s1->v1); p2 = GTS_POINT (s1->v2); + p3 = GTS_POINT (s2->v1); p4 = GTS_POINT (s2->v2); + d1 = gts_point_orientation (p1, p2, p3); + d2 = gts_point_orientation (p1, p2, p4); + if ((d1 > 0.0 && d2 > 0.0) || + (d1 < 0.0 && d2 < 0.0)) + return GTS_OUT; + d3 = gts_point_orientation (p3, p4, p1); + d4 = gts_point_orientation (p3, p4, p2); + if ((d3 > 0.0 && d4 > 0.0) || + (d3 < 0.0 && d4 < 0.0)) + return GTS_OUT; + if (d1 == 0.0 || d2 == 0.0 || d3 == 0.0 || d4 == 0.0) + return GTS_ON; + return GTS_IN; +} + +/** + * gts_segment_midvertex: + * @s: a #GtsSegment. + * @klass: a #GtsVertexClass to be used for the new vertex. + * + * Returns: a new #GtsVertex, midvertex of @s. + */ +GtsVertex * gts_segment_midvertex (GtsSegment * s, GtsVertexClass * klass) +{ + GtsPoint * p1, * p2; + + g_return_val_if_fail (s != NULL, NULL); + g_return_val_if_fail (klass != NULL, NULL); + + p1 = GTS_POINT (s->v1); p2 = GTS_POINT (s->v2); + return gts_vertex_new (klass, + (p1->x + p2->x)/2., + (p1->y + p2->y)/2., + (p1->z + p2->z)/2.); +} + +/** + * gts_segments_from_vertices: + * @vertices: a list of #GtsVertex. + * + * Returns: a list of unique #GtsSegment which have one of their vertices in + * @vertices. + */ +GSList * gts_segments_from_vertices (GSList * vertices) +{ + GHashTable * hash; + GSList * segments = NULL, * i; + + hash = g_hash_table_new (NULL, NULL); + i = vertices; + while (i) { + GSList * j = GTS_VERTEX (i->data)->segments; + while (j) { + GtsSegment * s = j->data; + if (g_hash_table_lookup (hash, s) == NULL) { + segments = g_slist_prepend (segments, s); + g_hash_table_insert (hash, s, i); + } + j = j->next; + } + i = i->next; + } + g_hash_table_destroy (hash); + return segments; +} + +/** + * gts_segment_is_ok: + * @s: a #GtsSegment. + * + * Returns: %TRUE if @s is not degenerate (i.e. @s->v1 != @s->v2) and not + * duplicate, %FALSE otherwise. + */ +gboolean gts_segment_is_ok (GtsSegment * s) +{ + g_return_val_if_fail (s != NULL, FALSE); + g_return_val_if_fail (s->v1 != s->v2, FALSE); + g_return_val_if_fail (!gts_segment_is_duplicate (s), FALSE); + g_return_val_if_fail (GTS_OBJECT (s)->reserved == NULL, FALSE); + return TRUE; +} Index: toporouter/src_3rd/gts/split.c =================================================================== --- toporouter/src_3rd/gts/split.c (nonexistent) +++ toporouter/src_3rd/gts/split.c (revision 6803) @@ -0,0 +1,1840 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include +#include "gts.h" + +#define DYNAMIC_SPLIT +#define NEW + +/* #define DEBUG + #define DEBUG_HEXPAND + #define DEBUG_EXPAND */ + +struct _GtsSplitCFace { + GtsFace * f; + GtsTriangle ** a1, ** a2; +}; + +typedef struct _CFace CFace; +typedef struct _CFaceClass CFaceClass; + +struct _CFace { + GtsObject object; + + GtsSplit * parent_split; + GtsTriangle * t; + guint flags; +}; +/* the size of the CFace structure must be smaller or equal to the size + of the GtsFace structure as both structures use the same memory location */ + +struct _CFaceClass { + GtsObjectClass parent_class; +}; + +#define IS_CFACE(obj) (gts_object_is_from_class (obj, cface_class ())) +#define CFACE(obj) ((CFace *) obj) +#define CFACE_ORIENTATION(cf) ((cf)->flags & 0x1) +#define CFACE_ORIENTATION_DIRECT(cf) ((cf)->flags |= 0x1) +#define CFACE_VVS(cf) ((cf)->flags & 0x2) +#define CFACE_VVS_DIRECT(cf) ((cf)->flags |= 0x2) +#define CFACE_E1 0x4 +#define CFACE_E2 0x8 +#define CFACE_KEEP_VVS 0x10 + +#define ROTATE_ORIENT(e, e1, e2, e3) { if (e1 == e) { e1 = e2; e2 = e3; }\ + else if (e2 == e) { e2 = e1; e1 = e3; }\ + else g_assert (e3 == e); } +#define SEGMENT_USE_VERTEX(s, v) ((s)->v1 == v || (s)->v2 == v) +#define TRIANGLE_REPLACE_EDGE(t, e, with) { if ((t)->e1 == e)\ + (t)->e1 = with;\ + else if ((t)->e2 == e)\ + (t)->e2 = with;\ + else {\ + g_assert ((t)->e3 == e);\ + (t)->e3 = with;\ + }\ + } + +#define HEAP_INSERT_OBJECT(h, e) (GTS_OBJECT (e)->reserved =\ + gts_eheap_insert (h, e)) +#define HEAP_REMOVE_OBJECT(h, e) (gts_eheap_remove (h, GTS_OBJECT (e)->reserved),\ + GTS_OBJECT (e)->reserved = NULL) + +static GtsObjectClass * cface_class (void) +{ + static GtsObjectClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo cface_info = { + "GtsCFace", + sizeof (CFace), + sizeof (CFaceClass), + (GtsObjectClassInitFunc) NULL, + (GtsObjectInitFunc) NULL, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (gts_object_class (), &cface_info); + g_assert (sizeof (CFace) <= sizeof (GtsFace)); + } + + return klass; +} + +/* Replace @e with @with for all the triangles using @e but @f. + Destroys @e and removes it from @heap (if not %NULL). + Returns a triangle using e different from f or %NULL. */ +static GtsTriangle * replace_edge_collapse (GtsEdge * e, + GtsEdge * with, + CFace * cf, + GtsEHeap * heap +#ifdef DYNAMIC_SPLIT + , GtsTriangle *** a1 +#endif +#ifdef NEW + , guint edge_flag +#endif + ) +{ + GSList * i; + GtsTriangle * rt = NULL; +#ifdef DYNAMIC_SPLIT + guint size; + GtsTriangle ** a; +#endif + +#ifdef NEW + i = e->triangles; + e->triangles = NULL; + size = g_slist_length (i)*sizeof (GtsTriangle *); + *a1 = a = g_malloc (size > 0 ? size : sizeof (GtsTriangle *)); + while (i) { + GtsTriangle * t = i->data; + GSList * next = i->next; + if (t != ((GtsTriangle *) cf)) { + if (IS_CFACE (t)) { + i->next = e->triangles; + e->triangles = i; + /* set the edge given by edge_flag (CFACE_E1 or CFACE_E2) */ + GTS_OBJECT (t)->reserved = GUINT_TO_POINTER (edge_flag); + cf->flags |= CFACE_KEEP_VVS; + } + else { + TRIANGLE_REPLACE_EDGE (t, e, with); + i->next = with->triangles; + with->triangles = i; + rt = t; + *(a++) = t; + } + } + else + g_slist_free_1 (i); + i = next; + } + *a = NULL; + if (!e->triangles) { + if (heap) + HEAP_REMOVE_OBJECT (heap, e); + gts_object_destroy (GTS_OBJECT (e)); + } +#else /* not NEW */ + i = e->triangles; +#ifdef DYNAMIC_SPLIT + size = g_slist_length (i)*sizeof (GtsTriangle *); + *a1 = a = g_malloc (size > 0 ? size : sizeof (GtsTriangle *)); +#endif + while (i) { + GtsTriangle * t = i->data; + GSList * next = i->next; + if (t != ((GtsTriangle *) cf)) { + TRIANGLE_REPLACE_EDGE (t, e, with); + i->next = with->triangles; + with->triangles = i; + rt = t; +#ifdef DYNAMIC_SPLIT + *(a++) = t; +#endif + } + else + g_slist_free_1 (i); + i = next; + } +#ifdef DYNAMIC_SPLIT + *a = NULL; +#endif + if (heap) + HEAP_REMOVE_OBJECT (heap, e); + e->triangles = NULL; + gts_object_destroy (GTS_OBJECT (e)); +#endif /* NEW */ + + return rt; +} + +static CFace * cface_new (GtsFace * f, + GtsEdge * e, + GtsVertex * v1, + GtsVertex * v2, + GtsSplit * vs, + GtsEHeap * heap, + GtsEdgeClass * klass +#ifdef DYNAMIC_SPLIT + , GtsSplitCFace * scf +#endif + ) +{ + CFace * cf; + GtsVertex * v; + GtsEdge * e1, * e2, * e3, * vvs; + GSList * i; + GtsTriangle * t, * t1 = NULL, * t2 = NULL; + guint flags; + + g_return_val_if_fail (f != NULL, NULL); +#ifndef NEW + g_return_val_if_fail (GTS_IS_FACE (f), NULL); +#endif + g_return_val_if_fail (e != NULL, NULL); + g_return_val_if_fail (vs != NULL, NULL); + + t = ((GtsTriangle *) f); + if (heap) + g_return_val_if_fail (!gts_triangle_is_duplicate (t), NULL); + +#ifdef NEW + /* get CFACE_E1 and CFACE_E2 info */ + flags = GPOINTER_TO_UINT (GTS_OBJECT (f)->reserved); +#endif + GTS_OBJECT_SET_FLAGS (f, GTS_DESTROYED); + + i = f->surfaces; + while (i) { + GSList * next = i->next; + gts_surface_remove_face (i->data, f); + i = next; + } + g_slist_free (f->surfaces); + + e1 = t->e1; e2 = t->e2; e3 = t->e3; + ROTATE_ORIENT (e, e1, e2, e3); + + cf = (CFace *) f; +#ifndef NEW + GTS_OBJECT (cf)->klass = cface_class (); +#else + cf->flags = flags; +#endif + gts_object_init (GTS_OBJECT (cf), cface_class ()); + cf->parent_split = vs; + + if (SEGMENT_USE_VERTEX (GTS_SEGMENT (e1), v2)) { + CFACE_ORIENTATION_DIRECT (cf); /* v1->v2->v */ + e3 = e1; e1 = e2; e2 = e3; + } + v = GTS_SEGMENT (e1)->v1 == v1 ? + GTS_SEGMENT (e1)->v2 : GTS_SEGMENT (e1)->v1; +#ifdef NEW + if ((cf->flags & CFACE_E1) || (cf->flags & CFACE_E2)) + g_assert ((vvs = GTS_EDGE (gts_vertices_are_connected (vs->v, v)))); + else +#endif + vvs = gts_edge_new (klass, v, vs->v); + + t1 = replace_edge_collapse (e1, vvs, cf, heap +#ifdef DYNAMIC_SPLIT + , &scf->a1 +#endif +#ifdef NEW + , CFACE_E1 +#endif + ); + t2 = replace_edge_collapse (e2, vvs, cf, heap +#ifdef DYNAMIC_SPLIT + , &scf->a2 +#endif +#ifdef NEW + , CFACE_E2 +#endif + ); + t = cf->t = t1 ? t1 : t2; + g_assert (t); + + /* set up flags necessary to find vvs */ + if (t->e1 == vvs) e2 = t->e2; + else if (t->e2 == vvs) e2 = t->e3; + else { + g_assert (t->e3 == vvs); + e2 = t->e1; + } + if (SEGMENT_USE_VERTEX (GTS_SEGMENT (e2), v)) + CFACE_VVS_DIRECT (cf); + + return cf; +} + +static void find_vvs (GtsVertex * vs, + GtsTriangle * t, + GtsVertex ** v, GtsEdge ** vvs, + gboolean orientation) +{ + GtsEdge * e1 = t->e1, * e2 = t->e2, * e3 = t->e3, * tmp; + + if (SEGMENT_USE_VERTEX (GTS_SEGMENT (e2), vs)) { + tmp = e1; e1 = e2; e2 = e3; e3 = tmp; + } + else if (SEGMENT_USE_VERTEX (GTS_SEGMENT (e3), vs)) { + tmp = e1; e1 = e3; e3 = e2; e2 = tmp; + } + else + g_assert (SEGMENT_USE_VERTEX (GTS_SEGMENT (e1), vs)); + if (SEGMENT_USE_VERTEX (GTS_SEGMENT (e2), vs) || + !gts_segments_touch (GTS_SEGMENT (e1), GTS_SEGMENT (e2))) { + tmp = e1; e1 = e2; e2 = e3; e3 = tmp; + g_assert (gts_segments_touch (GTS_SEGMENT (e1), GTS_SEGMENT (e2))); + } + + *vvs = orientation ? e1 : e3; + + if (GTS_SEGMENT (*vvs)->v1 != vs) { + g_assert (GTS_SEGMENT (*vvs)->v2 == vs); + *v = GTS_SEGMENT (*vvs)->v1; + } + else + *v = GTS_SEGMENT (*vvs)->v2; +} + +static void replace_edge_expand (GtsEdge * e, + GtsEdge * with, + GtsTriangle ** a, + GtsVertex * v) +{ + GtsTriangle ** i = a, * t; + + while ((t = *(i++))) { +#ifdef DEBUG_EXPAND + g_assert (!IS_CFACE (t)); + fprintf (stderr, "replacing %p->%d: e: %p->%d with: %p->%d\n", + t, id (t), e, id (e), with, id (with)); +#endif + TRIANGLE_REPLACE_EDGE (t, e, with); + with->triangles = g_slist_prepend (with->triangles, t); + if (GTS_OBJECT (t)->reserved) { + /* apart from the triangles having e as an edge, t is the only + triangle using v */ + g_assert (GTS_OBJECT (t)->reserved == v); + GTS_OBJECT (t)->reserved = NULL; + } + else + GTS_OBJECT (t)->reserved = v; + } +} + +static void cface_expand (CFace * cf, + GtsTriangle ** a1, + GtsTriangle ** a2, + GtsEdge * e, + GtsVertex * v1, + GtsVertex * v2, + GtsVertex * vs, + GtsEdgeClass * klass) +{ + GtsVertex * v; + GtsEdge * e1, * e2, * vvs; + gboolean orientation; + guint flags; + + g_return_if_fail (cf != NULL); + g_return_if_fail (IS_CFACE (cf)); + g_return_if_fail (e != NULL); + g_return_if_fail (vs != NULL); + + flags = cf->flags; + orientation = CFACE_ORIENTATION (cf); + + find_vvs (vs, cf->t, &v, &vvs, CFACE_VVS (cf)); + +#ifdef NEW + if (flags & CFACE_E1) + e1 = GTS_EDGE (gts_vertices_are_connected (v1, v)); + else + e1 = gts_edge_new (klass, v, v1); + if (flags & CFACE_E2) + e2 = GTS_EDGE (gts_vertices_are_connected (v2, v)); + else + e2 = gts_edge_new (klass, v, v2); +#else + e1 = gts_edge_new (v, v1); + e2 = gts_edge_new (v, v2); +#endif + + replace_edge_expand (vvs, e1, a1, v1); + replace_edge_expand (vvs, e2, a2, v2); + +#ifdef NEW + if (!(flags & CFACE_KEEP_VVS)) { + g_slist_free (vvs->triangles); + vvs->triangles = NULL; + gts_object_destroy (GTS_OBJECT (vvs)); + } +#else + g_slist_free (vvs->triangles); + vvs->triangles = NULL; + gts_object_destroy (GTS_OBJECT (vvs)); +#endif + + /* gts_face_new : because I am "creating" a face */ + GTS_OBJECT (cf)->klass = GTS_OBJECT_CLASS (gts_face_class ()); + gts_object_init (GTS_OBJECT (cf), GTS_OBJECT (cf)->klass); + + if (orientation) + gts_triangle_set (GTS_TRIANGLE (cf), e, e2, e1); + else + gts_triangle_set (GTS_TRIANGLE (cf), e, e1, e2); +} + +static void split_destroy (GtsObject * object) +{ + GtsSplit * vs = GTS_SPLIT (object); + guint i = vs->ncf; + GtsSplitCFace * cf = vs->cfaces; + + while (i--) { + if (IS_CFACE (cf->f)) + gts_object_destroy (GTS_OBJECT (cf->f)); + g_free (cf->a1); + g_free (cf->a2); + cf++; + } + g_free (vs->cfaces); + + if (!gts_allow_floating_vertices && vs->v && vs->v->segments == NULL) + gts_object_destroy (GTS_OBJECT (vs->v)); + + (* GTS_OBJECT_CLASS (gts_split_class ())->parent_class->destroy) (object); +} + +static void split_class_init (GtsObjectClass * klass) +{ + klass->destroy = split_destroy; +} + +static void split_init (GtsSplit * split) +{ + split->v1 = split->v2 = NULL; + split->v = NULL; + split->cfaces = NULL; + split->ncf = 0; +} + +/** + * gts_split_class: + * + * Returns: the #GtsSplitClass. + */ +GtsSplitClass * gts_split_class (void) +{ + static GtsSplitClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo split_info = { + "GtsSplit", + sizeof (GtsSplit), + sizeof (GtsSplitClass), + (GtsObjectClassInitFunc) split_class_init, + (GtsObjectInitFunc) split_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (gts_object_class (), + &split_info); + } + + return klass; +} + +#ifdef DEBUG +static gboolean edge_collapse_is_valid (GtsEdge * e) +{ + GSList * i; + + g_return_val_if_fail (e != NULL, FALSE); + + if (gts_segment_is_duplicate (GTS_SEGMENT (e))) { + g_warning ("collapsing duplicate edge"); + return FALSE; + } + + i = GTS_SEGMENT (e)->v1->segments; + while (i) { + GtsEdge * e1 = i->data; + if (e1 != e && GTS_IS_EDGE (e1)) { + GtsEdge * e2 = NULL; + GSList * j = GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e)->v1 ? + GTS_SEGMENT (e1)->v2->segments : GTS_SEGMENT (e1)->v1->segments; + while (j && !e2) { + GtsEdge * e1 = j->data; + if (GTS_IS_EDGE (e1) && + (GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e)->v2 || + GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e)->v2)) + e2 = e1; + j = j->next; + } + if (e2 && !gts_triangle_use_edges (e, e1, e2)) { + g_warning ("collapsing empty triangle"); + return FALSE; + } + } + i = i->next; + } + + if (gts_edge_is_boundary (e, NULL)) { + GtsTriangle * t = e->triangles->data; + if (gts_edge_is_boundary (t->e1, NULL) && + gts_edge_is_boundary (t->e2, NULL) && + gts_edge_is_boundary (t->e3, NULL)) { + g_warning ("collapsing single triangle"); + return FALSE; + } + } + else { + if (gts_vertex_is_boundary (GTS_SEGMENT (e)->v1, NULL) && + gts_vertex_is_boundary (GTS_SEGMENT (e)->v2, NULL)) { + g_warning ("collapsing two sides of a strip"); + return FALSE; + } + if (gts_edge_belongs_to_tetrahedron (e)) { + g_warning ("collapsing tetrahedron"); + return FALSE; + } + } + + return TRUE; +} +#endif /* DEBUG */ + +/* Not currently used. May be useful for some debug code */ +#ifdef DEBUG +static void print_split (GtsSplit * vs, FILE * fptr) +{ + guint j; + GtsSplitCFace * cf; + + g_return_if_fail (vs != NULL); + g_return_if_fail (fptr != NULL); + + fprintf (fptr, "%p: v: %p v1: %p v2: %p ncf: %u cfaces: %p\n", + vs, vs->v, vs->v1, vs->v2, vs->ncf, vs->cfaces); + cf = vs->cfaces; + j = vs->ncf; + while (j--) { + fprintf (stderr, " f: %p a1: %p a2: %p\n", + cf->f, cf->a1, cf->a2); + cf++; + } +} +#endif + +/** + * gts_split_collapse: + * @vs: a #GtsSplit. + * @klass: a #GtsEdgeClass. + * @heap: a #GtsEHeap or %NULL. + * + * Collapses the vertex split @vs. Any new edge created during the process will + * be of class @klass. If heap is not %NULL, the new edges will be inserted + * into it and the destroyed edges will be removed from it. + */ +void gts_split_collapse (GtsSplit * vs, + GtsEdgeClass * klass, + GtsEHeap * heap) +{ + GtsEdge * e; + GtsVertex * v, * v1, * v2; + GSList * i, * end; +#ifdef DYNAMIC_SPLIT + GtsSplitCFace * cf; + guint j; +#endif +#ifdef DEBUG + gboolean invalid = FALSE; + static guint ninvalid = 0; +#endif + + g_return_if_fail (vs != NULL); + g_return_if_fail (klass != NULL); + + v = vs->v; + + g_return_if_fail (v->segments == NULL); + + /* we don't want to destroy vertices */ + gts_allow_floating_vertices = TRUE; + + v1 = GTS_SPLIT_V1 (vs); + v2 = GTS_SPLIT_V2 (vs); + g_assert ((e = GTS_EDGE (gts_vertices_are_connected (v1, v2)))); + +#ifdef DEBUG + fprintf (stderr, "collapsing %p: v1: %p v2: %p v: %p\n", vs, v1, v2, v); + if (!edge_collapse_is_valid (e)) { + char fname[80]; + FILE * fptr; + GSList * triangles, * i; + + g_warning ("invalid edge collapse"); + invalid = TRUE; + sprintf (fname, "invalid.%d", ninvalid); + fptr = fopen (fname, "wt"); + gts_write_segment (GTS_SEGMENT (e), GTS_POINT (v), fptr); + triangles = gts_vertex_triangles (v1, NULL); + triangles = gts_vertex_triangles (v2, triangles); + i = triangles; + while (i) { + gts_write_triangle (i->data, GTS_POINT (v), fptr); + i = i->next; + } + g_slist_free (triangles); + fclose (fptr); + } +#endif + + i = e->triangles; +#ifdef DYNAMIC_SPLIT + cf = vs->cfaces; + j = vs->ncf; + while (j--) { + g_free (cf->a1); + g_free (cf->a2); + cf++; + } + g_free (vs->cfaces); + + vs->ncf = g_slist_length (i); + g_assert (vs->ncf > 0); + cf = vs->cfaces = g_malloc (vs->ncf*sizeof (GtsSplitCFace)); +#endif /* DYNAMIC_SPLIT */ +#ifdef NEW + while (i) { + cf->f = i->data; + g_assert (GTS_IS_FACE (cf->f)); + GTS_OBJECT (cf->f)->klass = GTS_OBJECT_CLASS (cface_class ()); + cf++; + i = i->next; + } + i = e->triangles; + cf = vs->cfaces; + while (i) { + cface_new (i->data, e, v1, v2, vs, heap, klass, cf); +#ifdef DEBUG + fprintf (stderr, "cface: %p->%d t: %p->%d a1: ", + cf->f, id (cf->f), CFACE (cf->f)->t, id (CFACE (cf->f)->t)); + { + GtsTriangle * t, ** a; + a = cf->a1; + while ((t = *(a++))) + fprintf (stderr, "%p->%d ", t, id (t)); + fprintf (stderr, "a2: "); + a = cf->a2; + while ((t = *(a++))) + fprintf (stderr, "%p->%d ", t, id (t)); + fprintf (stderr, "\n"); + } +#endif + cf++; + i = i->next; + } +#else /* not NEW */ + while (i) { + cface_new (i->data, e, v1, v2, vs, heap +#ifdef DYNAMIC_SPLIT + , cf +#endif /* DYNAMIC_SPLIT */ + ); +#ifdef DYNAMIC_SPLIT + cf->f = i->data; + cf++; +#endif /* DYNAMIC_SPLIT */ + i = i->next; + } +#endif /* NEW */ + g_slist_free (e->triangles); + e->triangles = NULL; + gts_object_destroy (GTS_OBJECT (e)); + + gts_allow_floating_vertices = FALSE; + + end = NULL; + i = v1->segments; + while (i) { + GtsSegment * s = i->data; + if (s->v1 == v1) + s->v1 = v; + else + s->v2 = v; + end = i; + i = i->next; + } + if (end) { + end->next = v->segments; + v->segments = v1->segments; + v1->segments = NULL; + } + + end = NULL; + i = v2->segments; + while (i) { + GtsSegment * s = i->data; + if (s->v1 == v2) + s->v1 = v; + else + s->v2 = v; + end = i; + i = i->next; + } + if (end) { + end->next = v->segments; + v->segments = v2->segments; + v2->segments = NULL; + } + +#ifdef DEBUG + if (invalid) { + char fname[80]; + FILE * fptr; + GSList * triangles, * i; + GtsSurface * surface = NULL; + + sprintf (fname, "invalid_after.%d", ninvalid); + fptr = fopen (fname, "wt"); + triangles = gts_vertex_triangles (v, NULL); + i = triangles; + while (i) { + GtsTriangle * t = i->data; + fprintf (stderr, "checking %p->%d\n", t, id (t)); + g_assert (GTS_IS_FACE (t)); + gts_write_triangle (t, GTS_POINT (v), fptr); + surface = GTS_FACE (t)->surfaces->data; + if (gts_triangle_is_duplicate (t)) + fprintf (stderr, "%p->%d is duplicate\n", t, id (t)); + if (gts_segment_is_duplicate (GTS_SEGMENT (t->e1))) + fprintf (stderr, "e1 of %p->%d is duplicate\n", t, id (t)); + if (gts_segment_is_duplicate (GTS_SEGMENT (t->e2))) + fprintf (stderr, "e2 of %p->%d is duplicate\n", t, id (t)); + if (gts_segment_is_duplicate (GTS_SEGMENT (t->e3))) + fprintf (stderr, "e3 of %p->%d is duplicate\n", t, id (t)); + i = i->next; + } + fclose (fptr); + g_slist_free (triangles); +#if 0 + gts_split_expand (vs, surface); + + sprintf (fname, "invalid_after_after.%d", ninvalid); + fptr = fopen (fname, "wt"); + triangles = gts_vertex_triangles (v1, NULL); + triangles = gts_vertex_triangles (v2, triangles); + i = triangles; + while (i) { + GtsTriangle * t = i->data; + gts_write_triangle (t, GTS_POINT (v), fptr); + surface = GTS_FACE (t)->surfaces->data; + if (gts_triangle_is_duplicate (t)) + fprintf (stderr, "%p->%d is duplicate\n", t, id (t)); + if (gts_segment_is_duplicate (GTS_SEGMENT (t->e1))) + fprintf (stderr, "e1 of %p->%d is duplicate\n", t, id (t)); + if (gts_segment_is_duplicate (GTS_SEGMENT (t->e2))) + fprintf (stderr, "e2 of %p->%d is duplicate\n", t, id (t)); + if (gts_segment_is_duplicate (GTS_SEGMENT (t->e3))) + fprintf (stderr, "e3 of %p->%d is duplicate\n", t, id (t)); + i = i->next; + } + fclose (fptr); + g_slist_free (triangles); + + exit (1); +#endif + ninvalid++; + } +#endif +} + +/** + * gts_split_expand: + * @vs: a #GtsSplit. + * @s: a #GtsSurface. + * @klass: a #GtsEdgeClass. + * + * Expands the vertex split @vs adding the newly created faces to @s. Any + * new edge will be of class @klass. + */ +void gts_split_expand (GtsSplit * vs, + GtsSurface * s, + GtsEdgeClass * klass) +{ + GSList * i; + GtsEdge * e; + GtsVertex * v, * v1, * v2; + gboolean changed = FALSE; + GtsSplitCFace * cf; + guint j; + + g_return_if_fail (vs != NULL); + g_return_if_fail (s != NULL); + g_return_if_fail (klass != NULL); + + /* we don't want to destroy vertices */ + gts_allow_floating_vertices = TRUE; + + v1 = GTS_SPLIT_V1 (vs); + v2 = GTS_SPLIT_V2 (vs); + v = vs->v; +#ifdef DEBUG_EXPAND + fprintf (stderr, "expanding %p->%d: v1: %p->%d v2: %p->%d v: %p->%d\n", + vs, id (vs), v1, id (v1), v2, id (v2), v, id (v)); +#endif + e = gts_edge_new (klass, v1, v2); + cf = vs->cfaces; + j = vs->ncf; + while (j--) { + cface_expand (CFACE (cf->f), cf->a1, cf->a2, e, v1, v2, v, klass); + gts_surface_add_face (s, cf->f); + cf++; + } + + gts_allow_floating_vertices = FALSE; + + /* this part is described by figure "expand.fig" */ + i = v->segments; + while (i) { + GtsEdge * e1 = i->data; + GtsVertex * with = NULL; + GSList * j = e1->triangles, * next = i->next; + // fprintf (stderr, "e1: %p->%d\n", e1, id (e1)); + while (j && !with) { + with = GTS_OBJECT (j->data)->reserved; + j = j->next; + } + if (with) { + j = e1->triangles; + while (j) { + GtsTriangle * t = j->data; + if (GTS_OBJECT (t)->reserved) { + g_assert (GTS_OBJECT (t)->reserved == with); + GTS_OBJECT (t)->reserved = NULL; + } + else + GTS_OBJECT (t)->reserved = with; + j = j->next; + } + if (GTS_SEGMENT (e1)->v1 == v) + GTS_SEGMENT (e1)->v1 = with; + else + GTS_SEGMENT (e1)->v2 = with; + + v->segments = g_slist_remove_link (v->segments, i); + i->next = with->segments; + with->segments = i; + changed = TRUE; + } + if (next) + i = next; + else { + /* check for infinite loop (the crossed out case in + figure "expand.fig") */ + g_assert (changed); + changed = FALSE; + i = v->segments; + } + } +} + +#ifndef DYNAMIC_SPLIT +static void cface_neighbors (GtsSplitCFace * cf, + GtsEdge * e, + GtsVertex * v1, + GtsVertex * v2) +{ + GtsTriangle * t = GTS_TRIANGLE (cf->f), ** a; + GtsEdge * e1 = t->e1, * e2 = t->e2, * e3 = t->e3; + GSList * i; + guint size; + + ROTATE_ORIENT (e, e1, e2, e3); + if (SEGMENT_USE_VERTEX (GTS_SEGMENT (e1), v2)) { + e3 = e1; e1 = e2; e2 = e3; + } + + i = e1->triangles; + size = g_slist_length (i)*sizeof (GtsTriangle *); + a = cf->a1 = g_malloc (size > 0 ? size : sizeof (GtsTriangle *)); + while (i) { + if (i->data != t) + *(a++) = i->data; + i = i->next; + } + *a = NULL; + + i = e2->triangles; + size = g_slist_length (i)*sizeof (GtsTriangle *); + a = cf->a2 = g_malloc (size > 0 ? size : sizeof (GtsTriangle *)); + while (i) { + if (i->data != t) + *(a++) = i->data; + i = i->next; + } + *a = NULL; +} +#endif /*ifndef DYNAMIC_SPLIT */ + +/** + * gts_split_new: + * @klass: a #GtsSplitClass. + * @v: a #GtsVertex. + * @o1: either a #GtsVertex or a #GtsSplit. + * @o2: either a #GtsVertex or a #GtsSplit. + * + * Creates a new #GtsSplit which would collapse @o1 and @o2 into @v. The + * collapse itself is not performed. + * + * Returns: the new #GtsSplit. + */ +GtsSplit * gts_split_new (GtsSplitClass * klass, + GtsVertex * v, + GtsObject * o1, + GtsObject * o2) +{ + GtsSplit * vs; +#ifndef DYNAMIC_SPLIT + GtsVertex * v1, * v2; + GtsEdge * e; + GSList * i; + GtsSplitCFace * cf; +#endif + + g_return_val_if_fail (klass != NULL, NULL); + g_return_val_if_fail (v != NULL, NULL); + g_return_val_if_fail (GTS_IS_SPLIT (o1) || GTS_IS_VERTEX (o1), NULL); + g_return_val_if_fail (GTS_IS_SPLIT (o2) || GTS_IS_VERTEX (o2), NULL); + + vs = GTS_SPLIT (gts_object_new (GTS_OBJECT_CLASS (klass))); + vs->v = v; + vs->v1 = o1; + vs->v2 = o2; +#ifdef DYNAMIC_SPLIT + vs->ncf = 0; + vs->cfaces = NULL; +#else + v1 = GTS_SPLIT_V1 (vs); + v2 = GTS_SPLIT_V2 (vs); + g_assert ((e = GTS_EDGE (gts_vertices_are_connected (v1, v2)))); + i = e->triangles; + vs->ncf = g_slist_length (i); + g_assert (vs->ncf > 0); + cf = vs->cfaces = g_malloc (vs->ncf*sizeof (GtsSplitCFace)); + while (i) { + cf->f = i->data; + cface_neighbors (cf, e, v1, v2); + i = i->next; + cf++; + } +#endif + + return vs; +} + +static gboolean +split_traverse_pre_order (GtsSplit * vs, + GtsSplitTraverseFunc func, + gpointer data) +{ + if (func (vs, data)) + return TRUE; + if (GTS_IS_SPLIT (vs->v1) && + split_traverse_pre_order (GTS_SPLIT (vs->v1), func, data)) + return TRUE; + if (GTS_IS_SPLIT (vs->v2) && + split_traverse_pre_order (GTS_SPLIT (vs->v2), func, data)) + return TRUE; + return FALSE; +} + +static gboolean +split_depth_traverse_pre_order (GtsSplit * vs, + guint depth, + GtsSplitTraverseFunc func, + gpointer data) +{ + if (func (vs, data)) + return TRUE; + + depth--; + if (!depth) + return FALSE; + + if (GTS_IS_SPLIT (vs->v1) && + split_depth_traverse_pre_order (GTS_SPLIT (vs->v1), depth, func, data)) + return TRUE; + if (GTS_IS_SPLIT (vs->v2) && + split_depth_traverse_pre_order (GTS_SPLIT (vs->v2), depth, func, data)) + return TRUE; + return FALSE; +} + +static gboolean +split_traverse_post_order (GtsSplit * vs, + GtsSplitTraverseFunc func, + gpointer data) +{ + if (GTS_IS_SPLIT (vs->v1) && + split_traverse_post_order (GTS_SPLIT (vs->v1), func, data)) + return TRUE; + if (GTS_IS_SPLIT (vs->v2) && + split_traverse_post_order (GTS_SPLIT (vs->v2), func, data)) + return TRUE; + if (func (vs, data)) + return TRUE; + return FALSE; +} + +static gboolean +split_depth_traverse_post_order (GtsSplit * vs, + guint depth, + GtsSplitTraverseFunc func, + gpointer data) +{ + depth--; + if (depth) { + if (GTS_IS_SPLIT (vs->v1) && + split_depth_traverse_post_order (GTS_SPLIT (vs->v1), + depth, func, data)) + return TRUE; + if (GTS_IS_SPLIT (vs->v2) && + split_depth_traverse_post_order (GTS_SPLIT (vs->v2), + depth, func, data)) + return TRUE; + } + if (func (vs, data)) + return TRUE; + return FALSE; +} + +/** + * gts_split_traverse: + * @root: the #GtsSplit to start the traversal from. + * @order: the order in which nodes are visited - G_PRE_ORDER or G_POST_ORDER. + * @depth: the maximum depth of the traversal. Nodes below this depth + * will not be visited. If depth is -1 all nodes in the tree are + * visited. If depth is 1, only the root is visited. If depth is 2, + * the root and its children are visited. And so on. + * @func: the function to call for each visited #GtsHSplit. + * @data: user data to pass to the function. + * + * Traverses the #GtsSplit tree having @root as root. Calls @func for each + * #GtsSplit of the tree in the order specified by @order. If order is set + * to G_PRE_ORDER @func is called for the #GtsSplit then its children, if order + * is set to G_POST_ORDER @func is called for the children and then for the + * #GtsSplit. + */ +void gts_split_traverse (GtsSplit * root, + GTraverseType order, + gint depth, + GtsSplitTraverseFunc func, + gpointer data) +{ + g_return_if_fail (root != NULL); + g_return_if_fail (func != NULL); + g_return_if_fail (order < G_LEVEL_ORDER); + g_return_if_fail (depth == -1 || depth > 0); + + switch (order) { + case G_PRE_ORDER: + if (depth < 0) + split_traverse_pre_order (root, func, data); + else + split_depth_traverse_pre_order (root, depth, func, data); + break; + case G_POST_ORDER: + if (depth < 0) + split_traverse_post_order (root, func, data); + else + split_depth_traverse_post_order (root, depth, func, data); + break; + default: + g_assert_not_reached (); + } +} + +/** + * gts_split_height: + * @root: a #GtsSplit. + * + * Returns: the maximum height of the vertex split tree having @root as root. + */ +guint gts_split_height (GtsSplit * root) +{ + guint height = 0, tmp_height; + + g_return_val_if_fail (root != NULL, 0); + + if (GTS_IS_SPLIT (root->v1)) { + tmp_height = gts_split_height (GTS_SPLIT (root->v1)); + if (tmp_height > height) + height = tmp_height; + } + if (GTS_IS_SPLIT (root->v2)) { + tmp_height = gts_split_height (GTS_SPLIT (root->v2)); + if (tmp_height > height) + height = tmp_height; + } + + return height + 1; +} + +#ifndef DYNAMIC_SPLIT +static gboolean list_array_are_identical (GSList * list, + gpointer * array, + gpointer excluded) +{ + while (list) { + gpointer data = list->data; + if (data != excluded) { + gboolean found = FALSE; + gpointer * a = array; + + while (!found && *a) + if (*(a++) == data) + found = TRUE; + if (!found) + return FALSE; + } + list = list->next; + } + return TRUE; +} +#endif /* ifndef DYNAMIC_SPLIT */ + +#ifndef NEW +gboolean gts_split_is_collapsable (GtsSplit * vs) +{ + guint i; + GtsSplitCFace * cf; + GtsVertex * v1, * v2; + GtsEdge * e; + + g_return_val_if_fail (vs != NULL, FALSE); + + v1 = GTS_SPLIT_V1 (vs); + v2 = GTS_SPLIT_V2 (vs); + g_return_val_if_fail ((e = GTS_EDGE (gts_vertices_are_connected (v1, v2))), + FALSE); + +#ifdef DYNAMIC_SPLIT + if (!gts_edge_collapse_is_valid (e)) + return FALSE; +#else + i = vs->ncf; + cf = vs->cfaces; + while (i--) { + GtsTriangle * t = GTS_TRIANGLE (cf->f); + GtsEdge * e1 = t->e1, * e2 = t->e2, * e3 = t->e3; + + ROTATE_ORIENT (e, e1, e2, e3); + if (SEGMENT_USE_VERTEX (GTS_SEGMENT (e1), v2)) { + e3 = e1; e1 = e2; e2 = e3; + } + + if (!list_array_are_identical (e1->triangles, (gpointer *) cf->a1, t)) + return FALSE; + if (!list_array_are_identical (e2->triangles, (gpointer *) cf->a2, t)) + return FALSE; + + cf++; + } +#endif + return TRUE; +} +#endif /* not NEW */ + +#ifdef DEBUG_HEXPAND +static guint expand_level = 0; + +static void expand_indent (FILE * fptr) +{ + guint i = expand_level; + while (i--) + fputc (' ', fptr); +} +#endif + +/** + * gts_hsplit_force_expand: + * @hs: a #GtsHSplit. + * @hsurface: a #GtsHSurface. + * + * Forces the expansion of @hs by first expanding all its dependencies not + * already expanded. + */ +void gts_hsplit_force_expand (GtsHSplit * hs, + GtsHSurface * hsurface) +{ + guint i; + GtsSplitCFace * cf; + + g_return_if_fail (hs != NULL); + g_return_if_fail (hsurface != NULL); + g_return_if_fail (hs->nchild == 0); + +#ifdef DEBUG_HEXPAND + expand_level += 2; +#endif + + if (hs->parent && hs->parent->nchild == 0) { +#ifdef DEBUG_HEXPAND + expand_indent (stderr); + fprintf (stderr, "expand parent %p\n", hs->parent); +#endif + gts_hsplit_force_expand (hs->parent, hsurface); + } + + i = GTS_SPLIT (hs)->ncf; + cf = GTS_SPLIT (hs)->cfaces; + while (i--) { + GtsTriangle ** j, * t; + + j = cf->a1; + while ((t = *(j++))) + if (IS_CFACE (t)) { +#ifdef DEBUG_HEXPAND + expand_indent (stderr); + fprintf (stderr, "expand a1: cf->f: %p t: %p parent_split: %p\n", + cf->f, + t, + GTS_HSPLIT (CFACE (t)->parent_split)); +#endif + gts_hsplit_force_expand (GTS_HSPLIT (CFACE (t)->parent_split), + hsurface); +#ifdef DEBUG_HEXPAND + g_assert (!IS_CFACE (t)); +#endif + } + j = cf->a2; + while ((t = *(j++))) + if (IS_CFACE (t)) { +#ifdef DEBUG_HEXPAND + expand_indent (stderr); + fprintf (stderr, "expand a2: cf->f: %p t: %p parent_split: %p\n", + cf->f, + t, + GTS_HSPLIT (CFACE (t)->parent_split)); +#endif + gts_hsplit_force_expand (GTS_HSPLIT (CFACE (t)->parent_split), + hsurface); + } + cf++; + } + + gts_hsplit_expand (hs, hsurface); + +#ifdef DEBUG_HEXPAND + expand_level -= 2; + expand_indent (stderr); + fprintf (stderr, "%p expanded\n", hs); +#endif +} + +static void index_object (GtsObject * o, guint * n) +{ + o->reserved = GUINT_TO_POINTER ((*n)++); +} + +static void index_face (GtsFace * f, gpointer * data) +{ + guint * nf = data[1]; + + g_hash_table_insert (data[0], f, GUINT_TO_POINTER ((*nf)++)); +} + +/** + * gts_psurface_write: + * @ps: a #GtsPSurface. + * @fptr: a file pointer. + * + * Writes to @fptr a GTS progressive surface description. + */ +void gts_psurface_write (GtsPSurface * ps, FILE * fptr) +{ + guint nv = 1; + guint nf = 1; + GHashTable * hash; + gpointer data[2]; + + g_return_if_fail (ps != NULL); + g_return_if_fail (fptr != NULL); + g_return_if_fail (GTS_PSURFACE_IS_CLOSED (ps)); + + while (gts_psurface_remove_vertex (ps)) + ; + + GTS_POINT_CLASS (ps->s->vertex_class)->binary = FALSE; + gts_surface_write (ps->s, fptr); + + gts_surface_foreach_vertex (ps->s, (GtsFunc) index_object, &nv); + hash = g_hash_table_new (NULL, NULL); + data[0] = hash; + data[1] = &nf; + gts_surface_foreach_face (ps->s, (GtsFunc) index_face, data); + + fprintf (fptr, "%u\n", ps->split->len); + while (ps->pos) { + GtsSplit * vs = g_ptr_array_index (ps->split, --ps->pos); + GtsSplitCFace * scf = vs->cfaces; + GtsVertex * v1, * v2; + guint i = vs->ncf; + + fprintf (fptr, "%u %u", + GPOINTER_TO_UINT (GTS_OBJECT (vs->v)->reserved), + vs->ncf); + if (GTS_OBJECT (vs)->klass->write) + (*GTS_OBJECT (vs)->klass->write) (GTS_OBJECT (vs), fptr); + fputc ('\n', fptr); + + v1 = GTS_IS_SPLIT (vs->v1) ? GTS_SPLIT (vs->v1)->v : GTS_VERTEX (vs->v1); + GTS_OBJECT (v1)->reserved = GUINT_TO_POINTER (nv++); + v2 = GTS_IS_SPLIT (vs->v2) ? GTS_SPLIT (vs->v2)->v : GTS_VERTEX (vs->v2); + GTS_OBJECT (v2)->reserved = GUINT_TO_POINTER (nv++); + + (*GTS_OBJECT (v1)->klass->write) (GTS_OBJECT (v1), fptr); + fputc ('\n', fptr); + + (*GTS_OBJECT (v2)->klass->write) (GTS_OBJECT (v2), fptr); + fputc ('\n', fptr); + + while (i--) { + CFace * cf = CFACE (scf->f); + GtsTriangle ** a, * t; + + fprintf (fptr, "%u %u", + GPOINTER_TO_UINT (g_hash_table_lookup (hash, cf->t)), + cf->flags); + if (GTS_OBJECT_CLASS (ps->s->face_class)->write) + (*GTS_OBJECT_CLASS (ps->s->face_class)->write) (GTS_OBJECT (cf), fptr); + fputc ('\n', fptr); + + a = scf->a1; + while ((t = *(a++))) + fprintf (fptr, "%u ", + GPOINTER_TO_UINT (g_hash_table_lookup (hash, t))); + fprintf (fptr, "\n"); + + a = scf->a2; + while ((t = *(a++))) + fprintf (fptr, "%u ", + GPOINTER_TO_UINT (g_hash_table_lookup (hash, t))); + fprintf (fptr, "\n"); + + g_hash_table_insert (hash, cf, GUINT_TO_POINTER (nf++)); + + scf++; + } + + gts_split_expand (vs, ps->s, ps->s->edge_class); + } + + gts_surface_foreach_vertex (ps->s, + (GtsFunc) gts_object_reset_reserved, NULL); + g_hash_table_destroy (hash); +} + +static guint surface_read (GtsSurface * surface, + GtsFile * f, + GPtrArray * vertices, + GPtrArray * faces) +{ + GtsEdge ** edges; + guint n, nv, ne, nf; + + g_return_val_if_fail (surface != NULL, 1); + g_return_val_if_fail (f != NULL, 1); + + if (f->type != GTS_INT) { + gts_file_error (f, "expecting an integer (number of vertices)"); + return f->line; + } + nv = atoi (f->token->str); + + gts_file_next_token (f); + if (f->type != GTS_INT) { + gts_file_error (f, "expecting an integer (number of edges)"); + return f->line; + } + ne = atoi (f->token->str); + + gts_file_next_token (f); + if (f->type != GTS_INT) { + gts_file_error (f, "expecting an integer (number of faces)"); + return f->line; + } + nf = atoi (f->token->str); + + gts_file_next_token (f); + if (f->type == GTS_STRING) { + if (f->type != GTS_STRING) { + gts_file_error (f, "expecting a string (GtsSurfaceClass)"); + return f->line; + } + gts_file_next_token (f); + if (f->type != GTS_STRING) { + gts_file_error (f, "expecting a string (GtsFaceClass)"); + return f->line; + } + gts_file_next_token (f); + if (f->type != GTS_STRING) { + gts_file_error (f, "expecting a string (GtsEdgeClass)"); + return f->line; + } + gts_file_next_token (f); + if (f->type != GTS_STRING) { + gts_file_error (f, "expecting a string (GtsVertexClass)"); + return f->line; + } + if (!strcmp (f->token->str, "GtsVertexBinary")) + GTS_POINT_CLASS (surface->vertex_class)->binary = TRUE; + else + gts_file_first_token_after (f, '\n'); + } + else + gts_file_first_token_after (f, '\n'); + + g_ptr_array_set_size (vertices, nv); + g_ptr_array_set_size (faces, nf); + /* allocate nv + 1 just in case nv == 0 */ + edges = g_malloc ((ne + 1)*sizeof (GtsEdge *)); + + n = 0; + while (n < nv && f->type != GTS_ERROR) { + GtsObject * new_vertex = + gts_object_new (GTS_OBJECT_CLASS (surface->vertex_class)); + + (* GTS_OBJECT_CLASS (surface->vertex_class)->read) (&new_vertex, f); + if (f->type != GTS_ERROR) { + if (!GTS_POINT_CLASS (surface->vertex_class)->binary) + gts_file_first_token_after (f, '\n'); + g_ptr_array_index (vertices, n++) = new_vertex; + } + else + gts_object_destroy (new_vertex); + } + if (f->type == GTS_ERROR) + nv = n; + if (GTS_POINT_CLASS (surface->vertex_class)->binary) + gts_file_first_token_after (f, '\n'); + + n = 0; + while (n < ne && f->type != GTS_ERROR) { + guint p1, p2; + + if (f->type != GTS_INT) + gts_file_error (f, "expecting an integer (first vertex index)"); + else { + p1 = atoi (f->token->str); + if (p1 == 0 || p1 > nv) + gts_file_error (f, "vertex index `%d' is out of range `[1,%d]'", + p1, nv); + else { + gts_file_next_token (f); + if (f->type != GTS_INT) + gts_file_error (f, "expecting an integer (second vertex index)"); + else { + p2 = atoi (f->token->str); + if (p2 == 0 || p2 > nv) + gts_file_error (f, "vertex index `%d' is out of range `[1,%d]'", + p2, nv); + else { + GtsEdge * new_edge = + gts_edge_new (surface->edge_class, + g_ptr_array_index (vertices, p1 - 1), + g_ptr_array_index (vertices, p2 - 1)); + + gts_file_next_token (f); + if (f->type != '\n') + if (GTS_OBJECT_CLASS (surface->edge_class)->read) + (*GTS_OBJECT_CLASS (surface->edge_class)->read) + ((GtsObject **) &new_edge, f); + gts_file_first_token_after (f, '\n'); + edges[n++] = new_edge; + } + } + } + } + } + if (f->type == GTS_ERROR) + ne = n; + + n = 0; + while (n < nf && f->type != GTS_ERROR) { + guint s1, s2, s3; + + if (f->type != GTS_INT) + gts_file_error (f, "expecting an integer (first edge index)"); + else { + s1 = atoi (f->token->str); + if (s1 == 0 || s1 > ne) + gts_file_error (f, "edge index `%d' is out of range `[1,%d]'", + s1, ne); + else { + gts_file_next_token (f); + if (f->type != GTS_INT) + gts_file_error (f, "expecting an integer (second edge index)"); + else { + s2 = atoi (f->token->str); + if (s2 == 0 || s2 > ne) + gts_file_error (f, "edge index `%d' is out of range `[1,%d]'", + s2, ne); + else { + gts_file_next_token (f); + if (f->type != GTS_INT) + gts_file_error (f, "expecting an integer (third edge index)"); + else { + s3 = atoi (f->token->str); + if (s3 == 0 || s3 > ne) + gts_file_error (f, "edge index `%d' is out of range `[1,%d]'", + s3, ne); + else { + GtsFace * new_face = gts_face_new (surface->face_class, + edges[s1 - 1], + edges[s2 - 1], + edges[s3 - 1]); + + gts_file_next_token (f); + if (f->type != '\n') + if (GTS_OBJECT_CLASS (surface->face_class)->read) + (*GTS_OBJECT_CLASS (surface->face_class)->read) + ((GtsObject **) &new_face, f); + gts_file_first_token_after (f, '\n'); + gts_surface_add_face (surface, new_face); + g_ptr_array_index (faces, n++) = new_face; + } + } + } + } + } + } + } + + g_free (edges); + + if (f->type == GTS_ERROR) { + gts_allow_floating_vertices = TRUE; + while (nv) + gts_object_destroy (GTS_OBJECT (g_ptr_array_index (vertices, nv-- - 1))); + gts_allow_floating_vertices = FALSE; + return f->line; + } + + return 0; +} + +/** + * gts_psurface_open: + * @klass: a #GtsPSurfaceClass. + * @s: a #GtsSurface. + * @split_class: a #GtsSplitClass to use for the #GtsSplit. + * @f: a #GtsFile. + * + * Creates a new #GtsPSurface prepared for input from the file @f + * containing a valid GTS representation of a progressive surface. The initial + * shape of the progressive surface is loaded into @s. + * + * Before being usable as such this progressive surface must be closed using + * gts_psurface_close(). While open however, the functions + * gts_psurface_get_vertex_number(), gts_psurface_min_vertex_number() and + * gts_psurface_max_vertex_number() can still be used. + * + * Returns: a new #GtsPSurface or %NULL if there was a format error while + * reading the file, in which case @f contains information about the error. + */ +GtsPSurface * gts_psurface_open (GtsPSurfaceClass * klass, + GtsSurface * s, + GtsSplitClass * split_class, + GtsFile * f) +{ + GtsPSurface * ps; + + g_return_val_if_fail (klass != NULL, NULL); + g_return_val_if_fail (s != NULL, NULL); + g_return_val_if_fail (split_class != NULL, NULL); + g_return_val_if_fail (f != NULL, NULL); + + ps = GTS_PSURFACE (gts_object_new (GTS_OBJECT_CLASS (klass))); + ps->s = s; + ps->split_class = split_class; + + ps->vertices = g_ptr_array_new (); + ps->faces = g_ptr_array_new (); + + if (surface_read (s, f, ps->vertices, ps->faces)) { + ps->s = NULL; + gts_object_destroy (GTS_OBJECT (ps)); + return NULL; + } + + ps->min = gts_surface_vertex_number (ps->s); + ps->pos = 0; + + if (f->type == GTS_INT) { + gint ns = atoi (f->token->str); + + if (ns > 0) { + g_ptr_array_set_size (ps->split, ns); + gts_file_first_token_after (f, '\n'); + } + } + + return ps; +} + +/** + * gts_psurface_read_vertex: + * @ps: a #GtsPSurface prealably created with gts_psurface_open(). + * @fp: a #GtsFile. + * + * Reads in one vertex split operation from @fp and performs the expansion. + * + * If an error occurs while reading the file, the @error field of @fp is set. + * + * Returns: the newly created #GtsSplit or %NULL if no vertex split could be + * read from @fp. + */ +GtsSplit * gts_psurface_read_vertex (GtsPSurface * ps, GtsFile * fp) +{ + guint nv, ncf; + GtsSplit * vs, * parent; + GtsSplitCFace * scf; + + g_return_val_if_fail (ps != NULL, NULL); + g_return_val_if_fail (fp != NULL, NULL); + g_return_val_if_fail (!GTS_PSURFACE_IS_CLOSED (ps), NULL); + + if (ps->pos >= ps->split->len) + return NULL; + + if (fp->type == GTS_NONE) + return NULL; + if (fp->type != GTS_INT) { + gts_file_error (fp, "expecting an integer (vertex index)"); + return NULL; + } + nv = atoi (fp->token->str); + if (nv == 0 || nv > ps->vertices->len) { + gts_file_error (fp, "vertex index `%d' is out of range `[1,%d]'", + nv, ps->vertices->len); + return NULL; + } + + gts_file_next_token (fp); + if (fp->type != GTS_INT) { + gts_file_error (fp, "expecting an integer (ncf)"); + return NULL; + } + ncf = atoi (fp->token->str); + + vs = GTS_SPLIT (gts_object_new (GTS_OBJECT_CLASS (ps->split_class))); + + vs->v = g_ptr_array_index (ps->vertices, nv - 1); + vs->v1 = vs->v2 = NULL; + vs->cfaces = NULL; + vs->ncf = 0; + + gts_file_next_token (fp); + if (fp->type != '\n') + if (GTS_OBJECT (vs)->klass->read) + (* GTS_OBJECT (vs)->klass->read) ((GtsObject **) &vs, fp); + gts_file_first_token_after (fp, '\n'); + + if (fp->type != GTS_ERROR) { + vs->v1 = gts_object_new (GTS_OBJECT_CLASS (ps->s->vertex_class)); + (* GTS_OBJECT_CLASS (ps->s->vertex_class)->read) (&(vs->v1), fp); + if (fp->type != GTS_ERROR) { + vs->v1->reserved = vs; + g_ptr_array_add (ps->vertices, vs->v1); + + gts_file_first_token_after (fp, '\n'); + + vs->v2 = gts_object_new (GTS_OBJECT_CLASS (ps->s->vertex_class)); + (*GTS_OBJECT_CLASS (ps->s->vertex_class)->read) (&(vs->v2), fp); + if (fp->type != GTS_ERROR) { + vs->v2->reserved = vs; + g_ptr_array_add (ps->vertices, vs->v2); + gts_file_first_token_after (fp, '\n'); + } + } + } + + if (fp->type != GTS_ERROR) { + scf = vs->cfaces = g_malloc (sizeof (GtsSplitCFace)*ncf); + while (fp->type != GTS_ERROR && ncf--) { + guint it, flags; + GtsFace * f; + CFace * cf; + GPtrArray * a; + + if (fp->type != GTS_INT) + gts_file_error (fp, "expecting an integer (face index)"); + else { + it = atoi (fp->token->str); + if (it == 0 || it > ps->faces->len) + gts_file_error (fp, "face index `%d' is out of range `[1,%d]'", + it, ps->faces->len); + else { + gts_file_next_token (fp); + if (fp->type != GTS_INT) + gts_file_error (fp, "expecting an integer (flags)"); + else { + flags = atoi (fp->token->str); + f = + GTS_FACE (gts_object_new (GTS_OBJECT_CLASS (ps->s->face_class))); + + gts_file_next_token (fp); + if (fp->type != '\n') + if (GTS_OBJECT (f)->klass->read) + (*GTS_OBJECT (f)->klass->read) ((GtsObject **) &f, fp); + gts_file_first_token_after (fp, '\n'); + if (fp->type != GTS_ERROR) { + scf->f = f; + + cf = (CFace *) f; + GTS_OBJECT (cf)->klass = GTS_OBJECT_CLASS (cface_class ()); + cf->parent_split = vs; + cf->t = g_ptr_array_index (ps->faces, it - 1); + cf->flags = flags; + + a = g_ptr_array_new (); + do { + if (fp->type != GTS_INT) + gts_file_error (fp, "expecting an integer (face index)"); + else { + it = atoi (fp->token->str); + if (it > ps->faces->len) + gts_file_error (fp, + "face index `%d' is out of range `[1,%d]'", + it, ps->faces->len); + else { + g_ptr_array_add (a, g_ptr_array_index (ps->faces, + it - 1)); + gts_file_next_token (fp); + } + } + } while (fp->type != GTS_ERROR && fp->type != '\n'); + gts_file_first_token_after (fp, '\n'); + g_ptr_array_add (a, NULL); + scf->a1 = (GtsTriangle **) a->pdata; + g_ptr_array_free (a, FALSE); + + if (fp->type != GTS_ERROR) { + a = g_ptr_array_new (); + do { + if (fp->type != GTS_INT) + gts_file_error (fp, "expecting an integer (face index)"); + else { + it = atoi (fp->token->str); + if (it > ps->faces->len) + gts_file_error (fp, + "face index `%d' is out of range `[1,%d]'", + it, ps->faces->len); + else { + g_ptr_array_add (a, g_ptr_array_index (ps->faces, + it - 1)); + gts_file_next_token (fp); + } + } + } while (fp->type != GTS_ERROR && fp->type != '\n'); + gts_file_first_token_after (fp, '\n'); + g_ptr_array_add (a, NULL); + scf->a2 = (GtsTriangle **) a->pdata; + g_ptr_array_free (a, FALSE); + + g_ptr_array_add (ps->faces, f); + + vs->ncf++; + scf++; + } + } + } + } + } + } + } + + if (fp->type != GTS_ERROR) { + if ((parent = GTS_OBJECT (vs->v)->reserved)) { + GTS_OBJECT (vs->v)->reserved = NULL; + if (parent->v1 == GTS_OBJECT (vs->v)) + parent->v1 = GTS_OBJECT (vs); + else { + g_assert (parent->v2 == GTS_OBJECT (vs->v)); + parent->v2 = GTS_OBJECT (vs); + } + } + g_ptr_array_index (ps->split, ps->pos++) = vs; + gts_split_expand (vs, ps->s, ps->s->edge_class); + + return vs; + } + + if (vs->v1) gts_object_destroy (vs->v1); + if (vs->v2) gts_object_destroy (vs->v2); + gts_object_destroy (GTS_OBJECT (vs)); + + return NULL; +} + +/** + * gts_psurface_close: + * @ps: a #GtsPSurface prealably created with gts_psurface_open(). + * + * Closes a progressive surface. + */ +void gts_psurface_close (GtsPSurface * ps) +{ + g_return_if_fail (ps != NULL); + g_return_if_fail (!GTS_PSURFACE_IS_CLOSED (ps)); + + g_ptr_array_free (ps->vertices, TRUE); + g_ptr_array_free (ps->faces, TRUE); + ps->faces = ps->vertices = NULL; + + gts_surface_foreach_vertex (ps->s, + (GtsFunc) gts_object_reset_reserved, NULL); + if (ps->pos > 0) + g_ptr_array_set_size (ps->split, ps->pos); + if (ps->split->len > 1) { + guint i, half = ps->split->len/2, n = ps->split->len - 1; + + for (i = 0; i < half; i++) { + gpointer p1 = g_ptr_array_index (ps->split, i); + gpointer p2 = g_ptr_array_index (ps->split, n - i); + g_ptr_array_index (ps->split, n - i) = p1; + g_ptr_array_index (ps->split, i) = p2; + } + } + ps->pos = 0; +} Index: toporouter/src_3rd/gts/stripe.c =================================================================== --- toporouter/src_3rd/gts/stripe.c (nonexistent) +++ toporouter/src_3rd/gts/stripe.c (revision 6803) @@ -0,0 +1,766 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999-2003 Wagner Toledo Correa, Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "gts.h" + +#define PRINT_HEAP_ELEMENTS 0 + +typedef struct { + GtsTriangle * t; + gboolean used; + GSList * neighbors; + GtsEHeapPair *pos; +} tri_data_t; + +typedef struct { + GHashTable * ht; +} map_t; + +typedef struct { + map_t * map; + GtsEHeap * heap; +} heap_t; + +static tri_data_t * tri_data_new (GtsTriangle * t); +static void tri_data_destroy (tri_data_t * td); +static guint tri_data_num_unused_neighbors2 (const tri_data_t * td, + const map_t * map); +static GHashTable * tri_data_unused_neighbors2 (const tri_data_t * td, + const map_t * map); + +static map_t * map_new (GtsSurface * s); +static void map_destroy (map_t * map); +static tri_data_t * map_lookup (const map_t * map, GtsTriangle * t); + + +static heap_t * heap_new (GtsSurface * s); +static void heap_destroy (heap_t * heap); +static gboolean heap_is_empty (const heap_t * heap); +static GtsTriangle * heap_top (const heap_t * heap); +static void heap_remove (heap_t * heap, GtsTriangle * t); + +/* helper functions */ + +static gboolean vertices_are_unique (GtsVertex * v1, + GtsVertex * v2, + GtsVertex * v3) +{ + g_assert (v1 && v2 && v3); + return (v1 != v2 && v1 != v3 && v2 != v3); +} + +static gboolean vertex_is_one_of (GtsVertex * v, + GtsVertex * v1, + GtsVertex * v2, + GtsVertex * v3) +{ + g_assert (v && v1 && v2 && v3); + return v == v1 || v == v2 || v == v3; +} + +static guint num_shared_vertices (GtsVertex * u1, + GtsVertex * u2, + GtsVertex * u3, + GtsVertex * v1, + GtsVertex * v2, + GtsVertex * v3) +{ + guint n = 0; + + g_assert (u1 && u2 && u3); + g_assert (v1 && v2 && v3); + g_assert (vertices_are_unique (u1, u2, u3)); + g_assert (vertices_are_unique (v1, v2, v3)); + + if (vertex_is_one_of (v1, u1, u2, u3)) + n++; + if (vertex_is_one_of (v2, u1, u2, u3)) + n++; + if (vertex_is_one_of (v3, u1, u2, u3)) + n++; + return n; +} + +static gboolean vertices_match (GtsVertex * v1, + GtsVertex * v2, + GtsVertex * v3, + GtsVertex ** v4, + GtsVertex ** v5, + GtsVertex ** v6) +{ + guint i; + + g_assert (v4 && v5 && v6); + g_assert (*v4 && *v5 && *v6); + g_assert (vertices_are_unique (*v4, *v5, *v6)); + + for (i = 0; i < 2; i++) { + if ((!v1 || (v1 == *v4)) && + (!v2 || (v2 == *v5)) && + (!v3 || (v3 == *v6))) + return TRUE; + else { + GtsVertex * v7 = * v4; + + *v4 = *v5; + *v5 = *v6; + *v6 = v7; + } + } + return ((!v1 || (v1 == *v4)) && + (!v2 || (v2 == *v5)) && + (!v3 || (v3 == *v6))); +} + +static GtsVertex * non_shared_vertex1 (GtsVertex * u1, + GtsVertex * u2, + GtsVertex * u3, + GtsVertex * v1, + GtsVertex * v2, + GtsVertex * v3) +{ + GtsVertex * u = NULL; + + g_assert (u1 && u2 && u3); + g_assert (v1 && v2 && v3); + g_assert (vertices_are_unique (u1, u2, u3)); + g_assert (vertices_are_unique (v1, v2, v3)); + g_assert (num_shared_vertices (u1, u2, u3, v1, v2, v3) == 2); + + if (!vertex_is_one_of (u1, v1, v2, v3)) { + g_assert (vertex_is_one_of (u2, v1, v2, v3)); + g_assert (vertex_is_one_of (u3, v1, v2, v3)); + u = u1; + } else if (!vertex_is_one_of (u2, v1, v2, v3)) { + g_assert (vertex_is_one_of (u1, v1, v2, v3)); + g_assert (vertex_is_one_of (u3, v1, v2, v3)); + u = u2; + } else if (!vertex_is_one_of (u3, v1, v2, v3)) { + g_assert (vertex_is_one_of (u1, v1, v2, v3)); + g_assert (vertex_is_one_of (u2, v1, v2, v3)); + u = u3; + } else + g_assert_not_reached (); + + return u; +} + +static void match_vertex (GtsVertex * v, + GtsVertex ** v1, + GtsVertex ** v2, + GtsVertex ** v3) +{ + g_assert (v && v1 && v2 && v3); + g_assert (*v1 && *v2 && *v3); + g_assert (vertex_is_one_of (v, *v1, *v2, *v3)); + while (*v1 != v) { + GtsVertex *v0 = *v1; + + *v1 = *v2; + *v2 = *v3; + *v3 = v0; + } +} + +/* tri_data_t functions */ + +static tri_data_t * tri_data_new (GtsTriangle * t) +{ + tri_data_t * td; + + td = g_malloc (sizeof (tri_data_t)); + td->t = t; + td->used = FALSE; + td->neighbors = gts_triangle_neighbors (t); + td->pos = NULL; + + return td; +} + +static void tri_data_destroy (tri_data_t * td) +{ + if (!td) + return; + g_slist_free (td->neighbors); + g_free (td); +} + +static guint tri_data_num_unused_neighbors2 (const tri_data_t * td, + const map_t * map) +{ + GHashTable *h; + guint n; + + g_assert (td); + g_assert (map); + h = tri_data_unused_neighbors2 (td, map); + n = g_hash_table_size (h); + g_hash_table_destroy (h); + return n; +} + +static void copy_key_to_array (gpointer key, + gpointer value, + gpointer user_data) +{ + GtsTriangle * t = key; + GtsTriangle *** p = user_data; + + (void) value; + g_assert (t); + g_assert (p && *p); + **p = t; + (*p)++; +} + +static gboolean are_neighbors_unique (GHashTable *h) +{ + GtsTriangle ** a; + GtsTriangle ** p; + gint i, j, n; /* guint won't work if n == 0 */ + + g_assert (h); + n = g_hash_table_size (h); +#ifdef DEBUG + if (n > 9) + g_warning ("triangle has %d 2-level neighbors", n); +#endif /* DEBUG */ + a = g_malloc(n*sizeof (GtsTriangle *)); + p = a; + g_hash_table_foreach (h, copy_key_to_array, &p); + for (i = 0; i < n - 1; i++) { + g_assert (a[i]); + for (j = i + 1; j < n; j++) { + g_assert (a[j]); + if (a[i] == a[j]) { + g_free (a); + return FALSE; + } + } + } + g_free (a); + return TRUE; +} + +static GHashTable * tri_data_unused_neighbors2 (const tri_data_t * td, + const map_t * map) +{ + GHashTable * h = g_hash_table_new (NULL, NULL); + GSList * li; + + g_assert (td); + g_assert (map); + for (li = td->neighbors; li != NULL; li = li->next) { + GtsTriangle * t2 = li->data; + tri_data_t * td2 = map_lookup (map, t2); + GSList * lj; + + g_assert (td2); + if (!td2->used) { + g_hash_table_insert (h, t2, td2); + for (lj = td2->neighbors; lj != NULL; lj = lj->next) { + GtsTriangle * t3 = lj->data; + tri_data_t * td3 = map_lookup (map, t3); + + g_assert (td3); + if (td3 != td && !td3->used) + g_hash_table_insert (h, t3, td3); + } + } + } + g_assert (are_neighbors_unique (h)); + return h; +} + +#if PRINT_HEAP_ELEMENTS +static void tri_data_print (const tri_data_t * td, FILE * fp) +{ + g_assert (td); + g_assert (fp); + fprintf(fp, "td=%p t=%p used=%d pos=%p key=%f\n", + td, td->t, td->used, td->pos, + td->pos ? td->pos->key : -1.0); +} +#endif /* PRINT_HEAP_ELEMENTS */ + +/* heap_t functions */ + +static gdouble triangle_priority (gpointer item, gpointer data) +{ + GtsTriangle * t = item; + map_t * map = data; + tri_data_t * td; + gdouble k; + + g_assert (t); + g_assert (map); + td = map_lookup (map, t); + g_assert (td); + k = tri_data_num_unused_neighbors2 (td, map); + return k; +} + +#if PRINT_HEAP_ELEMENTS +static void print_heap_element (gpointer data, gpointer user_data) +{ + GtsTriangle * t = data; + map_t * map = user_data; + tri_data_t * td; + + g_assert (t); + g_assert (map); + td = map_lookup (map, t); + g_assert (td); + g_assert (!td->used); + g_assert (td->pos); + tri_data_print (td, stderr); +} +#endif /* PRINT_HEAP_ELEMENTS */ + +static void insert_entry_into_heap (gpointer key, + gpointer value, + gpointer user_data) +{ + GtsTriangle * t = key; + tri_data_t * td = value; + GtsEHeap * heap = user_data; + + g_assert (!td->pos); + td->pos = gts_eheap_insert (heap, t); + g_assert (td->pos); +} + +static heap_t * heap_new (GtsSurface *s) +{ + heap_t * heap; + + g_assert (s); + heap = g_malloc (sizeof (heap_t)); + heap->map = map_new (s); + heap->heap = gts_eheap_new (triangle_priority, heap->map); + g_hash_table_foreach (heap->map->ht, + insert_entry_into_heap, + heap->heap); +#if PRINT_HEAP_ELEMENTS + gts_eheap_foreach (heap->heap, print_heap_element, heap->map); +#endif /* PRINT_HEAP_ELEMENTS */ + return heap; +} + +static void heap_destroy (heap_t * heap) +{ + if (!heap) + return; + map_destroy (heap->map); + gts_eheap_destroy (heap->heap); + g_free (heap); +} + +static gboolean heap_is_empty (const heap_t * heap) +{ + g_assert (heap); + g_assert (heap->heap); + return gts_eheap_size (heap->heap) == 0; +} + +typedef struct { + const heap_t * heap; + double min_key; +} min_key_t; + +static GtsTriangle * heap_top (const heap_t * heap) +{ + GtsTriangle * t; + + g_assert (heap); + g_assert (heap->heap); + t = gts_eheap_top (heap->heap, NULL); + return t; +} + +static void decrease_key (gpointer key, gpointer value, gpointer user_data) +{ + GtsTriangle * t = key; + tri_data_t * td = value; + heap_t *heap = user_data; + gdouble k; + + (void) t; + g_assert (heap); + g_assert (heap->map); + g_assert (heap->heap); + g_assert (td); + g_assert (!td->used); + g_assert (td->pos); + + k = tri_data_num_unused_neighbors2 (td, heap->map); + g_assert (k <= td->pos->key); +#ifdef DEBUG + if (k == td->pos->key) + g_warning ("same key: %f\n", k); +#endif /* DEBUG */ + if (k != td->pos->key) { + g_assert (k < td->pos->key); + g_assert (k >= 0.0); + gts_eheap_decrease_key (heap->heap, td->pos, k); + } +} + +static void heap_remove (heap_t * heap, GtsTriangle * t) +{ + tri_data_t * td; + GHashTable * h; + + g_assert (heap); + g_assert (t); + td = map_lookup (heap->map, t); + g_assert (td); + g_assert (!td->used); + g_assert (td->pos); + td->used = TRUE; + gts_eheap_remove (heap->heap, td->pos); + td->pos = NULL; + + /* fprintf(stderr, "td: %p\n", td); */ + h = tri_data_unused_neighbors2 (td, heap->map); + g_hash_table_foreach (h, decrease_key, heap); + g_hash_table_destroy (h); +} + +/* map_t functions */ + +static gint create_map_entry (gpointer item, gpointer data) +{ + GtsTriangle * t = item; + GHashTable * ht = data; + tri_data_t * td; + + g_assert (t); + g_assert (ht); + td = tri_data_new (t); + g_hash_table_insert (ht, t, td); + return 0; +} + +static void free_map_entry (gpointer key, gpointer value, gpointer user_data) +{ + GtsTriangle * t = key; + tri_data_t * td = value; + + (void) user_data; + g_assert (t); + g_assert (td); + g_assert (td->t == t); + tri_data_destroy (td); +} + +static map_t * map_new (GtsSurface * s) +{ + map_t * map; + + map = g_malloc (sizeof (map_t)); + map->ht = g_hash_table_new (NULL, NULL); + gts_surface_foreach_face (s, create_map_entry, map->ht); + return map; +} + +static void map_destroy (map_t * map) +{ + if (!map) + return; + g_hash_table_foreach (map->ht, free_map_entry, NULL); + g_hash_table_destroy (map->ht); + g_free (map); +} + +static tri_data_t * map_lookup (const map_t * map, GtsTriangle * t) +{ + tri_data_t * td; + + g_assert (map); + g_assert (map->ht); + g_assert (t); + td = g_hash_table_lookup (map->ht, t); + g_assert (td); + g_assert (td->t == t); + return td; +} + +/* other helper functions */ + +static GtsTriangle * find_min_neighbor (heap_t * heap, GtsTriangle * t) +{ + GtsTriangle * min_neighbor = NULL; + gdouble min_key = G_MAXDOUBLE; + tri_data_t * td; + GSList * li; + + g_assert (heap); + g_assert (t); + + td = map_lookup (heap->map, t); + for (li = td->neighbors; li != NULL; li = li->next) { + GtsTriangle * t2 = li->data; + tri_data_t * td2 = map_lookup (heap->map, t2); + gdouble k; + + g_assert (td2); + if (td2->used) + continue; + g_assert (td2->pos); + k = td2->pos->key; + if (k < min_key) { + min_key = k; + min_neighbor = t2; + } + } + return min_neighbor; +} + +static GtsTriangle * find_neighbor_forward (heap_t * heap, + GtsTriangle * t, + GtsVertex ** v1, + GtsVertex ** v2, + GtsVertex ** v3, + gboolean left_turn) +{ + GtsTriangle * neighbor = NULL; + tri_data_t * td; + GSList * li; + + g_assert (heap); + g_assert (t); + g_assert (v1 && v2 && v3); + g_assert (vertices_are_unique (*v1, *v2, *v3)); + + td = map_lookup (heap->map, t); + g_assert (td); + for (li = td->neighbors; li && !neighbor; li = li->next) { + GtsTriangle * t2 = li->data; + tri_data_t * td2 = map_lookup (heap->map, t2); + GtsVertex * v4, * v5, * v6; + + g_assert (td2); + if (t2 == t || td2->used) + continue; + gts_triangle_vertices (t2, &v4, &v5, &v6); + if (left_turn) { + if (!vertices_match (*v1, *v3, NULL, &v4, &v5, &v6)) + continue; + } else { + if (!vertices_match (*v3, *v2, NULL, &v4, &v5, &v6)) + continue; + } + neighbor = t2; + *v1 = v4; + *v2 = v5; + *v3 = v6; + } + return neighbor; +} + +static GtsTriangle * find_neighbor_backward (heap_t * heap, + GtsTriangle * t, + GtsVertex ** v1, + GtsVertex ** v2, + GtsVertex ** v3, + gboolean left_turn) +{ + GtsTriangle * neighbor = NULL; + tri_data_t * td; + GSList * li; + + g_assert (heap); + g_assert (t); + g_assert (v1 && v2 && v3); + g_assert (vertices_are_unique (*v1, *v2, *v3)); + + td = map_lookup (heap->map, t); + g_assert (td); + for (li = td->neighbors; li && !neighbor; li = li->next) { + GtsTriangle * t2 = li->data; + tri_data_t * td2 = map_lookup (heap->map, t2); + GtsVertex * v4, * v5, * v6; + + g_assert (td2); + if (t2 == t || td2->used) + continue; + gts_triangle_vertices (t2, &v4, &v5, &v6); + if (left_turn) { + if (!vertices_match (NULL, *v2, *v1, &v4, &v5, &v6)) + continue; + } else if (!vertices_match(*v1, NULL, *v2, &v4, &v5, &v6)) + continue; + neighbor = t2; + *v1 = v4; + *v2 = v5; + *v3 = v6; + } + return neighbor; +} + +static GSList * grow_strip_forward (heap_t * heap, + GSList * strip, + GtsTriangle * t, + GtsVertex * v1, + GtsVertex * v2, + GtsVertex * v3) +{ + gboolean left_turn; + + g_assert (heap); + g_assert (g_slist_length(strip) == 2); + g_assert (t); + g_assert (v1 && v2 && v3); + g_assert (vertices_are_unique (v1, v2, v3)); + + left_turn = TRUE; + while ((t = find_neighbor_forward (heap, t, &v1, &v2, &v3, + left_turn)) != NULL) { + heap_remove (heap, t); + strip = g_slist_prepend (strip, t); + left_turn = !left_turn; + } + return strip; +} + +static GSList * grow_strip_backward (heap_t * heap, + GSList * strip, + GtsTriangle * t, + GtsVertex * v1, + GtsVertex * v2, + GtsVertex * v3) +{ + /* we have to make sure we add an even number of triangles */ + GtsTriangle * t2; + + g_assert (heap); + g_assert (g_slist_length(strip) >= 2); + g_assert (t); + g_assert (v1 && v2 && v3); + g_assert (vertices_are_unique (v1, v2, v3)); + + while ((t2 = find_neighbor_backward (heap, t, &v1, &v2, &v3, + FALSE)) != NULL + && (t = find_neighbor_backward (heap, t2, &v1, &v2, &v3, + TRUE)) != NULL) { + heap_remove (heap, t2); + heap_remove (heap, t); + strip = g_slist_prepend (strip, t2); + strip = g_slist_prepend (strip, t); + } + return strip; +} + +static gboolean find_right_turn (GtsVertex ** v1, + GtsVertex ** v2, + GtsVertex ** v3, + GtsVertex ** v4, + GtsVertex ** v5, + GtsVertex ** v6) +{ + GtsVertex * v; + + g_assert (v1 && v2 && v3); + g_assert (v4 && v5 && v6); + g_assert (vertices_are_unique (*v1, *v2, *v3)); + g_assert (vertices_are_unique (*v4, *v5, *v6)); + g_assert (num_shared_vertices (*v1, *v2, *v3, *v4, *v5, *v6) == 2); + + v = non_shared_vertex1 (*v1, *v2, *v3, *v4, *v5, *v6); + match_vertex (v, v1, v2, v3); + match_vertex (*v3, v4, v5, v6); + + g_assert (v1 && v2 && v3); + g_assert (v4 && v5 && v6); + g_assert (*v4 == *v3); + + if (*v5 == *v2) { + g_assert (vertices_are_unique (*v1, *v2, *v3)); + g_assert (vertices_are_unique (*v4, *v5, *v6)); + g_assert (num_shared_vertices (*v1, *v2, *v3, + *v4, *v5, *v6) == 2); + return TRUE; + } else { +#ifdef DEBUG + g_warning ("couldn't find a right turn"); +#endif /* DEBUG */ + return FALSE; + } +} + +/** + * gts_surface_strip: + * @s: a #GtsSurface. + * + * Decompose @s into triangle strips for fast-rendering. + * + * Returns: a list of triangle strips containing all the triangles of @s. + * A triangle strip is itself a list of successive triangles having one edge + * in common. + */ +GSList * gts_surface_strip (GtsSurface *s) +{ + GSList * strips = NULL; + heap_t * heap; + + g_return_val_if_fail (s != NULL, NULL); + + heap = heap_new (s); + while (!heap_is_empty (heap)) { + GtsTriangle * t1, * t2; + GtsVertex * v1, * v2, * v3, * v4, * v5, * v6; + GSList * strip = NULL; + + /* remove heap top */ + t1 = heap_top (heap); + g_assert (t1); + heap_remove (heap, t1); + + /* start a new strip */ + strip = g_slist_prepend (strip, t1); + + /* find second triangle */ + t2 = find_min_neighbor (heap, t1); + if (t2) { + g_assert (t2 != t1); + + /* find right turn */ + gts_triangle_vertices (t1, &v1, &v2, &v3); + gts_triangle_vertices (t2, &v4, &v5, &v6); + if (find_right_turn (&v1, &v2, &v3, &v4, &v5, &v6)) { + heap_remove (heap, t2); + strip = g_slist_prepend (strip, t2); + + /* grow strip forward */ + strip = grow_strip_forward (heap, strip, t2, v4, v5, v6); + + strip = g_slist_reverse (strip); + + /* grow strip backward */ + strip = grow_strip_backward (heap, strip, t1, v1, v2, v3); + } + } + strips = g_slist_prepend (strips, strip); + } + strips = g_slist_reverse (strips); + heap_destroy (heap); + + return strips; +} Index: toporouter/src_3rd/gts/surface.c =================================================================== --- toporouter/src_3rd/gts/surface.c (nonexistent) +++ toporouter/src_3rd/gts/surface.c (revision 6803) @@ -0,0 +1,2743 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include "gts.h" + +#include "gts-private.h" + +static void destroy_foreach_face (GtsFace * f, GtsSurface * s) +{ + f->surfaces = g_slist_remove (f->surfaces, s); + if (!GTS_OBJECT_DESTROYED (f) && + !gts_allow_floating_faces && f->surfaces == NULL) + gts_object_destroy (GTS_OBJECT (f)); +} + +static void surface_destroy (GtsObject * object) +{ + GtsSurface * surface = GTS_SURFACE (object); + + gts_surface_foreach_face (surface, (GtsFunc) destroy_foreach_face, surface); +#ifdef USE_SURFACE_BTREE + g_tree_destroy (surface->faces); +#else /* not USE_SURFACE_BTREE */ + g_hash_table_destroy (surface->faces); +#endif /* not USE_SURFACE_BTREE */ + + (* GTS_OBJECT_CLASS (gts_surface_class ())->parent_class->destroy) (object); +} + +static void surface_write (GtsObject * object, FILE * fptr) +{ + fprintf (fptr, " %s %s %s %s", + object->klass->info.name, + GTS_OBJECT_CLASS (GTS_SURFACE (object)->face_class)->info.name, + GTS_OBJECT_CLASS (GTS_SURFACE (object)->edge_class)->info.name, + GTS_POINT_CLASS (GTS_SURFACE (object)->vertex_class)->binary ? + "GtsVertexBinary" : + GTS_OBJECT_CLASS (GTS_SURFACE (object)->vertex_class)->info.name); +} + +static void surface_class_init (GtsSurfaceClass * klass) +{ + GTS_OBJECT_CLASS (klass)->destroy = surface_destroy; + GTS_OBJECT_CLASS (klass)->write = surface_write; + klass->add_face = NULL; + klass->remove_face = NULL; +} + +#ifdef USE_SURFACE_BTREE +static gint compare_pointers (gconstpointer a, gconstpointer b) +{ + if (GPOINTER_TO_UINT (a) < GPOINTER_TO_UINT (b)) + return -1; + if (GPOINTER_TO_UINT (a) > GPOINTER_TO_UINT (b)) + return 1; + return 0; +} +#endif /* USE_SURFACE_BTREE */ + +static void surface_init (GtsSurface * surface) +{ +#ifdef USE_SURFACE_BTREE + surface->faces = g_tree_new (compare_pointers); +#else /* not USE_SURFACE_BTREE */ + surface->faces = g_hash_table_new (NULL, NULL); +#endif /* not USE_SURFACE_BTREE */ + surface->vertex_class = gts_vertex_class (); + surface->edge_class = gts_edge_class (); + surface->face_class = gts_face_class (); + surface->keep_faces = FALSE; +} + +/** + * gts_surface_class: + * + * Returns: the #GtsSurfaceClass. + */ +GtsSurfaceClass * gts_surface_class (void) +{ + static GtsSurfaceClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo surface_info = { + "GtsSurface", + sizeof (GtsSurface), + sizeof (GtsSurfaceClass), + (GtsObjectClassInitFunc) surface_class_init, + (GtsObjectInitFunc) surface_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (gts_object_class (), &surface_info); + } + + return klass; +} + +/** + * gts_surface_new: + * @klass: a #GtsSurfaceClass. + * @face_class: a #GtsFaceClass. + * @edge_class: a #GtsEdgeClass. + * @vertex_class: a #GtsVertexClass. + * + * Returns: a new empty #GtsSurface. + */ +GtsSurface * gts_surface_new (GtsSurfaceClass * klass, + GtsFaceClass * face_class, + GtsEdgeClass * edge_class, + GtsVertexClass * vertex_class) +{ + GtsSurface * s; + + s = GTS_SURFACE (gts_object_new (GTS_OBJECT_CLASS (klass))); + s->vertex_class = vertex_class; + s->edge_class = edge_class; + s->face_class = face_class; + + return s; +} + +/** + * gts_surface_add_face: + * @s: a #GtsSurface. + * @f: a #GtsFace. + * + * Adds face @f to surface @s. + */ +void gts_surface_add_face (GtsSurface * s, GtsFace * f) +{ + g_return_if_fail (s != NULL); + g_return_if_fail (f != NULL); + + g_assert (s->keep_faces == FALSE); + +#ifdef USE_SURFACE_BTREE + if (!g_tree_lookup (s->faces, f)) { + f->surfaces = g_slist_prepend (f->surfaces, s); + g_tree_insert (s->faces, f, f); + } +#else /* not USE_SURFACE_BTREE */ + if (!g_hash_table_lookup (s->faces, f)) { + f->surfaces = g_slist_prepend (f->surfaces, s); + g_hash_table_insert (s->faces, f, f); + } +#endif /* not USE_SURFACE_BTREE */ + + if (GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass)->add_face) + (* GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass)->add_face) (s, f); +} + +/** + * gts_surface_remove_face: + * @s: a #GtsSurface. + * @f: a #GtsFace. + * + * Removes face @f from surface @s. + */ +void gts_surface_remove_face (GtsSurface * s, + GtsFace * f) +{ + g_return_if_fail (s != NULL); + g_return_if_fail (f != NULL); + + g_assert (s->keep_faces == FALSE); + +#ifdef USE_SURFACE_BTREE + g_tree_remove (s->faces, f); +#else /* not USE_SURFACE_BTREE */ + g_hash_table_remove (s->faces, f); +#endif /* not USE_SURFACE_BTREE */ + + f->surfaces = g_slist_remove (f->surfaces, s); + + if (GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass)->remove_face) + (* GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass)->remove_face) (s, f); + + if (!GTS_OBJECT_DESTROYED (f) && + !gts_allow_floating_faces && + f->surfaces == NULL) + gts_object_destroy (GTS_OBJECT (f)); +} + +/** + * gts_surface_read: + * @surface: a #GtsSurface. + * @f: a #GtsFile. + * + * Add to @surface the data read from @f. The format of the file pointed to + * by @f is as described in gts_surface_write(). + * + * Returns: 0 if successful or the line number at which the parsing + * stopped in case of error (in which case the @error field of @f is + * set to a description of the error which occured). + */ +/* Update split.c/surface_read() if modifying this function */ +guint gts_surface_read (GtsSurface * surface, GtsFile * f) +{ + GtsVertex ** vertices; + GtsEdge ** edges; + guint n, nv, ne, nf; + + g_return_val_if_fail (surface != NULL, 1); + g_return_val_if_fail (f != NULL, 1); + + if (f->type != GTS_INT) { + gts_file_error (f, "expecting an integer (number of vertices)"); + return f->line; + } + nv = atoi (f->token->str); + + gts_file_next_token (f); + if (f->type != GTS_INT) { + gts_file_error (f, "expecting an integer (number of edges)"); + return f->line; + } + ne = atoi (f->token->str); + + gts_file_next_token (f); + if (f->type != GTS_INT) { + gts_file_error (f, "expecting an integer (number of faces)"); + return f->line; + } + nf = atoi (f->token->str); + + gts_file_next_token (f); + if (f->type == GTS_STRING) { + if (f->type != GTS_STRING) { + gts_file_error (f, "expecting a string (GtsSurfaceClass)"); + return f->line; + } + gts_file_next_token (f); + if (f->type != GTS_STRING) { + gts_file_error (f, "expecting a string (GtsFaceClass)"); + return f->line; + } + gts_file_next_token (f); + if (f->type != GTS_STRING) { + gts_file_error (f, "expecting a string (GtsEdgeClass)"); + return f->line; + } + gts_file_next_token (f); + if (f->type != GTS_STRING) { + gts_file_error (f, "expecting a string (GtsVertexClass)"); + return f->line; + } + if (!strcmp (f->token->str, "GtsVertexBinary")) + GTS_POINT_CLASS (surface->vertex_class)->binary = TRUE; + else { + GTS_POINT_CLASS (surface->vertex_class)->binary = FALSE; + gts_file_first_token_after (f, '\n'); + } + } + else + gts_file_first_token_after (f, '\n'); + + if (nf <= 0) + return 0; + + /* allocate nv + 1 just in case nv == 0 */ + vertices = g_malloc ((nv + 1)*sizeof (GtsVertex *)); + edges = g_malloc ((ne + 1)*sizeof (GtsEdge *)); + + n = 0; + while (n < nv && f->type != GTS_ERROR) { + GtsObject * new_vertex = + gts_object_new (GTS_OBJECT_CLASS (surface->vertex_class)); + + (* GTS_OBJECT_CLASS (surface->vertex_class)->read) (&new_vertex, f); + if (f->type != GTS_ERROR) { + if (!GTS_POINT_CLASS (surface->vertex_class)->binary) + gts_file_first_token_after (f, '\n'); + vertices[n++] = GTS_VERTEX (new_vertex); + } + else + gts_object_destroy (new_vertex); + } + if (f->type == GTS_ERROR) + nv = n; + if (GTS_POINT_CLASS (surface->vertex_class)->binary) + gts_file_first_token_after (f, '\n'); + + n = 0; + while (n < ne && f->type != GTS_ERROR) { + guint p1, p2; + + if (f->type != GTS_INT) + gts_file_error (f, "expecting an integer (first vertex index)"); + else { + p1 = atoi (f->token->str); + if (p1 == 0 || p1 > nv) + gts_file_error (f, "vertex index `%d' is out of range `[1,%d]'", + p1, nv); + else { + gts_file_next_token (f); + if (f->type != GTS_INT) + gts_file_error (f, "expecting an integer (second vertex index)"); + else { + p2 = atoi (f->token->str); + if (p2 == 0 || p2 > nv) + gts_file_error (f, "vertex index `%d' is out of range `[1,%d]'", + p2, nv); + else { + GtsEdge * new_edge = + gts_edge_new (surface->edge_class, + vertices[p1 - 1], vertices[p2 - 1]); + + gts_file_next_token (f); + if (f->type != '\n') + if (GTS_OBJECT_CLASS (surface->edge_class)->read) + (*GTS_OBJECT_CLASS (surface->edge_class)->read) + ((GtsObject **) &new_edge, f); + gts_file_first_token_after (f, '\n'); + edges[n++] = new_edge; + } + } + } + } + } + if (f->type == GTS_ERROR) + ne = n; + + n = 0; + while (n < nf && f->type != GTS_ERROR) { + guint s1, s2, s3; + + if (f->type != GTS_INT) + gts_file_error (f, "expecting an integer (first edge index)"); + else { + s1 = atoi (f->token->str); + if (s1 == 0 || s1 > ne) + gts_file_error (f, "edge index `%d' is out of range `[1,%d]'", + s1, ne); + else { + gts_file_next_token (f); + if (f->type != GTS_INT) + gts_file_error (f, "expecting an integer (second edge index)"); + else { + s2 = atoi (f->token->str); + if (s2 == 0 || s2 > ne) + gts_file_error (f, "edge index `%d' is out of range `[1,%d]'", + s2, ne); + else { + gts_file_next_token (f); + if (f->type != GTS_INT) + gts_file_error (f, "expecting an integer (third edge index)"); + else { + s3 = atoi (f->token->str); + if (s3 == 0 || s3 > ne) + gts_file_error (f, "edge index `%d' is out of range `[1,%d]'", + s3, ne); + else { + GtsFace * new_face = gts_face_new (surface->face_class, + edges[s1 - 1], + edges[s2 - 1], + edges[s3 - 1]); + + gts_file_next_token (f); + if (f->type != '\n') + if (GTS_OBJECT_CLASS (surface->face_class)->read) + (*GTS_OBJECT_CLASS (surface->face_class)->read) + ((GtsObject **) &new_face, f); + gts_file_first_token_after (f, '\n'); + gts_surface_add_face (surface, new_face); + n++; + } + } + } + } + } + } + } + + if (f->type == GTS_ERROR) { + gts_allow_floating_vertices = TRUE; + while (nv) + gts_object_destroy (GTS_OBJECT (vertices[nv-- - 1])); + gts_allow_floating_vertices = FALSE; + } + + g_free (vertices); + g_free (edges); + + if (f->type == GTS_ERROR) + return f->line; + return 0; +} + +static void sum_area (GtsFace * f, gdouble * area) { + *area += gts_triangle_area (GTS_TRIANGLE (f)); +} + +/** + * gts_surface_area: + * @s: a #GtsSurface. + * + * Returns: the area of @s obtained as the sum of the signed areas of its + * faces. + */ +gdouble gts_surface_area (GtsSurface * s) +{ + gdouble area = 0.0; + gts_surface_foreach_face (s, (GtsFunc)sum_area, &area); + return area; +} + +/** + * gts_range_init: + * @r: a #GtsRange. + * + * Initializes a #GtsRange. + */ +void gts_range_init (GtsRange * r) +{ + g_return_if_fail (r != NULL); + + r->max = - G_MAXDOUBLE; + r->min = G_MAXDOUBLE; + r->sum = r->sum2 = 0.0; + r->n = 0; +} + +/** + * gts_range_reset: + * @r: a #GtsRange. + * + * Sets all the fields of @r to 0. + */ +void gts_range_reset (GtsRange * r) +{ + g_return_if_fail (r != NULL); + + r->max = 0.0; + r->min = 0.0; + r->sum = r->sum2 = 0.0; + r->n = 0; +} + +/** + * gts_range_add_value: + * @r: a #GtsRange. + * @val: a value to add to @r. + * + * Adds @val to @r. + */ +void gts_range_add_value (GtsRange * r, gdouble val) +{ + g_return_if_fail (r != NULL); + + if (val < r->min) r->min = val; + if (val > r->max) r->max = val; + r->sum += val; + r->sum2 += val*val; + r->n++; +} + +/** + * gts_range_update: + * @r: a #GtsRange. + * + * Updates the fields of @r. + */ +void gts_range_update (GtsRange * r) +{ + g_return_if_fail (r != NULL); + + if (r->n > 0) { + if (r->sum2 - r->sum*r->sum/(gdouble) r->n >= 0.) + r->stddev = sqrt ((r->sum2 - r->sum*r->sum/(gdouble) r->n) + /(gdouble) r->n); + else + r->stddev = 0.; + r->mean = r->sum/(gdouble) r->n; + } + else + r->min = r->max = r->mean = r->stddev = 0.; +} + +/** + * gts_range_print: + * @r: a #GtsRange. + * @fptr: a file pointer. + * + * Writes a text representation of @r in @fptr. + */ +void gts_range_print (GtsRange * r, FILE * fptr) +{ + g_return_if_fail (r != NULL); + g_return_if_fail (fptr != NULL); + fprintf (fptr, "min: %g mean: %g | %g max: %g", + r->min, r->mean, r->stddev, r->max); +} + +static void stats_foreach_vertex (GtsVertex * v, GtsSurfaceStats * stats) +{ + GSList * i = v->segments; + guint nedges = 0; + + while (i) { + if (GTS_IS_EDGE (i->data) && + gts_edge_has_parent_surface (i->data, stats->parent)) + nedges++; + i = i->next; + } + gts_range_add_value (&stats->edges_per_vertex, nedges); +} + +static void stats_foreach_edge (GtsEdge * e, GtsSurfaceStats * stats) +{ + guint nt = gts_edge_face_number (e, stats->parent); + + if (gts_segment_is_duplicate (GTS_SEGMENT (e))) + stats->n_duplicate_edges++; + if (nt == 1) + stats->n_boundary_edges++; + else if (nt > 2) + stats->n_non_manifold_edges++; + gts_range_add_value (&stats->faces_per_edge, nt); +} + +static void stats_foreach_face (GtsTriangle * t, GtsSurfaceStats * stats) +{ + if (!gts_face_is_compatible (GTS_FACE (t), stats->parent)) + stats->n_incompatible_faces++; + if (gts_triangle_is_duplicate (t)) + stats->n_duplicate_faces++; + stats->n_faces++; +} + +/** + * gts_surface_stats: + * @s: a #GtsSurface. + * @stats: a #GtsSurfaceStats. + * + * Fills @stats with the statistics relevant to surface @s. + */ +void gts_surface_stats (GtsSurface * s, GtsSurfaceStats * stats) +{ + g_return_if_fail (s != NULL); + g_return_if_fail (stats != NULL); + + stats->parent = s; + stats->n_faces = 0; + stats->n_incompatible_faces = 0; + stats->n_duplicate_faces = 0; + stats->n_duplicate_edges = 0; + stats->n_boundary_edges = 0; + stats->n_non_manifold_edges = 0; + gts_range_init (&stats->edges_per_vertex); + gts_range_init (&stats->faces_per_edge); + + gts_surface_foreach_vertex (s, (GtsFunc) stats_foreach_vertex, stats); + gts_surface_foreach_edge (s, (GtsFunc) stats_foreach_edge, stats); + gts_surface_foreach_face (s, (GtsFunc) stats_foreach_face, stats); + + gts_range_update (&stats->edges_per_vertex); + gts_range_update (&stats->faces_per_edge); +} + +static void quality_foreach_edge (GtsSegment * s, + GtsSurfaceQualityStats * stats) +{ + GSList * i = GTS_EDGE (s)->triangles; + + gts_range_add_value (&stats->edge_length, + gts_point_distance (GTS_POINT (s->v1), + GTS_POINT (s->v2))); + while (i) { + GSList * j = i->next; + while (j) { + gts_range_add_value (&stats->edge_angle, + fabs (gts_triangles_angle (i->data, j->data))); + j = j->next; + } + i = i->next; + } +} + +static void quality_foreach_face (GtsTriangle * t, + GtsSurfaceQualityStats * stats) +{ + gts_range_add_value (&stats->face_quality, gts_triangle_quality (t)); + gts_range_add_value (&stats->face_area, gts_triangle_area (t)); +} + +/** + * gts_surface_quality_stats: + * @s: a #GtsSurface. + * @stats: a #GtsSurfaceQualityStats. + * + * Fills @stats with quality statistics relevant to surface @s. + */ +void gts_surface_quality_stats (GtsSurface * s, GtsSurfaceQualityStats * stats) +{ + g_return_if_fail (s != NULL); + g_return_if_fail (stats != NULL); + + stats->parent = s; + gts_range_init (&stats->face_quality); + gts_range_init (&stats->face_area); + gts_range_init (&stats->edge_length); + gts_range_init (&stats->edge_angle); + + gts_surface_foreach_edge (s, (GtsFunc) quality_foreach_edge, stats); + gts_surface_foreach_face (s, (GtsFunc) quality_foreach_face, stats); + + gts_range_update (&stats->face_quality); + gts_range_update (&stats->face_area); + gts_range_update (&stats->edge_length); + gts_range_update (&stats->edge_angle); +} + +/** + * gts_surface_print_stats: + * @s: a #GtsSurface. + * @fptr: a file pointer. + * + * Writes in the file pointed to by @fptr the statistics for surface @s. + */ +void gts_surface_print_stats (GtsSurface * s, FILE * fptr) +{ + GtsSurfaceStats stats; + GtsSurfaceQualityStats qstats; + + g_return_if_fail (s != NULL); + g_return_if_fail (fptr != NULL); + + gts_surface_stats (s, &stats); + gts_surface_quality_stats (s, &qstats); + + fprintf (fptr, + "# vertices: %u edges: %u faces: %u\n" + "# Connectivity statistics\n" + "# incompatible faces: %u\n" + "# duplicate faces: %u\n" + "# boundary edges: %u\n" + "# duplicate edges: %u\n" + "# non-manifold edges: %u\n", + stats.edges_per_vertex.n, + stats.faces_per_edge.n, + stats.n_faces, + stats.n_incompatible_faces, + stats.n_duplicate_faces, + stats.n_boundary_edges, + stats.n_duplicate_edges, + stats.n_non_manifold_edges); + fputs ("# edges per vertex: ", fptr); + gts_range_print (&stats.edges_per_vertex, fptr); + fputs ("\n# faces per edge: ", fptr); + gts_range_print (&stats.faces_per_edge, fptr); + fputs ("\n# Geometric statistics\n# face quality: ", fptr); + gts_range_print (&qstats.face_quality, fptr); + fputs ("\n# face area : ", fptr); + gts_range_print (&qstats.face_area, fptr); + fputs ("\n# edge length : ", fptr); + gts_range_print (&qstats.edge_length, fptr); + fputc ('\n', fptr); +} + +static void write_vertex (GtsPoint * p, gpointer * data) +{ + (*GTS_OBJECT (p)->klass->write) (GTS_OBJECT (p), (FILE *) data[0]); + if (!GTS_POINT_CLASS (GTS_OBJECT (p)->klass)->binary) + fputc ('\n', (FILE *) data[0]); + g_hash_table_insert (data[2], p, + GUINT_TO_POINTER (++(*((guint *) data[1])))); +} + +static void write_edge (GtsSegment * s, gpointer * data) +{ + fprintf ((FILE *) data[0], "%u %u", + GPOINTER_TO_UINT (g_hash_table_lookup (data[2], s->v1)), + GPOINTER_TO_UINT (g_hash_table_lookup (data[2], s->v2))); + if (GTS_OBJECT (s)->klass->write) + (*GTS_OBJECT (s)->klass->write) (GTS_OBJECT (s), (FILE *) data[0]); + fputc ('\n', (FILE *) data[0]); + g_hash_table_insert (data[3], s, + GUINT_TO_POINTER (++(*((guint *) data[1])))); +} + +static void write_face (GtsTriangle * t, gpointer * data) +{ + fprintf (data[0], "%u %u %u", + GPOINTER_TO_UINT (g_hash_table_lookup (data[3], t->e1)), + GPOINTER_TO_UINT (g_hash_table_lookup (data[3], t->e2)), + GPOINTER_TO_UINT (g_hash_table_lookup (data[3], t->e3))); + if (GTS_OBJECT (t)->klass->write) + (*GTS_OBJECT (t)->klass->write) (GTS_OBJECT (t), data[0]); + fputc ('\n', data[0]); +} + +/** + * gts_surface_write: + * @s: a #GtsSurface. + * @fptr: a file pointer. + * + * Writes in the file @fptr an ASCII representation of @s. The file + * format is as follows. + * + * All the lines beginning with #GTS_COMMENTS are ignored. The first line + * contains three unsigned integers separated by spaces. The first + * integer is the number of vertices, nv, the second is the number of + * edges, ne and the third is the number of faces, nf. + * + * Follows nv lines containing the x, y and z coordinates of the + * vertices. Follows ne lines containing the two indices (starting + * from one) of the vertices of each edge. Follows nf lines containing + * the three ordered indices (also starting from one) of the edges of + * each face. + * + * The format described above is the least common denominator to all + * GTS files. Consistent with an object-oriented approach, the GTS + * file format is extensible. Each of the lines of the file can be + * extended with user-specific attributes accessible through the + * read() and write() virtual methods of each of the objects written + * (surface, vertices, edges or faces). When read with different + * object classes, these extra attributes are just ignored. + */ +void gts_surface_write (GtsSurface * s, FILE * fptr) +{ + guint n; + gpointer data[4]; + GHashTable * vindex, * eindex; + GtsSurfaceStats stats; + + g_return_if_fail (s != NULL); + g_return_if_fail (fptr != NULL); + + data[0] = fptr; + data[1] = &n; + data[2] = vindex = g_hash_table_new (NULL, NULL); + data[3] = eindex = g_hash_table_new (NULL, NULL); + + gts_surface_stats (s, &stats); + fprintf (fptr, "%u %u %u", + stats.edges_per_vertex.n, + stats.faces_per_edge.n, + stats.n_faces); + if (GTS_OBJECT (s)->klass->write) + (*GTS_OBJECT (s)->klass->write) (GTS_OBJECT (s), fptr); + fputc ('\n', fptr); + n = 0; + gts_surface_foreach_vertex (s, (GtsFunc) write_vertex, data); + n = 0; + if (GTS_POINT_CLASS (s->vertex_class)->binary) + fputc ('\n', fptr); + gts_surface_foreach_edge (s, (GtsFunc) write_edge, data); + gts_surface_foreach_face (s, (GtsFunc) write_face, data); + g_hash_table_destroy (vindex); + g_hash_table_destroy (eindex); +} + +static void write_vertex_oogl (GtsPoint * p, gpointer * data) +{ + FILE * fp = data[0]; + + fprintf (fp, "%g %g %g", p->x, p->y, p->z); + if (GTS_OBJECT (p)->klass->color) { + GtsColor c = (* GTS_OBJECT (p)->klass->color) (GTS_OBJECT (p)); + fprintf (fp, " %g %g %g 1.0\n", c.r, c.g, c.b); + } + else + fputc ('\n', fp); + GTS_OBJECT (p)->reserved = GUINT_TO_POINTER ((*((guint *) data[1]))++); +} + +static void write_face_oogl (GtsTriangle * t, FILE * fp) +{ + GtsVertex * v1, * v2, * v3; + gts_triangle_vertices (t, &v1, &v2, &v3); + fprintf (fp, "3 %u %u %u", + GPOINTER_TO_UINT (GTS_OBJECT (v1)->reserved), + GPOINTER_TO_UINT (GTS_OBJECT (v2)->reserved), + GPOINTER_TO_UINT (GTS_OBJECT (v3)->reserved)); + if (GTS_OBJECT (t)->klass->color) { + GtsColor c = (* GTS_OBJECT (t)->klass->color) (GTS_OBJECT (t)); + fprintf (fp, " %g %g %g\n", c.r, c.g, c.b); + } + else + fputc ('\n', fp); +} + +/** + * gts_surface_write_oogl: + * @s: a #GtsSurface. + * @fptr: a file pointer. + * + * Writes in the file @fptr an OOGL (Geomview) representation of @s. + */ +void gts_surface_write_oogl (GtsSurface * s, FILE * fptr) +{ + guint n = 0; + gpointer data[2]; + GtsSurfaceStats stats; + + g_return_if_fail (s != NULL); + g_return_if_fail (fptr != NULL); + + data[0] = fptr; + data[1] = &n; + + gts_surface_stats (s, &stats); + if (GTS_OBJECT_CLASS (s->vertex_class)->color) + fputs ("COFF ", fptr); + else + fputs ("OFF ", fptr); + fprintf (fptr, "%u %u %u\n", + stats.edges_per_vertex.n, + stats.n_faces, + stats.faces_per_edge.n); + gts_surface_foreach_vertex (s, (GtsFunc) write_vertex_oogl, data); + gts_surface_foreach_face (s, (GtsFunc) write_face_oogl, fptr); + gts_surface_foreach_vertex (s, (GtsFunc) gts_object_reset_reserved, NULL); +} + +static void write_vertex_vtk (GtsPoint * p, gpointer * data) +{ + FILE * fp = data[0]; + + fprintf (fp, "%g %g %g\n", p->x, p->y, p->z); + GTS_OBJECT (p)->reserved = GUINT_TO_POINTER ((*((guint *) data[1]))++); +} + +static void write_face_vtk (GtsTriangle * t, FILE * fp) +{ + GtsVertex * v1, * v2, * v3; + gts_triangle_vertices (t, &v1, &v2, &v3); + fprintf (fp, "3 %u %u %u\n", + GPOINTER_TO_UINT (GTS_OBJECT (v1)->reserved), + GPOINTER_TO_UINT (GTS_OBJECT (v2)->reserved), + GPOINTER_TO_UINT (GTS_OBJECT (v3)->reserved)); +} + +/** + * gts_surface_write_vtk: + * @s: a #GtsSurface. + * @fptr: a file pointer. + * + * Writes in the file @fptr a VTK representation of @s. + */ +void gts_surface_write_vtk (GtsSurface * s, FILE * fptr) +{ + guint n = 0; + gpointer data[2]; + GtsSurfaceStats stats; + + g_return_if_fail (s != NULL); + g_return_if_fail (fptr != NULL); + + data[0] = fptr; + data[1] = &n; + + gts_surface_stats (s, &stats); + fprintf (fptr, + "# vtk DataFile Version 2.0\n" + "Generated by GTS\n" + "ASCII\n" + "DATASET POLYDATA\n" + "POINTS %u float\n", + stats.edges_per_vertex.n); + gts_surface_foreach_vertex (s, (GtsFunc) write_vertex_vtk, data); + fprintf (fptr, + "POLYGONS %u %u\n", + stats.n_faces, stats.n_faces*4); + gts_surface_foreach_face (s, (GtsFunc) write_face_vtk, fptr); + gts_surface_foreach_vertex (s, (GtsFunc) gts_object_reset_reserved, NULL); +} + +static void write_edge_oogl_boundary (GtsSegment * s, gpointer * data) +{ + if (!gts_edge_is_boundary (GTS_EDGE (s), data[1])) + return; + + if (GTS_OBJECT (s)->klass->color) { + GtsColor c = (* GTS_OBJECT (s)->klass->color) (GTS_OBJECT (s)); + fprintf (data[0], "VECT 1 2 1 2 1 %g %g %g %g %g %g %g %g %g 1.\n", + GTS_POINT (s->v1)->x, GTS_POINT (s->v1)->y, GTS_POINT (s->v1)->z, + GTS_POINT (s->v2)->x, GTS_POINT (s->v2)->y, GTS_POINT (s->v2)->z, + c.r, c.g, c.b); + } + else + fprintf (data[0], "VECT 1 2 0 2 0 %g %g %g %g %g %g\n", + GTS_POINT (s->v1)->x, GTS_POINT (s->v1)->y, GTS_POINT (s->v1)->z, + GTS_POINT (s->v2)->x, GTS_POINT (s->v2)->y, GTS_POINT (s->v2)->z); +} + +/** + * gts_surface_write_oogl_boundary: + * @s: a #GtsSurface. + * @fptr: a file pointer. + * + * Writes in the file @fptr an OOGL (Geomview) representation of the + * boundary of @s. + */ +void gts_surface_write_oogl_boundary (GtsSurface * s, FILE * fptr) +{ + gpointer data[2]; + + g_return_if_fail (s != NULL); + g_return_if_fail (fptr != NULL); + + data[0] = fptr; + data[1] = s; + fputs ("LIST {\n", fptr); + gts_surface_foreach_edge (s, (GtsFunc) write_edge_oogl_boundary, data); + fputs ("}\n", fptr); +} + +#ifdef USE_SURFACE_BTREE +static gint vertex_foreach_face (GtsTriangle * t, + gpointer t_data, + gpointer * info) +#else /* not USE_SURFACE_BTREE */ +static void vertex_foreach_face (GtsTriangle * t, + gpointer t_data, + gpointer * info) +#endif /* not USE_SURFACE_BTREE */ +{ + GHashTable * hash = info[0]; + gpointer data = info[1]; + GtsFunc func = (GtsFunc) info[2]; + GtsSegment + * s1 = GTS_SEGMENT (t->e1); + + if (!g_hash_table_lookup (hash, s1->v1)) { + (*func) (s1->v1, data); + g_hash_table_insert (hash, s1->v1, GINT_TO_POINTER (-1)); + } + if (!g_hash_table_lookup (hash, s1->v2)) { + (*func) (s1->v2, data); + g_hash_table_insert (hash, s1->v2, GINT_TO_POINTER (-1)); + } + if (!g_hash_table_lookup (hash, gts_triangle_vertex (t))) { + (*func) (gts_triangle_vertex (t), data); + g_hash_table_insert (hash, gts_triangle_vertex (t), + GINT_TO_POINTER (-1)); + } +#ifdef USE_SURFACE_BTREE + return FALSE; +#endif /* USE_SURFACE_BTREE */ +} + +/** + * gts_surface_foreach_vertex: + * @s: a #GtsSurface. + * @func: a #GtsFunc. + * @data: user data to be passed to @func. + * + * Calls @func once for each vertex of @s. + */ +void gts_surface_foreach_vertex (GtsSurface * s, GtsFunc func, gpointer data) +{ + gpointer info[3]; + + g_return_if_fail (s != NULL); + g_return_if_fail (func != NULL); + + /* forbid removal of faces */ + s->keep_faces = TRUE; + info[0] = g_hash_table_new (NULL, NULL); + info[1] = data; + info[2] = func; +#ifdef USE_SURFACE_BTREE + g_tree_traverse (s->faces, (GTraverseFunc) vertex_foreach_face, G_IN_ORDER, + info); +#else /* not USE_SURFACE_BTREE */ + g_hash_table_foreach (s->faces, (GHFunc) vertex_foreach_face, info); +#endif /* not USE_SURFACE_BTREE */ + g_hash_table_destroy (info[0]); + /* allow removal of faces */ + s->keep_faces = FALSE; +} + +#ifdef USE_SURFACE_BTREE +static gint edge_foreach_face (GtsTriangle * t, + gpointer t_data, + gpointer * info) +#else /* not USE_SURFACE_BTREE */ +static void edge_foreach_face (GtsTriangle * t, + gpointer t_data, + gpointer * info) +#endif /* not USE_SURFACE_BTREE */ +{ + GHashTable * hash = info[0]; + gpointer data = info[1]; + GtsFunc func = (GtsFunc) info[2]; + + if (!g_hash_table_lookup (hash, t->e1)) { + (*func) (t->e1, data); + g_hash_table_insert (hash, t->e1, GINT_TO_POINTER (-1)); + } + if (!g_hash_table_lookup (hash, t->e2)) { + (*func) (t->e2, data); + g_hash_table_insert (hash, t->e2, GINT_TO_POINTER (-1)); + } + if (!g_hash_table_lookup (hash, t->e3)) { + (*func) (t->e3, data); + g_hash_table_insert (hash, t->e3, GINT_TO_POINTER (-1)); + } +#ifdef USE_SURFACE_BTREE + return FALSE; +#endif /* not USE_SURFACE_BTREE */ +} + +/** + * gts_surface_foreach_edge: + * @s: a #GtsSurface. + * @func: a #GtsFunc. + * @data: user data to be passed to @func. + * + * Calls @func once for each edge of @s. + */ +void gts_surface_foreach_edge (GtsSurface * s, GtsFunc func, gpointer data) +{ + gpointer info[3]; + + g_return_if_fail (s != NULL); + g_return_if_fail (func != NULL); + + /* forbid removal of faces */ + s->keep_faces = TRUE; + info[0] = g_hash_table_new (NULL, NULL); + info[1] = data; + info[2] = func; +#ifdef USE_SURFACE_BTREE + g_tree_traverse (s->faces, (GTraverseFunc) edge_foreach_face, G_IN_ORDER, + info); +#else /* not USE_SURFACE_BTREE */ + g_hash_table_foreach (s->faces, (GHFunc) edge_foreach_face, info); +#endif /* not USE_SURFACE_BTREE */ + g_hash_table_destroy (info[0]); + /* allow removal of faces */ + s->keep_faces = FALSE; +} + +#ifdef USE_SURFACE_BTREE +static gint foreach_face (GtsFace * f, + gpointer t_data, + gpointer * info) +#else /* not USE_SURFACE_BTREE */ +static void foreach_face (GtsFace * f, + gpointer t_data, + gpointer * info) +#endif /* not USE_SURFACE_BTREE */ +{ + (*((GtsFunc) info[0])) (f, info[1]); +#ifdef USE_SURFACE_BTREE + return FALSE; +#endif /* USE_SURFACE_BTREE */ +} + +/** + * gts_surface_foreach_face: + * @s: a #GtsSurface. + * @func: a #GtsFunc. + * @data: user data to be passed to @func. + * + * Calls @func once for each face of @s. + */ +void gts_surface_foreach_face (GtsSurface * s, + GtsFunc func, + gpointer data) +{ + gpointer info[2]; + + g_return_if_fail (s != NULL); + g_return_if_fail (func != NULL); + + /* forbid removal of faces */ + s->keep_faces = TRUE; + info[0] = func; + info[1] = data; +#ifdef USE_SURFACE_BTREE + g_tree_traverse (s->faces, (GTraverseFunc) foreach_face, G_IN_ORDER, + info); +#else /* not USE_SURFACE_BTREE */ + g_hash_table_foreach (s->faces, (GHFunc) foreach_face, info); +#endif /* not USE_SURFACE_BTREE */ + /* allow removal of faces */ + s->keep_faces = FALSE; +} + +#ifdef USE_SURFACE_BTREE +static gint foreach_face_remove (GtsFace * f, + gpointer t_data, + gpointer * info) +{ + if ((*((GtsFunc) info[0])) (f, info[1])) { + GtsSurface * s = info[2]; + guint * n = info[3]; + + f->surfaces = g_slist_remove (f->surfaces, s); + if (!GTS_OBJECT_DESTROYED (f) && + !gts_allow_floating_faces && + f->surfaces == NULL) + gts_object_destroy (GTS_OBJECT (f)); + + if (GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass)->remove_face) + (* GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass)->remove_face) (s, f); + + g_tree_remove (s->faces, f); + (*n)++; + } + return FALSE; +} +#else /* not USE_SURFACE_BTREE */ +static gboolean foreach_face_remove (GtsFace * f, + gpointer t_data, + gpointer * info) +{ + if ((*((GtsFunc) info[0])) (f, info[1])) { + GtsSurface * s = info[2]; + + f->surfaces = g_slist_remove (f->surfaces, s); + if (!GTS_OBJECT_DESTROYED (f) && + !gts_allow_floating_faces && + f->surfaces == NULL) + gts_object_destroy (GTS_OBJECT (f)); + + if (GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass)->remove_face) + (* GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass)->remove_face) (s, f); + + return TRUE; + } + return FALSE; +} +#endif /* not USE_SURFACE_BTREE */ + +/** + * gts_surface_foreach_face_remove: + * @s: a #GtsSurface. + * @func: a #GtsFunc. + * @data: user data to be passed to @func. + * + * Calls @func once for each face of @s. If @func returns %TRUE the + * corresponding face is removed from @s (and destroyed if it does not + * belong to any other surface and #gts_allow_floating_faces is set to + * %FALSE). + * + * Returns: the number of faces removed from @s. + */ +guint gts_surface_foreach_face_remove (GtsSurface * s, + GtsFunc func, + gpointer data) +{ + gpointer info[4]; + guint n = 0; + + g_return_val_if_fail (s != NULL, 0); + g_return_val_if_fail (func != NULL, 0); + + /* forbid removal of faces */ + s->keep_faces = TRUE; + info[0] = func; + info[1] = data; + info[2] = s; +#ifdef USE_SURFACE_BTREE + info[3] = &n; + g_tree_traverse (s->faces, (GTraverseFunc) foreach_face_remove, G_PRE_ORDER, + info); +#else /* not USE_SURFACE_BTREE */ + n = g_hash_table_foreach_remove (s->faces, + (GHRFunc) foreach_face_remove, + info); +#endif /* not USE_SURFACE_BTREE */ + /* allow removal of faces */ + s->keep_faces = FALSE; + + return n; +} + +static void midvertex_insertion (GtsEdge * e, + GtsSurface * surface, + GtsEHeap * heap, + GtsRefineFunc refine_func, + gpointer refine_data, + GtsVertexClass * vertex_class, + GtsEdgeClass * edge_class) +{ + GtsVertex * midvertex; + GtsEdge * e1, * e2; + GSList * i; + + midvertex = (*refine_func) (e, vertex_class, refine_data); + e1 = gts_edge_new (edge_class, GTS_SEGMENT (e)->v1, midvertex); + gts_eheap_insert (heap, e1); + e2 = gts_edge_new (edge_class, GTS_SEGMENT (e)->v2, midvertex); + gts_eheap_insert (heap, e2); + + /* creates new faces and modifies old ones */ + i = e->triangles; + while (i) { + GtsTriangle * t = i->data; + GtsVertex * v1, * v2, * v3; + GtsEdge * te2, * te3, * ne, * tmp; + + gts_triangle_vertices_edges (t, e, &v1, &v2, &v3, &e, &te2, &te3); + ne = gts_edge_new (edge_class, midvertex, v3); + gts_eheap_insert (heap, ne); + if (GTS_SEGMENT (e1)->v1 == v2) { + tmp = e1; e1 = e2; e2 = tmp; + } + e1->triangles = g_slist_prepend (e1->triangles, t); + ne->triangles = g_slist_prepend (ne->triangles, t); + te2->triangles = g_slist_remove (te2->triangles, t); + t->e1 = e1; t->e2 = ne; t->e3 = te3; + gts_surface_add_face (surface, + gts_face_new (surface->face_class, e2, te2, ne)); + i = i->next; + } + /* destroys edge */ + g_slist_free (e->triangles); + e->triangles = NULL; + gts_object_destroy (GTS_OBJECT (e)); +} + +static gdouble edge_length2_inverse (GtsSegment * s) +{ + return - gts_point_distance2 (GTS_POINT (s->v1), GTS_POINT (s->v2)); +} + +static void create_heap_refine (GtsEdge * e, GtsEHeap * heap) +{ + gts_eheap_insert (heap, e); +} + +/** + * gts_surface_refine: + * @surface: a #GtsSurface. + * @cost_func: a function returning the cost for a given edge. + * @cost_data: user data to be passed to @cost_func. + * @refine_func: a #GtsRefineFunc. + * @refine_data: user data to be passed to @refine_func. + * @stop_func: a #GtsStopFunc. + * @stop_data: user data to be passed to @stop_func. + * + * Refine @surface using a midvertex insertion technique. All the + * edges of @surface are ordered according to @cost_func. The edges + * are then processed in order until @stop_func returns %TRUE. Each + * edge is split in two and new edges and faces are created. + * + * If @cost_func is set to %NULL, the edges are sorted according + * to their length squared (the longest is on top). + * + * If @refine_func is set to %NULL gts_segment_midvertex() is used. + * + */ +void gts_surface_refine (GtsSurface * surface, + GtsKeyFunc cost_func, + gpointer cost_data, + GtsRefineFunc refine_func, + gpointer refine_data, + GtsStopFunc stop_func, + gpointer stop_data) +{ + GtsEHeap * heap; + GtsEdge * e; + gdouble top_cost; + + g_return_if_fail (surface != NULL); + g_return_if_fail (stop_func != NULL); + + if (cost_func == NULL) + cost_func = (GtsKeyFunc) edge_length2_inverse; + if (refine_func == NULL) + refine_func = (GtsRefineFunc) gts_segment_midvertex; + + heap = gts_eheap_new (cost_func, cost_data); + gts_eheap_freeze (heap); + gts_surface_foreach_edge (surface, (GtsFunc) create_heap_refine, heap); + gts_eheap_thaw (heap); + while ((e = gts_eheap_remove_top (heap, &top_cost)) && + !(*stop_func) (top_cost, + gts_eheap_size (heap) + + gts_edge_face_number (e, surface) + 2, + stop_data)) + midvertex_insertion (e, surface, heap, refine_func, refine_data, + surface->vertex_class, surface->edge_class); + gts_eheap_destroy (heap); +} + +static GSList * edge_triangles (GtsEdge * e1, GtsEdge * e) +{ + GSList * i = e1->triangles; + GSList * triangles = NULL; + + while (i) { + GtsTriangle * t = i->data; + if (t->e1 == e || t->e2 == e || t->e3 == e) { + GtsEdge * e2; + GSList * j; + if (t->e1 == e) { + if (t->e2 == e1) + e2 = t->e3; + else + e2 = t->e2; + } + else if (t->e2 == e) { + if (t->e3 == e1) + e2 = t->e1; + else + e2 = t->e3; + } + else { + if (t->e2 == e1) + e2 = t->e1; + else + e2 = t->e2; + } + j = e2->triangles; + while (j) { + GtsTriangle * t = j->data; + if (t->e1 != e && t->e2 != e && t->e3 != e) + triangles = g_slist_prepend (triangles, t); + j = j->next; + } + } + else + triangles = g_slist_prepend (triangles, t); + i = i->next; + } + return triangles; +} + +static void replace_vertex (GSList * i, GtsVertex * v1, GtsVertex * v) +{ + while (i) { + GtsSegment * s = i->data; + if (s->v1 == v1) + s->v1 = v; + else + s->v2 = v; + i = i->next; + } +} + +/** + * gts_edge_collapse_creates_fold: + * @e: a #GtsEdge. + * @v: a #GtsVertex. + * @max: the maximum value of the square of the cosine of the angle between + * two triangles. + * + * Returns: %TRUE if collapsing edge @e to vertex @v would create + * faces making an angle the cosine squared of which would be larger than max, + * %FALSE otherwise. + */ +gboolean gts_edge_collapse_creates_fold (GtsEdge * e, + GtsVertex * v, + gdouble max) +{ + GtsVertex * v1, * v2; + GtsSegment * s; + GSList * i; + gboolean folded = FALSE; + + g_return_val_if_fail (e != NULL, TRUE); + g_return_val_if_fail (v != NULL, TRUE); + + s = GTS_SEGMENT (e); + v1 = s->v1; + v2 = s->v2; + replace_vertex (v1->segments, v1, v); + replace_vertex (v2->segments, v2, v); + + i = v1->segments; + while (i && !folded) { + GtsSegment * s = i->data; + if (GTS_IS_EDGE (s)) { + GtsEdge * e1 = GTS_EDGE (s); + if (e1 != e) { + GSList * triangles = edge_triangles (e1, e); + folded = gts_triangles_are_folded (triangles, s->v1, s->v2, max); + g_slist_free (triangles); + } + } + i = i->next; + } + + i = v2->segments; + while (i && !folded) { + GtsSegment * s = i->data; + if (GTS_IS_EDGE (s)) { + GtsEdge * e1 = GTS_EDGE (s); + if (e1 != e) { + GSList * triangles = edge_triangles (e1, e); + folded = gts_triangles_are_folded (triangles, s->v1, s->v2, max); + g_slist_free (triangles); + } + } + i = i->next; + } +#if 1 + if (!folded) { + GSList * triangles = gts_vertex_triangles (v1, NULL); + i = triangles = gts_vertex_triangles (v2, triangles); + while (i && !folded) { + GtsTriangle * t = i->data; + if (t->e1 != e && t->e2 != e && t->e3 != e) { + GtsEdge * e1 = gts_triangle_edge_opposite (t, v); + g_assert (e1); + folded = gts_triangles_are_folded (e1->triangles, + GTS_SEGMENT (e1)->v1, + GTS_SEGMENT (e1)->v2, + max); + } + i = i->next; + } + g_slist_free (triangles); + } +#endif + replace_vertex (v1->segments, v, v1); + replace_vertex (v2->segments, v, v2); + return folded; +} + +/** + * gts_edge_collapse_is_valid: + * @e: a #GtsEdge. + * + * An implementation of the topological constraints described in the + * "Mesh Optimization" article of Hoppe et al (1993). + * + * Returns: %TRUE if @e can be collapsed without violation of the topological + * constraints, %FALSE otherwise. + */ +gboolean gts_edge_collapse_is_valid (GtsEdge * e) +{ + GSList * i; + + g_return_val_if_fail (e != NULL, FALSE); + + i = GTS_SEGMENT (e)->v1->segments; + while (i) { + GtsEdge * e1 = i->data; + if (e1 != e && GTS_IS_EDGE (e1)) { + GtsEdge * e2 = NULL; + GSList * j = GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e)->v1 ? + GTS_SEGMENT (e1)->v2->segments : GTS_SEGMENT (e1)->v1->segments; + while (j && !e2) { + GtsEdge * e1 = j->data; + if (GTS_IS_EDGE (e1) && + (GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e)->v2 || + GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e)->v2)) + e2 = e1; + j = j->next; + } + if (e2 && !gts_triangle_use_edges (e, e1, e2)) + return FALSE; + } + i = i->next; + } + + if (gts_edge_is_boundary (e, NULL)) { + GtsTriangle * t = e->triangles->data; + if (gts_edge_is_boundary (t->e1, NULL) && + gts_edge_is_boundary (t->e2, NULL) && + gts_edge_is_boundary (t->e3, NULL)) + return FALSE; + } + else { + if (gts_vertex_is_boundary (GTS_SEGMENT (e)->v1, NULL) && + gts_vertex_is_boundary (GTS_SEGMENT (e)->v2, NULL)) + return FALSE; + if (gts_edge_belongs_to_tetrahedron (e)) + return FALSE; + } + + return TRUE; +} + +#define HEAP_INSERT_EDGE(h, e) (GTS_OBJECT (e)->reserved = gts_eheap_insert (h, e)) +#define HEAP_REMOVE_EDGE(h, e) (gts_eheap_remove (h, GTS_OBJECT (e)->reserved),\ + GTS_OBJECT (e)->reserved = NULL) + +static GtsVertex * edge_collapse (GtsEdge * e, + GtsEHeap * heap, + GtsCoarsenFunc coarsen_func, + gpointer coarsen_data, + GtsVertexClass * klass, + gdouble maxcosine2) +{ + GSList * i; + GtsVertex * v1 = GTS_SEGMENT (e)->v1, * v2 = GTS_SEGMENT (e)->v2, * mid; + + /* if the edge is degenerate (i.e. v1 == v2), destroy and return */ + if (v1 == v2) { + gts_object_destroy (GTS_OBJECT (e)); + return NULL; + } + + if (!gts_edge_collapse_is_valid (e)) { + GTS_OBJECT (e)->reserved = + gts_eheap_insert_with_key (heap, e, G_MAXDOUBLE); + return NULL; + } + + mid = (*coarsen_func) (e, klass, coarsen_data); + + if (gts_edge_collapse_creates_fold (e, mid, maxcosine2)) { + GTS_OBJECT (e)->reserved = + gts_eheap_insert_with_key (heap, e, G_MAXDOUBLE); + gts_object_destroy (GTS_OBJECT (mid)); + return NULL; + } + + gts_object_destroy (GTS_OBJECT (e)); + + gts_vertex_replace (v1, mid); + gts_object_destroy (GTS_OBJECT (v1)); + gts_vertex_replace (v2, mid); + gts_object_destroy (GTS_OBJECT (v2)); + + /* destroy duplicate edges */ + i = mid->segments; + while (i) { + GtsEdge * e1 = i->data; + GtsEdge * duplicate; + while ((duplicate = gts_edge_is_duplicate (e1))) { + gts_edge_replace (duplicate, GTS_EDGE (e1)); + HEAP_REMOVE_EDGE (heap, duplicate); + gts_object_destroy (GTS_OBJECT (duplicate)); + } + i = i->next; + if (!e1->triangles) { + /* e1 is the result of the collapse of one edge of a pair of identical + faces (it should not happen unless duplicate triangles are present in + the initial surface) */ + g_warning ("file %s: line %d (%s): probably duplicate triangle.", + __FILE__, __LINE__, G_GNUC_PRETTY_FUNCTION); + HEAP_REMOVE_EDGE (heap, e1); + gts_object_destroy (GTS_OBJECT (e1)); + if (i == NULL) /* mid has been destroyed */ + mid = NULL; + } + } + + return mid; +} + +/* + * I don't see where this code is ever used, but keep it for a bit + * in case it is needed for debugging + */ +#ifdef GTS_NEED_UPDATE_CLOSEST_NEIGHBORS +static void update_closest_neighbors (GtsVertex * v, GtsEHeap * heap) +{ + GSList * i = v->segments; + + while (i) { + GtsSegment * s = i->data; + if (GTS_IS_EDGE (s)) { + HEAP_REMOVE_EDGE (heap, GTS_EDGE (s)); + HEAP_INSERT_EDGE (heap, GTS_EDGE (s)); + } + i = i->next; + } +} +#endif + +static void update_2nd_closest_neighbors (GtsVertex * v, GtsEHeap * heap) +{ + GSList * i = v->segments; + GSList * list = NULL; + + while (i) { + GtsSegment * s = i->data; + if (GTS_IS_EDGE (s)) { + GtsVertex * v1 = s->v1 == v ? s->v2 : s->v1; + GSList * j = v1->segments; + while (j) { + GtsSegment * s1 = j->data; + if (GTS_IS_EDGE (s1) && !g_slist_find (list, s1)) + list = g_slist_prepend (list, s1); + j = j->next; + } + } + i = i->next; + } + + i = list; + while (i) { + GtsEdge * e = i->data; + HEAP_REMOVE_EDGE (heap, e); + HEAP_INSERT_EDGE (heap, e); + i = i->next; + } + + g_slist_free (list); +} + +static gdouble edge_length2 (GtsEdge * e) +{ + return gts_point_distance2 (GTS_POINT (GTS_SEGMENT (e)->v1), + GTS_POINT (GTS_SEGMENT (e)->v2)); +} + +static void create_heap_coarsen (GtsEdge * e, GtsEHeap * heap) +{ + HEAP_INSERT_EDGE (heap, e); +} + +/** + * gts_surface_coarsen: + * @surface: a #GtsSurface. + * @cost_func: a function returning the cost for a given edge. + * @cost_data: user data to be passed to @cost_func. + * @coarsen_func: a #GtsCoarsenVertexFunc. + * @coarsen_data: user data to be passed to @coarsen_func. + * @stop_func: a #GtsStopFunc. + * @stop_data: user data to be passed to @stop_func. + * @minangle: minimum angle between two neighboring triangles. + * + * The edges of @surface are sorted according to @cost_func to + * create a priority heap (a #GtsEHeap). The edges are extracted in + * turn from the top of the heap and collapsed (i.e. the vertices are + * replaced by the vertex returned by the @coarsen_func function) + * until the @stop_func functions returns %TRUE. + * + * If @cost_func is set to %NULL, the edges are sorted according + * to their length squared (the shortest is on top). + * + * If @coarsen_func is set to %NULL gts_segment_midvertex() is used. + * + * The minimum angle is used to avoid introducing faces which would be folded. + */ +void gts_surface_coarsen (GtsSurface * surface, + GtsKeyFunc cost_func, + gpointer cost_data, + GtsCoarsenFunc coarsen_func, + gpointer coarsen_data, + GtsStopFunc stop_func, + gpointer stop_data, + gdouble minangle) +{ + GtsEHeap * heap; + GtsEdge * e; + gdouble top_cost; + gdouble maxcosine2; + + g_return_if_fail (surface != NULL); + g_return_if_fail (stop_func != NULL); + + if (cost_func == NULL) + cost_func = (GtsKeyFunc) edge_length2; + if (coarsen_func == NULL) + coarsen_func = (GtsCoarsenFunc) gts_segment_midvertex; + + heap = gts_eheap_new (cost_func, cost_data); + maxcosine2 = cos (minangle); maxcosine2 *= maxcosine2; + + gts_eheap_freeze (heap); + gts_surface_foreach_edge (surface, (GtsFunc) create_heap_coarsen, heap); + gts_eheap_thaw (heap); + /* we want to control edge destruction manually */ + gts_allow_floating_edges = TRUE; + while ((e = gts_eheap_remove_top (heap, &top_cost)) && + (top_cost < G_MAXDOUBLE) && + !(*stop_func) (top_cost, gts_eheap_size (heap) - + gts_edge_face_number (e, surface), stop_data)) + { + GtsVertex * v = edge_collapse (e, heap, coarsen_func, coarsen_data, + surface->vertex_class, maxcosine2); + if (v != NULL) + update_2nd_closest_neighbors (v, heap); + } + gts_allow_floating_edges = FALSE; + + /* set reserved field of remaining edges back to NULL */ + if (e) GTS_OBJECT (e)->reserved = NULL; + gts_eheap_foreach (heap, (GFunc) gts_object_reset_reserved, NULL); + + gts_eheap_destroy (heap); +} + +/** + * gts_coarsen_stop_number: + * @cost: the cost of the edge collapse considered. + * @nedge: the current number of edges of the surface being simplified. + * @min_number: a pointer to the minimum number of edges desired for the + * surface being simplified. + * + * This function is to be used as the @stop_func argument of + * gts_surface_coarsen() or gts_psurface_new(). + * + * Returns: %TRUE if the edge collapse would create a surface with a smaller + * number of edges than given by @min_number, %FALSE otherwise. + */ +gboolean gts_coarsen_stop_number (gdouble cost, + guint nedge, + guint * min_number) +{ + g_return_val_if_fail (min_number != NULL, TRUE); + + if (nedge < *min_number) + return TRUE; + return FALSE; +} + +/** + * gts_coarsen_stop_cost: + * @cost: the cost of the edge collapse considered. + * @nedge: the current number of edges of the surface being simplified. + * @max_cost: a pointer to the maximum cost allowed for an edge collapse. + * + * This function is to be used as the @stop_func argument of + * gts_surface_coarsen() or gts_psurface_new(). + * + * Returns: %TRUE if the cost of the edge collapse considered is larger than + * given by @max_cost, %FALSE otherwise. + */ +gboolean gts_coarsen_stop_cost (gdouble cost, + guint nedge, + gdouble * max_cost) +{ + g_return_val_if_fail (max_cost != NULL, TRUE); + + if (cost > *max_cost) + return TRUE; + return FALSE; +} + +#define GTS_M_ICOSAHEDRON_X /* sqrt(sqrt(5)+1)/sqrt(2*sqrt(5)) */ \ + 0.850650808352039932181540497063011072240401406 +#define GTS_M_ICOSAHEDRON_Y /* sqrt(2)/sqrt(5+sqrt(5)) */ \ + 0.525731112119133606025669084847876607285497935 +#define GTS_M_ICOSAHEDRON_Z 0.0 + +static guint generate_icosahedron (GtsSurface * s) +{ + GtsVertex * v01 = gts_vertex_new (s->vertex_class, + +GTS_M_ICOSAHEDRON_Z, +GTS_M_ICOSAHEDRON_X, -GTS_M_ICOSAHEDRON_Y); + GtsVertex * v02 = gts_vertex_new (s->vertex_class, + +GTS_M_ICOSAHEDRON_X, +GTS_M_ICOSAHEDRON_Y, +GTS_M_ICOSAHEDRON_Z); + GtsVertex * v03 = gts_vertex_new (s->vertex_class, + +GTS_M_ICOSAHEDRON_Y, +GTS_M_ICOSAHEDRON_Z, -GTS_M_ICOSAHEDRON_X); + GtsVertex * v04 = gts_vertex_new (s->vertex_class, + +GTS_M_ICOSAHEDRON_Y, +GTS_M_ICOSAHEDRON_Z, +GTS_M_ICOSAHEDRON_X); + GtsVertex * v05 = gts_vertex_new (s->vertex_class, + +GTS_M_ICOSAHEDRON_X, -GTS_M_ICOSAHEDRON_Y, +GTS_M_ICOSAHEDRON_Z); + GtsVertex * v06 = gts_vertex_new (s->vertex_class, + +GTS_M_ICOSAHEDRON_Z, +GTS_M_ICOSAHEDRON_X, +GTS_M_ICOSAHEDRON_Y); + GtsVertex * v07 = gts_vertex_new (s->vertex_class, + -GTS_M_ICOSAHEDRON_Y, +GTS_M_ICOSAHEDRON_Z, +GTS_M_ICOSAHEDRON_X); + GtsVertex * v08 = gts_vertex_new (s->vertex_class, + +GTS_M_ICOSAHEDRON_Z, -GTS_M_ICOSAHEDRON_X, -GTS_M_ICOSAHEDRON_Y); + GtsVertex * v09 = gts_vertex_new (s->vertex_class, + -GTS_M_ICOSAHEDRON_X, +GTS_M_ICOSAHEDRON_Y, +GTS_M_ICOSAHEDRON_Z); + GtsVertex * v10 = gts_vertex_new (s->vertex_class, + -GTS_M_ICOSAHEDRON_Y, +GTS_M_ICOSAHEDRON_Z, -GTS_M_ICOSAHEDRON_X); + GtsVertex * v11 = gts_vertex_new (s->vertex_class, + -GTS_M_ICOSAHEDRON_X, -GTS_M_ICOSAHEDRON_Y, +GTS_M_ICOSAHEDRON_Z); + GtsVertex * v12 = gts_vertex_new (s->vertex_class, + +GTS_M_ICOSAHEDRON_Z, -GTS_M_ICOSAHEDRON_X, +GTS_M_ICOSAHEDRON_Y); + + GtsEdge * e01 = gts_edge_new (s->edge_class, v01, v02); + GtsEdge * e02 = gts_edge_new (s->edge_class, v03, v02); + GtsEdge * e03 = gts_edge_new (s->edge_class, v01, v03); + GtsEdge * e04 = gts_edge_new (s->edge_class, v04, v05); + GtsEdge * e05 = gts_edge_new (s->edge_class, v02, v05); + GtsEdge * e06 = gts_edge_new (s->edge_class, v04, v02); + GtsEdge * e07 = gts_edge_new (s->edge_class, v06, v07); + GtsEdge * e08 = gts_edge_new (s->edge_class, v04, v07); + GtsEdge * e09 = gts_edge_new (s->edge_class, v06, v04); + GtsEdge * e10 = gts_edge_new (s->edge_class, v08, v03); + GtsEdge * e11 = gts_edge_new (s->edge_class, v03, v05); + GtsEdge * e12 = gts_edge_new (s->edge_class, v08, v05); + GtsEdge * e13 = gts_edge_new (s->edge_class, v06, v09); + GtsEdge * e14 = gts_edge_new (s->edge_class, v07, v09); + GtsEdge * e15 = gts_edge_new (s->edge_class, v08, v10); + GtsEdge * e16 = gts_edge_new (s->edge_class, v03, v10); + GtsEdge * e17 = gts_edge_new (s->edge_class, v06, v01); + GtsEdge * e18 = gts_edge_new (s->edge_class, v01, v09); + GtsEdge * e19 = gts_edge_new (s->edge_class, v08, v11); + GtsEdge * e20 = gts_edge_new (s->edge_class, v10, v11); + GtsEdge * e21 = gts_edge_new (s->edge_class, v06, v02); + GtsEdge * e22 = gts_edge_new (s->edge_class, v12, v11); + GtsEdge * e23 = gts_edge_new (s->edge_class, v12, v08); + GtsEdge * e24 = gts_edge_new (s->edge_class, v12, v07); + GtsEdge * e25 = gts_edge_new (s->edge_class, v07, v11); + GtsEdge * e26 = gts_edge_new (s->edge_class, v12, v04); + GtsEdge * e27 = gts_edge_new (s->edge_class, v09, v11); + GtsEdge * e28 = gts_edge_new (s->edge_class, v10, v09); + GtsEdge * e29 = gts_edge_new (s->edge_class, v12, v05); + GtsEdge * e30 = gts_edge_new (s->edge_class, v01, v10); + + gts_surface_add_face (s, gts_face_new (s->face_class, e01, e02, e03)); + gts_surface_add_face (s, gts_face_new (s->face_class, e04, e05, e06)); + gts_surface_add_face (s, gts_face_new (s->face_class, e07, e08, e09)); + gts_surface_add_face (s, gts_face_new (s->face_class, e10, e11, e12)); + gts_surface_add_face (s, gts_face_new (s->face_class, e13, e14, e07)); + gts_surface_add_face (s, gts_face_new (s->face_class, e15, e16, e10)); + gts_surface_add_face (s, gts_face_new (s->face_class, e17, e18, e13)); + gts_surface_add_face (s, gts_face_new (s->face_class, e19, e20, e15)); + gts_surface_add_face (s, gts_face_new (s->face_class, e21, e01, e17)); + gts_surface_add_face (s, gts_face_new (s->face_class, e22, e19, e23)); + gts_surface_add_face (s, gts_face_new (s->face_class, e09, e06, e21)); + gts_surface_add_face (s, gts_face_new (s->face_class, e24, e25, e22)); + gts_surface_add_face (s, gts_face_new (s->face_class, e26, e08, e24)); + gts_surface_add_face (s, gts_face_new (s->face_class, e20, e27, e28)); + gts_surface_add_face (s, gts_face_new (s->face_class, e29, e04, e26)); + gts_surface_add_face (s, gts_face_new (s->face_class, e14, e27, e25)); + gts_surface_add_face (s, gts_face_new (s->face_class, e23, e12, e29)); + gts_surface_add_face (s, gts_face_new (s->face_class, e02, e05, e11)); + gts_surface_add_face (s, gts_face_new (s->face_class, e30, e28, e18)); + gts_surface_add_face (s, gts_face_new (s->face_class, e03, e16, e30)); + + return 0; +} + +static GtsVertex * unit_sphere_arc_midvertex (GtsSegment * s, + GtsVertexClass * vertex_class) +{ + GtsPoint * p1, * p2; + gdouble x, y, z, norm; + + p1 = GTS_POINT (s->v1); p2 = GTS_POINT (s->v2); + + x = 0.5*(p1->x + p2->x); + y = 0.5*(p1->y + p2->y); + z = 0.5*(p1->z + p2->z); + + norm = x*x + y*y + z*z; + norm = sqrt (norm); + + x /= norm; y /= norm; z /= norm; + + return gts_vertex_new (vertex_class, x, y, z); +} + +static void tessellate_face (GtsFace * f, + GtsSurface * s, + GtsRefineFunc refine_func, + gpointer refine_data, + GtsVertexClass * vertex_class, + GtsEdgeClass * edge_class) +{ + GtsTriangle * t; + GtsEdge * e1, * e2, * e3; /* former edges */ + GtsVertex * v1, * v2, * v3; /* initial vertices */ + GtsVertex * v4, * v5, * v6; /* new vertices */ + GtsEdge * e56, * e64, * e45; /* new inside edges */ + GtsEdge * e24, * e34, * e35, * e15, * e16, * e26; /* new border edges */ + GSList * dum; + GtsEdge * edum; + + t = GTS_TRIANGLE (f); + e1 = t->e1; e2 = t->e2; e3 = t->e3; + + if (GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e2)->v1) { + v1 = GTS_SEGMENT (e2)->v2; + v2 = GTS_SEGMENT (e1)->v1; + v3 = GTS_SEGMENT (e1)->v2; + } + else if (GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e2)->v2) { + v1 = GTS_SEGMENT (e2)->v1; + v2 = GTS_SEGMENT (e1)->v1; + v3 = GTS_SEGMENT (e1)->v2; + } + else if (GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e2)->v1) { + v1 = GTS_SEGMENT (e2)->v2; + v2 = GTS_SEGMENT (e1)->v2; + v3 = GTS_SEGMENT (e1)->v1; + } + else if (GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e2)->v2) { + v1 = GTS_SEGMENT (e2)->v1; + v2 = GTS_SEGMENT (e1)->v2; + v3 = GTS_SEGMENT (e1)->v1; + } + else { + v1 = v2 = v3 = NULL; + g_assert_not_reached (); + } + + e1->triangles = g_slist_remove (e1->triangles, t); + e2->triangles = g_slist_remove (e2->triangles, t); + e3->triangles = g_slist_remove (e3->triangles, t); + + if (GTS_OBJECT (e1)->reserved) { + dum = (GTS_OBJECT (e1)->reserved); + e24 = dum->data; + e34 = dum->next->data; + v4 = GTS_SEGMENT (e24)->v2; + if (GTS_SEGMENT (e24)->v1 == v3) { + edum = e34; e34 = e24; e24 = edum; + } + } + else { + v4 = (*refine_func) (e1, vertex_class, refine_data); + e24 = gts_edge_new (edge_class, v2, v4); + e34 = gts_edge_new (edge_class, v3, v4); + dum = g_slist_append (NULL, e24); + dum = g_slist_append (dum, e34); + GTS_OBJECT (e1)->reserved = dum; + } + if (GTS_OBJECT (e2)->reserved) { + dum = (GTS_OBJECT (e2)->reserved); + e35 = dum->data; + e15 = dum->next->data; + v5 = GTS_SEGMENT (e35)->v2; + if (GTS_SEGMENT (e35)->v1 == v1) { + edum = e15; e15 = e35; e35 = edum; + } + } + else { + v5 = (*refine_func) (e2, vertex_class, refine_data); + e35 = gts_edge_new (edge_class, v3, v5); + e15 = gts_edge_new (edge_class, v1, v5); + dum = g_slist_append (NULL, e35); + dum = g_slist_append (dum, e15); + GTS_OBJECT (e2)->reserved = dum; + } + if (GTS_OBJECT (e3)->reserved) { + dum = (GTS_OBJECT (e3)->reserved); + e16 = dum->data; + e26 = dum->next->data; + v6 = GTS_SEGMENT (e16)->v2; + if (GTS_SEGMENT (e16)->v1 == v2) { + edum = e16; e16 = e26; e26 = edum; + } + } + else { + v6 = (*refine_func) (e3, vertex_class, refine_data); + e16 = gts_edge_new (edge_class, v1, v6); + e26 = gts_edge_new (edge_class, v2, v6); + dum = g_slist_append (NULL, e16); + dum = g_slist_append (dum, e26); + GTS_OBJECT (e3)->reserved = dum; + } + + if (e1->triangles == NULL) { + g_slist_free (GTS_OBJECT (e1)->reserved); + GTS_OBJECT (e1)->reserved = NULL; + gts_object_destroy (GTS_OBJECT (e1)); + e1 = NULL; + } + if (e2->triangles == NULL) { + g_slist_free (GTS_OBJECT (e2)->reserved); + GTS_OBJECT (e2)->reserved = NULL; + gts_object_destroy (GTS_OBJECT (e2)); + e2 = NULL; + } + if (e3->triangles == NULL) { + g_slist_free (GTS_OBJECT (e3)->reserved); + GTS_OBJECT (e3)->reserved = NULL; + gts_object_destroy (GTS_OBJECT (e3)); + e3 = NULL; + } + + e56 = gts_edge_new (edge_class, v5, v6); + e64 = gts_edge_new (edge_class, v6, v4); + e45 = gts_edge_new (edge_class, v4, v5); + t->e1 = e56; e56->triangles = g_slist_prepend (e56->triangles, t); + t->e2 = e64; e64->triangles = g_slist_prepend (e64->triangles, t); + t->e3 = e45; e45->triangles = g_slist_prepend (e45->triangles, t); + + gts_surface_add_face (s, gts_face_new (s->face_class, e16, e56, e15)); + gts_surface_add_face (s, gts_face_new (s->face_class, e26, e24, e64)); + gts_surface_add_face (s, gts_face_new (s->face_class, e45, e34, e35)); +} + +static void create_array_tessellate (GtsFace * f, GPtrArray * array) +{ + g_ptr_array_add (array, f); +} + +/** + * gts_surface_tessellate: + * @s: a #GtsSurface. + * @refine_func: a #GtsRefineFunc. + * @refine_data: user data to be passed to @refine_func. + * + * Tessellate each triangle of @s with 4 triangles: + * the number of triangles is increased by a factor of 4. + * http://mathworld.wolfram.com/GeodesicDome.html + * + * If @refine_func is set to %NULL a mid arc function is used: if + * the surface is a polyhedron with the unit sphere as circum sphere, + * then gts_surface_tessellate() corresponds to a geodesation step + * (see gts_surface_generate_sphere()). + * + */ +void gts_surface_tessellate (GtsSurface * s, + GtsRefineFunc refine_func, + gpointer refine_data) +{ + GPtrArray * array; + guint i; + + g_return_if_fail (s != NULL); + + if (refine_func == NULL) /* tessellate_surface == geodesate_surface */ + refine_func = (GtsRefineFunc) unit_sphere_arc_midvertex; + + array = g_ptr_array_new (); + gts_surface_foreach_face (s, (GtsFunc) create_array_tessellate, array); + for(i = 0; i < array->len; i++) + tessellate_face (g_ptr_array_index (array, i), + s, refine_func, refine_data, + s->vertex_class, s->edge_class); + g_ptr_array_free (array, TRUE); +} + +/** + * gts_surface_generate_sphere: + * @s: a #GtsSurface. + * @geodesation_order: a #guint. + * + * Add a triangulated unit sphere generated by recursive subdivision to @s. + * First approximation is an isocahedron; each level of refinement + * (@geodesation_order) increases the number of triangles by a factor of 4. + * http://mathworld.wolfram.com/GeodesicDome.html + * + * Returns: @s. + */ +GtsSurface * gts_surface_generate_sphere (GtsSurface * s, + guint geodesation_order) +{ + guint cgo; + + g_return_val_if_fail (s != NULL, NULL); + g_return_val_if_fail (geodesation_order != 0, NULL); + + generate_icosahedron (s); + + for (cgo = 1; cgo < geodesation_order; cgo++) + gts_surface_tessellate (s, NULL, NULL); + + return s; +} + +static void foreach_vertex_copy (GtsPoint * p, GtsVertexClass * klass) +{ + GTS_OBJECT (p)->reserved = gts_vertex_new (klass, p->x, p->y, p->z); +} + +static void foreach_edge_copy (GtsSegment * s, GtsEdgeClass * klass) +{ + GTS_OBJECT (s)->reserved = gts_edge_new (klass, + GTS_OBJECT (s->v1)->reserved, + GTS_OBJECT (s->v2)->reserved); +} + +static void foreach_face_copy (GtsTriangle * t, + GtsSurface * s) +{ + gts_surface_add_face (s, gts_face_new (s->face_class, + GTS_OBJECT (t->e1)->reserved, + GTS_OBJECT (t->e2)->reserved, + GTS_OBJECT (t->e3)->reserved)); +} + +/** + * gts_surface_copy: + * @s1: a #GtsSurface. + * @s2: a #GtsSurface. + * + * Add a copy of all the faces, edges and vertices of @s2 to @s1. + * + * Returns: @s1. + */ +GtsSurface * gts_surface_copy (GtsSurface * s1, GtsSurface * s2) +{ + g_return_val_if_fail (s1 != NULL, NULL); + g_return_val_if_fail (s2 != NULL, NULL); + + gts_surface_foreach_vertex (s2, (GtsFunc) foreach_vertex_copy, + s1->vertex_class); + gts_surface_foreach_edge (s2, (GtsFunc) foreach_edge_copy, s1->edge_class); + gts_surface_foreach_face (s2, (GtsFunc) foreach_face_copy, s1); + + gts_surface_foreach_vertex (s2, (GtsFunc) gts_object_reset_reserved, NULL); + gts_surface_foreach_edge (s2, (GtsFunc) gts_object_reset_reserved, NULL); + + return s1; +} + +static void merge_foreach_face (GtsFace * f, + GtsSurface * s) +{ + gts_surface_add_face (s, f); +} + +/** + * gts_surface_merge: + * @s: a #GtsSurface. + * @with: another #GtsSurface. + * + * Adds all the faces of @with which do not already belong to @s + * to @s. + */ +void gts_surface_merge (GtsSurface * s, GtsSurface * with) +{ + g_return_if_fail (s != NULL); + g_return_if_fail (with != NULL); + + gts_surface_foreach_face (with, (GtsFunc) merge_foreach_face, s); +} + +static void manifold_foreach_edge (GtsEdge * e, gpointer * data) +{ + gboolean * is_manifold = data[0]; + + if (*is_manifold) { + if (gts_edge_face_number (e, data[1]) > 2) + *is_manifold = FALSE; + } +} + +/** + * gts_surface_is_manifold: + * @s: a #GtsSurface. + * + * Returns: %TRUE if the surface is a manifold, %FALSE otherwise. + */ +gboolean gts_surface_is_manifold (GtsSurface * s) +{ + gboolean is_manifold = TRUE; + gpointer data[2]; + + g_return_val_if_fail (s != NULL, FALSE); + + data[0] = &is_manifold; + data[1] = s; + gts_surface_foreach_edge (s, (GtsFunc) manifold_foreach_edge, data); + return is_manifold; +} + +static void closed_foreach_edge (GtsEdge * e, gpointer * data) +{ + gboolean * is_closed = data[0]; + + if (*is_closed) { + if (gts_edge_face_number (e, data[1]) != 2) + *is_closed = FALSE; + } +} + +/** + * gts_surface_is_closed: + * @s: a #GtsSurface. + * + * Returns: %TRUE if @s is a closed surface, %FALSE otherwise. Note that a + * closed surface is also a manifold. + */ +gboolean gts_surface_is_closed (GtsSurface * s) +{ + gboolean is_closed = TRUE; + gpointer data[2]; + + g_return_val_if_fail (s != NULL, FALSE); + + data[0] = &is_closed; + data[1] = s; + gts_surface_foreach_edge (s, (GtsFunc) closed_foreach_edge, data); + return is_closed; +} + +static void orientable_foreach_edge (GtsEdge * e, gpointer * data) +{ + gboolean * is_orientable = data[0]; + + if (*is_orientable) { + GtsSurface * surface = data[1]; + GtsFace * f1 = NULL, * f2 = NULL; + GSList * i = e->triangles; + while (i && *is_orientable) { + GtsFace * f = i->data; + if (GTS_IS_FACE (f) && gts_face_has_parent_surface (f, surface)) { + if (!f1) f1 = f; + else if (!f2) f2 = f; + else *is_orientable = FALSE; + } + i = i->next; + } + if (f1 && f2 && !gts_triangles_are_compatible (GTS_TRIANGLE (f1), + GTS_TRIANGLE (f2), e)) + *is_orientable = FALSE; + } +} + +/** + * gts_surface_is_orientable: + * @s: a #GtsSurface. + * + * Returns: %TRUE if all the faces of @s have compatible orientation + * as checked by gts_faces_are_compatible(), %FALSE otherwise. Note that + * an orientable surface is also a manifold. + */ +gboolean gts_surface_is_orientable (GtsSurface * s) +{ + gboolean is_orientable = TRUE; + gpointer data[2]; + + g_return_val_if_fail (s != NULL, FALSE); + + data[0] = &is_orientable; + data[1] = s; + gts_surface_foreach_edge (s, (GtsFunc) orientable_foreach_edge, data); + return is_orientable; +} + +static void volume_foreach_face (GtsTriangle * t, + gdouble * volume) +{ + GtsVertex * va, * vb, * vc; + GtsPoint * pa, * pb, * pc; + + gts_triangle_vertices (t, &va, &vb, &vc); + pa = GTS_POINT (va); + pb = GTS_POINT (vb); + pc = GTS_POINT (vc); + + *volume += (pa->x * (pb->y * pc->z - pb->z * pc->y) + + pb->x * (pc->y * pa->z - pc->z * pa->y) + + pc->x * (pa->y * pb->z - pa->z * pb->y)); +} + +/** + * gts_surface_volume: + * @s: a #GtsSurface. + * + * Returns: the signed volume of the domain bounded by the surface @s. It + * makes sense only if @s is a closed and orientable manifold. + */ +gdouble gts_surface_volume (GtsSurface * s) +{ + gdouble volume = 0.0; + + g_return_val_if_fail (s != NULL, 0.0); + + gts_surface_foreach_face (s, (GtsFunc) volume_foreach_face, &volume); + + return volume/6.; +} + +static void center_of_mass_foreach_face (GtsTriangle * t, + gpointer * data) +{ + GtsVertex * v1, * v2, * v3; + GtsPoint * p1, * p2, * p3; + gdouble x1, y1, z1, x2, y2, z2, nx, ny, nz; + gdouble * volume = data[0]; + gdouble * cm = data[1]; + + gts_triangle_vertices (t, &v1, &v2, &v3); + p1 = GTS_POINT (v1); + p2 = GTS_POINT (v2); + p3 = GTS_POINT (v3); + + x1 = p2->x - p1->x; + y1 = p2->y - p1->y; + z1 = p2->z - p1->z; + + x2 = p3->x - p1->x; + y2 = p3->y - p1->y; + z2 = p3->z - p1->z; + + nx = y1*z2 - z1*y2; + ny = z1*x2 - x1*z2; + nz = x1*y2 - y1*x2; + + cm[0] += nx*(p1->x*p1->x + p2->x*p2->x + p3->x*p3->x + + p1->x*p2->x + p1->x*p3->x + p2->x*p3->x); + cm[1] += ny*(p1->y*p1->y + p2->y*p2->y + p3->y*p3->y + + p1->y*p2->y + p1->y*p3->y + p2->y*p3->y); + cm[2] += nz*(p1->z*p1->z + p2->z*p2->z + p3->z*p3->z + + p1->z*p2->z + p1->z*p3->z + p2->z*p3->z); + + *volume += nx*(p1->x + p2->x + p3->x); +} + + +/** + * gts_surface_center_of_mass: + * @s: a #GtsSurface. + * @cm: a #GtsVector. + * + * Fills @cm with the coordinates of the center of mass of @s. + * + * Returns: the signed volume of the domain bounded by the surface @s. + */ +gdouble gts_surface_center_of_mass (GtsSurface * s, + GtsVector cm) +{ + gdouble volume = 0.; + gpointer data[2]; + + g_return_val_if_fail (s != NULL, 0.0); + + data[0] = &volume; + data[1] = &(cm[0]); + cm[0] = cm[1] = cm[2] = 0.; + gts_surface_foreach_face (s, (GtsFunc) center_of_mass_foreach_face, data); + + if (volume != 0.) { + cm[0] /= 4.*volume; + cm[1] /= 4.*volume; + cm[2] /= 4.*volume; + } + + return volume/6.; +} + +static void center_of_area_foreach_face (GtsTriangle * t, + gpointer * data) +{ + GtsVertex * v1, * v2, * v3; + GtsPoint * p1, * p2, * p3; + gdouble a; + gdouble * area = data[0]; + gdouble * cm = data[1]; + + gts_triangle_vertices (t, &v1, &v2, &v3); + p1 = GTS_POINT (v1); + p2 = GTS_POINT (v2); + p3 = GTS_POINT (v3); + + a = gts_triangle_area (t); + cm[0] += a*(p1->x + p2->x + p3->x); + cm[1] += a*(p1->y + p2->y + p3->y); + cm[2] += a*(p1->z + p2->z + p3->z); + *area += a; +} + + +/** + * gts_surface_center_of_area: + * @s: a #GtsSurface. + * @cm: a #GtsVector. + * + * Fills @cm with the coordinates of the center of area of @s. + * + * Returns: the area of surface @s. + */ +gdouble gts_surface_center_of_area (GtsSurface * s, + GtsVector cm) +{ + gdouble area = 0.; + gpointer data[2]; + + g_return_val_if_fail (s != NULL, 0.0); + + data[0] = &area; + data[1] = &(cm[0]); + cm[0] = cm[1] = cm[2] = 0.; + gts_surface_foreach_face (s, (GtsFunc) center_of_area_foreach_face, data); + + if (area != 0.) { + cm[0] /= 3.*area; + cm[1] /= 3.*area; + cm[2] /= 3.*area; + } + + return area; +} + +static void number_foreach (gpointer data, guint * n) +{ + (*n)++; +} + +/** + * gts_surface_vertex_number: + * @s: a #GtsSurface. + * + * Returns: the number of vertices of @s. + */ +guint gts_surface_vertex_number (GtsSurface * s) +{ + guint n = 0; + + g_return_val_if_fail (s != NULL, 0); + + gts_surface_foreach_vertex (s, (GtsFunc) number_foreach, &n); + + return n; +} + +/** + * gts_surface_edge_number: + * @s: a #GtsSurface. + * + * Returns: the number of edges of @s. + */ +guint gts_surface_edge_number (GtsSurface * s) +{ + guint n = 0; + + g_return_val_if_fail (s != NULL, 0); + + gts_surface_foreach_edge (s, (GtsFunc) number_foreach, &n); + + return n; +} + +/** + * gts_surface_face_number: + * @s: a #GtsSurface. + * + * Returns: the number of faces of @s + */ +guint gts_surface_face_number (GtsSurface * s) +{ + g_return_val_if_fail (s != NULL, 0); + +#ifdef USE_SURFACE_BTREE + return g_tree_nnodes (s->faces); +#else /* not USE_SURFACE_BTREE */ + return g_hash_table_size (s->faces); +#endif /* not USE_SURFACE_BTREE */ +} + +static void build_list_face (GtsTriangle * t, GSList ** list) +{ + *list = g_slist_prepend (*list, gts_bbox_triangle (gts_bbox_class (), t)); +} + +static void build_list_boundary (GtsEdge * e, GSList ** list) +{ + if (gts_edge_is_boundary (e, NULL)) + *list = g_slist_prepend (*list, gts_bbox_segment (gts_bbox_class (), + GTS_SEGMENT (e))); +} + +/** + * gts_surface_distance: + * @s1: a #GtsSurface. + * @s2: a #GtsSurface. + * @delta: a spatial increment defined as the percentage of the diagonal + * of the bounding box of @s2. + * @face_range: a #GtsRange. + * @boundary_range: a #GtsRange. + * + * Using the gts_bb_tree_surface_distance() and + * gts_bb_tree_surface_boundary_distance() functions fills @face_range + * and @boundary_range with the min, max and average Euclidean + * (minimum) distances between the faces of @s1 and the faces of @s2 + * and between the boundary edges of @s1 and @s2. + */ +void gts_surface_distance (GtsSurface * s1, GtsSurface * s2, gdouble delta, + GtsRange * face_range, GtsRange * boundary_range) +{ + GNode * face_tree, * boundary_tree; + GSList * bboxes; + + g_return_if_fail (s1 != NULL); + g_return_if_fail (s2 != NULL); + g_return_if_fail (delta > 0. && delta < 1.); + g_return_if_fail (face_range != NULL); + g_return_if_fail (boundary_range != NULL); + + bboxes = NULL; + gts_surface_foreach_face (s2, (GtsFunc) build_list_face, &bboxes); + if (bboxes != NULL) { + face_tree = gts_bb_tree_new (bboxes); + g_slist_free (bboxes); + + gts_bb_tree_surface_distance (face_tree, s1, + (GtsBBoxDistFunc) gts_point_triangle_distance, + delta, face_range); + gts_bb_tree_destroy (face_tree, TRUE); + + bboxes = NULL; + gts_surface_foreach_edge (s2, (GtsFunc) build_list_boundary, &bboxes); + if (bboxes != NULL) { + boundary_tree = gts_bb_tree_new (bboxes); + g_slist_free (bboxes); + + gts_bb_tree_surface_boundary_distance (boundary_tree, + s1, + (GtsBBoxDistFunc) gts_point_segment_distance, + delta, boundary_range); + gts_bb_tree_destroy (boundary_tree, TRUE); + } + else + gts_range_reset (boundary_range); + } + else { + gts_range_reset (face_range); + gts_range_reset (boundary_range); + } +} + +static void surface_boundary (GtsEdge * e, gpointer * data) +{ + GSList ** list = data[0]; + + if (gts_edge_is_boundary (e, data[1])) + *list = g_slist_prepend (*list, e); +} + +/** + * gts_surface_boundary: + * @surface: a #GtsSurface. + * + * Returns: a list of #GtsEdge boundary of @surface. + */ +GSList * gts_surface_boundary (GtsSurface * surface) +{ + GSList * list = NULL; + gpointer data[2]; + + g_return_val_if_fail (surface != NULL, NULL); + + data[0] = &list; + data[1] = surface; + gts_surface_foreach_edge (surface, (GtsFunc) surface_boundary, data); + + return list; +} + +struct _GtsSurfaceTraverse { + GtsFifo * q; + GtsSurface * s; +}; + +/** + * gts_surface_traverse_new: + * @s: a #GtsSurface. + * @f: a #GtsFace belonging to @s. + * + * Returns: a new #GtsSurfaceTraverse, initialized to start traversing + * from face @f of surface @s. + */ +GtsSurfaceTraverse * gts_surface_traverse_new (GtsSurface * s, + GtsFace * f) +{ + GtsSurfaceTraverse * t; + + g_return_val_if_fail (s != NULL, NULL); + g_return_val_if_fail (f != NULL, NULL); + g_return_val_if_fail (gts_face_has_parent_surface (f, s), NULL); + + t = g_malloc (sizeof (GtsSurfaceTraverse)); + t->q = gts_fifo_new (); + t->s = s; + GTS_OBJECT (f)->reserved = GUINT_TO_POINTER (1); + gts_fifo_push (t->q, f); + return t; +} + +static void push_neighbor (GtsFace * v, gpointer * data) +{ + if (!GTS_OBJECT (v)->reserved) { + GTS_OBJECT (v)->reserved = + GUINT_TO_POINTER (GPOINTER_TO_UINT (GTS_OBJECT (data[1])->reserved) + 1); + gts_fifo_push (data[0], v); + } +} + +/** + * gts_surface_traverse_next: + * @t: a #GtsSurfaceTraverse. + * @level: a pointer to a guint or %NULL. + * + * Returns: the next face of the traversal in breadth-first order or + * %NULL if no faces are left. If @level if not %NULL, it is filled + * with the level of the returned face (0 for the initial face, 1 for + * its neighbors and so on). + */ +GtsFace * gts_surface_traverse_next (GtsSurfaceTraverse * t, + guint * level) +{ + GtsFace * u; + + g_return_val_if_fail (t != NULL, NULL); + + u = gts_fifo_pop (t->q); + if (u) { + gpointer data[2]; + + if (level) + *level = GPOINTER_TO_UINT (GTS_OBJECT (u)->reserved); + data[0] = t->q; + data[1] = u; + gts_face_foreach_neighbor (u, t->s, (GtsFunc) push_neighbor, data); + } + return u; +} + +/** + * gts_surface_traverse_destroy: + * @t: a #GtsSurfaceTraverse. + * + * Frees all the memory allocated for @t. + */ +void gts_surface_traverse_destroy (GtsSurfaceTraverse * t) +{ + g_return_if_fail (t != NULL); + + gts_surface_foreach_face (t->s, (GtsFunc) gts_object_reset_reserved, NULL); + gts_fifo_destroy (t->q); + g_free (t); +} + +static void traverse_manifold (GtsTriangle * t, GtsSurface * s) +{ + if (g_slist_length (GTS_FACE (t)->surfaces) > 1) + return; + + gts_surface_add_face (s, GTS_FACE (t)); + if (g_slist_length (t->e1->triangles) == 2) { + if (t->e1->triangles->data != t) + traverse_manifold (t->e1->triangles->data, s); + else + traverse_manifold (t->e1->triangles->next->data, s); + } + if (g_slist_length (t->e2->triangles) == 2) { + if (t->e2->triangles->data != t) + traverse_manifold (t->e2->triangles->data, s); + else + traverse_manifold (t->e2->triangles->next->data, s); + } + if (g_slist_length (t->e3->triangles) == 2) { + if (t->e3->triangles->data != t) + traverse_manifold (t->e3->triangles->data, s); + else + traverse_manifold (t->e3->triangles->next->data, s); + } +} + +static void non_manifold_edges (GtsEdge * e, gpointer * data) +{ + GtsSurface * s = data[0]; + GSList ** non_manifold = data[1]; + + if (gts_edge_face_number (e, s) > 2) { + GSList * i = e->triangles; + + while (i) { + if (gts_face_has_parent_surface (i->data, s) && + !g_slist_find (*non_manifold, i->data)) + *non_manifold = g_slist_prepend (*non_manifold, i->data); + i = i->next; + } + } +} + +static void traverse_boundary (GtsEdge * e, gpointer * data) +{ + GtsSurface * orig = data[0]; + GSList ** components = data[1]; + GtsFace * f = gts_edge_is_boundary (e, orig); + + if (f != NULL && g_slist_length (f->surfaces) == 1) { + GtsSurface * s = + gts_surface_new (GTS_SURFACE_CLASS (GTS_OBJECT (orig)->klass), + orig->face_class, + orig->edge_class, + orig->vertex_class); + GSList * non_manifold = NULL, * i; + gpointer data[2]; + + *components = g_slist_prepend (*components, s); + data[0] = s; + data[1] = &non_manifold; + traverse_manifold (GTS_TRIANGLE (f), s); + + gts_surface_foreach_edge (s, (GtsFunc) non_manifold_edges, data); + i = non_manifold; + while (i) { + gts_surface_remove_face (s, i->data); + i = i->next; + } + g_slist_free (non_manifold); + } +} + +static void traverse_remaining (GtsFace * f, gpointer * data) +{ + GtsSurface * orig = data[0]; + GSList ** components = data[1]; + + if (g_slist_length (f->surfaces) == 1) { + GtsSurface * s = + gts_surface_new (GTS_SURFACE_CLASS (GTS_OBJECT (orig)->klass), + orig->face_class, + orig->edge_class, + orig->vertex_class); + GSList * non_manifold = NULL, * i; + gpointer data[2]; + + *components = g_slist_prepend (*components, s); + data[0] = s; + data[1] = &non_manifold; + traverse_manifold (GTS_TRIANGLE (f), s); + + gts_surface_foreach_edge (s, (GtsFunc) non_manifold_edges, data); + i = non_manifold; + while (i) { + gts_surface_remove_face (s, i->data); + i = i->next; + } + g_slist_free (non_manifold); + } +} + +/** + * gts_surface_split: + * @s: a #GtsSurface. + * + * Splits a surface into connected and manifold components. + * + * Returns: a list of new #GtsSurface. + */ +GSList * gts_surface_split (GtsSurface * s) +{ + gpointer data[2]; + GSList * components = NULL; + + g_return_val_if_fail (s != NULL, NULL); + + data[0] = s; + data[1] = &components; + + /* boundary components */ + gts_surface_foreach_edge (s, (GtsFunc) traverse_boundary, data); + + /* remaining components */ + gts_surface_foreach_face (s, (GtsFunc) traverse_remaining, data); + + return components; +} Index: toporouter/src_3rd/gts/triangle.c =================================================================== --- toporouter/src_3rd/gts/triangle.c (nonexistent) +++ toporouter/src_3rd/gts/triangle.c (revision 6803) @@ -0,0 +1,1094 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "gts.h" + +static void triangle_destroy (GtsObject * object) +{ + GtsTriangle * triangle = GTS_TRIANGLE (object); + GtsEdge * e1 = triangle->e1; + GtsEdge * e2 = triangle->e2; + GtsEdge * e3 = triangle->e3; + + e1->triangles = g_slist_remove (e1->triangles, triangle); + if (!GTS_OBJECT_DESTROYED (e1) && + !gts_allow_floating_edges && e1->triangles == NULL) + gts_object_destroy (GTS_OBJECT (e1)); + + e2->triangles = g_slist_remove (e2->triangles, triangle); + if (!GTS_OBJECT_DESTROYED (e2) && + !gts_allow_floating_edges && e2->triangles == NULL) + gts_object_destroy (GTS_OBJECT (e2)); + + e3->triangles = g_slist_remove (e3->triangles, triangle); + if (!GTS_OBJECT_DESTROYED (e3) && + !gts_allow_floating_edges && e3->triangles == NULL) + gts_object_destroy (GTS_OBJECT (e3)); + + (* GTS_OBJECT_CLASS (gts_triangle_class ())->parent_class->destroy) (object); +} + +static void triangle_class_init (GtsObjectClass * klass) +{ + klass->destroy = triangle_destroy; +} + +static void triangle_init (GtsTriangle * triangle) +{ + triangle->e1 = triangle->e2 = triangle->e3 = NULL; +} + +/** + * gts_triangle_class: + * + * Returns: the #GtsTriangleClass. + */ +GtsTriangleClass * gts_triangle_class (void) +{ + static GtsTriangleClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo triangle_info = { + "GtsTriangle", + sizeof (GtsTriangle), + sizeof (GtsTriangleClass), + (GtsObjectClassInitFunc) triangle_class_init, + (GtsObjectInitFunc) triangle_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (gts_object_class (), + &triangle_info); + } + + return klass; +} + +/** + * gts_triangle_set: + * @triangle: a #GtsTriangle. + * @e1: a #GtsEdge. + * @e2: another #GtsEdge touching @e1. + * @e3: another #GtsEdge touching both @e1 and @e2. + * + * Sets the edge of @triangle to @e1, @e2 and @e3 while checking that they + * define a valid triangle. + */ +void gts_triangle_set (GtsTriangle * triangle, + GtsEdge * e1, + GtsEdge * e2, + GtsEdge * e3) +{ + g_return_if_fail (e1 != NULL); + g_return_if_fail (e2 != NULL); + g_return_if_fail (e3 != NULL); + g_return_if_fail (e1 != e2 && e1 != e3 && e2 != e3); + + triangle->e1 = e1; + triangle->e2 = e2; + triangle->e3 = e3; + + if (GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e2)->v1) + g_return_if_fail (gts_segment_connect (GTS_SEGMENT (e3), + GTS_SEGMENT (e1)->v2, + GTS_SEGMENT (e2)->v2)); + else if (GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e2)->v1) + g_return_if_fail (gts_segment_connect (GTS_SEGMENT (e3), + GTS_SEGMENT (e1)->v1, + GTS_SEGMENT (e2)->v2)); + else if (GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e2)->v2) + g_return_if_fail (gts_segment_connect (GTS_SEGMENT (e3), + GTS_SEGMENT (e1)->v1, + GTS_SEGMENT (e2)->v1)); + else if (GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e2)->v2) + g_return_if_fail (gts_segment_connect (GTS_SEGMENT (e3), + GTS_SEGMENT (e1)->v2, + GTS_SEGMENT (e2)->v1)); + else + g_assert_not_reached (); + + e1->triangles = g_slist_prepend (e1->triangles, triangle); + e2->triangles = g_slist_prepend (e2->triangles, triangle); + e3->triangles = g_slist_prepend (e3->triangles, triangle); +} + +/** + * gts_triangle_new: + * @klass: a #GtsTriangleClass. + * @e1: a #GtsEdge. + * @e2: another #GtsEdge touching @e1. + * @e3: another #GtsEdge touching both @e1 and @e2. + * + * Returns: a new #GtsTriangle having @e1, @e2 and @e3 as edges. + */ +GtsTriangle * gts_triangle_new (GtsTriangleClass * klass, + GtsEdge * e1, + GtsEdge * e2, + GtsEdge * e3) +{ + GtsTriangle * t; + + t = GTS_TRIANGLE (gts_object_new (GTS_OBJECT_CLASS (klass))); + gts_triangle_set (t, e1, e2, e3); + + return t; +} + +/** + * gts_triangle_vertex_opposite: + * @t: a #GtsTriangle. + * @e: a #GtsEdge used by @t. + * + * This function fails if @e is not an edge of @t. + * + * Returns: a #GtsVertex, vertex of @t which does not belong to @e. + */ +GtsVertex * gts_triangle_vertex_opposite (GtsTriangle * t, GtsEdge * e) +{ + g_return_val_if_fail (t != NULL, NULL); + g_return_val_if_fail (e != NULL, NULL); + + if (t->e1 == e) { + GtsVertex * v = GTS_SEGMENT (t->e2)->v1; + if (v != GTS_SEGMENT (e)->v1 && v != GTS_SEGMENT (e)->v2) + return v; + return GTS_SEGMENT (t->e2)->v2; + } + if (t->e2 == e) { + GtsVertex * v = GTS_SEGMENT (t->e1)->v1; + if (v != GTS_SEGMENT (e)->v1 && v != GTS_SEGMENT (e)->v2) + return v; + return GTS_SEGMENT (t->e1)->v2; + } + if (t->e3 == e) { + GtsVertex * v = GTS_SEGMENT (t->e2)->v1; + if (v != GTS_SEGMENT (e)->v1 && v != GTS_SEGMENT (e)->v2) + return v; + return GTS_SEGMENT (t->e2)->v2; + } + g_assert_not_reached (); + return NULL; +} + +/** + * gts_triangle_edge_opposite: + * @t: a #GtsTriangle. + * @v: a #GtsVertex of @t. + * + * Returns: the edge of @t opposite @v or %NULL if @v is not a vertice of @t. + */ +GtsEdge * gts_triangle_edge_opposite (GtsTriangle * t, GtsVertex * v) +{ + GtsSegment * s1, * s2, * s3; + + g_return_val_if_fail (t != NULL, NULL); + g_return_val_if_fail (v != NULL, NULL); + + s1 = GTS_SEGMENT (t->e1); + s2 = GTS_SEGMENT (t->e2); + + if (s1->v1 != v && s1->v2 != v) { + if (s2->v1 != v && s2->v2 != v) + return NULL; + return t->e1; + } + if (s2->v1 != v && s2->v2 != v) + return t->e2; + s3 = GTS_SEGMENT (t->e3); + g_assert (s3->v1 != v && s3->v2 != v); + return t->e3; +} + +/** + * gts_triangles_angle: + * @t1: a #GtsTriangle. + * @t2: a #GtsTriangle. + * + * Returns: the value (in radians) of the angle between @t1 and @t2. + */ +gdouble gts_triangles_angle (GtsTriangle * t1, + GtsTriangle * t2) +{ + gdouble nx1, ny1, nz1, nx2, ny2, nz2; + gdouble pvx, pvy, pvz; + gdouble theta; + + g_return_val_if_fail (t1 != NULL && t2 != NULL, 0.0); + + gts_triangle_normal (t1, &nx1, &ny1, &nz1); + gts_triangle_normal (t2, &nx2, &ny2, &nz2); + + pvx = ny1*nz2 - nz1*ny2; + pvy = nz1*nx2 - nx1*nz2; + pvz = nx1*ny2 - ny1*nx2; + + theta = atan2 (sqrt (pvx*pvx + pvy*pvy + pvz*pvz), + nx1*nx2 + ny1*ny2 + nz1*nz2) - M_PI; + return theta < - M_PI ? theta + 2.*M_PI : theta; +} + +/** + * gts_triangles_are_compatible: + * @t1: a #GtsTriangle. + * @t2: a #GtsTriangle. + * @e: a #GtsEdge used by both @t1 and @t2. + * + * Checks if @t1 and @t2 have compatible orientations i.e. if @t1 and + * @t2 can be part of the same surface without conflict in the surface + * normal orientation. + * + * Returns: %TRUE if @t1 and @t2 are compatible, %FALSE otherwise. + */ +gboolean gts_triangles_are_compatible (GtsTriangle * t1, + GtsTriangle * t2, + GtsEdge * e) +{ + GtsEdge * e1 = NULL, * e2 = NULL; + + g_return_val_if_fail (t1 != NULL, FALSE); + g_return_val_if_fail (t2 != NULL, FALSE); + g_return_val_if_fail (e != NULL, FALSE); + + if (t1->e1 == e) e1 = t1->e2; + else if (t1->e2 == e) e1 = t1->e3; + else if (t1->e3 == e) e1 = t1->e1; + else + g_assert_not_reached (); + if (t2->e1 == e) e2 = t2->e2; + else if (t2->e2 == e) e2 = t2->e3; + else if (t2->e3 == e) e2 = t2->e1; + else + g_assert_not_reached (); + if (GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e2)->v1 || + GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e2)->v2 || + GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e2)->v1 || + GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e2)->v2) + return FALSE; + return TRUE; +} + +/** + * gts_triangle_area: + * @t: a #GtsTriangle. + * + * Returns: the area of the triangle @t. + */ +gdouble gts_triangle_area (GtsTriangle * t) +{ + gdouble x, y, z; + + g_return_val_if_fail (t != NULL, 0.0); + + gts_triangle_normal (t, &x, &y, &z); + + return sqrt (x*x + y*y + z*z)/2.; +} + +/** + * gts_triangle_perimeter: + * @t: a #GtsTriangle. + * + * Returns: the perimeter of the triangle @t. + */ +gdouble gts_triangle_perimeter (GtsTriangle * t) +{ + GtsVertex * v; + + g_return_val_if_fail (t != NULL, 0.0); + + v = gts_triangle_vertex (t); + return + gts_point_distance (GTS_POINT (GTS_SEGMENT (t->e1)->v1), + GTS_POINT (GTS_SEGMENT (t->e1)->v2)) + + gts_point_distance (GTS_POINT (GTS_SEGMENT (t->e1)->v1), + GTS_POINT (v)) + + gts_point_distance (GTS_POINT (GTS_SEGMENT (t->e1)->v2), + GTS_POINT (v)); +} + +/* perimeter of the equilateral triangle of area unity */ +#define GOLDEN_PERIMETER 4.5590141139 + +/** + * gts_triangle_quality: + * @t: a #GtsTriangle. + * + * The quality of a triangle is defined as the ratio of the square + * root of its surface area to its perimeter relative to this same + * ratio for an equilateral triangle with the same area. The quality + * is then one for an equilateral triangle and tends to zero for a + * very stretched triangle. + * + * Returns: the quality of the triangle @t. + */ +gdouble gts_triangle_quality (GtsTriangle * t) +{ + gdouble perimeter; + + g_return_val_if_fail (t != NULL, 0.0); + + perimeter = gts_triangle_perimeter (t); + return perimeter > 0.0 ? + GOLDEN_PERIMETER*sqrt (gts_triangle_area (t))/perimeter : + 0.0; +} + +/** + * gts_triangle_normal: + * @t: a #GtsTriangle. + * @x: the x coordinate of the normal. + * @y: the y coordinate of the normal. + * @z: the z coordinate of the normal. + * + * Computes the coordinates of the oriented normal of @t as the + * cross-product of two edges, using the left-hand rule. The normal is + * not normalized. If this triangle is part of a closed and oriented + * surface, the normal points to the outside of the surface. + */ +void gts_triangle_normal (GtsTriangle * t, + gdouble * x, + gdouble * y, + gdouble * z) +{ + GtsVertex * v1, * v2 = NULL, * v3 = NULL; + GtsPoint * p1, * p2, * p3; + gdouble x1, y1, z1, x2, y2, z2; + + g_return_if_fail (t != NULL); + + v1 = GTS_SEGMENT (t->e1)->v1; + if (GTS_SEGMENT (t->e1)->v1 == GTS_SEGMENT (t->e2)->v1) { + v2 = GTS_SEGMENT (t->e2)->v2; + v3 = GTS_SEGMENT (t->e1)->v2; + } + else if (GTS_SEGMENT (t->e1)->v2 == GTS_SEGMENT (t->e2)->v2) { + v2 = GTS_SEGMENT (t->e1)->v2; + v3 = GTS_SEGMENT (t->e2)->v1; + } + else if (GTS_SEGMENT (t->e1)->v1 == GTS_SEGMENT (t->e2)->v2) { + v2 = GTS_SEGMENT (t->e2)->v1; + v3 = GTS_SEGMENT (t->e1)->v2; + } + else if (GTS_SEGMENT (t->e1)->v2 == GTS_SEGMENT (t->e2)->v1) { + v2 = GTS_SEGMENT (t->e1)->v2; + v3 = GTS_SEGMENT (t->e2)->v2; + } + else { + fprintf (stderr, "t: %p t->e1: %p t->e2: %p t->e3: %p t->e1->v1: %p t->e1->v2: %p t->e2->v1: %p t->e2->v2: %p t->e3->v1: %p t->e3->v2: %p\n", + t, t->e1, t->e2, + t->e3, GTS_SEGMENT (t->e1)->v1, GTS_SEGMENT (t->e1)->v2, + GTS_SEGMENT (t->e2)->v1, GTS_SEGMENT (t->e2)->v2, + GTS_SEGMENT (t->e3)->v1, GTS_SEGMENT (t->e3)->v2); + g_assert_not_reached (); + } + + p1 = GTS_POINT (v1); + p2 = GTS_POINT (v2); + p3 = GTS_POINT (v3); + + x1 = p2->x - p1->x; + y1 = p2->y - p1->y; + z1 = p2->z - p1->z; + + x2 = p3->x - p1->x; + y2 = p3->y - p1->y; + z2 = p3->z - p1->z; + + *x = y1*z2 - z1*y2; + *y = z1*x2 - x1*z2; + *z = x1*y2 - y1*x2; +} + +/** + * gts_triangle_orientation: + * @t: a #GtsTriangle. + * + * Checks for the orientation of the plane (x,y) projection of a + * triangle. See gts_point_orientation() for details. This function + * is geometrically robust. + * + * Returns: a number depending on the orientation of the vertices of @t. + */ +gdouble gts_triangle_orientation (GtsTriangle * t) +{ + GtsVertex * v1, * v2 = NULL, * v3 = NULL; + + g_return_val_if_fail (t != NULL, 0.0); + + v1 = GTS_SEGMENT (t->e1)->v1; + if (GTS_SEGMENT (t->e1)->v1 == GTS_SEGMENT (t->e2)->v1) { + v2 = GTS_SEGMENT (t->e2)->v2; + v3 = GTS_SEGMENT (t->e1)->v2; + } + else if (GTS_SEGMENT (t->e1)->v2 == GTS_SEGMENT (t->e2)->v2) { + v2 = GTS_SEGMENT (t->e1)->v2; + v3 = GTS_SEGMENT (t->e2)->v1; + } + else if (GTS_SEGMENT (t->e1)->v1 == GTS_SEGMENT (t->e2)->v2) { + v2 = GTS_SEGMENT (t->e2)->v1; + v3 = GTS_SEGMENT (t->e1)->v2; + } + else if (GTS_SEGMENT (t->e1)->v2 == GTS_SEGMENT (t->e2)->v1) { + v2 = GTS_SEGMENT (t->e1)->v2; + v3 = GTS_SEGMENT (t->e2)->v2; + } + else + g_assert_not_reached (); + return gts_point_orientation (GTS_POINT (v1), + GTS_POINT (v2), + GTS_POINT (v3)); +} + +/** + * gts_triangle_revert: + * @t: a #GtsTriangle. + * + * Changes the orientation of triangle @t, turning it inside out. + */ +void gts_triangle_revert (GtsTriangle * t) +{ + GtsEdge * e; + + g_return_if_fail (t != NULL); + + e = t->e1; + t->e1 = t->e2; + t->e2 = e; +} + +/** + * gts_triangles_from_edges: + * @edges: a list of #GtsEdge. + * + * Builds a list of unique triangles which have one of their edges in @edges. + * + * Returns: the list of triangles. + */ +GSList * gts_triangles_from_edges (GSList * edges) +{ + GHashTable * hash; + GSList * triangles = NULL, * i; + + hash = g_hash_table_new (NULL, NULL); + i = edges; + while (i) { + GSList * j = GTS_EDGE (i->data)->triangles; + while (j) { + GtsTriangle * t = j->data; + if (g_hash_table_lookup (hash, t) == NULL) { + triangles = g_slist_prepend (triangles, t); + g_hash_table_insert (hash, t, i); + } + j = j->next; + } + i = i->next; + } + g_hash_table_destroy (hash); + + return triangles; +} + +/** + * gts_triangle_vertices_edges: + * @t: a #GtsTriangle. + * @e: a #GtsEdge belonging to the edges of @t or %NULL. + * @v1: a #GtsVertex used by @t. + * @v2: a #GtsVertex used by @t. + * @v3: a #GtsVertex used by @t. + * @e1: a #GtsEdge used by @t. + * @e2: a #GtsEdge used by @t. + * @e3: a #GtsEdge used by @t. + * + * Given @t and @e, returns @v1, @v2, @v3, @e1, @e2 and @e3. @e1 + * has @v1 and @v2 as vertices, @e2 has @v2 and @v3 as vertices + * and @e3 has @v3 and @v1 as vertices. @v1, @v2 and @v3 respects + * the orientation of @t. If @e is not NULL, @e1 and @e are + * identical. + */ +void gts_triangle_vertices_edges (GtsTriangle * t, + GtsEdge * e, + GtsVertex ** v1, + GtsVertex ** v2, + GtsVertex ** v3, + GtsEdge ** e1, + GtsEdge ** e2, + GtsEdge ** e3) +{ + GtsEdge * ee1, * ee2; + + g_return_if_fail (t != NULL); + + if (e == t->e1 || e == NULL) { + *e1 = ee1 = t->e1; *e2 = ee2 = t->e2; *e3 = t->e3; + } + else if (e == t->e2) { + *e1 = ee1 = e; *e2 = ee2 = t->e3; *e3 = t->e1; + } + else if (e == t->e3) { + *e1 = ee1 = e; *e2 = ee2 = t->e1; *e3 = t->e2; + } + else { + g_assert_not_reached (); + ee1 = ee2 = NULL; /* to avoid complaints from the compiler */ + } + if (GTS_SEGMENT (ee1)->v2 == GTS_SEGMENT (ee2)->v1) { + *v1 = GTS_SEGMENT (ee1)->v1; + *v2 = GTS_SEGMENT (ee1)->v2; + *v3 = GTS_SEGMENT (ee2)->v2; + } + else if (GTS_SEGMENT (ee1)->v2 == GTS_SEGMENT (ee2)->v2) { + *v1 = GTS_SEGMENT (ee1)->v1; + *v2 = GTS_SEGMENT (ee1)->v2; + *v3 = GTS_SEGMENT (ee2)->v1; + } + else if (GTS_SEGMENT (ee1)->v1 == GTS_SEGMENT (ee2)->v1) { + *v1 = GTS_SEGMENT (ee1)->v2; + *v2 = GTS_SEGMENT (ee1)->v1; + *v3 = GTS_SEGMENT (ee2)->v2; + } + else if (GTS_SEGMENT (ee1)->v1 == GTS_SEGMENT (ee2)->v2) { + *v1 = GTS_SEGMENT (ee1)->v2; + *v2 = GTS_SEGMENT (ee1)->v1; + *v3 = GTS_SEGMENT (ee2)->v1; + } + else + g_assert_not_reached (); +} + +/* sqrt(3) */ +#define SQRT3 1.73205080757 + +/** + * gts_triangle_enclosing: + * @klass: the class of the new triangle. + * @points: a list of #GtsPoint. + * @scale: a scaling factor (must be larger than one). + * + * Builds a new triangle (including new vertices and edges) enclosing + * the plane projection of all the points in @points. This triangle is + * equilateral and encloses a rectangle defined by the maximum and + * minimum x and y coordinates of the points. @scale is an homothetic + * scaling factor. If equal to one, the triangle encloses exactly the + * enclosing rectangle. + * + * Returns: a new #GtsTriangle. + */ +GtsTriangle * gts_triangle_enclosing (GtsTriangleClass * klass, + GSList * points, gdouble scale) +{ + gdouble xmax, xmin, ymax, ymin; + gdouble xo, yo, r; + GtsVertex * v1, * v2, * v3; + GtsEdge * e1, * e2, * e3; + + if (points == NULL) + return NULL; + + xmax = xmin = GTS_POINT (points->data)->x; + ymax = ymin = GTS_POINT (points->data)->y; + points = points->next; + while (points) { + GtsPoint * p = points->data; + if (p->x > xmax) xmax = p->x; + else if (p->x < xmin) xmin = p->x; + if (p->y > ymax) ymax = p->y; + else if (p->y < ymin) ymin = p->y; + points = points->next; + } + xo = (xmax + xmin)/2.; + yo = (ymax + ymin)/2.; + r = scale*sqrt((xmax - xo)*(xmax - xo) + (ymax - yo)*(ymax - yo)); + if (r == 0.0) r = scale; + v1 = gts_vertex_new (gts_vertex_class (), + xo + r*SQRT3, yo - r, 0.0); + v2 = gts_vertex_new (gts_vertex_class (), + xo, yo + 2.*r, 0.0); + v3 = gts_vertex_new (gts_vertex_class (), + xo - r*SQRT3, yo - r, 0.0); + e1 = gts_edge_new (gts_edge_class (), v1, v2); + e2 = gts_edge_new (gts_edge_class (), v2, v3); + e3 = gts_edge_new (gts_edge_class (), v3, v1); + return gts_triangle_new (gts_triangle_class (), e1, e2, e3); +} + +/** + * gts_triangle_neighbor_number: + * @t: a #GtsTriangle. + * + * Returns: the number of triangles neighbors of @t. + */ +guint gts_triangle_neighbor_number (GtsTriangle * t) +{ + GSList * i; + guint nn = 0; + GtsEdge * ee[4], ** e = ee; + + g_return_val_if_fail (t != NULL, 0); + + ee[0] = t->e1; ee[1] = t->e2; ee[2] = t->e3; ee[3] = NULL; + while (*e) { + i = (*e++)->triangles; + while (i) { + GtsTriangle * t1 = i->data; + if (t1 != t) + nn++; + i = i->next; + } + } + return nn; +} + +/** + * gts_triangle_neighbors: + * @t: a #GtsTriangle. + * + * Returns: a list of #GtsTriangle neighbors of @t. + */ +GSList * gts_triangle_neighbors (GtsTriangle * t) +{ + GSList * i, * list = NULL; + GtsEdge * ee[4], ** e = ee; + + g_return_val_if_fail (t != NULL, NULL); + + ee[0] = t->e1; ee[1] = t->e2; ee[2] = t->e3; ee[3] = NULL; + while (*e) { + i = (*e++)->triangles; + while (i) { + GtsTriangle * t1 = i->data; + if (t1 != t) + list = g_slist_prepend (list, t1); + i = i->next; + } + } + return list; +} + +/** + * gts_triangles_common_edge: + * @t1: a #GtsTriangle. + * @t2: a #GtsTriangle. + * + * Returns: a #GtsEdge common to both @t1 and @t2 or %NULL if @t1 and @t2 + * do not share any edge. + */ +GtsEdge * gts_triangles_common_edge (GtsTriangle * t1, + GtsTriangle * t2) +{ + g_return_val_if_fail (t1 != NULL, NULL); + g_return_val_if_fail (t2 != NULL, NULL); + + if (t1->e1 == t2->e1 || t1->e1 == t2->e2 || t1->e1 == t2->e3) + return t1->e1; + if (t1->e2 == t2->e1 || t1->e2 == t2->e2 || t1->e2 == t2->e3) + return t1->e2; + if (t1->e3 == t2->e1 || t1->e3 == t2->e2 || t1->e3 == t2->e3) + return t1->e3; + return NULL; +} + +/** + * gts_triangle_is_duplicate: + * @t: a #GtsTriangle. + * + * Returns: a #GtsTriangle different from @t but sharing all its edges + * with @t or %NULL if there is none. + */ +GtsTriangle * gts_triangle_is_duplicate (GtsTriangle * t) +{ + GSList * i; + GtsEdge * e2, * e3; + + g_return_val_if_fail (t != NULL, NULL); + + e2 = t->e2; + e3 = t->e3; + i = t->e1->triangles; + while (i) { + GtsTriangle * t1 = i->data; + if (t1 != t && + (t1->e1 == e2 || t1->e2 == e2 || t1->e3 == e2) && + (t1->e1 == e3 || t1->e2 == e3 || t1->e3 == e3)) + return t1; + i = i->next; + } + + return NULL; +} + +/** + * gts_triangle_use_edges: + * @e1: a #GtsEdge. + * @e2: a #GtsEdge. + * @e3: a #GtsEdge. + * + * Returns: a #GtsTriangle having @e1, @e2 and @e3 as edges or %NULL if @e1, + * @e2 and @e3 are not part of any triangle. + */ +GtsTriangle * gts_triangle_use_edges (GtsEdge * e1, + GtsEdge * e2, + GtsEdge * e3) +{ + GSList * i; + + g_return_val_if_fail (e1 != NULL, NULL); + g_return_val_if_fail (e2 != NULL, NULL); + g_return_val_if_fail (e3 != NULL, NULL); + + i = e1->triangles; + while (i) { + GtsTriangle * t = i->data; + if ((t->e1 == e2 && (t->e2 == e3 || t->e3 == e3)) || + (t->e2 == e2 && (t->e1 == e3 || t->e3 == e3)) || + (t->e3 == e2 && (t->e1 == e3 || t->e2 == e3))) + return t; + i = i->next; + } + + return NULL; +} + +/** + * gts_triangle_is_ok: + * @t: a #GtsTriangle. + * + * Returns: %TRUE if @t is a non-degenerate, non-duplicate triangle, + * %FALSE otherwise. + */ +gboolean gts_triangle_is_ok (GtsTriangle * t) +{ + g_return_val_if_fail (t != NULL, FALSE); + g_return_val_if_fail (t->e1 != NULL, FALSE); + g_return_val_if_fail (t->e2 != NULL, FALSE); + g_return_val_if_fail (t->e3 != NULL, FALSE); + g_return_val_if_fail (t->e1 != t->e2 && t->e1 != t->e3 && t->e2 != t->e3, + FALSE); + g_return_val_if_fail (gts_segments_touch (GTS_SEGMENT (t->e1), + GTS_SEGMENT (t->e2)), + FALSE); + g_return_val_if_fail (gts_segments_touch (GTS_SEGMENT (t->e1), + GTS_SEGMENT (t->e3)), + FALSE); + g_return_val_if_fail (gts_segments_touch (GTS_SEGMENT (t->e2), + GTS_SEGMENT (t->e3)), + FALSE); + g_return_val_if_fail (GTS_SEGMENT (t->e1)->v1 != GTS_SEGMENT (t->e1)->v2, + FALSE); + g_return_val_if_fail (GTS_SEGMENT (t->e2)->v1 != GTS_SEGMENT (t->e2)->v2, + FALSE); + g_return_val_if_fail (GTS_SEGMENT (t->e3)->v1 != GTS_SEGMENT (t->e3)->v2, + FALSE); + g_return_val_if_fail (GTS_OBJECT (t)->reserved == NULL, FALSE); + g_return_val_if_fail (!gts_triangle_is_duplicate (t), FALSE); + return TRUE; +} + +/** + * gts_triangle_vertices: + * @t: a #GtsTriangle. + * @v1: a pointer on a #GtsVertex. + * @v2: a pointer on a #GtsVertex. + * @v3: a pointer on a #GtsVertex. + * + * Fills @v1, @v2 and @v3 with the oriented set of vertices, summits of @t. + */ +void gts_triangle_vertices (GtsTriangle * t, + GtsVertex ** v1, GtsVertex ** v2, GtsVertex ** v3) +{ + GtsSegment * e1, * e2; + + g_return_if_fail (t != NULL); + g_return_if_fail (v1 != NULL && v2 != NULL && v3 != NULL); + + e1 = GTS_SEGMENT (t->e1); + e2 = GTS_SEGMENT (t->e2); + + if (GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e2)->v1) { + *v1 = GTS_SEGMENT (e1)->v1; + *v2 = GTS_SEGMENT (e1)->v2; + *v3 = GTS_SEGMENT (e2)->v2; + } + else if (GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e2)->v2) { + *v1 = GTS_SEGMENT (e1)->v1; + *v2 = GTS_SEGMENT (e1)->v2; + *v3 = GTS_SEGMENT (e2)->v1; + } + else if (GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e2)->v1) { + *v1 = GTS_SEGMENT (e1)->v2; + *v2 = GTS_SEGMENT (e1)->v1; + *v3 = GTS_SEGMENT (e2)->v2; + } + else { + *v1 = GTS_SEGMENT (e1)->v2; + *v2 = GTS_SEGMENT (e1)->v1; + *v3 = GTS_SEGMENT (e2)->v1; + } +} + +/** + * gts_triangle_circumcircle_center: + * @t: a #GtsTriangle. + * @point_class: a #GtsPointClass. + * + * Returns: a new #GtsPoint, center of the circumscribing circle of @t or + * %NULL if the circumscribing circle is not defined. + */ +GtsPoint * gts_triangle_circumcircle_center (GtsTriangle * t, + GtsPointClass * point_class) +{ + GtsVertex * v1, * v2, * v3; + gdouble xa, ya, xb, yb, xc, yc; + gdouble xd, yd, xe, ye; + gdouble xad, yad, xae, yae; + gdouble det; + + g_return_val_if_fail (t != NULL, NULL); + g_return_val_if_fail (point_class != NULL, NULL); + + gts_triangle_vertices (t, &v1, &v2, &v3); + + xa = GTS_POINT (v1)->x; ya = GTS_POINT (v1)->y; + xb = GTS_POINT (v2)->x; yb = GTS_POINT (v2)->y; + xc = GTS_POINT (v3)->x; yc = GTS_POINT (v3)->y; + xd = (xa + xb)/2.; yd = (ya + yb)/2.; + xe = (xa + xc)/2.; ye = (ya + yc)/2.; + xad = xd - xa; yad = yd - ya; + xae = xe - xa; yae = ye - ya; + det = xad*yae - xae*yad; + if (det == 0.) + return NULL; + return gts_point_new (point_class, + (yae*yad*(yd - ye) + xad*yae*xd - xae*yad*xe)/det, + -(xae*xad*(xd - xe) + yad*xae*yd - yae*xad*ye)/det, + 0.); +} + +/* square of the maximum area ratio admissible */ +#define AREA_RATIO_MAX2 1e8 + +static gboolean points_are_folded (GtsPoint * A, + GtsPoint * B, + GtsPoint * C, + GtsPoint * D, + gdouble max) +{ + GtsVector AB, AC, AD; + GtsVector n1, n2; + gdouble nn1, nn2, n1n2; + + gts_vector_init (AB, A, B); + gts_vector_init (AC, A, C); + gts_vector_init (AD, A, D); + gts_vector_cross (n1, AB, AC); + gts_vector_cross (n2, AD, AB); + + nn1 = gts_vector_scalar (n1, n1); + nn2 = gts_vector_scalar (n2, n2); + if (nn1 >= AREA_RATIO_MAX2*nn2 || nn2 >= AREA_RATIO_MAX2*nn1) + return TRUE; + n1n2 = gts_vector_scalar (n1, n2); + if (n1n2 > 0.) + return FALSE; + if (n1n2*n1n2/(nn1*nn2) > max) + return TRUE; + return FALSE; +} + +static GtsVertex * triangle_use_vertices (GtsTriangle * t, + GtsVertex * A, + GtsVertex * B) +{ + GtsVertex + * v1 = GTS_SEGMENT (t->e1)->v1, + * v2 = GTS_SEGMENT (t->e1)->v2, + * v3 = gts_triangle_vertex (t); + + if (v1 == A) { + if (v2 == B) + return v3; + g_assert (v3 == B); + return v2; + } + if (v2 == A) { + if (v1 == B) + return v3; + g_assert (v3 == B); + return v1; + } + if (v3 == A) { + if (v1 == B) + return v2; + g_assert (v2 == B); + return v1; + } + g_assert_not_reached (); + return NULL; +} + +/** + * gts_triangles_are_folded: + * @triangles: a list of #GtsTriangle. + * @A: a #GtsVertex. + * @B: another #GtsVertex. + * @max: the maximum value of the square of the cosine of the angle between + * two triangles. + * + * Given a list of triangles sharing @A and @B as vertices, checks if any + * two triangles in the list make an angle larger than a given value defined + * by @max. + * + * Returns: %TRUE if any pair of triangles in @triangles makes an angle larger + * than the maximum value, %FALSE otherwise. + */ +gboolean gts_triangles_are_folded (GSList * triangles, + GtsVertex * A, GtsVertex * B, + gdouble max) +{ + GSList * i; + + g_return_val_if_fail (A != NULL, TRUE); + g_return_val_if_fail (B != NULL, TRUE); + + i = triangles; + while (i) { + GtsVertex * C = triangle_use_vertices (i->data, A, B); + GSList * j = i->next; + while (j) { + GtsVertex * D = triangle_use_vertices (j->data, A, B); + if (points_are_folded (GTS_POINT (A), + GTS_POINT (B), + GTS_POINT (C), + GTS_POINT (D), + max)) + return TRUE; + j = j->next; + } + i = i->next; + } + return FALSE; +} + +/** + * gts_triangle_is_stabbed: + * @t: a #GtsTriangle. + * @p: a #GtsPoint. + * @orientation: a pointer or %NULL. + * + * Returns: one of the vertices of @t, one of the edges of @t or @t if + * any of these are stabbed by the ray starting at @p (included) and + * ending at (@p->x, @p->y, +infty), %NULL otherwise. If the ray is + * contained in the plane of the triangle %NULL is also returned. If + * @orientation is not %NULL, it is set to the value of the + * orientation of @p relative to @t (as given by + * gts_point_orientation_3d()). + */ +GtsObject * gts_triangle_is_stabbed (GtsTriangle * t, + GtsPoint * p, + gdouble * orientation) +{ + GtsVertex * v1, * v2, * v3, * inverted = NULL; + GtsEdge * e1, * e2, * e3, * tmp; + gdouble o, o1, o2, o3; + + g_return_val_if_fail (t != NULL, NULL); + g_return_val_if_fail (p != NULL, NULL); + + gts_triangle_vertices_edges (t, NULL, &v1, &v2, &v3, &e1, &e2, &e3); + o = gts_point_orientation (GTS_POINT (v1), GTS_POINT (v2), GTS_POINT (v3)); + if (o == 0.) + return NULL; + if (o < 0.) { + inverted = v1; + v1 = v2; + v2 = inverted; + tmp = e2; + e2 = e3; + e3 = tmp; + } + o = gts_point_orientation_3d (GTS_POINT (v1), + GTS_POINT (v2), + GTS_POINT (v3), + p); + if (o < 0.) + return NULL; + o1 = gts_point_orientation (GTS_POINT (v1), GTS_POINT (v2), p); + if (o1 < 0.) + return NULL; + o2 = gts_point_orientation (GTS_POINT (v2), GTS_POINT (v3), p); + if (o2 < 0.) + return NULL; + o3 = gts_point_orientation (GTS_POINT (v3), GTS_POINT (v1), p); + if (o3 < 0.) + return NULL; + if (orientation) *orientation = inverted ? -o : o; + if (o1 == 0.) { + if (o2 == 0.) + return GTS_OBJECT (v2); + if (o3 == 0.) + return GTS_OBJECT (v1); + return GTS_OBJECT (e1); + } + if (o2 == 0.) { + if (o3 == 0.) + return GTS_OBJECT (v3); + return GTS_OBJECT (e2); + } + if (o3 == 0.) + return GTS_OBJECT (e3); + return GTS_OBJECT (t); +} + +/** + * gts_triangle_interpolate_height: + * @t: a #GtsTriangle. + * @p: a #GtsPoint. + * + * Fills the z-coordinate of point @p belonging to the plane + * projection of triangle @t with the linearly interpolated value of + * the z-coordinates of the vertices of @t. + */ +void gts_triangle_interpolate_height (GtsTriangle * t, GtsPoint * p) +{ + GtsPoint * p1, * p2, * p3; + gdouble x1, x2, y1, y2, det; + + g_return_if_fail (t != NULL); + g_return_if_fail (p != NULL); + + p1 = GTS_POINT (GTS_SEGMENT (t->e1)->v1); + p2 = GTS_POINT (GTS_SEGMENT (t->e1)->v2); + p3 = GTS_POINT (gts_triangle_vertex (t)); + + x1 = p2->x - p1->x; + y1 = p2->y - p1->y; + x2 = p3->x - p1->x; + y2 = p3->y - p1->y; + det = x1*y2 - x2*y1; + if (det == 0.) + p->z = (p1->z + p2->z + p3->z)/3.; + else { + gdouble x = p->x - p1->x; + gdouble y = p->y - p1->y; + gdouble a = (x*y2 - y*x2)/det; + gdouble b = (y*x1 - x*y1)/det; + + p->z = (1. - a - b)*p1->z + a*p2->z + b*p3->z; + } +} Index: toporouter/src_3rd/gts/tribox3.c =================================================================== --- toporouter/src_3rd/gts/tribox3.c (nonexistent) +++ toporouter/src_3rd/gts/tribox3.c (revision 6803) @@ -0,0 +1,192 @@ +/** + * History: + * 2004-10-27 Stephane Popinet: changed float to double + */ + +/********************************************************/ +/* AABB-triangle overlap test code */ +/* by Tomas Akenine-Möller */ +/* Function: int triBoxOverlap(float boxcenter[3], */ +/* float boxhalfsize[3],float triverts[3][3]); */ +/* History: */ +/* 2001-03-05: released the code in its first version */ +/* 2001-06-18: changed the order of the tests, faster */ +/* */ +/* Acknowledgement: Many thanks to Pierre Terdiman for */ +/* suggestions and discussions on how to optimize code. */ +/* Thanks to David Hunt for finding a ">="-bug! */ +/********************************************************/ +#include +#include + +#define X 0 +#define Y 1 +#define Z 2 + +#define CROSS(dest,v1,v2) \ + dest[0]=v1[1]*v2[2]-v1[2]*v2[1]; \ + dest[1]=v1[2]*v2[0]-v1[0]*v2[2]; \ + dest[2]=v1[0]*v2[1]-v1[1]*v2[0]; + +#define DOT(v1,v2) (v1[0]*v2[0]+v1[1]*v2[1]+v1[2]*v2[2]) + +#define SUB(dest,v1,v2) \ + dest[0]=v1[0]-v2[0]; \ + dest[1]=v1[1]-v2[1]; \ + dest[2]=v1[2]-v2[2]; + +#define FINDMINMAX(x0,x1,x2,min,max) \ + min = max = x0; \ + if(x1max) max=x1;\ + if(x2max) max=x2; + +int planeBoxOverlap(double normal[3], double vert[3], double maxbox[3]) // -NJMP- +{ + int q; + double vmin[3],vmax[3],v; + for(q=X;q<=Z;q++) + { + v=vert[q]; // -NJMP- + if(normal[q]>0.0f) + { + vmin[q]=-maxbox[q] - v; // -NJMP- + vmax[q]= maxbox[q] - v; // -NJMP- + } + else + { + vmin[q]= maxbox[q] - v; // -NJMP- + vmax[q]=-maxbox[q] - v; // -NJMP- + } + } + if(DOT(normal,vmin)>0.0f) return 0; // -NJMP- + if(DOT(normal,vmax)>=0.0f) return 1; // -NJMP- + + return 0; +} + + +/*======================== X-tests ========================*/ +#define AXISTEST_X01(a, b, fa, fb) \ + p0 = a*v0[Y] - b*v0[Z]; \ + p2 = a*v2[Y] - b*v2[Z]; \ + if(p0rad || max<-rad) return 0; + +#define AXISTEST_X2(a, b, fa, fb) \ + p0 = a*v0[Y] - b*v0[Z]; \ + p1 = a*v1[Y] - b*v1[Z]; \ + if(p0rad || max<-rad) return 0; + +/*======================== Y-tests ========================*/ +#define AXISTEST_Y02(a, b, fa, fb) \ + p0 = -a*v0[X] + b*v0[Z]; \ + p2 = -a*v2[X] + b*v2[Z]; \ + if(p0rad || max<-rad) return 0; + +#define AXISTEST_Y1(a, b, fa, fb) \ + p0 = -a*v0[X] + b*v0[Z]; \ + p1 = -a*v1[X] + b*v1[Z]; \ + if(p0rad || max<-rad) return 0; + +/*======================== Z-tests ========================*/ + +#define AXISTEST_Z12(a, b, fa, fb) \ + p1 = a*v1[X] - b*v1[Y]; \ + p2 = a*v2[X] - b*v2[Y]; \ + if(p2rad || max<-rad) return 0; + +#define AXISTEST_Z0(a, b, fa, fb) \ + p0 = a*v0[X] - b*v0[Y]; \ + p1 = a*v1[X] - b*v1[Y]; \ + if(p0rad || max<-rad) return 0; + +int triBoxOverlap(double boxcenter[3],double boxhalfsize[3],double triverts[3][3]) +{ + + /* use separating axis theorem to test overlap between triangle and box */ + /* need to test for overlap in these directions: */ + /* 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle */ + /* we do not even need to test these) */ + /* 2) normal of the triangle */ + /* 3) crossproduct(edge from tri, {x,y,z}-directin) */ + /* this gives 3x3=9 more tests */ + double v0[3],v1[3],v2[3]; +// double axis[3]; + double min,max,p0,p1,p2,rad,fex,fey,fez; // -NJMP- "d" local variable removed + double normal[3],e0[3],e1[3],e2[3]; + + /* This is the fastest branch on Sun */ + /* move everything so that the boxcenter is in (0,0,0) */ + SUB(v0,triverts[0],boxcenter); + SUB(v1,triverts[1],boxcenter); + SUB(v2,triverts[2],boxcenter); + + /* compute triangle edges */ + SUB(e0,v1,v0); /* tri edge 0 */ + SUB(e1,v2,v1); /* tri edge 1 */ + SUB(e2,v0,v2); /* tri edge 2 */ + + /* Bullet 3: */ + /* test the 9 tests first (this was faster) */ + fex = fabsf(e0[X]); + fey = fabsf(e0[Y]); + fez = fabsf(e0[Z]); + AXISTEST_X01(e0[Z], e0[Y], fez, fey); + AXISTEST_Y02(e0[Z], e0[X], fez, fex); + AXISTEST_Z12(e0[Y], e0[X], fey, fex); + + fex = fabsf(e1[X]); + fey = fabsf(e1[Y]); + fez = fabsf(e1[Z]); + AXISTEST_X01(e1[Z], e1[Y], fez, fey); + AXISTEST_Y02(e1[Z], e1[X], fez, fex); + AXISTEST_Z0(e1[Y], e1[X], fey, fex); + + fex = fabsf(e2[X]); + fey = fabsf(e2[Y]); + fez = fabsf(e2[Z]); + AXISTEST_X2(e2[Z], e2[Y], fez, fey); + AXISTEST_Y1(e2[Z], e2[X], fez, fex); + AXISTEST_Z12(e2[Y], e2[X], fey, fex); + + /* Bullet 1: */ + /* first test overlap in the {x,y,z}-directions */ + /* find min, max of the triangle each direction, and test for overlap in */ + /* that direction -- this is equivalent to testing a minimal AABB around */ + /* the triangle against the AABB */ + + /* test in X-direction */ + FINDMINMAX(v0[X],v1[X],v2[X],min,max); + if(min>boxhalfsize[X] || max<-boxhalfsize[X]) return 0; + + /* test in Y-direction */ + FINDMINMAX(v0[Y],v1[Y],v2[Y],min,max); + if(min>boxhalfsize[Y] || max<-boxhalfsize[Y]) return 0; + + /* test in Z-direction */ + FINDMINMAX(v0[Z],v1[Z],v2[Z],min,max); + if(min>boxhalfsize[Z] || max<-boxhalfsize[Z]) return 0; + + /* Bullet 2: */ + /* test if the box intersects the plane of the triangle */ + /* compute plane equation of triangle: normal*x+d=0 */ + CROSS(normal,e0,e1); + // -NJMP- (line removed here) + if(!planeBoxOverlap(normal,v0,boxhalfsize)) return 0; // -NJMP- + + return 1; /* box and triangle overlaps */ +} + Index: toporouter/src_3rd/gts/vertex.c =================================================================== --- toporouter/src_3rd/gts/vertex.c (nonexistent) +++ toporouter/src_3rd/gts/vertex.c (revision 6803) @@ -0,0 +1,780 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "gts.h" + +gboolean gts_allow_floating_vertices = FALSE; + +static void vertex_destroy (GtsObject * object) +{ + GtsVertex * vertex = GTS_VERTEX (object); + GSList * i; + + i = vertex->segments; + while (i) { + GTS_OBJECT_SET_FLAGS (i->data, GTS_DESTROYED); + i = i->next; + } + i = vertex->segments; + while (i) { + GSList * next = i->next; + gts_object_destroy (i->data); + i = next; + } + g_assert (vertex->segments == NULL); + + (* GTS_OBJECT_CLASS (gts_vertex_class ())->parent_class->destroy) (object); +} + +static void vertex_clone (GtsObject * clone, GtsObject * object) +{ + (* GTS_OBJECT_CLASS (gts_vertex_class ())->parent_class->clone) (clone, + object); + GTS_VERTEX (clone)->segments = NULL; +} + +static void vertex_class_init (GtsVertexClass * klass) +{ + klass->intersection_attributes = NULL; + GTS_OBJECT_CLASS (klass)->clone = vertex_clone; + GTS_OBJECT_CLASS (klass)->destroy = vertex_destroy; +} + +static void vertex_init (GtsVertex * vertex) +{ + vertex->segments = NULL; +} + +/** + * gts_vertex_class: + * + * Returns: the #GtsVertexClass. + */ +GtsVertexClass * gts_vertex_class (void) +{ + static GtsVertexClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo vertex_info = { + "GtsVertex", + sizeof (GtsVertex), + sizeof (GtsVertexClass), + (GtsObjectClassInitFunc) vertex_class_init, + (GtsObjectInitFunc) vertex_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_point_class ()), + &vertex_info); + } + + return klass; +} + +/** + * gts_vertex_new: + * @klass: a #GtsVertexClass. + * @x: the x-coordinate of the vertex to create. + * @y: the y-coordinate of the vertex to create. + * @z: the y-coordinate of the vertex to create. + * + * Returns: a new #GtsVertex with @x, @y and @z as coordinates. + */ +GtsVertex * gts_vertex_new (GtsVertexClass * klass, + gdouble x, gdouble y, gdouble z) +{ + GtsVertex * v; + + v = GTS_VERTEX (gts_object_new (GTS_OBJECT_CLASS (klass))); + gts_point_set (GTS_POINT (v), x, y, z); + + return v; +} + +/** + * gts_vertex_replace: + * @v: a #GtsVertex. + * @with: another #GtsVertex. + * + * Replaces vertex @v with vertex @with. @v and @with must be + * different. All the #GtsSegment which have @v has one of their + * vertices are updated. The segments list of vertex @v is freed and + * @v->segments is set to %NULL. + */ +void gts_vertex_replace (GtsVertex * v, GtsVertex * with) +{ + GSList * i; + + g_return_if_fail (v != NULL); + g_return_if_fail (with != NULL); + g_return_if_fail (v != with); + + i = v->segments; + while (i) { + GtsSegment * s = i->data; + if (s->v1 != with && s->v2 != with) + with->segments = g_slist_prepend (with->segments, s); + if (s->v1 == v) s->v1 = with; + if (s->v2 == v) s->v2 = with; + i = i->next; + } + g_slist_free (v->segments); + v->segments = NULL; +} + +/** + * gts_vertex_is_unattached: + * @v: a #GtsVertex. + * + * Returns: %TRUE if @v is not the endpoint of any #GtsSegment, + * %FALSE otherwise. + */ +gboolean gts_vertex_is_unattached (GtsVertex * v) +{ + g_return_val_if_fail (v != NULL, FALSE); + if (v->segments == NULL) + return TRUE; + return FALSE; +} + +/** + * gts_vertices_are_connected: + * @v1: a #GtsVertex. + * @v2: another #GtsVertex. + * + * Returns: if @v1 and @v2 are the vertices of the same #GtsSegment + * this segment else %NULL. + */ +GtsSegment * gts_vertices_are_connected (GtsVertex * v1, GtsVertex * v2) +{ + GSList * i; + + g_return_val_if_fail (v1 != NULL, FALSE); + g_return_val_if_fail (v2 != NULL, FALSE); + + i = v1->segments; + while (i) { + GtsSegment * s = i->data; + + if (s->v1 == v2 || s->v2 == v2) + return s; + i = i->next; + } + return NULL; +} + +/** + * gts_vertices_from_segments: + * @segments: a list of #GtsSegment. + * + * Returns: a list of #GtsVertex, vertices of a #GtsSegment in @segments. + * Each element in the list is unique (no duplicates). + */ +GSList * gts_vertices_from_segments (GSList * segments) +{ + GHashTable * hash; + GSList * vertices = NULL, * i; + + hash = g_hash_table_new (NULL, NULL); + i = segments; + while (i) { + GtsSegment * s = i->data; + if (g_hash_table_lookup (hash, s->v1) == NULL) { + vertices = g_slist_prepend (vertices, s->v1); + g_hash_table_insert (hash, s->v1, s); + } + if (g_hash_table_lookup (hash, s->v2) == NULL) { + vertices = g_slist_prepend (vertices, s->v2); + g_hash_table_insert (hash, s->v2, s); + } + i = i->next; + } + g_hash_table_destroy (hash); + return vertices; +} + +/** + * gts_vertex_triangles: + * @v: a #GtsVertex. + * @list: a list of #GtsTriangle. + * + * Adds all the #GtsTriangle which share @v as a vertex and do not + * already belong to @list. + * + * Returns: the new list of unique #GtsTriangle which share @v as a + * vertex. + */ +GSList * gts_vertex_triangles (GtsVertex * v, + GSList * list) +{ + GSList * i; + + g_return_val_if_fail (v != NULL, NULL); + + i = v->segments; + while (i) { + GtsSegment * s = i->data; + if (GTS_IS_EDGE (s)) { + GSList * j = GTS_EDGE (s)->triangles; + while (j) { + if (!g_slist_find (list, j->data)) + list = g_slist_prepend (list, j->data); + j = j->next; + } + } + i = i->next; + } + return list; +} + +/** + * gts_vertex_faces: + * @v: a #GtsVertex. + * @surface: a #GtsSurface or %NULL. + * @list: a list of #GtsFace. + * + * Adds all the #GtsFace belonging to @surface (if not %NULL) which share + * @v as a vertex and do not already belong to @list. + * + * Returns: the new list of unique #GtsFace belonging to @surface + * which share @v as a vertex. + */ +GSList * gts_vertex_faces (GtsVertex * v, + GtsSurface * surface, + GSList * list) +{ + GSList * i; + + g_return_val_if_fail (v != NULL, NULL); + + i = v->segments; + while (i) { + GtsSegment * s = i->data; + if (GTS_IS_EDGE (s)) { + GSList * j = GTS_EDGE (s)->triangles; + while (j) { + GtsTriangle * t = j->data; + if (GTS_IS_FACE (t) + && + (!surface || gts_face_has_parent_surface (GTS_FACE (t), surface)) + && + !g_slist_find (list, t)) + list = g_slist_prepend (list, t); + j = j->next; + } + } + i = i->next; + } + return list; +} + +/** + * gts_vertex_neighbors: + * @v: a #GtsVertex. + * @list: a list of #GtsVertex. + * @surface: a #GtsSurface or %NULL. + * + * Adds to @list all the #GtsVertex connected to @v by a #GtsSegment and not + * already in @list. If @surface is not %NULL only the vertices connected to + * @v by an edge belonging to @surface are considered. + * + * Returns: the new list of unique #GtsVertex. + */ +GSList * gts_vertex_neighbors (GtsVertex * v, + GSList * list, + GtsSurface * surface) +{ + GSList * i; + + g_return_val_if_fail (v != NULL, NULL); + + i = v->segments; + while (i) { + GtsSegment * s = i->data; + GtsVertex * v1 = s->v1 == v ? s->v2 : s->v1; + if (v1 != v && + (!surface || + (GTS_IS_EDGE (s) && + gts_edge_has_parent_surface (GTS_EDGE (s), surface))) && + !g_slist_find (list, v1)) + list = g_slist_prepend (list, v1); + i = i->next; + } + return list; +} + +/** + * gts_vertex_is_boundary: + * @v: a #GtsVertex. + * @surface: a #GtsSurface or %NULL. + * + * Returns: %TRUE if @v is used by a #GtsEdge boundary of @surface as + * determined by gts_edge_is_boundary(), %FALSE otherwise. + */ +gboolean gts_vertex_is_boundary (GtsVertex * v, GtsSurface * surface) +{ + GSList * i; + + g_return_val_if_fail (v != NULL, FALSE); + + i = v->segments; + while (i) { + if (GTS_IS_EDGE (i->data) && + gts_edge_is_boundary (i->data, surface)) + return TRUE; + i = i->next; + } + + return FALSE; +} + +/** + * gts_vertices_merge: + * @vertices: a list of #GtsVertex. + * @epsilon: half the size of the bounding box to consider for each vertex. + * @check: function called for each pair of vertices about to be merged + * or %NULL. + * + * For each vertex v in @vertices look if there are any vertex of + * @vertices contained in a box centered on v of size 2*@epsilon. If + * there are and if @check is not %NULL and returns %TRUE, replace + * them with v (using gts_vertex_replace()), destroy them and remove + * them from list. This is done efficiently using Kd-Trees. + * + * Returns: the updated list of vertices. + */ +GList * gts_vertices_merge (GList * vertices, + gdouble epsilon, + gboolean (* check) (GtsVertex *, GtsVertex *)) +{ + GPtrArray * array; + GList * i; + GNode * kdtree; + + g_return_val_if_fail (vertices != NULL, 0); + + array = g_ptr_array_new (); + i = vertices; + while (i) { + g_ptr_array_add (array, i->data); + i = i->next; + } + kdtree = gts_kdtree_new (array, NULL); + g_ptr_array_free (array, TRUE); + + i = vertices; + while (i) { + GtsVertex * v = i->data; + if (!GTS_OBJECT (v)->reserved) { /* Do something only if v is active */ + GtsBBox * bbox; + GSList * selected, * j; + + /* build bounding box */ + bbox = gts_bbox_new (gts_bbox_class (), + v, + GTS_POINT (v)->x - epsilon, + GTS_POINT (v)->y - epsilon, + GTS_POINT (v)->z - epsilon, + GTS_POINT (v)->x + epsilon, + GTS_POINT (v)->y + epsilon, + GTS_POINT (v)->z + epsilon); + + /* select vertices which are inside bbox using kdtree */ + j = selected = gts_kdtree_range (kdtree, bbox, NULL); + while (j) { + GtsVertex * sv = j->data; + if (sv != v && !GTS_OBJECT (sv)->reserved && (!check || (*check) (sv, v))) { + /* sv is not v and is active */ + gts_vertex_replace (sv, v); + GTS_OBJECT (sv)->reserved = sv; /* mark sv as inactive */ + } + j = j->next; + } + g_slist_free (selected); + gts_object_destroy (GTS_OBJECT (bbox)); + } + i = i->next; + } + + gts_kdtree_destroy (kdtree); + + /* destroy inactive vertices and removes them from list */ + + /* we want to control vertex destruction */ + gts_allow_floating_vertices = TRUE; + + i = vertices; + while (i) { + GtsVertex * v = i->data; + GList * next = i->next; + if (GTS_OBJECT (v)->reserved) { /* v is inactive */ + gts_object_destroy (GTS_OBJECT (v)); + vertices = g_list_remove_link (vertices, i); + g_list_free_1 (i); + } + i = next; + } + gts_allow_floating_vertices = FALSE; + + return vertices; +} + +/* returns the list of edges belonging to @surface turning around @v */ +static GSList * edge_fan_list (GtsVertex * v, + GtsSurface * surface, + GtsFace * f, + GtsEdge * e, + GtsFace * first) +{ + GSList * i = e->triangles; + GtsFace * neighbor = NULL; + GtsEdge * next = NULL, * enext = NULL; + + while (i) { + GtsFace * f1 = i->data; + if (GTS_IS_FACE (f1) && + f1 != f && + gts_face_has_parent_surface (f1, surface)) { + g_return_val_if_fail (neighbor == NULL, NULL); /* non-manifold edge */ + neighbor = f1; + } + i = i->next; + } + if (neighbor == NULL || neighbor == first) /* end of fan */ + return NULL; + + if (GTS_TRIANGLE (neighbor)->e1 == e) { + next = GTS_TRIANGLE (neighbor)->e2; + enext = GTS_TRIANGLE (neighbor)->e3; + } + else if (GTS_TRIANGLE (neighbor)->e2 == e) { + next = GTS_TRIANGLE (neighbor)->e3; + enext = GTS_TRIANGLE (neighbor)->e1; + } + else if (GTS_TRIANGLE (neighbor)->e3 == e) { + next = GTS_TRIANGLE (neighbor)->e1; + enext = GTS_TRIANGLE (neighbor)->e2; + } + else + g_assert_not_reached (); + + /* checking for correct orientation */ + g_return_val_if_fail (GTS_SEGMENT (enext)->v1 == v || + GTS_SEGMENT (enext)->v2 == v, NULL); + + return g_slist_prepend (edge_fan_list (v, surface, neighbor, enext, first), + next); +} + +/** + * gts_vertex_fan_oriented: + * @v: a #GtsVertex. + * @surface: a #GtsSurface. + * + * Returns: a list of #GtsEdge describing in counterclockwise order the + * boundary of the fan of summit @v, the faces of the fan belonging to + * @surface. + */ +GSList * gts_vertex_fan_oriented (GtsVertex * v, GtsSurface * surface) +{ + GtsFace * f = NULL; + guint d = 2; + GSList * i; + GtsVertex * v1, * v2, * v3; + GtsEdge * e1, * e2, * e3; + + g_return_val_if_fail (v != NULL, NULL); + g_return_val_if_fail (surface != NULL, NULL); + + i = v->segments; + while (i) { + GtsEdge * e = i->data; + if (GTS_IS_EDGE (e)) { + GSList * j = e->triangles; + GtsFace * f1 = NULL; + guint degree = 0; + while (j) { + if (GTS_IS_FACE (j->data) && + gts_face_has_parent_surface (j->data, surface)) { + f1 = j->data; + degree++; + } + j = j->next; + } + if (f1 != NULL) { + g_return_val_if_fail (degree <= 2, NULL); /* non-manifold edge */ + if (degree == 1) { + gts_triangle_vertices_edges (GTS_TRIANGLE (f1), NULL, + &v1, &v2, &v3, &e1, &e2, &e3); + if (v == v2) { + e2 = e3; + e3 = e1; + } + else if (v == v3) { + e3 = e2; + e2 = e1; + } + if (e3 != e) { + d = 1; + f = f1; + } + } + else if (degree <= d) + f = f1; + } + } + i = i->next; + } + + if (f == NULL) + return NULL; + + gts_triangle_vertices_edges (GTS_TRIANGLE (f), NULL, + &v1, &v2, &v3, &e1, &e2, &e3); + if (v == v2) { + e2 = e3; + e3 = e1; + } + else if (v == v3) { + e3 = e2; + e2 = e1; + } + + return g_slist_prepend (edge_fan_list (v, surface, f, e3, f), e2); +} + +#define edge_use_vertex(e, v) (GTS_SEGMENT(e)->v1 == v ||\ + GTS_SEGMENT(e)->v2 == v) + +static GtsEdge * replace_vertex (GtsTriangle * t, + GtsEdge * e1, + GtsVertex * v, + GtsVertex * with) +{ + GtsEdge * e = NULL; + + if (t->e1 != e1 && edge_use_vertex (t->e1, v)) + e = t->e1; + else if (t->e2 != e1 && edge_use_vertex (t->e2, v)) + e = t->e2; + else if (t->e3 != e1 && edge_use_vertex (t->e3, v)) + e = t->e3; + else + return NULL; + + if (with != v) { + GtsSegment * s = GTS_SEGMENT (e); + if (s->v1 == v) s->v1 = with; + if (s->v2 == v) s->v2 = with; + with->segments = g_slist_prepend (with->segments, s); + v->segments = g_slist_remove (v->segments, s); + } + + return e; +} + +static void triangle_next (GtsEdge * e, GtsVertex * v, GtsVertex * with) +{ + GSList * i; + + if (e == NULL) + return; + + i = e->triangles; + while (i) { + GtsTriangle * t = i->data; + if (GTS_OBJECT (t)->reserved) { + GTS_OBJECT (t)->reserved = NULL; + triangle_next (replace_vertex (t, e, v, with), v, with); + } + i = i->next; + } +} + +/** + * gts_vertex_is_contact: + * @v: a #GtsVertex. + * @sever: if %TRUE and if @v is a contact vertex between two or more + * sets of connected triangles replaces it with as many vertices, + * clones of @v. + * + * Returns: the number of sets of connected triangles sharing @v as a + * contact vertex. + */ +guint gts_vertex_is_contact (GtsVertex * v, gboolean sever) +{ + GSList * triangles, * i; + GtsVertex * with = v; + guint ncomponent = 0; + + g_return_val_if_fail (v != NULL, 0); + + triangles = gts_vertex_triangles (v, NULL); + i = triangles; + while (i) { + GTS_OBJECT (i->data)->reserved = i; + i = i->next; + } + + i = triangles; + while (i) { + GtsTriangle * t = i->data; + if (GTS_OBJECT (t)->reserved) { + GtsEdge * e; + if (ncomponent && sever) + with = GTS_VERTEX (gts_object_clone (GTS_OBJECT (v))); + GTS_OBJECT (t)->reserved = NULL; + e = replace_vertex (t, NULL, v, with); + triangle_next (e, v, with); + triangle_next (replace_vertex (t, e, v, with), v, with); + ncomponent++; + } + i = i->next; + } + g_slist_free (triangles); + + return ncomponent; +} + +/* GtsVertexNormal: Object */ + +static void vertex_normal_attributes (GtsVertex * v, + GtsObject * e, + GtsObject * t) +{ + g_return_if_fail (GTS_IS_EDGE (e)); + g_return_if_fail (GTS_IS_TRIANGLE (t)); + + if (GTS_IS_VERTEX_NORMAL (GTS_SEGMENT (e)->v1) && + GTS_IS_VERTEX_NORMAL (GTS_SEGMENT (e)->v2)) { + GtsPoint * p1 = GTS_POINT (GTS_SEGMENT (e)->v1); + GtsPoint * p2 = GTS_POINT (GTS_SEGMENT (e)->v2); + GtsPoint * p = GTS_POINT (v); + gdouble a, b, lambda; + guint i; + + a = p2->x - p1->x; b = p->x - p1->x; + if (fabs (p2->y - p1->y) > fabs (a)) { + a = p2->y - p1->y; b = p->y - p1->y; + } + if (fabs (p2->z - p1->z) > fabs (a)) { + a = p2->z - p1->z; b = p->z - p1->z; + } + lambda = a != 0. ? b/a : 0.; + for (i = 0; i < 3; i++) + GTS_VERTEX_NORMAL (v)->n[i] = + (1. - lambda)*GTS_VERTEX_NORMAL (GTS_SEGMENT (e)->v1)->n[i] + + lambda*GTS_VERTEX_NORMAL (GTS_SEGMENT (e)->v2)->n[i]; + } + else { + GtsVertex * v1, * v2, * v3; + + gts_triangle_vertices (GTS_TRIANGLE (t), &v1, &v2, &v3); + if (GTS_IS_VERTEX_NORMAL (v1) && + GTS_IS_VERTEX_NORMAL (v2) && + GTS_IS_VERTEX_NORMAL (v3)) { + GtsVector a1, a2, a3, det; + guint i, j = 0; + gdouble l1, l2; + + gts_vector_init (a1, GTS_POINT (v1), GTS_POINT (v)); + gts_vector_init (a2, GTS_POINT (v1), GTS_POINT (v2)); + gts_vector_init (a3, GTS_POINT (v1), GTS_POINT (v3)); + gts_vector_cross (det, a2, a3); + if (fabs (det[1]) > fabs (det[0])) j = 1; + if (fabs (det[2]) > fabs (det[j])) j = 2; + if (det[j] == 0.) { + g_warning ("vertex_normal_attributes: det[%d] == 0.", j); + return; + } + switch (j) { + case 0: + l1 = (a1[1]*a3[2] - a1[2]*a3[1])/det[0]; + l2 = (a1[2]*a2[1] - a1[1]*a2[2])/det[0]; + break; + case 1: + l1 = (a1[2]*a3[0] - a1[0]*a3[2])/det[1]; + l2 = (a1[0]*a2[2] - a1[2]*a2[0])/det[1]; + break; + case 2: + l1 = (a1[0]*a3[1] - a1[1]*a3[0])/det[2]; + l2 = (a1[1]*a2[0] - a1[0]*a2[1])/det[2]; + break; + default: + l1 = l2 = 0.; + } + for (i = 0; i < 3; i++) + GTS_VERTEX_NORMAL (v)->n[i] = + GTS_VERTEX_NORMAL (v1)->n[i]*(1. - l1 - l2) + + GTS_VERTEX_NORMAL (v2)->n[i]*l1 + + GTS_VERTEX_NORMAL (v3)->n[i]*l2; + } + } +} + +static void gts_vertex_normal_class_init (GtsVertexClass * klass) +{ + klass->intersection_attributes = vertex_normal_attributes; +} + +GtsVertexClass * gts_vertex_normal_class (void) +{ + static GtsVertexClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo gts_vertex_normal_info = { + "GtsVertexNormal", + sizeof (GtsVertexNormal), + sizeof (GtsVertexClass), + (GtsObjectClassInitFunc) gts_vertex_normal_class_init, + (GtsObjectInitFunc) NULL, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_vertex_class ()), + >s_vertex_normal_info); + } + + return klass; +} + +/* GtsColorVertex: Object */ + +GtsVertexClass * gts_color_vertex_class (void) +{ + static GtsVertexClass * klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo gts_color_vertex_info = { + "GtsColorVertex", + sizeof (GtsColorVertex), + sizeof (GtsVertexClass), + (GtsObjectClassInitFunc) NULL, + (GtsObjectInitFunc) NULL, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_vertex_class ()), + >s_color_vertex_info); + } + + return klass; +} + Index: toporouter/src_3rd/gts/vopt.c =================================================================== --- toporouter/src_3rd/gts/vopt.c (nonexistent) +++ toporouter/src_3rd/gts/vopt.c (revision 6803) @@ -0,0 +1,521 @@ +/* GTS - Library for the manipulation of triangulated surfaces + * Copyright (C) 1999 Stéphane Popinet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "gts.h" + +/* #define DEBUG_VOPT */ + +/* compute the normal (nx, ny, nz) as the cross-product of the first two + oriented edges and the norm nt = |t| as (v1xv2).v3 */ +static void triangle_normal (GtsTriangle * t, + gdouble * nx, + gdouble * ny, + gdouble * nz, + gdouble * nt) +{ + GtsPoint * p1, * p2 = NULL, * p3 = NULL; + gdouble x1, y1, z1, x2, y2, z2; + + g_return_if_fail (t != NULL); + + p1 = GTS_POINT (GTS_SEGMENT (t->e1)->v1); + if (GTS_SEGMENT (t->e1)->v1 == GTS_SEGMENT (t->e2)->v1) { + p2 = GTS_POINT (GTS_SEGMENT (t->e2)->v2); + p3 = GTS_POINT (GTS_SEGMENT (t->e1)->v2); + } + else if (GTS_SEGMENT (t->e1)->v2 == GTS_SEGMENT (t->e2)->v2) { + p2 = GTS_POINT (GTS_SEGMENT (t->e1)->v2); + p3 = GTS_POINT (GTS_SEGMENT (t->e2)->v1); + } + else if (GTS_SEGMENT (t->e1)->v1 == GTS_SEGMENT (t->e2)->v2) { + p2 = GTS_POINT (GTS_SEGMENT (t->e2)->v1); + p3 = GTS_POINT (GTS_SEGMENT (t->e1)->v2); + } + else if (GTS_SEGMENT (t->e1)->v2 == GTS_SEGMENT (t->e2)->v1) { + p2 = GTS_POINT (GTS_SEGMENT (t->e1)->v2); + p3 = GTS_POINT (GTS_SEGMENT (t->e2)->v2); + } + else + g_assert_not_reached (); + + x1 = p2->x - p1->x; + y1 = p2->y - p1->y; + z1 = p2->z - p1->z; + + x2 = p3->x - p1->x; + y2 = p3->y - p1->y; + z2 = p3->z - p1->z; + + *nt = ((p1->y*p2->z - p1->z*p2->y)*p3->x + + (p1->z*p2->x - p1->x*p2->z)*p3->y + + (p1->x*p2->y - p1->y*p2->x)*p3->z); + *nx = y1*z2 - z1*y2; + *ny = z1*x2 - x1*z2; + *nz = x1*y2 - y1*x2; +} + +static void boundary_preservation (GtsEdge * edge, + GtsFace * f, + GtsVector e1, GtsVector e2, + GtsMatrix * H, GtsVector c) +{ + GtsTriangle * t = GTS_TRIANGLE (f); + GtsEdge * edge2; + GtsVertex * v1 = GTS_SEGMENT (edge)->v1, * v2 = GTS_SEGMENT (edge)->v2; + GtsPoint * p1, * p2; + GtsVector e, e3; + + /* find orientation of segment */ + edge2 = edge == t->e1 ? t->e2 : edge == t->e2 ? t->e3 : t->e1; + if (v2 != GTS_SEGMENT (edge2)->v1 && v2 != GTS_SEGMENT (edge2)->v2) { + v2 = v1; v1 = GTS_SEGMENT (edge)->v2; + } + p1 = GTS_POINT (v1); + p2 = GTS_POINT (v2); + + e[0] = p2->x - p1->x; + e[1] = p2->y - p1->y; + e[2] = p2->z - p1->z; + + e1[0] += e[0]; + e1[1] += e[1]; + e1[2] += e[2]; + + e3[0] = p2->y*p1->z - p2->z*p1->y; + e3[1] = p2->z*p1->x - p2->x*p1->z; + e3[2] = p2->x*p1->y - p2->y*p1->x; + + e2[0] += e3[0]; + e2[1] += e3[1]; + e2[2] += e3[2]; + + H[0][0] += e[1]*e[1] + e[2]*e[2]; + H[0][1] -= e[0]*e[1]; + H[0][2] -= e[0]*e[2]; + H[1][0] = H[0][1]; + H[1][1] += e[0]*e[0] + e[2]*e[2]; + H[1][2] -= e[1]*e[2]; + H[2][0] = H[0][2]; + H[2][1] = H[1][2]; + H[2][2] += e[0]*e[0] + e[1]*e[1]; + + c[0] += e[1]*e3[2] - e[2]*e3[1]; + c[1] += e[2]*e3[0] - e[0]*e3[2]; + c[2] += e[0]*e3[1] - e[1]*e3[0]; +} + +static gdouble boundary_cost (GtsEdge * edge, + GtsFace * f, + GtsVertex * v) +{ + GtsTriangle * t = GTS_TRIANGLE (f); + GtsEdge * edge2; + GtsVertex * v1 = GTS_SEGMENT (edge)->v1, * v2 = GTS_SEGMENT (edge)->v2; + GtsPoint * p1, * p2; + GtsVector e; + GtsPoint * p = GTS_POINT (v); + + /* find orientation of segment */ + edge2 = edge == t->e1 ? t->e2 : edge == t->e2 ? t->e3 : t->e1; + if (v2 != GTS_SEGMENT (edge2)->v1 && v2 != GTS_SEGMENT (edge2)->v2) { + v2 = v1; v1 = GTS_SEGMENT (edge)->v2; + } + p1 = GTS_POINT (v1); + p2 = GTS_POINT (v2); + + e[0] = (p2->y - p1->y)*(p->z - p2->z) - (p2->z - p1->z)*(p->y - p2->y); + e[1] = (p2->z - p1->z)*(p->x - p2->x) - (p2->x - p1->x)*(p->z - p2->z); + e[2] = (p2->x - p1->x)*(p->y - p2->y) - (p2->y - p1->y)*(p->x - p2->x); + + return e[0]*e[0] + e[1]*e[1] + e[2]*e[2]; +} + +static gdouble edge_boundary_cost (GtsEdge * e, GtsVertex * v) +{ + gdouble cost = 0.; + GSList * i; + + i = GTS_SEGMENT (e)->v1->segments; + while (i) { + GtsFace * f; + if (GTS_IS_EDGE (i->data) && + (f = gts_edge_is_boundary (i->data, NULL))) + cost += boundary_cost (i->data, f, v); + i = i->next; + } + i = GTS_SEGMENT (e)->v2->segments; + while (i) { + GtsFace * f; + if (i->data != e && + GTS_IS_EDGE (i->data) && + (f = gts_edge_is_boundary (i->data, NULL))) + cost += boundary_cost (i->data, f, v); + i = i->next; + } + + return cost/4.; +} + +static gdouble edge_volume_cost (GtsEdge * e, GtsVertex * v) +{ + GSList * i, * triangles; + gdouble n1, n2, n3, nt; + gdouble cost = 0.0, a; + + triangles = gts_vertex_triangles (GTS_SEGMENT (e)->v1, NULL); + triangles = gts_vertex_triangles (GTS_SEGMENT (e)->v2, triangles); + + i = triangles; + while (i) { + if (GTS_IS_FACE (i->data)) { + triangle_normal (i->data, &n1, &n2, &n3, &nt); + a = GTS_POINT (v)->x*n1 + + GTS_POINT (v)->y*n2 + + GTS_POINT (v)->z*n3 - nt; + cost += a*a; + } + i = i->next; + } + g_slist_free (triangles); + + return cost/36.; +} + +static gdouble edge_shape_cost (GtsEdge * e, GtsVertex * v) +{ + GSList * list, * i; + GtsVertex + * v1 = GTS_SEGMENT (e)->v1, + * v2 = GTS_SEGMENT (e)->v2; + gdouble cost = 0.; + + list = gts_vertex_neighbors (v1, NULL, NULL); + list = gts_vertex_neighbors (v2, list, NULL); + i = list; + while (i) { + GtsPoint * p = i->data; + if (p != GTS_POINT (v1) && p != GTS_POINT (v2)) + cost += gts_point_distance2 (p, GTS_POINT (v)); + i = i->next; + } + g_slist_free (list); + + return cost; +} + +/** + * gts_volume_optimized_vertex: + * @edge: a #GtsEdge. + * @klass: a #GtsVertexClass to be used for the new vertex. + * @params: a #GtsVolumeOptimizedParms. + * + * Returns: a #GtsVertex which can be used to replace @edge for an + * edge collapse operation. The position of the vertex is optimized in + * order to minimize the changes in area and volume for the surface + * using @edge. The volume enclosed by the surface is locally + * preserved. For more details see "Fast and memory efficient + * polygonal simplification" (1998) and "Evaluation of memoryless + * simplification" (1999) by Lindstrom and Turk. + */ +GtsVertex * gts_volume_optimized_vertex (GtsEdge * edge, + GtsVertexClass * klass, + GtsVolumeOptimizedParams * params) +{ + GSList * triangles, * i; + gdouble sn1 = 0., sn2 = 0., sn3 = 0.; + gdouble sn11 = 0., sn22 = 0., sn33 = 0.; + gdouble sn12 = 0., sn13 = 0., sn23 = 0.; + gdouble st = 0., stn1 = 0., stn2 = 0., stn3 = 0.; + gdouble n1, n2, n3, nt; + GtsMatrix * A, * Ai; + GtsVector A1, b; + GtsVector e1 = {0., 0., 0.}, e2 = {0., 0., 0.}; + GtsMatrix * Hb; + GtsVector cb = {0., 0., 0.}; + GtsVertex * v; + GtsVertex * v1, * v2; + guint n = 0, nb = 0; +#ifdef DEBUG_VOPT + guint nold = 0; +#endif + + g_return_val_if_fail (edge != NULL, NULL); + g_return_val_if_fail (klass != NULL, NULL); + g_return_val_if_fail (params != NULL, NULL); + + A = gts_matrix_zero (NULL); + Hb = gts_matrix_zero (NULL); + v1 = GTS_SEGMENT (edge)->v1; + v2 = GTS_SEGMENT (edge)->v2; + + /* boundary preservation */ + i = v1->segments; + while (i) { + GtsEdge * edge1 = i->data; + GtsFace * f; + if (GTS_IS_EDGE (edge1) && + (f = gts_edge_is_boundary (edge1, NULL))) { + boundary_preservation (edge1, f, e1, e2, Hb, cb); + nb++; + } + i = i->next; + } + i = v2->segments; + while (i) { + GtsEdge * edge1 = i->data; + GtsFace * f; + if (edge1 != edge && + GTS_IS_EDGE (edge1) && + (f = gts_edge_is_boundary (edge1, NULL))) { + boundary_preservation (edge1, f, e1, e2, Hb, cb); + nb++; + } + i = i->next; + } + if (nb > 0) { + GtsMatrix * H = gts_matrix_new ( + e1[2]*e1[2] + e1[1]*e1[1], - e1[0]*e1[1], - e1[0]*e1[2], 0., + - e1[0]*e1[1], e1[2]*e1[2] + e1[0]*e1[0], - e1[1]*e1[2], 0., + - e1[0]*e1[2], - e1[1]*e1[2], e1[1]*e1[1] + e1[0]*e1[0], 0., + 0., 0., 0., 0.); + GtsVector c; + + c[0] = e1[1]*e2[2] - e1[2]*e2[1]; + c[1] = e1[2]*e2[0] - e1[0]*e2[2]; + c[2] = e1[0]*e2[1] - e1[1]*e2[0]; + n = gts_matrix_quadratic_optimization (A, b, n, H, c); + gts_matrix_destroy (H); + } + + g_assert (n <= 2); + +#ifdef DEBUG_VOPT + if (n != nold) { + fprintf (stderr, "--- boundary preservation ---\n"); + gts_matrix_print (A, stderr); + gts_vector_print (b, stderr); + nold = n; + } +#endif + + /* volume preservation */ + triangles = gts_vertex_triangles (v1, NULL); + triangles = gts_vertex_triangles (v2, triangles); + + i = triangles; + while (i) { + if (GTS_IS_FACE (i->data)) { + triangle_normal (i->data, &n1, &n2, &n3, &nt); + sn1 += n1; sn2 += n2; sn3 += n3; + sn11 += n1*n1; sn22 += n2*n2; sn33 += n3*n3; + sn12 += n1*n2; sn13 += n1*n3; sn23 += n2*n3; + st += nt; stn1 += nt*n1; stn2 += nt*n2; stn3 += nt*n3; + } + i = i->next; + } + g_slist_free (triangles); + + A1[0] = sn1; A1[1] = sn2; A1[2] = sn3; + n = gts_matrix_compatible_row (A, b, n, A1, st); + +#ifdef DEBUG_VOPT + if (n != nold) { + fprintf (stderr, "--- volume preservation ---\n"); + gts_matrix_print (A, stderr); + gts_vector_print (b, stderr); + nold = n; + } +#endif + +#if 1 /* Weighted average of volume and boundary optimization */ + if (n < 3) { + /* volume optimization and boundary optimization */ + GtsMatrix * H = gts_matrix_new (sn11, sn12, sn13, 0., + sn12, sn22, sn23, 0., + sn13, sn23, sn33, 0., + 0., 0., 0., 0.); + GtsVector c; + gdouble le = 9.*params->boundary_weight* + gts_point_distance2 (GTS_POINT (v1), + GTS_POINT (v2)); + guint i, j; + + c[0] = - stn1; c[1] = - stn2; c[2] = - stn3; + if (nb > 0) + for (i = 0; i < 3; i++) { + for (j = 0; j < 3; j++) + H[i][j] = params->volume_weight*H[i][j] + le*Hb[i][j]; + c[i] = params->volume_weight*c[i] + le*cb[i]; + } + n = gts_matrix_quadratic_optimization (A, b, n, H, c); + gts_matrix_destroy (H); + } + +#ifdef DEBUG_VOPT + if (n != nold) { + fprintf (stderr, "--- volume and boundary optimization ---\n"); + gts_matrix_print (A, stderr); + gts_vector_print (b, stderr); + nold = n; + } +#endif + + if (n < 3) { + /* triangle shape optimization */ + gdouble nv = 0.0; + GtsMatrix * H; + GtsVector c = {0., 0., 0.}; + GSList * list, * i; + + list = gts_vertex_neighbors (v1, NULL, NULL); + list = gts_vertex_neighbors (v2, list, NULL); + + i = list; + while (i) { + GtsPoint * p1 = i->data; + if (p1 != GTS_POINT (v1) && p1 != GTS_POINT (v2)) { + nv += 1.0; + c[0] -= p1->x; + c[1] -= p1->y; + c[2] -= p1->z; + } + i = i->next; + } + g_slist_free (list); + + H = gts_matrix_new (nv, 0., 0., 0., + 0., nv, 0., 0., + 0., 0., nv, 0., + 0., 0., 0., 0.); + n = gts_matrix_quadratic_optimization (A, b, n, H, c); + gts_matrix_destroy (H); + } + +#ifdef DEBUG_VOPT + if (n != nold) { + fprintf (stderr, "--- triangle shape optimization ---\n"); + gts_matrix_print (A, stderr); + gts_vector_print (b, stderr); + nold = n; + } +#endif +#else /* Weighted average of volume, boundary and shape optimization */ + if (n < 3) { + /* volume optimization, boundary and shape optimization */ + GtsMatrix * H; + GtsVector c; + gdouble l2 = gts_point_distance2 (GTS_POINT (v1), + GTS_POINT (v2)); + gdouble wv = params->volume_weight/32.; + gdouble wb = params->boundary_weight/4.*l2; + gdouble ws = params->shape_weight*l2*l2; + + gdouble nv = 0.0; + GtsVector cs = {0., 0., 0.}; + GSList * list, * i; + + list = gts_vertex_neighbors (v1, NULL, NULL); + list = gts_vertex_neighbors (v2, list, NULL); + + i = list; + while (i) { + GtsPoint * p1 = i->data; + if (p1 != GTS_POINT (v1) && p1 != GTS_POINT (v2)) { + nv += 1.0; + cs[0] -= p1->x; + cs[1] -= p1->y; + cs[2] -= p1->z; + } + i = i->next; + } + g_slist_free (list); + + H = gts_matrix_new (wv*sn11 + wb*Hb[0][0] + ws*nv, + wv*sn12 + wb*Hb[0][1], + wv*sn13 + wb*Hb[0][2], + wv*sn12 + wb*Hb[1][0], + wv*sn22 + wb*Hb[1][1] + ws*nv, + wv*sn23 + wb*Hb[1][2], + wv*sn13 + wb*Hb[2][0], + wv*sn23 + wb*Hb[2][1], + wv*sn33 + wb*Hb[2][2] + ws*nv); + + c[0] = - wv*stn1 + wb*cb[0] + ws*cs[0]; + c[1] = - wv*stn2 + wb*cb[1] + ws*cs[1]; + c[2] = - wv*stn3 + wb*cb[2] + ws*cs[2]; + + n = gts_matrix_quadratic_optimization (A, b, n, H, c); + gts_matrix_destroy (H); + } + +#ifdef DEBUG_VOPT + if (n != nold) { + fprintf (stderr, "--- volume, boundary and shape optimization ---\n"); + gts_matrix_print (A, stderr); + gts_vector_print (b, stderr); + nold = n; + } +#endif +#endif /* Weighted average of volume, boundary and shape optimization */ + + g_assert (n == 3); + g_assert ((Ai = gts_matrix3_inverse (A))); + + v = gts_vertex_new (klass, + Ai[0][0]*b[0] + Ai[0][1]*b[1] + Ai[0][2]*b[2], + Ai[1][0]*b[0] + Ai[1][1]*b[1] + Ai[1][2]*b[2], + Ai[2][0]*b[0] + Ai[2][1]*b[1] + Ai[2][2]*b[2]); + + gts_matrix_destroy (A); + gts_matrix_destroy (Ai); + gts_matrix_destroy (Hb); + + return v; +} + +/** + * gts_volume_optimized_cost: + * @e: a #GtsEdge. + * @params: a #GtsVolumeOptimizedParams. + * + * Returns: the cost for the collapse of @e as minimized by the function + * gts_volume_optimized_vertex(). + */ +gdouble gts_volume_optimized_cost (GtsEdge * e, + GtsVolumeOptimizedParams * params) +{ + GtsVertex * v; + gdouble cost; + gdouble length2; + + g_return_val_if_fail (e != NULL, G_MAXDOUBLE); + g_return_val_if_fail (params != NULL, G_MAXDOUBLE); + + v = gts_volume_optimized_vertex (e, gts_vertex_class (), params); + + length2 = gts_point_distance2 (GTS_POINT (GTS_SEGMENT (e)->v1), + GTS_POINT (GTS_SEGMENT (e)->v2)); + cost = + params->volume_weight*edge_volume_cost (e, v) + + params->boundary_weight*length2*edge_boundary_cost (e, v) + + params->shape_weight*length2*length2*edge_shape_cost (e, v); + gts_object_destroy (GTS_OBJECT (v)); + + return cost; +} Index: toporouter/src_plugins/toporouter/Makefile =================================================================== --- toporouter/src_plugins/toporouter/Makefile (nonexistent) +++ toporouter/src_plugins/toporouter/Makefile (revision 6803) @@ -0,0 +1,6 @@ +all: + cd ../../src && $(MAKE) mod_toporouter + +clean: + rm *.o *.so 2>/dev/null ; true + Index: toporouter/src_plugins/toporouter/Plug.tmpasm =================================================================== --- toporouter/src_plugins/toporouter/Plug.tmpasm (nonexistent) +++ toporouter/src_plugins/toporouter/Plug.tmpasm (revision 6803) @@ -0,0 +1,28 @@ +put /local/pcb/mod {toporouter} +put /local/pcb/mod/OBJS [@ $(PLUGDIR)/toporouter/toporouter.o @] + + +put /local/pcb/toporouter_rules [@ +../src_3rd/gts/libgts.a: + cd ../src_3rd/gts && make +@] + +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 + end + case {disable} + put /local/pcb/mod/OBJS {} + include /local/pcb/tmpasm/disable + end +end Index: toporouter/src_plugins/toporouter/README =================================================================== --- toporouter/src_plugins/toporouter/README (nonexistent) +++ toporouter/src_plugins/toporouter/README (revision 6803) @@ -0,0 +1,7 @@ +Automatically route selected or all rats using a topological algorithm. This +is the new autorouter from 2009. + +#state: fails +#lstate: infinite loop in gts +#default: disabled +#implements: (feature) Index: toporouter/src_plugins/toporouter/toporouter.c =================================================================== --- toporouter/src_plugins/toporouter/toporouter.c (nonexistent) +++ toporouter/src_plugins/toporouter/toporouter.c (revision 6803) @@ -0,0 +1,8249 @@ +/* + * COPYRIGHT + * + * Topological Autorouter for + * PCB, interactive printed circuit board design + * Copyright (C) 2009 Anthony Blake + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Contact addresses for email: + * Anthony Blake, tonyb33@gmail.com + * + * + * + * This is *EXPERIMENTAL* code. + * + * As the code is experimental, the algorithms and code + * are likely to change. Which means it isn't documented + * or optimized. If you would like to learn about Topological + * Autorouters, the following papers are good starting points: + * + * This file implements a topological autorouter, and uses techniques from the + * following publications: + * + * Dayan, T. and Dai, W.W.M., "Layer Assignment for a Rubber Band Router" Tech + * Report UCSC-CRL-92-50, Univ. of California, Santa Cruz, 1992. + * + * Dai, W.W.M and Dayan, T. and Staepelaere, D., "Topological Routing in SURF: + * Generating a Rubber-Band Sketch" Proc. 28th ACM/IEEE Design Automation + * Conference, 1991, pp. 39-44. + * + * David Staepelaere, Jeffrey Jue, Tal Dayan, Wayne Wei-Ming Dai, "SURF: + * Rubber-Band Routing System for Multichip Modules," IEEE Design and Test of + * Computers ,vol. 10, no. 4, pp. 18-26, October/December, 1993. + * + * Dayan, T., "Rubber-band based topological router" PhD Thesis, Univ. of + * California, Santa Cruz, 1997. + * + * David Staepelaere, "Geometric transformations for a rubber-band sketch" + * Master's thesis, Univ. of California, Santa Cruz, September 1992. + * + */ + +#include "config.h" +#include "toporouter.h" +#include "pcb-printf.h" +#include "compat_nls.h" + +static void toporouter_edge_init(toporouter_edge_t * edge) +{ + edge->routing = NULL; + edge->flags = 0; +} + +toporouter_edge_class_t *toporouter_edge_class(void) +{ + static toporouter_edge_class_t *klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo constraint_info = { + "toporouter_edge_t", + sizeof(toporouter_edge_t), + sizeof(toporouter_edge_class_t), + (GtsObjectClassInitFunc) NULL, + (GtsObjectInitFunc) toporouter_edge_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = (toporouter_edge_class_t *) gts_object_class_new(GTS_OBJECT_CLASS(gts_edge_class()), &constraint_info); + } + + return klass; +} + +static void toporouter_bbox_init(toporouter_bbox_t * box) +{ + box->data = NULL; + box->type = OTHER; + box->constraints = NULL; + box->cluster = NULL; +} + +toporouter_bbox_class_t *toporouter_bbox_class(void) +{ + static toporouter_bbox_class_t *klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo constraint_info = { + "toporouter_bbox_t", + sizeof(toporouter_bbox_t), + sizeof(toporouter_bbox_class_t), + (GtsObjectClassInitFunc) NULL, + (GtsObjectInitFunc) toporouter_bbox_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = (toporouter_bbox_class_t *) gts_object_class_new(GTS_OBJECT_CLASS(gts_bbox_class()), &constraint_info); + } + + return klass; +} + +static void toporouter_vertex_class_init(toporouter_vertex_class_t * klass) +{ + +} + +static void toporouter_vertex_init(toporouter_vertex_t * vertex) +{ + vertex->bbox = NULL; + vertex->parent = NULL; + vertex->child = NULL; + vertex->flags = 0; + vertex->routingedge = NULL; + vertex->arc = NULL; + vertex->oproute = NULL; + vertex->route = NULL; + + vertex->gcost = 0.; + vertex->hcost = 0.; + vertex->gn = 0; +} + +toporouter_vertex_class_t *toporouter_vertex_class(void) +{ + static toporouter_vertex_class_t *klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo constraint_info = { + "toporouter_vertex_t", + sizeof(toporouter_vertex_t), + sizeof(toporouter_vertex_class_t), + (GtsObjectClassInitFunc) toporouter_vertex_class_init, + (GtsObjectInitFunc) toporouter_vertex_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = (toporouter_vertex_class_t *) gts_object_class_new(GTS_OBJECT_CLASS(gts_vertex_class()), &constraint_info); + } + + return klass; +} + +static void toporouter_constraint_class_init(toporouter_constraint_class_t * klass) +{ + +} + +static void toporouter_constraint_init(toporouter_constraint_t * constraint) +{ + constraint->box = NULL; + constraint->routing = NULL; +} + +toporouter_constraint_class_t *toporouter_constraint_class(void) +{ + static toporouter_constraint_class_t *klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo constraint_info = { + "toporouter_constraint_t", + sizeof(toporouter_constraint_t), + sizeof(toporouter_constraint_class_t), + (GtsObjectClassInitFunc) toporouter_constraint_class_init, + (GtsObjectInitFunc) toporouter_constraint_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = (toporouter_constraint_class_t *) gts_object_class_new(GTS_OBJECT_CLASS(gts_constraint_class()), &constraint_info); + } + + return klass; +} + +static void toporouter_arc_init(toporouter_arc_t * arc) +{ + arc->x0 = -1.; + arc->y0 = -1.; + arc->x1 = -1.; + arc->y1 = -1.; + arc->centre = NULL; + arc->v = NULL; + arc->v1 = NULL; + arc->v2 = NULL; + arc->r = -1.; + arc->dir = 31337; + arc->clearance = NULL; + arc->oproute = NULL; +} + +toporouter_arc_class_t *toporouter_arc_class(void) +{ + static toporouter_arc_class_t *klass = NULL; + + if (klass == NULL) { + GtsObjectClassInfo constraint_info = { + "toporouter_arc_t", + sizeof(toporouter_arc_t), + sizeof(toporouter_arc_class_t), + (GtsObjectClassInitFunc) NULL, + (GtsObjectInitFunc) toporouter_arc_init, + (GtsArgSetFunc) NULL, + (GtsArgGetFunc) NULL + }; + klass = (toporouter_arc_class_t *) gts_object_class_new(GTS_OBJECT_CLASS(gts_constraint_class()), &constraint_info); + } + + return klass; +} + +#define MARGIN 10.0f + +drawing_context_t *toporouter_output_init(int w, int h, char *filename) +{ + drawing_context_t *dc; + + dc = (drawing_context_t *) malloc(sizeof(drawing_context_t)); + + dc->iw = w; + dc->ih = h; + dc->filename = filename; + + /* Calculate scaling to maintain aspect ratio */ + if (PCB->MaxWidth > PCB->MaxHeight) { + /* Scale board width to match image width minus 2xMARGIN */ + dc->s = ((double) dc->iw - (2 * MARGIN)) / (double) PCB->MaxWidth; + dc->ih = (double) PCB->MaxHeight * dc->s + (2 * MARGIN); + } + else { + /* Scale board height to match image height minus 2xMARGIN */ + dc->s = ((double) dc->ih - (2 * MARGIN)) / (double) PCB->MaxHeight; + dc->iw = (double) PCB->MaxWidth * dc->s + (2 * MARGIN); + } + +#if TOPO_OUTPUT_ENABLED + dc->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, dc->iw, dc->ih); + dc->cr = cairo_create(dc->surface); + + cairo_rectangle(dc->cr, 0, 0, dc->iw, dc->ih); + cairo_set_source_rgb(dc->cr, 0, 0, 0); + cairo_fill(dc->cr); + +#endif + + return dc; +} + +void toporouter_output_close(drawing_context_t * dc) +{ +#if TOPO_OUTPUT_ENABLED + cairo_surface_write_to_png(dc->surface, dc->filename); + cairo_destroy(dc->cr); + cairo_surface_destroy(dc->surface); +#endif +} + +gdouble lookup_clearance(char *name) +{ + if (name) { + int idx = pcb_route_style_lookup(&PCB->RouteStyle, 0, 0, 0, 0, menu->Style); + if (idx >= 0) + return PCB->RouteStyle.array[idx].Clearance; + } + return Settings.Clearance; +} + +gdouble lookup_thickness(char *name) +{ + if (name) { + int idx = pcb_route_style_lookup(&PCB->RouteStyle, 0, 0, 0, 0, menu->Style); + if (idx >= 0) + return PCB->RouteStyle.array[idx].Thick; + } + return Settings.LineThickness; +} + +static inline gdouble cluster_clearance(toporouter_cluster_t * cluster) +{ + if (cluster) + return lookup_clearance(cluster->netlist->style); + return lookup_clearance(NULL); +} + +static inline gdouble cluster_thickness(toporouter_cluster_t * cluster) +{ + if (cluster) + return lookup_thickness(cluster->netlist->style); + return lookup_thickness(NULL); +} + +gint toporouter_draw_vertex(gpointer item, gpointer data) +{ +#if TOPO_OUTPUT_ENABLED + drawing_context_t *dc = (drawing_context_t *) data; + toporouter_vertex_t *tv; + pcb_pin_t *pin; + pcb_pad_t *pad; + gdouble blue; + + if (TOPOROUTER_IS_VERTEX((GtsObject *) item)) { + tv = TOPOROUTER_VERTEX((GtsObject *) item); + + if (tv->flags & VERTEX_FLAG_RED) { + cairo_set_source_rgba(dc->cr, 1., 0., 0., 0.8f); + cairo_arc(dc->cr, tv->v.p.x * dc->s + MARGIN, tv->v.p.y * dc->s + MARGIN, 500. * dc->s, 0, 2 * M_PI); + cairo_fill(dc->cr); + + } + else if (tv->flags & VERTEX_FLAG_GREEN) { + cairo_set_source_rgba(dc->cr, 0., 1., 0., 0.8f); + cairo_arc(dc->cr, tv->v.p.x * dc->s + MARGIN, tv->v.p.y * dc->s + MARGIN, 500. * dc->s, 0, 2 * M_PI); + cairo_fill(dc->cr); + + } + else if (tv->flags & VERTEX_FLAG_BLUE) { + cairo_set_source_rgba(dc->cr, 0., 0., 1., 0.8f); + cairo_arc(dc->cr, tv->v.p.x * dc->s + MARGIN, tv->v.p.y * dc->s + MARGIN, 500. * dc->s, 0, 2 * M_PI); + cairo_fill(dc->cr); + + + } + /* printf("tv->type = %d\n", tv->type); */ + if (!dc->mode) { + if (tv->bbox) { + pin = (pcb_pin_t *) tv->bbox->data; + pad = (pcb_pad_t *) tv->bbox->data; + + blue = 0.0f; + switch (tv->bbox->type) { + case PIN: + cairo_set_source_rgba(dc->cr, 1.0f, 0., 0.0f, 0.2f); + cairo_arc(dc->cr, + tv->v.p.x * dc->s + MARGIN, + tv->v.p.y * dc->s + MARGIN, + (((gdouble) pin->Thickness / 2.0f) + (gdouble) lookup_clearance(pin->Name)) * dc->s, 0, 2 * M_PI); + cairo_fill(dc->cr); + + cairo_set_source_rgba(dc->cr, 1.0f, 0., 0., 0.4f); + cairo_arc(dc->cr, + tv->v.p.x * dc->s + MARGIN, + tv->v.p.y * dc->s + MARGIN, (gdouble) (pin->Thickness) / 2.0f * dc->s, 0, 2 * M_PI); + cairo_fill(dc->cr); + + break; + case VIA: + cairo_set_source_rgba(dc->cr, 0.0f, 0., 1., 0.2f); + cairo_arc(dc->cr, + tv->v.p.x * dc->s + MARGIN, + tv->v.p.y * dc->s + MARGIN, + (((gdouble) pin->Thickness / 2.0f) + (gdouble) lookup_clearance(pin->Name)) * dc->s, 0, 2 * M_PI); + cairo_fill(dc->cr); + + cairo_set_source_rgba(dc->cr, 0.0f, 0., 1., 0.4f); + cairo_arc(dc->cr, + tv->v.p.x * dc->s + MARGIN, + tv->v.p.y * dc->s + MARGIN, (gdouble) (pin->Thickness) / 2.0f * dc->s, 0, 2 * M_PI); + cairo_fill(dc->cr); + + break; + case PAD: + cairo_set_source_rgba(dc->cr, 0.0f, 1., 0., 0.5f); + cairo_arc(dc->cr, tv->v.p.x * dc->s + MARGIN, tv->v.p.y * dc->s + MARGIN, 400. * dc->s, 0, 2 * M_PI); + cairo_fill(dc->cr); + + break; + default: + break; + } + } + } + else { + if (tv->flags & VERTEX_FLAG_BLUE) { + cairo_set_source_rgba(dc->cr, 0., 0., 1., 0.8f); + cairo_arc(dc->cr, tv->v.p.x * dc->s + MARGIN, tv->v.p.y * dc->s + MARGIN, 500. * dc->s, 0, 2 * M_PI); + cairo_fill(dc->cr); + } + else if (tv->flags & VERTEX_FLAG_RED) { + cairo_set_source_rgba(dc->cr, 1., 0., 0., 0.8f); + cairo_arc(dc->cr, tv->v.p.x * dc->s + MARGIN, tv->v.p.y * dc->s + MARGIN, 500. * dc->s, 0, 2 * M_PI); + cairo_fill(dc->cr); + + } + else if (tv->flags & VERTEX_FLAG_GREEN) { + cairo_set_source_rgba(dc->cr, 0., 1., 0., 0.8f); + cairo_arc(dc->cr, tv->v.p.x * dc->s + MARGIN, tv->v.p.y * dc->s + MARGIN, 500. * dc->s, 0, 2 * M_PI); + cairo_fill(dc->cr); + } + + } + } + else { + fprintf(stderr, "Unknown data passed to toporouter_draw_vertex, aborting foreach\n"); + return -1; + } + return 0; +#else + return -1; +#endif +} + +gint toporouter_draw_edge(gpointer item, gpointer data) +{ +#if TOPO_OUTPUT_ENABLED + drawing_context_t *dc = (drawing_context_t *) data; + toporouter_edge_t *te; + toporouter_constraint_t *tc; + + if (TOPOROUTER_IS_EDGE((GtsObject *) item)) { + te = TOPOROUTER_EDGE((GtsObject *) item); + cairo_set_source_rgba(dc->cr, 1.0f, 1.0f, 1.0f, 0.5f); + cairo_move_to(dc->cr, te->e.segment.v1->p.x * dc->s + MARGIN, te->e.segment.v1->p.y * dc->s + MARGIN); + cairo_line_to(dc->cr, te->e.segment.v2->p.x * dc->s + MARGIN, te->e.segment.v2->p.y * dc->s + MARGIN); + cairo_stroke(dc->cr); + } + else if (TOPOROUTER_IS_CONSTRAINT((GtsObject *) item)) { + tc = TOPOROUTER_CONSTRAINT((GtsObject *) item); + if (tc->box) { + switch (tc->box->type) { + case BOARD: + cairo_set_source_rgba(dc->cr, 1.0f, 0.0f, 1.0f, 0.9f); + cairo_move_to(dc->cr, tc->c.edge.segment.v1->p.x * dc->s + MARGIN, tc->c.edge.segment.v1->p.y * dc->s + MARGIN); + cairo_line_to(dc->cr, tc->c.edge.segment.v2->p.x * dc->s + MARGIN, tc->c.edge.segment.v2->p.y * dc->s + MARGIN); + cairo_stroke(dc->cr); + break; + case PIN: + case PAD: + cairo_set_source_rgba(dc->cr, 1.0f, 0.0f, 0.0f, 0.9f); + cairo_move_to(dc->cr, tc->c.edge.segment.v1->p.x * dc->s + MARGIN, tc->c.edge.segment.v1->p.y * dc->s + MARGIN); + cairo_line_to(dc->cr, tc->c.edge.segment.v2->p.x * dc->s + MARGIN, tc->c.edge.segment.v2->p.y * dc->s + MARGIN); + cairo_stroke(dc->cr); + break; + case LINE: + cairo_set_source_rgba(dc->cr, 0.0f, 1.0f, 0.0f, 0.9f); + cairo_move_to(dc->cr, tc->c.edge.segment.v1->p.x * dc->s + MARGIN, tc->c.edge.segment.v1->p.y * dc->s + MARGIN); + cairo_line_to(dc->cr, tc->c.edge.segment.v2->p.x * dc->s + MARGIN, tc->c.edge.segment.v2->p.y * dc->s + MARGIN); + cairo_stroke(dc->cr); + break; + + default: + cairo_set_source_rgba(dc->cr, 1.0f, 1.0f, 0.0f, 0.9f); + cairo_move_to(dc->cr, tc->c.edge.segment.v1->p.x * dc->s + MARGIN, tc->c.edge.segment.v1->p.y * dc->s + MARGIN); + cairo_line_to(dc->cr, tc->c.edge.segment.v2->p.x * dc->s + MARGIN, tc->c.edge.segment.v2->p.y * dc->s + MARGIN); + cairo_stroke(dc->cr); + break; + } + } + else { + printf("CONSTRAINT without box\n"); + + } + } + else { + fprintf(stderr, "Unknown data passed to toporouter_draw_edge, aborting foreach\n"); + return -1; + } + + return 0; +#else + return -1; +#endif +} + + +/*#define vertex_bbox(v) (v->bbox)*/ +toporouter_bbox_t *vertex_bbox(toporouter_vertex_t * v) +{ + return v ? v->bbox : NULL; +} + + +char *vertex_netlist(toporouter_vertex_t * v) +{ + toporouter_bbox_t *box = vertex_bbox(v); + + if (box && box->cluster) + return box->cluster->netlist->netlist; + + return NULL; +} + +char *constraint_netlist(toporouter_constraint_t * c) +{ + toporouter_bbox_t *box = c->box; + + if (box && box->cluster) + return box->cluster->netlist->netlist; + + return NULL; +} + +static inline guint epsilon_equals(gdouble a, gdouble b) +{ + if (a > b - EPSILON && a < b + EPSILON) + return 1; + return 0; +} + +void print_bbox(toporouter_bbox_t * box) +{ + printf("[BBOX "); + switch (box->type) { + case PAD: + printf("PAD "); + break; + case PIN: + printf("PIN "); + break; + case VIA: + printf("VIA "); + break; + case LINE: + printf("LINE "); + break; + case BOARD: + printf("BOARD "); + break; + case POLYGON: + printf("POLYGON "); + break; + default: + printf("UNKNOWN "); + break; + } + + if (box->point) + printf("P: %f,%f,%f ", vx(box->point), vy(box->point), vz(box->point)); + else + printf("P: NONE "); + + printf("LAYER: %d ", box->layer); + printf("CLUSTER: %d]\n", box->cluster ? box->cluster->c : -1); + +} + +void print_vertex(toporouter_vertex_t * v) +{ + if (v) + printf("[V %f,%f,%f ", vx(v), vy(v), vz(v)); + else + printf("[V (null) "); + + printf("%s ", vertex_netlist(v)); + if (v->route && v->route->netlist) + printf("%s ", v->route->netlist->netlist); + + if (v->routingedge) { + guint n = g_list_length(edge_routing(v->routingedge)); + guint pos = g_list_index(edge_routing(v->routingedge), v); + + if (TOPOROUTER_IS_CONSTRAINT(v->routingedge)) + printf("[CONST "); + else + printf("[EDGE "); + + printf("%d/%d] ", pos, n); + + } + + + if (v->flags & VERTEX_FLAG_TEMP) + printf("TEMP "); + if (v->flags & VERTEX_FLAG_ROUTE) + printf("ROUTE "); + if (v->flags & VERTEX_FLAG_SPECCUT) + printf("SPECCUT "); + if (v->flags & VERTEX_FLAG_FAKE) + printf("FAKE "); + + printf("]\n"); + +} + +gdouble vertex_net_thickness(toporouter_vertex_t * v) +{ + toporouter_bbox_t *box = vertex_bbox(v); + + if (!box) { + + while (v && (v->flags & VERTEX_FLAG_TEMP || v->flags & VERTEX_FLAG_ROUTE)) { + v = v->parent; + } + + box = vertex_bbox(v); + + } + else { + if (box->type == PIN || box->type == VIA) { + pcb_pin_t *pin = (pcb_pin_t *) box->data; + if (PCB_FLAG_TEST(PCB_FLAG_SQUARE, pin) || PCB_FLAG_TEST(PCB_FLAG_OCTAGON, pin)) { + return 0.; + } +/* return ((pcb_pin_t *)box->data)->Thickness + 1.;*/ + return ((pcb_pin_t *) box->data)->Thickness; + } + else if (box->type == PAD) { + pcb_pad_t *pad = (pcb_pad_t *) box->data; + if (pad->Point1.X == pad->Point2.X && pad->Point1.Y == pad->Point2.Y && !PCB_FLAG_TEST(PCB_FLAG_SQUARE, pad)) { + return pad->Thickness; + } + return 0.; + } + else if (box->type == BOARD) { + return 0.; + } + else if (box->type == LINE) { + pcb_line_t *line = (pcb_line_t *) box->data; + return line->Thickness; + } + else if (box->type == POLYGON) { + return 0.; + } + + printf("Unrecognized type in thickness lookup..\n"); + } + +/* if(!box || !box->cluster) return Settings.LineThickness + 1.;*/ + if (!box || !box->cluster) + return Settings.LineThickness; + + return cluster_thickness(box->cluster); +} + +gdouble vertex_net_clearance(toporouter_vertex_t * v) +{ + toporouter_bbox_t *box = vertex_bbox(v); + if (!box) { + + while (v && (v->flags & VERTEX_FLAG_TEMP || v->flags & VERTEX_FLAG_ROUTE)) { + v = v->parent; + } + box = vertex_bbox(v); + } + else { + /* if(box->type == PIN || box->type == VIA) + return ((pcb_pin_t *)box->data)->Clearance; + else if(box->type == PAD) + return ((pcb_pad_t *)box->data)->Clearance; */ + + } + +/* if(!box || !box->cluster) return Settings.Clearance + 1.; */ + if (!box || !box->cluster) + return Settings.Clearance; + return cluster_clearance(box->cluster); +} + +/* +void +print_trace (void) +{ + void *array[10]; + size_t size; + char **strings; + size_t i; + + size = backtrace (array, 10); + strings = backtrace_symbols (array, size); + + printf ("Obtained %zd stack frames.\n", size); + + for (i = 0; i < size; i++) + printf ("%s\n", strings[i]); + + free (strings); +} +*/ +/* fills in x and y with coordinates of point from a towards b of distance d */ +void point_from_point_to_point(toporouter_vertex_t * a, toporouter_vertex_t * b, gdouble d, gdouble * x, gdouble * y) +{ + gdouble dx = vx(b) - vx(a); + gdouble dy = vy(b) - vy(a); + gdouble theta = atan(fabs(dy / dx)); + +/*#ifdef DEBUG_EXPORT */ + if (!finite(theta)) { +/* printf("!finte(theta): a = %f,%f b = %f,%f d = %f\n", vx(a), vy(a), vx(b), vy(b), d); + print_trace(); */ + /*TODO: this shouldn't happen, fix the hack */ + *x = vx(a); + *y = vy(a); + return; + } +/*#endif*/ + + g_assert(finite(theta)); + + *x = vx(a); + *y = vy(a); + + if (dx >= 0.) { + + if (dy >= 0.) { + *x += d * cos(theta); + *y += d * sin(theta); + } + else { + *x += d * cos(theta); + *y -= d * sin(theta); + } + + } + else { + + if (dy >= 0.) { + *x -= d * cos(theta); + *y += d * sin(theta); + } + else { + *x -= d * cos(theta); + *y -= d * sin(theta); + } + + } +} + + +static inline gint coord_wind(gdouble ax, gdouble ay, gdouble bx, gdouble by, gdouble cx, gdouble cy) +{ + gdouble rval, dx1, dx2, dy1, dy2; + dx1 = bx - ax; + dy1 = by - ay; + dx2 = cx - bx; + dy2 = cy - by; + rval = (dx1 * dy2) - (dy1 * dx2); + return (rval > EPSILON) ? 1 : ((rval < -EPSILON) ? -1 : 0); +} + +/* wind_v: + * returns 1,0,-1 for counterclockwise, collinear or clockwise, respectively. + */ +int point_wind(GtsPoint * a, GtsPoint * b, GtsPoint * c) +{ + gdouble rval, dx1, dx2, dy1, dy2; + dx1 = b->x - a->x; + dy1 = b->y - a->y; + dx2 = c->x - b->x; + dy2 = c->y - b->y; + rval = (dx1 * dy2) - (dy1 * dx2); + return (rval > EPSILON) ? 1 : ((rval < -EPSILON) ? -1 : 0); +} + +static inline int vertex_wind(GtsVertex * a, GtsVertex * b, GtsVertex * c) +{ + return point_wind(GTS_POINT(a), GTS_POINT(b), GTS_POINT(c)); +} + +static inline int tvertex_wind(toporouter_vertex_t * a, toporouter_vertex_t * b, toporouter_vertex_t * c) +{ + return point_wind(GTS_POINT(a), GTS_POINT(b), GTS_POINT(c)); +} + +int sloppy_point_wind(GtsPoint * a, GtsPoint * b, GtsPoint * c) +{ + gdouble rval, dx1, dx2, dy1, dy2; + dx1 = b->x - a->x; + dy1 = b->y - a->y; + dx2 = c->x - b->x; + dy2 = c->y - b->y; + rval = (dx1 * dy2) - (dy1 * dx2); + return (rval > 10.) ? 1 : ((rval < -10.) ? -1 : 0); +} + +static inline int sloppy_vertex_wind(GtsVertex * a, GtsVertex * b, GtsVertex * c) +{ + return point_wind(GTS_POINT(a), GTS_POINT(b), GTS_POINT(c)); +} + +/* moves vertex v d units in the direction of vertex p */ +void coord_move_towards_coord_values(gdouble ax, gdouble ay, gdouble px, gdouble py, gdouble d, gdouble * x, gdouble * y) +{ + gdouble dx = px - ax; + gdouble dy = py - ay; + gdouble theta = atan(fabs(dy / dx)); + + + if (!finite(theta)) { + printf("!finite(theta) a = %f,%f p = %f,%f d = %f\n", ax, ay, px, py, d); + + } + + g_assert(finite(theta)); + + if (dx >= 0.) { + + if (dy >= 0.) { + *x = ax + (d * cos(theta)); + *y = ay + (d * sin(theta)); + } + else { + *x = ax + (d * cos(theta)); + *y = ay - (d * sin(theta)); + } + + } + else { + + if (dy >= 0.) { + *x = ax - (d * cos(theta)); + *y = ay + (d * sin(theta)); + } + else { + *x = ax - (d * cos(theta)); + *y = ay - (d * sin(theta)); + } + + } + +} + +/* moves vertex v d units in the direction of vertex p */ +void vertex_move_towards_point_values(GtsVertex * v, gdouble px, gdouble py, gdouble d, gdouble * x, gdouble * y) +{ + gdouble dx = px - GTS_POINT(v)->x; + gdouble dy = py - GTS_POINT(v)->y; + gdouble theta = atan(fabs(dy / dx)); + + g_assert(finite(theta)); + + if (dx >= 0.) { + + if (dy >= 0.) { + *x = GTS_POINT(v)->x + (d * cos(theta)); + *y = GTS_POINT(v)->y + (d * sin(theta)); + } + else { + *x = GTS_POINT(v)->x + (d * cos(theta)); + *y = GTS_POINT(v)->y - (d * sin(theta)); + } + + } + else { + + if (dy >= 0.) { + *x = GTS_POINT(v)->x - (d * cos(theta)); + *y = GTS_POINT(v)->y + (d * sin(theta)); + } + else { + *x = GTS_POINT(v)->x - (d * cos(theta)); + *y = GTS_POINT(v)->y - (d * sin(theta)); + } + + } + +} + +/* moves vertex v d units in the direction of vertex p */ +void vertex_move_towards_vertex_values(GtsVertex * v, GtsVertex * p, gdouble d, gdouble * x, gdouble * y) +{ + gdouble dx = GTS_POINT(p)->x - GTS_POINT(v)->x; + gdouble dy = GTS_POINT(p)->y - GTS_POINT(v)->y; + gdouble theta = atan(fabs(dy / dx)); + + g_assert(finite(theta)); + + if (dx >= 0.) { + + if (dy >= 0.) { + *x = GTS_POINT(v)->x + (d * cos(theta)); + *y = GTS_POINT(v)->y + (d * sin(theta)); + } + else { + *x = GTS_POINT(v)->x + (d * cos(theta)); + *y = GTS_POINT(v)->y - (d * sin(theta)); + } + + } + else { + + if (dy >= 0.) { + *x = GTS_POINT(v)->x - (d * cos(theta)); + *y = GTS_POINT(v)->y + (d * sin(theta)); + } + else { + *x = GTS_POINT(v)->x - (d * cos(theta)); + *y = GTS_POINT(v)->y - (d * sin(theta)); + } + + } + +} + +#define tv_on_layer(v,l) (l == TOPOROUTER_BBOX(TOPOROUTER_VERTEX(v)->boxes->data)->layer) + +static inline gdouble min_spacing(toporouter_vertex_t * v1, toporouter_vertex_t * v2) +{ + + gdouble v1halfthick, v2halfthick, v1clearance, v2clearance, ms; +/* toporouter_edge_t *e = v1->routingedge;*/ + + v1halfthick = vertex_net_thickness(TOPOROUTER_VERTEX(v1)) / 2.; + v2halfthick = vertex_net_thickness(TOPOROUTER_VERTEX(v2)) / 2.; + + v1clearance = vertex_net_clearance(TOPOROUTER_VERTEX(v1)); + v2clearance = vertex_net_clearance(TOPOROUTER_VERTEX(v2)); + + ms = v1halfthick + v2halfthick + MAX(v1clearance, v2clearance); + +#ifdef SPACING_DEBUG + printf("v1halfthick = %f v2halfthick = %f v1clearance = %f v2clearance = %f ms = %f\n", + v1halfthick, v2halfthick, v1clearance, v2clearance, ms); +#endif + + return ms; +} + +/* v1 is a vertex in the CDT, and v2 is a net... other way around?*/ +static inline gdouble min_vertex_net_spacing(toporouter_vertex_t * v1, toporouter_vertex_t * v2) +{ + + gdouble v1halfthick, v2halfthick, v1clearance, v2clearance, ms; + + v1halfthick = vertex_net_thickness(TOPOROUTER_VERTEX(v1)) / 2.; + v2halfthick = cluster_thickness(vertex_bbox(v2)->cluster) / 2.; + + v1clearance = vertex_net_clearance(TOPOROUTER_VERTEX(v1)); + v2clearance = cluster_clearance(vertex_bbox(v2)->cluster); + + ms = v1halfthick + v2halfthick + MAX(v1clearance, v2clearance); + + return ms; +} + +static inline gdouble min_oproute_vertex_spacing(toporouter_oproute_t * oproute, toporouter_vertex_t * v2) +{ + + gdouble v1halfthick, v2halfthick, v1clearance, v2clearance, ms; + + v1halfthick = lookup_thickness(oproute->style) / 2.; + v2halfthick = vertex_net_thickness(v2) / 2.; + + v1clearance = lookup_clearance(oproute->style); + v2clearance = vertex_net_clearance(v2); + + ms = v1halfthick + v2halfthick + MAX(v1clearance, v2clearance); + + return ms; +} + +gdouble min_oproute_net_spacing(toporouter_oproute_t * oproute, toporouter_vertex_t * v2) +{ + + gdouble v1halfthick, v2halfthick, v1clearance, v2clearance, ms; + + v1halfthick = lookup_thickness(oproute->style) / 2.; + v2halfthick = cluster_thickness(v2->route->src) / 2.; + + v1clearance = lookup_clearance(oproute->style); + v2clearance = cluster_clearance(v2->route->src); + + ms = v1halfthick + v2halfthick + MAX(v1clearance, v2clearance); + + return ms; +} + +gdouble min_net_net_spacing(toporouter_vertex_t * v1, toporouter_vertex_t * v2) +{ + + gdouble v1halfthick, v2halfthick, v1clearance, v2clearance, ms; + + v1halfthick = cluster_thickness(v1->route->src) / 2.; + v2halfthick = cluster_thickness(v2->route->src) / 2.; + + v1clearance = cluster_clearance(v1->route->src); + v2clearance = cluster_clearance(v2->route->src); + + ms = v1halfthick + v2halfthick + MAX(v1clearance, v2clearance); + + return ms; +} + +void +toporouter_draw_cluster(toporouter_t * r, drawing_context_t * dc, toporouter_cluster_t * cluster, gdouble red, gdouble green, + gdouble blue, guint layer) +{ +#if TOPO_OUTPUT_ENABLED +/*GList *i = cluster->i; + + while(i) { + toporouter_bbox_t *box = TOPOROUTER_BBOX(i->data); + + if(box->point && vz(box->point) == layer) { + cairo_set_source_rgba(dc->cr, red, green, blue, 0.8f); + cairo_arc(dc->cr, vx(box->point) * dc->s + MARGIN, vy(box->point) * dc->s + MARGIN, 500. * dc->s, 0, 2 * M_PI); + cairo_fill(dc->cr); + } + + i = i->next; + } +*/ +#endif +} + +void +toporouter_draw_surface(toporouter_t * r, GtsSurface * s, char *filename, int w, int h, int mode, GList * datas, int layer, + GList * candidatepoints) +{ +#if TOPO_OUTPUT_ENABLED + drawing_context_t *dc; + GList *i; + toporouter_vertex_t *tv, *tv2 = NULL; + + dc = toporouter_output_init(w, h, filename); + dc->mode = mode; + dc->data = NULL; + + gts_surface_foreach_edge(s, toporouter_draw_edge, dc); + gts_surface_foreach_vertex(s, toporouter_draw_vertex, dc); + + i = r->routednets; + while (i) { + GList *j = TOPOROUTER_ROUTE(i->data)->path; + tv2 = NULL; + while (j) { + tv = TOPOROUTER_VERTEX(j->data); + if (GTS_POINT(tv)->z == layer) { + if (tv && tv2) { + cairo_set_source_rgba(dc->cr, 0.0f, 1.0f, 0.0f, 0.8f); + cairo_move_to(dc->cr, GTS_POINT(tv)->x * dc->s + MARGIN, GTS_POINT(tv)->y * dc->s + MARGIN); + cairo_line_to(dc->cr, GTS_POINT(tv2)->x * dc->s + MARGIN, GTS_POINT(tv2)->y * dc->s + MARGIN); + cairo_stroke(dc->cr); + } + + if (tv->flags & VERTEX_FLAG_RED) { + cairo_set_source_rgba(dc->cr, 1., 0., 0., 0.8f); + cairo_arc(dc->cr, tv->v.p.x * dc->s + MARGIN, tv->v.p.y * dc->s + MARGIN, 500. * dc->s, 0, 2 * M_PI); + cairo_fill(dc->cr); + + } + else if (tv->flags & VERTEX_FLAG_GREEN) { + cairo_set_source_rgba(dc->cr, 0., 1., 0., 0.8f); + cairo_arc(dc->cr, tv->v.p.x * dc->s + MARGIN, tv->v.p.y * dc->s + MARGIN, 500. * dc->s, 0, 2 * M_PI); + cairo_fill(dc->cr); + + } + else if (tv->flags & VERTEX_FLAG_BLUE) { + cairo_set_source_rgba(dc->cr, 0., 0., 1., 0.8f); + cairo_arc(dc->cr, tv->v.p.x * dc->s + MARGIN, tv->v.p.y * dc->s + MARGIN, 500. * dc->s, 0, 2 * M_PI); + cairo_fill(dc->cr); + + } + else { + + cairo_set_source_rgba(dc->cr, 1., 1., 1., 0.8f); + cairo_arc(dc->cr, tv->v.p.x * dc->s + MARGIN, tv->v.p.y * dc->s + MARGIN, 500. * dc->s, 0, 2 * M_PI); + cairo_fill(dc->cr); + + + } + + if (tv->routingedge && !TOPOROUTER_IS_CONSTRAINT(tv->routingedge)) { + gdouble tempx, tempy, nms, pms; + GList *i = g_list_find(edge_routing(tv->routingedge), tv); + toporouter_vertex_t *nextv, *prevv; + + nextv = edge_routing_next(tv->routingedge, i); + prevv = edge_routing_prev(tv->routingedge, i); + + nms = min_spacing(tv, nextv); + pms = min_spacing(tv, prevv); + + g_assert(finite(nms)); + g_assert(finite(pms)); + + point_from_point_to_point(tv, nextv, nms, &tempx, &tempy); + + cairo_set_source_rgba(dc->cr, 0.0f, 1.0f, 1.0f, 0.8f); + cairo_move_to(dc->cr, vx(tv) * dc->s + MARGIN, vy(tv) * dc->s + MARGIN); + cairo_line_to(dc->cr, tempx * dc->s + MARGIN, tempy * dc->s + MARGIN); + cairo_stroke(dc->cr); + + point_from_point_to_point(tv, prevv, pms, &tempx, &tempy); + + cairo_move_to(dc->cr, vx(tv) * dc->s + MARGIN, vy(tv) * dc->s + MARGIN); + cairo_line_to(dc->cr, tempx * dc->s + MARGIN, tempy * dc->s + MARGIN); + cairo_stroke(dc->cr); + + + + + } + + + } + tv2 = tv; + j = j->next; + } + i = i->next; + } + + while (datas) { + toporouter_route_t *routedata = (toporouter_route_t *) datas->data; + + GList *i; /*, *k; */ + + toporouter_draw_cluster(r, dc, routedata->src, 1., 0., 0., layer); + toporouter_draw_cluster(r, dc, routedata->dest, 0., 0., 1., layer); + + tv2 = NULL; + i = routedata->path; + while (i) { + tv = TOPOROUTER_VERTEX(i->data); + if (GTS_POINT(tv)->z == layer) { + if (tv && tv2) { + cairo_set_source_rgba(dc->cr, 0.0f, 1.0f, 0.0f, 0.8f); + cairo_move_to(dc->cr, GTS_POINT(tv)->x * dc->s + MARGIN, GTS_POINT(tv)->y * dc->s + MARGIN); + cairo_line_to(dc->cr, GTS_POINT(tv2)->x * dc->s + MARGIN, GTS_POINT(tv2)->y * dc->s + MARGIN); + cairo_stroke(dc->cr); + } + } + tv2 = tv; + i = i->next; + } + + + if (routedata->alltemppoints) { + GList *i, *j; + i = j = g_hash_table_get_keys(routedata->alltemppoints); + while (i) { + toporouter_vertex_t *tv = TOPOROUTER_VERTEX(i->data); + + if (GTS_POINT(tv)->z != layer) { + i = i->next; + continue; + } + if (tv->flags & VERTEX_FLAG_BLUE) { + cairo_set_source_rgba(dc->cr, 0., 0., 1., 0.8f); + cairo_arc(dc->cr, tv->v.p.x * dc->s + MARGIN, tv->v.p.y * dc->s + MARGIN, 500. * dc->s, 0, 2 * M_PI); + cairo_fill(dc->cr); + } + else if (tv->flags & VERTEX_FLAG_RED) { + cairo_set_source_rgba(dc->cr, 1., 0., 0., 0.8f); + cairo_arc(dc->cr, tv->v.p.x * dc->s + MARGIN, tv->v.p.y * dc->s + MARGIN, 500. * dc->s, 0, 2 * M_PI); + cairo_fill(dc->cr); + + } + else if (tv->flags & VERTEX_FLAG_GREEN) { + cairo_set_source_rgba(dc->cr, 0., 1., 0., 0.8f); + cairo_arc(dc->cr, tv->v.p.x * dc->s + MARGIN, tv->v.p.y * dc->s + MARGIN, 500. * dc->s, 0, 2 * M_PI); + cairo_fill(dc->cr); + } + else { + cairo_set_source_rgba(dc->cr, 1., 1., 1., 0.8f); + cairo_arc(dc->cr, tv->v.p.x * dc->s + MARGIN, tv->v.p.y * dc->s + MARGIN, 500. * dc->s, 0, 2 * M_PI); + cairo_fill(dc->cr); + } + i = i->next; + } + g_list_free(j); + } + datas = datas->next; + } + toporouter_output_close(dc); +#endif +} + + +void toporouter_layer_free(toporouter_layer_t * l) +{ + g_list_free(l->vertices); + g_list_free(l->constraints); + +} + +guint groupcount(void) +{ + int group; + guint count = 0; + + for (group = 0; group < pcb_max_group; group++) { + if (PCB->LayerGroups.Number[group] > 0) + count++; + } + + return count; +} + +void toporouter_free(toporouter_t * r) +{ + struct timeval endtime; + int secs, usecs; + + int i; + for (i = 0; i < groupcount(); i++) { + toporouter_layer_free(&r->layers[i]); + } + + + gettimeofday(&endtime, NULL); + + secs = (int) (endtime.tv_sec - r->starttime.tv_sec); + usecs = (int) ((endtime.tv_usec - r->starttime.tv_usec) / 1000); + + if (usecs < 0) { + secs -= 1; + usecs += 1000; + } + + pcb_message(PCB_MSG_INFO, _("Elapsed time: %d.%02d seconds\n"), secs, usecs); + free(r->layers); + free(r); + +} + +/* wind: + * returns 1,0,-1 for counterclockwise, collinear or clockwise, respectively. + */ +int wind(toporouter_spoint_t * p1, toporouter_spoint_t * p2, toporouter_spoint_t * p3) +{ + double rval, dx1, dx2, dy1, dy2; + dx1 = p2->x - p1->x; + dy1 = p2->y - p1->y; + dx2 = p3->x - p2->x; + dy2 = p3->y - p2->y; + rval = (dx1 * dy2) - (dy1 * dx2); + return (rval > 0.0001) ? 1 : ((rval < -0.0001) ? -1 : 0); +} + +/* wind_double: + * returns 1,0,-1 for counterclockwise, collinear or clockwise, respectively. + */ +int wind_double(gdouble p1_x, gdouble p1_y, gdouble p2_x, gdouble p2_y, gdouble p3_x, gdouble p3_y) +{ + double rval, dx1, dx2, dy1, dy2; + dx1 = p2_x - p1_x; + dy1 = p2_y - p1_y; + dx2 = p3_x - p2_x; + dy2 = p3_y - p2_y; + rval = (dx1 * dy2) - (dy1 * dx2); + return (rval > 0.0001) ? 1 : ((rval < -0.0001) ? -1 : 0); +} + +static inline void print_toporouter_constraint(toporouter_constraint_t * tc) +{ + printf("%f,%f -> %f,%f ", + tc->c.edge.segment.v1->p.x, tc->c.edge.segment.v1->p.y, tc->c.edge.segment.v2->p.x, tc->c.edge.segment.v2->p.y); +} + +static inline void print_toporouter_vertex(toporouter_vertex_t * tv) +{ + printf("%f,%f ", tv->v.p.x, tv->v.p.y); +} + + +/** + * vertices_on_line: + * Given vertex a, gradient m, and radius r: + * + * Return vertices on line of a & m at r from a + */ +void vertices_on_line(toporouter_spoint_t * a, gdouble m, gdouble r, toporouter_spoint_t * b0, toporouter_spoint_t * b1) +{ + + gdouble c, temp; + + if (m == INFINITY || m == -INFINITY) { + b0->y = a->y + r; + b1->y = a->y - r; + + b0->x = a->x; + b1->x = a->x; + + return; + } + + c = a->y - (m * a->x); + + temp = sqrt(pow(r, 2) / (1 + pow(m, 2))); + + b0->x = a->x + temp; + b1->x = a->x - temp; + + b0->y = b0->x * m + c; + b1->y = b1->x * m + c; + +} + +/** + * vertices_on_line: + * Given vertex a, gradient m, and radius r: + * + * Return vertices on line of a & m at r from a + */ +void coords_on_line(gdouble ax, gdouble ay, gdouble m, gdouble r, gdouble * b0x, gdouble * b0y, gdouble * b1x, gdouble * b1y) +{ + + gdouble c, temp; + + if (m == INFINITY || m == -INFINITY) { + *b0y = ay + r; + *b1y = ay - r; + + *b0x = ax; + *b1x = ax; + + return; + } + + c = ay - (m * ax); + + temp = sqrt(pow(r, 2) / (1 + pow(m, 2))); + + *b0x = ax + temp; + *b1x = ax - temp; + + *b0y = *b0x * m + c; + *b1y = *b1x * m + c; + +} + +/** + * vertices_on_line: + * Given vertex a, gradient m, and radius r: + * + * Return vertices on line of a & m at r from a + */ +void points_on_line(GtsPoint * a, gdouble m, gdouble r, GtsPoint * b0, GtsPoint * b1) +{ + + gdouble c, temp; + + if (m == INFINITY || m == -INFINITY) { + b0->y = a->y + r; + b1->y = a->y - r; + + b0->x = a->x; + b1->x = a->x; + + return; + } + + c = a->y - (m * a->x); + + temp = sqrt(pow(r, 2) / (1 + pow(m, 2))); + + b0->x = a->x + temp; + b1->x = a->x - temp; + + b0->y = b0->x * m + c; + b1->y = b1->x * m + c; + +} + +/* + * Returns gradient of segment given by a & b + */ +gdouble vertex_gradient(toporouter_spoint_t * a, toporouter_spoint_t * b) +{ + if (a->x == b->x) + return INFINITY; + + return ((b->y - a->y) / (b->x - a->x)); +} + +/* + * Returns gradient of segment given by (x0,y0) & (x1,y1) + */ +static inline gdouble cartesian_gradient(gdouble x0, gdouble y0, gdouble x1, gdouble y1) +{ + if (epsilon_equals(x0, x1)) + return INFINITY; + + return ((y1 - y0) / (x1 - x0)); +} + +/* + * Returns gradient of segment given by (x0,y0) & (x1,y1) + */ +static inline gdouble point_gradient(GtsPoint * a, GtsPoint * b) +{ + return cartesian_gradient(a->x, a->y, b->x, b->y); +} + +gdouble segment_gradient(GtsSegment * s) +{ + return cartesian_gradient(GTS_POINT(s->v1)->x, GTS_POINT(s->v1)->y, GTS_POINT(s->v2)->x, GTS_POINT(s->v2)->y); +} + +/* + * Returns gradient perpendicular to m + */ +gdouble perpendicular_gradient(gdouble m) +{ + if (isinf(m)) + return 0.0f; + if (m < EPSILON && m > -EPSILON) + return INFINITY; + return -1.0f / m; +} + +/* + * Returns the distance between two vertices in the x-y plane + */ +gdouble vertices_plane_distance(toporouter_spoint_t * a, toporouter_spoint_t * b) +{ + return sqrt(pow(a->x - b->x, 2) + pow(a->y - b->y, 2)); +} + +/* + * Finds the point p distance r away from a on the line segment of a & b + */ +static inline void vertex_outside_segment(toporouter_spoint_t * a, toporouter_spoint_t * b, gdouble r, toporouter_spoint_t * p) +{ + toporouter_spoint_t temp[2]; + + vertices_on_line(a, vertex_gradient(a, b), r, &temp[0], &temp[1]); + + if (vertices_plane_distance(&temp[0], b) > vertices_plane_distance(&temp[1], b)) { + p->x = temp[0].x; + p->y = temp[0].y; + } + else { + p->x = temp[1].x; + p->y = temp[1].y; + } + +} + +/* proper intersection: + * AB and CD must share a point interior to both segments. + * returns TRUE if AB properly intersects CD. + */ +gint coord_intersect_prop(gdouble ax, gdouble ay, gdouble bx, gdouble by, gdouble cx, gdouble cy, gdouble dx, gdouble dy) +{ + gint wind_abc = coord_wind(ax, ay, bx, by, cx, cy); + gint wind_abd = coord_wind(ax, ay, bx, by, dx, dy); + gint wind_cda = coord_wind(cx, cy, dx, dy, ax, ay); + gint wind_cdb = coord_wind(cx, cy, dx, dy, bx, by); + + if (!wind_abc || !wind_abd || !wind_cda || !wind_cdb) + return 0; + + return (wind_abc ^ wind_abd) && (wind_cda ^ wind_cdb); +} + +/* proper intersection: + * AB and CD must share a point interior to both segments. + * returns TRUE if AB properly intersects CD. + */ +int point_intersect_prop(GtsPoint * a, GtsPoint * b, GtsPoint * c, GtsPoint * d) +{ + + if (point_wind(a, b, c) == 0 || point_wind(a, b, d) == 0 || point_wind(c, d, a) == 0 || point_wind(c, d, b) == 0) + return 0; + + return (point_wind(a, b, c) ^ point_wind(a, b, d)) && (point_wind(c, d, a) ^ point_wind(c, d, b)); +} + +static inline int vertex_intersect_prop(GtsVertex * a, GtsVertex * b, GtsVertex * c, GtsVertex * d) +{ + return point_intersect_prop(GTS_POINT(a), GTS_POINT(b), GTS_POINT(c), GTS_POINT(d)); +} + +static inline int +tvertex_intersect_prop(toporouter_vertex_t * a, toporouter_vertex_t * b, toporouter_vertex_t * c, toporouter_vertex_t * d) +{ + return point_intersect_prop(GTS_POINT(a), GTS_POINT(b), GTS_POINT(c), GTS_POINT(d)); +} + +/* +static inline int +tvertex_intersect(toporouter_vertex_t *a, toporouter_vertex_t *b, toporouter_vertex_t *c, toporouter_vertex_t *d) +{ + if( !point_wind(GTS_POINT(a), GTS_POINT(d), GTS_POINT(b)) || !point_wind(GTS_POINT(a), GTS_POINT(c), GTS_POINT(b)) ) return 1; + + return + ( point_wind(GTS_POINT(a), GTS_POINT(b), GTS_POINT(c)) ^ point_wind(GTS_POINT(a), GTS_POINT(b), GTS_POINT(d)) ) && + ( point_wind(GTS_POINT(c), GTS_POINT(d), GTS_POINT(a)) ^ point_wind(GTS_POINT(c), GTS_POINT(d), GTS_POINT(b)) ); +} +*/ + +/* intersection vertex: + * AB and CD must share a point interior to both segments. + * returns vertex at intersection of AB and CD. + */ +GtsVertex *vertex_intersect(GtsVertex * a, GtsVertex * b, GtsVertex * c, GtsVertex * d) +{ + GtsVertexClass *vertex_class = GTS_VERTEX_CLASS(toporouter_vertex_class()); + GtsVertex *rval; + gdouble ua_top, ua_bot, ua, rx, ry; + + /* TODO: this could be done more efficiently without duplicating computation */ + if (!vertex_intersect_prop(a, b, c, d)) + return NULL; + + ua_top = ((d->p.x - c->p.x) * (a->p.y - c->p.y)) - ((d->p.y - c->p.y) * (a->p.x - c->p.x)); + ua_bot = ((d->p.y - c->p.y) * (b->p.x - a->p.x)) - ((d->p.x - c->p.x) * (b->p.y - a->p.y)); + ua = ua_top / ua_bot; + rx = a->p.x + (ua * (b->p.x - a->p.x)); + ry = a->p.y + (ua * (b->p.y - a->p.y)); + + rval = gts_vertex_new(vertex_class, rx, ry, 0.0f); + + return rval; +} + +/* intersection vertex: + * AB and CD must share a point interior to both segments. + * returns vertex at intersection of AB and CD. + */ +void +coord_intersect(gdouble ax, gdouble ay, gdouble bx, gdouble by, gdouble cx, gdouble cy, gdouble dx, gdouble dy, gdouble * rx, + gdouble * ry) +{ + gdouble ua_top, ua_bot, ua; + + ua_top = ((dx - cx) * (ay - cy)) - ((dy - cy) * (ax - cx)); + ua_bot = ((dy - cy) * (bx - ax)) - ((dx - cx) * (by - ay)); + ua = ua_top / ua_bot; + *rx = ax + (ua * (bx - ax)); + *ry = ay + (ua * (by - ay)); + +} + + +/* + * returns true if c is between a and b + */ +int point_between(GtsPoint * a, GtsPoint * b, GtsPoint * c) +{ + if (point_wind(a, b, c) != 0) + return 0; + + if (a->x != b->x) { + return ((a->x <= c->x) && (c->x <= b->x)) || ((a->x >= c->x) && (c->x >= b->x)); + } + return ((a->y <= c->y) && (c->y <= b->y)) || ((a->y >= c->y) && (c->y >= b->y)); +} + +static inline int vertex_between(GtsVertex * a, GtsVertex * b, GtsVertex * c) +{ + return point_between(GTS_POINT(a), GTS_POINT(b), GTS_POINT(c)); +} + +void delaunay_create_from_vertices(GList * vertices, GtsSurface ** surface, GtsTriangle ** t) +{ + GList *i = vertices; + GtsVertex *v1, *v2, *v3; + GSList *vertices_slist = NULL; + + while (i) { + vertices_slist = g_slist_prepend(vertices_slist, i->data); + i = i->next; + } + + /* TODO: just work this out from the board outline */ + *t = gts_triangle_enclosing(gts_triangle_class(), vertices_slist, 100000.0f); + gts_triangle_vertices(*t, &v1, &v2, &v3); + + *surface = gts_surface_new(gts_surface_class(), gts_face_class(), + GTS_EDGE_CLASS(toporouter_edge_class()), GTS_VERTEX_CLASS(toporouter_vertex_class())); + + gts_surface_add_face(*surface, gts_face_new(gts_face_class(), (*t)->e1, (*t)->e2, (*t)->e3)); + + i = vertices; + while (i) { + toporouter_vertex_t *v = TOPOROUTER_VERTEX(gts_delaunay_add_vertex(*surface, (GtsVertex *) i->data, NULL)); + + if (v) { + printf("ERROR: vertex could not be added to CDT "); + print_vertex(v); + } + + i = i->next; + } + + gts_allow_floating_vertices = TRUE; + gts_object_destroy(GTS_OBJECT(v1)); + gts_object_destroy(GTS_OBJECT(v2)); + gts_object_destroy(GTS_OBJECT(v3)); + gts_allow_floating_vertices = FALSE; + + g_slist_free(vertices_slist); +/* return surface;*/ +} + +GSList *list_to_slist(GList * i) +{ + GSList *rval = NULL; + while (i) { + rval = g_slist_prepend(rval, i->data); + i = i->next; + } + return rval; +} + +toporouter_bbox_t *toporouter_bbox_create_from_points(int layer, GList * vertices, toporouter_term_t type, gpointer data) +{ + toporouter_bbox_t *bbox; + GSList *vertices_slist = list_to_slist(vertices); + +/* delaunay_create_from_vertices(vertices, &s, &t);*/ + bbox = TOPOROUTER_BBOX(gts_bbox_points(GTS_BBOX_CLASS(toporouter_bbox_class()), vertices_slist)); + bbox->type = type; + bbox->data = data; + + bbox->surface = NULL; + bbox->enclosing = NULL; + + bbox->layer = layer; + + bbox->point = NULL; + bbox->realpoint = NULL; + + g_slist_free(vertices_slist); + return bbox; +} + +toporouter_bbox_t *toporouter_bbox_create(int layer, GList * vertices, toporouter_term_t type, gpointer data) +{ + toporouter_bbox_t *bbox; + GtsSurface *s; + GtsTriangle *t; + + delaunay_create_from_vertices(vertices, &s, &t); + bbox = TOPOROUTER_BBOX(gts_bbox_surface(GTS_BBOX_CLASS(toporouter_bbox_class()), s)); + bbox->type = type; + bbox->data = data; + + bbox->surface = s; + bbox->enclosing = t; + + bbox->layer = layer; + + return bbox; +} + +GtsVertex *insert_vertex(toporouter_t * r, toporouter_layer_t * l, gdouble x, gdouble y, toporouter_bbox_t * box) +{ + GtsVertexClass *vertex_class = GTS_VERTEX_CLASS(toporouter_vertex_class()); + GtsVertex *v; + GList *i; + + i = l->vertices; + while (i) { + v = (GtsVertex *) i->data; + if (v->p.x == x && v->p.y == y) { + TOPOROUTER_VERTEX(v)->bbox = box; + return v; + } + i = i->next; + } + + v = gts_vertex_new(vertex_class, x, y, l - r->layers); + TOPOROUTER_VERTEX(v)->bbox = box; + l->vertices = g_list_prepend(l->vertices, v); + + return v; +} + +GList *insert_constraint_edge(toporouter_t * r, toporouter_layer_t * l, gdouble x1, gdouble y1, guint flags1, + gdouble x2, gdouble y2, guint flags2, toporouter_bbox_t * box) +{ + GtsVertexClass *vertex_class = GTS_VERTEX_CLASS(toporouter_vertex_class()); + GtsEdgeClass *edge_class = GTS_EDGE_CLASS(toporouter_constraint_class()); + GtsVertex *p[2]; + GtsVertex *v; + GList *i; + GtsEdge *e; + + p[0] = p[1] = NULL; + + /* insert or find points */ + + i = l->vertices; + while (i) { + v = (GtsVertex *) i->data; + if (v->p.x == x1 && v->p.y == y1) + p[0] = v; + if (v->p.x == x2 && v->p.y == y2) + p[1] = v; + i = i->next; + } + + if (p[0] == NULL) { + p[0] = gts_vertex_new(vertex_class, x1, y1, l - r->layers); + TOPOROUTER_VERTEX(p[0])->bbox = box; + l->vertices = g_list_prepend(l->vertices, p[0]); + } + if (p[1] == NULL) { + p[1] = gts_vertex_new(vertex_class, x2, y2, l - r->layers); + TOPOROUTER_VERTEX(p[1])->bbox = box; + l->vertices = g_list_prepend(l->vertices, p[1]); + } + + TOPOROUTER_VERTEX(p[0])->flags = flags1; + TOPOROUTER_VERTEX(p[1])->flags = flags2; + + e = gts_edge_new(edge_class, p[0], p[1]); + TOPOROUTER_CONSTRAINT(e)->box = box; + l->constraints = g_list_prepend(l->constraints, e); +/* return insert_constraint_edge_rec(r, l, p, box);*/ + return g_list_prepend(NULL, e); + +} + +void insert_constraints_from_list(toporouter_t * r, toporouter_layer_t * l, GList * vlist, toporouter_bbox_t * box) +{ + GList *i = vlist; + toporouter_vertex_t *pv = NULL, *v; + + while (i) { + v = TOPOROUTER_VERTEX(i->data); + + if (pv) { + box->constraints = + g_list_concat(box->constraints, insert_constraint_edge(r, l, vx(v), vy(v), v->flags, vx(pv), vy(pv), pv->flags, box)); + } + + pv = v; + i = i->next; + } + + v = TOPOROUTER_VERTEX(vlist->data); + box->constraints = + g_list_concat(box->constraints, insert_constraint_edge(r, l, vx(v), vy(v), v->flags, vx(pv), vy(pv), pv->flags, box)); + +} + +void insert_centre_point(toporouter_t * r, toporouter_layer_t * l, gdouble x, gdouble y) +{ + GList *i; + GtsVertexClass *vertex_class = GTS_VERTEX_CLASS(toporouter_vertex_class()); + + i = l->vertices; + while (i) { + GtsPoint *p = GTS_POINT(i->data); + if (p->x == x && p->y == y) + return; + i = i->next; + } + + l->vertices = g_list_prepend(l->vertices, gts_vertex_new(vertex_class, x, y, 0.0f)); +} + +GtsPoint *midpoint(GtsPoint * a, GtsPoint * b) +{ + return gts_point_new(gts_point_class(), (a->x + b->x) / 2., (a->y + b->y) / 2., 0.); +} + +static inline gdouble pad_rad(pcb_pad_t * pad) +{ + return (lookup_thickness(pad->Name) / 2.) + lookup_clearance(pad->Name); +} + +static inline gdouble pin_rad(pcb_pin_t * pin) +{ + return (lookup_thickness(pin->Name) / 2.) + lookup_clearance(pin->Name); +} + +GList *rect_with_attachments(gdouble rad, + gdouble x0, gdouble y0, + gdouble x1, gdouble y1, gdouble x2, gdouble y2, gdouble x3, gdouble y3, gdouble z) +{ + GtsVertexClass *vertex_class = GTS_VERTEX_CLASS(toporouter_vertex_class()); + GList *r = NULL, *rr = NULL, *i; + toporouter_vertex_t *curpoint, *temppoint; + + + curpoint = TOPOROUTER_VERTEX(gts_vertex_new(vertex_class, x0, y0, z)); + + r = g_list_prepend(NULL, curpoint); + r = g_list_prepend(r, gts_vertex_new(vertex_class, x1, y1, z)); + r = g_list_prepend(r, gts_vertex_new(vertex_class, x2, y2, z)); + r = g_list_prepend(r, gts_vertex_new(vertex_class, x3, y3, z)); + + i = r; + while (i) { + temppoint = TOPOROUTER_VERTEX(i->data); + rr = g_list_prepend(rr, curpoint); + + curpoint = temppoint; + i = i->next; + } + + g_list_free(r); + + return rr; +} + +#define VERTEX_CENTRE(x) TOPOROUTER_VERTEX( vertex_bbox(x)->point ) + +/* + * Read pad data from layer into toporouter_layer_t struct + * + * Inserts points and constraints into GLists + */ +int read_pads(toporouter_t * r, toporouter_layer_t * l, guint layer) +{ + toporouter_spoint_t p[2], rv[5]; + gdouble x[2], y[2], t, m; + + GList *vlist = NULL; + toporouter_bbox_t *bbox = NULL; + + back = front = -1; + if (pcb_layer_group_list(PCB_LYT_BOTTOM | PCB_LYT_COPPER, &back, 1) <= 0) + return -1; + if (pcb_layer_group_list(PCB_LYT_TOP | PCB_LYT_COPPER, &front, 1) <= 0) + return -1; + +/* printf("read_pads: front = %d back = %d layer = %d\n", + front, back, layer);*/ + + /* If its not the top or bottom layer, there are no pads to read */ + if (l - r->layers != front && l - r->layers != back) + return 0; + + PCB_ELEMENT_LOOP(PCB->Data); + { + PCB_PAD_LOOP(element); + { + if ((l - r->layers == back && PCB_FLAG_TEST(PCB_FLAG_ONSOLDER, pad)) || (l - r->layers == front && !PCB_FLAG_TEST(PCB_FLAG_ONSOLDER, pad))) { + + t = (gdouble) pad->Thickness / 2.0f; + x[0] = pad->Point1.X; + x[1] = pad->Point2.X; + y[0] = pad->Point1.Y; + y[1] = pad->Point2.Y; + + + if (PCB_FLAG_TEST(PCB_FLAG_SQUARE, pad)) { + /* Square or oblong pad. Four points and four constraint edges are + * used */ + + if (x[0] == x[1] && y[0] == y[1]) { + /* Pad is square */ + +/* vlist = g_list_prepend(NULL, gts_vertex_new (vertex_class, x[0]-t, y[0]-t, 0.)); + vlist = g_list_prepend(vlist, gts_vertex_new (vertex_class, x[0]-t, y[0]+t, 0.)); + vlist = g_list_prepend(vlist, gts_vertex_new (vertex_class, x[0]+t, y[0]+t, 0.)); + vlist = g_list_prepend(vlist, gts_vertex_new (vertex_class, x[0]+t, y[0]-t, 0.)); */ + vlist = rect_with_attachments(pad_rad(pad), + x[0] - t, y[0] - t, + x[0] - t, y[0] + t, x[0] + t, y[0] + t, x[0] + t, y[0] - t, l - r->layers); + bbox = toporouter_bbox_create(l - r->layers, vlist, PAD, pad); + r->bboxes = g_slist_prepend(r->bboxes, bbox); + insert_constraints_from_list(r, l, vlist, bbox); + g_list_free(vlist); + + /*bbox->point = GTS_POINT( gts_vertex_new(vertex_class, x[0], y[0], 0.) ); */ + bbox->point = GTS_POINT(insert_vertex(r, l, x[0], y[0], bbox)); + g_assert(TOPOROUTER_VERTEX(bbox->point)->bbox == bbox); + } + else { + /* Pad is diagonal oblong or othogonal oblong */ + + m = cartesian_gradient(x[0], y[0], x[1], y[1]); + + p[0].x = x[0]; + p[0].y = y[0]; + p[1].x = x[1]; + p[1].y = y[1]; + + vertex_outside_segment(&p[0], &p[1], t, &rv[0]); + vertices_on_line(&rv[0], perpendicular_gradient(m), t, &rv[1], &rv[2]); + + vertex_outside_segment(&p[1], &p[0], t, &rv[0]); + vertices_on_line(&rv[0], perpendicular_gradient(m), t, &rv[3], &rv[4]); + + if (wind(&rv[1], &rv[2], &rv[3]) != wind(&rv[2], &rv[3], &rv[4])) { + rv[0].x = rv[3].x; + rv[0].y = rv[3].y; + rv[3].x = rv[4].x; + rv[3].y = rv[4].y; + rv[4].x = rv[0].x; + rv[4].y = rv[0].y; + } + +/* vlist = g_list_prepend(NULL, gts_vertex_new (vertex_class, rv[1].x, rv[1].y, 0.)); + vlist = g_list_prepend(vlist, gts_vertex_new (vertex_class, rv[2].x, rv[2].y, 0.)); + vlist = g_list_prepend(vlist, gts_vertex_new (vertex_class, rv[3].x, rv[3].y, 0.)); + vlist = g_list_prepend(vlist, gts_vertex_new (vertex_class, rv[4].x, rv[4].y, 0.));*/ + vlist = rect_with_attachments(pad_rad(pad), + rv[1].x, rv[1].y, + rv[2].x, rv[2].y, rv[3].x, rv[3].y, rv[4].x, rv[4].y, l - r->layers); + bbox = toporouter_bbox_create(l - r->layers, vlist, PAD, pad); + r->bboxes = g_slist_prepend(r->bboxes, bbox); + insert_constraints_from_list(r, l, vlist, bbox); + g_list_free(vlist); + + /*bbox->point = GTS_POINT( gts_vertex_new(vertex_class, (x[0] + x[1]) / 2., (y[0] + y[1]) / 2., 0.) ); */ + bbox->point = GTS_POINT(insert_vertex(r, l, (x[0] + x[1]) / 2., (y[0] + y[1]) / 2., bbox)); + g_assert(TOPOROUTER_VERTEX(bbox->point)->bbox == bbox); + + } + + } + else { + /* Either round pad or pad with curved edges */ + + if (x[0] == x[1] && y[0] == y[1]) { + /* One point */ + + /* bounding box same as square pad */ + vlist = rect_with_attachments(pad_rad(pad), + x[0] - t, y[0] - t, + x[0] - t, y[0] + t, x[0] + t, y[0] + t, x[0] + t, y[0] - t, l - r->layers); + bbox = toporouter_bbox_create(l - r->layers, vlist, PAD, pad); + r->bboxes = g_slist_prepend(r->bboxes, bbox); + g_list_free(vlist); + + /*bbox->point = GTS_POINT( insert_vertex(r, l, x[0], y[0], bbox) ); */ + bbox->point = GTS_POINT(insert_vertex(r, l, x[0], y[0], bbox)); + + } + else { + /* Two points and one constraint edge */ + + /* the rest is just for bounding box */ + m = cartesian_gradient(x[0], y[0], x[1], y[1]); + + p[0].x = x[0]; + p[0].y = y[0]; + p[1].x = x[1]; + p[1].y = y[1]; + + vertex_outside_segment(&p[0], &p[1], t, &rv[0]); + vertices_on_line(&rv[0], perpendicular_gradient(m), t, &rv[1], &rv[2]); + + vertex_outside_segment(&p[1], &p[0], t, &rv[0]); + vertices_on_line(&rv[0], perpendicular_gradient(m), t, &rv[3], &rv[4]); + + if (wind(&rv[1], &rv[2], &rv[3]) != wind(&rv[2], &rv[3], &rv[4])) { + rv[0].x = rv[3].x; + rv[0].y = rv[3].y; + rv[3].x = rv[4].x; + rv[3].y = rv[4].y; + rv[4].x = rv[0].x; + rv[4].y = rv[0].y; + } + + vlist = rect_with_attachments(pad_rad(pad), + rv[1].x, rv[1].y, + rv[2].x, rv[2].y, rv[3].x, rv[3].y, rv[4].x, rv[4].y, l - r->layers); + bbox = toporouter_bbox_create(l - r->layers, vlist, PAD, pad); + r->bboxes = g_slist_prepend(r->bboxes, bbox); + insert_constraints_from_list(r, l, vlist, bbox); + g_list_free(vlist); + + /*bbox->point = GTS_POINT( gts_vertex_new(vertex_class, (x[0] + x[1]) / 2., (y[0] + y[1]) / 2., 0.) ); */ + bbox->point = GTS_POINT(insert_vertex(r, l, (x[0] + x[1]) / 2., (y[0] + y[1]) / 2., bbox)); + + /*bbox->constraints = g_list_concat(bbox->constraints, insert_constraint_edge(r, l, x[0], y[0], x[1], y[1], bbox)); */ + + } + + + } + + } + } + PCB_END_LOOP; + } + PCB_END_LOOP; + + return 0; +} + +/* + * Read points data (all layers) into GList + * + * Inserts pin and via points + */ +int read_points(toporouter_t * r, toporouter_layer_t * l, int layer) +{ + gdouble x, y, t; + + GList *vlist = NULL; + toporouter_bbox_t *bbox = NULL; + + PCB_ELEMENT_LOOP(PCB->Data); + { + PCB_PIN_LOOP(element); + { + + t = (gdouble) pin->Thickness / 2.0f; + x = pin->X; + y = pin->Y; + + if (PCB_FLAG_TEST(PCB_FLAG_SQUARE, pin)) { + + vlist = rect_with_attachments(pin_rad(pin), x - t, y - t, x - t, y + t, x + t, y + t, x + t, y - t, l - r->layers); + bbox = toporouter_bbox_create(l - r->layers, vlist, PIN, pin); + r->bboxes = g_slist_prepend(r->bboxes, bbox); + insert_constraints_from_list(r, l, vlist, bbox); + g_list_free(vlist); + bbox->point = GTS_POINT(insert_vertex(r, l, x, y, bbox)); + + } + else if (PCB_FLAG_TEST(PCB_FLAG_OCTAGON, pin)) { + /* TODO: Handle octagon pins */ + fprintf(stderr, "No support for octagon pins yet\n"); + } + else { + vlist = rect_with_attachments(pin_rad(pin), x - t, y - t, x - t, y + t, x + t, y + t, x + t, y - t, l - r->layers); + bbox = toporouter_bbox_create(l - r->layers, vlist, PIN, pin); + r->bboxes = g_slist_prepend(r->bboxes, bbox); + g_list_free(vlist); + bbox->point = GTS_POINT(insert_vertex(r, l, x, y, bbox)); + } + } + PCB_END_LOOP; + } + PCB_END_LOOP; + + PCB_VIA_LOOP(PCB->Data); + { + + t = (gdouble) via->Thickness / 2.0f; + x = via->X; + y = via->Y; + + if (PCB_FLAG_TEST(PCB_FLAG_SQUARE, via)) { + + vlist = rect_with_attachments(pin_rad((pcb_pin_t *) via), + x - t, y - t, x - t, y + t, x + t, y + t, x + t, y - t, l - r->layers); + bbox = toporouter_bbox_create(l - r->layers, vlist, VIA, via); + r->bboxes = g_slist_prepend(r->bboxes, bbox); + insert_constraints_from_list(r, l, vlist, bbox); + g_list_free(vlist); + bbox->point = GTS_POINT(insert_vertex(r, l, x, y, bbox)); + + } + else if (PCB_FLAG_TEST(PCB_FLAG_OCTAGON, via)) { + /* TODO: Handle octagon vias */ + fprintf(stderr, "No support for octagon vias yet\n"); + } + else { + + vlist = rect_with_attachments(pin_rad((pcb_pin_t *) via), + x - t, y - t, x - t, y + t, x + t, y + t, x + t, y - t, l - r->layers); + bbox = toporouter_bbox_create(l - r->layers, vlist, VIA, via); + r->bboxes = g_slist_prepend(r->bboxes, bbox); + g_list_free(vlist); + + bbox->point = GTS_POINT(insert_vertex(r, l, x, y, bbox)); + + } + } + PCB_END_LOOP; + return 0; +} + +/* + * Read line data from layer into toporouter_layer_t struct + * + * Inserts points and constraints into GLists + */ +int read_lines(toporouter_t * r, toporouter_layer_t * l, pcb_layer_t * layer, int ln) +{ + gdouble xs[2], ys[2]; + + GList *vlist = NULL; + toporouter_bbox_t *bbox = NULL; + + GtsVertexClass *vertex_class = GTS_VERTEX_CLASS(toporouter_vertex_class()); + + PCB_LINE_LOOP(layer); + { + xs[0] = line->Point1.X; + xs[1] = line->Point2.X; + ys[0] = line->Point1.Y; + ys[1] = line->Point2.Y; + if (!(xs[0] == xs[1] && ys[0] == ys[1])) { + vlist = g_list_prepend(NULL, gts_vertex_new(vertex_class, xs[0], ys[0], l - r->layers)); + vlist = g_list_prepend(vlist, gts_vertex_new(vertex_class, xs[1], ys[1], l - r->layers)); + /* TODO: replace this with surface version */ + bbox = toporouter_bbox_create_from_points(pcb_layer_get_group(ln), vlist, LINE, line); + r->bboxes = g_slist_prepend(r->bboxes, bbox); + /*new;; + //insert_constraints_from_list(r, l, vlist, bbox); */ + g_list_free(vlist); +/* bbox->point = GTS_POINT( insert_vertex(r, l, (xs[0]+xs[1])/2., (ys[0]+ys[1])/2., bbox) );*/ + + bbox->constraints = + g_list_concat(bbox->constraints, insert_constraint_edge(r, l, xs[0], ys[0], 0, xs[1], ys[1], 0, bbox)); + } + } + PCB_END_LOOP; + + return 0; +} + +void create_board_edge(gdouble x0, gdouble y0, gdouble x1, gdouble y1, gdouble max, gint layer, GList ** vlist) +{ + GtsVertexClass *vertex_class = GTS_VERTEX_CLASS(toporouter_vertex_class()); + gdouble d = sqrt(pow(x0 - x1, 2) + pow(y0 - y1, 2)); + guint n = d / max, count = 1; + gdouble inc = n ? d / n : d; + gdouble x = x0, y = y0; + + *vlist = g_list_prepend(*vlist, gts_vertex_new(vertex_class, x0, y0, layer)); + + while (count < n) { + coord_move_towards_coord_values(x0, y0, x1, y1, inc, &x, &y); + *vlist = g_list_prepend(*vlist, gts_vertex_new(vertex_class, x, y, layer)); + + x0 = x; + y0 = y; + count++; + } + +} + + +int read_board_constraints(toporouter_t * r, toporouter_layer_t * l, int layer) +{ +/* GtsVertexClass *vertex_class = GTS_VERTEX_CLASS (toporouter_vertex_class ());*/ + GList *vlist = NULL; + toporouter_bbox_t *bbox = NULL; + + /* Add points for corners of board, and constrain those edges */ +/* vlist = g_list_prepend(NULL, gts_vertex_new (vertex_class, 0., 0., layer)); + vlist = g_list_prepend(vlist, gts_vertex_new (vertex_class, PCB->MaxWidth, 0., layer)); + vlist = g_list_prepend(vlist, gts_vertex_new (vertex_class, PCB->MaxWidth, PCB->MaxHeight, layer)); + vlist = g_list_prepend(vlist, gts_vertex_new (vertex_class, 0., PCB->MaxHeight, layer));*/ + + create_board_edge(0., 0., PCB->MaxWidth, 0., 10000., layer, &vlist); + create_board_edge(PCB->MaxWidth, 0., PCB->MaxWidth, PCB->MaxHeight, 10000., layer, &vlist); + create_board_edge(PCB->MaxWidth, PCB->MaxHeight, 0., PCB->MaxHeight, 10000., layer, &vlist); + create_board_edge(0., PCB->MaxHeight, 0., 0., 10000., layer, &vlist); + + bbox = toporouter_bbox_create(layer, vlist, BOARD, NULL); + r->bboxes = g_slist_prepend(r->bboxes, bbox); + insert_constraints_from_list(r, l, vlist, bbox); + g_list_free(vlist); + + return 0; +} + +gdouble triangle_cost(GtsTriangle * t, gpointer * data) +{ + + gdouble *min_quality = (gdouble *) data[0]; + gdouble *max_area = (gdouble *) data[1]; + gdouble quality = gts_triangle_quality(t); + gdouble area = gts_triangle_area(t); + + if (quality < *min_quality || area > *max_area) + return quality; + return 0.0; +} + + +void print_constraint(toporouter_constraint_t * e) +{ + printf("CONSTRAINT:\n"); + print_vertex(tedge_v1(e)); + print_vertex(tedge_v2(e)); +} + +void print_edge(toporouter_edge_t * e) +{ + GList *i = edge_routing(e); + + printf("EDGE:\n"); + + print_vertex(tedge_v1(e)); + + while (i) { + toporouter_vertex_t *v = TOPOROUTER_VERTEX(i->data); + print_vertex(v); + i = i->next; + } + + print_vertex(tedge_v2(e)); +} + +static void pick_first_face(GtsFace * f, GtsFace ** first) +{ + if (*first == NULL) + *first = f; +} + +void unconstrain(toporouter_layer_t * l, toporouter_constraint_t * c) +{ + toporouter_edge_t *e; + + gts_allow_floating_vertices = TRUE; + e = TOPOROUTER_EDGE(gts_edge_new(GTS_EDGE_CLASS(toporouter_edge_class()), GTS_SEGMENT(c)->v1, GTS_SEGMENT(c)->v2)); + gts_edge_replace(GTS_EDGE(c), GTS_EDGE(e)); + l->constraints = g_list_remove(l->constraints, c); + c->box->constraints = g_list_remove(c->box->constraints, c); + c->box = NULL; + gts_object_destroy(GTS_OBJECT(c)); + gts_allow_floating_vertices = FALSE; +} + +void build_cdt(toporouter_t * r, toporouter_layer_t * l) +{ + /* TODO: generalize into surface *cdt_create(vertices, constraints) */ + GList *i; + /*GtsEdge *temp; + GtsVertex *v; */ + GtsTriangle *t; + GtsVertex *v1, *v2, *v3; + GSList *vertices_slist; + + vertices_slist = list_to_slist(l->vertices); + + if (l->surface) { + GtsFace *first = NULL; + gts_surface_foreach_face(l->surface, (GtsFunc) pick_first_face, &first); + gts_surface_traverse_destroy(gts_surface_traverse_new(l->surface, first)); + } + + t = gts_triangle_enclosing(gts_triangle_class(), vertices_slist, 1000.0f); + gts_triangle_vertices(t, &v1, &v2, &v3); + + g_slist_free(vertices_slist); + + l->surface = gts_surface_new(gts_surface_class(), gts_face_class(), + GTS_EDGE_CLASS(toporouter_edge_class()), GTS_VERTEX_CLASS(toporouter_vertex_class())); + + gts_surface_add_face(l->surface, gts_face_new(gts_face_class(), t->e1, t->e2, t->e3)); + + +/* fprintf(stderr, "ADDED VERTICES\n");*/ +/* + GtsFace *debugface; + + if((debugface = gts_delaunay_check(l->surface))) { + fprintf(stderr, "WARNING: Delaunay check failed\n"); + fprintf(stderr, "\tViolating triangle:\n"); + fprintf(stderr, "\t%f,%f %f,%f\n", + debugface->triangle.e1->segment.v1->p.x, + debugface->triangle.e1->segment.v1->p.y, + debugface->triangle.e1->segment.v2->p.x, + debugface->triangle.e1->segment.v2->p.y + ); + fprintf(stderr, "\t%f,%f %f,%f\n", + debugface->triangle.e2->segment.v1->p.x, + debugface->triangle.e2->segment.v1->p.y, + debugface->triangle.e2->segment.v2->p.x, + debugface->triangle.e2->segment.v2->p.y + ); + fprintf(stderr, "\t%f,%f %f,%f\n", + debugface->triangle.e3->segment.v1->p.x, + debugface->triangle.e3->segment.v1->p.y, + debugface->triangle.e3->segment.v2->p.x, + debugface->triangle.e3->segment.v2->p.y + ); +/* toporouter_draw_surface(r, l->surface, "debug.png", 4096, 4096); */ + { + int i; + for (i = 0; i < groupcount(); i++) { + char buffer[256]; + sprintf(buffer, "debug-%d.png", i); + toporouter_draw_surface(r, r->layers[i].surface, buffer, 2048, 2048, 2, NULL, i, NULL); + } + } + +/* }*/ + +check_cons_continuation: + i = l->constraints; + while (i) { + toporouter_constraint_t *c1 = TOPOROUTER_CONSTRAINT(i->data); + GList *j = i->next; + /* printf("adding cons: "); print_constraint(c1); */ + + while (j) { + toporouter_constraint_t *c2 = TOPOROUTER_CONSTRAINT(j->data); + guint rem = 0; + GList *temp; + + /* printf("\tconflict: "); print_constraint(c2); */ + toporouter_bbox_t *c1box = c1->box, *c2box = c2->box; + toporouter_vertex_t *c1v1 = tedge_v1(c1); + toporouter_vertex_t *c1v2 = tedge_v2(c1); + toporouter_vertex_t *c2v1 = tedge_v1(c2); + toporouter_vertex_t *c2v2 = tedge_v2(c2); + + if (gts_segments_are_intersecting(GTS_SEGMENT(c1), GTS_SEGMENT(c2)) == GTS_IN) { + toporouter_vertex_t *v; + unconstrain(l, c1); + unconstrain(l, c2); + rem = 1; + /* proper intersection */ + v = TOPOROUTER_VERTEX(vertex_intersect(GTS_VERTEX(c1v1), GTS_VERTEX(c1v2), GTS_VERTEX(c2v1), GTS_VERTEX(c2v2))); + + /* remove both constraints + replace with 4x constraints + insert new intersection vertex */ + GTS_POINT(v)->z = vz(c1v1); + + l->vertices = g_list_prepend(l->vertices, v); +/* gts_delaunay_add_vertex (l->surface, GTS_VERTEX(v), NULL); */ + + v->bbox = c1box; + + temp = insert_constraint_edge(r, l, vx(c1v1), vy(c1v1), 0, vx(v), vy(v), 0, c1box); + c1box->constraints = g_list_concat(c1box->constraints, temp); + + temp = insert_constraint_edge(r, l, vx(c1v2), vy(c1v2), 0, vx(v), vy(v), 0, c1box); + c1box->constraints = g_list_concat(c1box->constraints, temp); + + temp = insert_constraint_edge(r, l, vx(c2v1), vy(c2v1), 0, vx(v), vy(v), 0, c2box); + c2box->constraints = g_list_concat(c2box->constraints, temp); + + temp = insert_constraint_edge(r, l, vx(c2v2), vy(c2v2), 0, vx(v), vy(v), 0, c2box); + c2box->constraints = g_list_concat(c2box->constraints, temp); + + } + else if (gts_segments_are_intersecting(GTS_SEGMENT(c1), GTS_SEGMENT(c2)) == GTS_ON || + gts_segments_are_intersecting(GTS_SEGMENT(c2), GTS_SEGMENT(c1)) == GTS_ON) { + + if (vertex_between(edge_v1(c2), edge_v2(c2), edge_v1(c1)) && vertex_between(edge_v1(c2), edge_v2(c2), edge_v2(c1))) { + unconstrain(l, c1); + unconstrain(l, c2); + rem = 1; + /* remove c1 */ + temp = insert_constraint_edge(r, l, vx(c2v1), vy(c2v1), 0, vx(c2v2), vy(c2v2), 0, c2box); + c2box->constraints = g_list_concat(c2box->constraints, temp); + + } + else if (vertex_between(edge_v1(c1), edge_v2(c1), edge_v1(c2)) && vertex_between(edge_v1(c1), edge_v2(c1), edge_v2(c2))) { + unconstrain(l, c1); + unconstrain(l, c2); + rem = 1; + /* remove c2 */ + temp = insert_constraint_edge(r, l, vx(c1v1), vy(c1v1), 0, vx(c1v2), vy(c1v2), 0, c1box); + c1box->constraints = g_list_concat(c1box->constraints, temp); + + /*}else if(!vertex_wind(edge_v1(c1), edge_v2(c1), edge_v1(c2)) && !vertex_wind(edge_v1(c1), edge_v2(c1), edge_v2(c2))) { */ + /* }else if(vertex_between(edge_v1(c1), edge_v2(c1), edge_v1(c2)) || vertex_between(edge_v1(c1), edge_v2(c1), edge_v2(c2))) { + unconstrain(l, c1); unconstrain(l, c2); + rem = 1; + printf("all colinear\n"); + // exit(1); + temp = insert_constraint_edge(r, l, vx(c1v1), vy(c1v1), 0, vx(c1v2), vy(c1v2), 0, c1box); + c1box->constraints = g_list_concat(c1box->constraints, temp); + + if(vertex_between(GTS_VERTEX(c1v1), GTS_VERTEX(c1v2), GTS_VERTEX(c2v2))) { + // v2 of c2 is inner + if(vertex_between(GTS_VERTEX(c2v1), GTS_VERTEX(c2v2), GTS_VERTEX(c1v2))) { + // v2 of c1 is inner + // c2 = c1.v2 -> c2.v1 + temp = insert_constraint_edge(r, l, vx(c1v2), vy(c1v2), 0, vx(c2v1), vy(c2v1), 0, c2box); + c2box->constraints = g_list_concat(c2box->constraints, temp); + }else{ + // v1 of c1 is inner + // c2 = c1.v1 -> c2.v1 + temp = insert_constraint_edge(r, l, vx(c1v1), vy(c1v1), 0, vx(c2v1), vy(c2v1), 0, c2box); + c2box->constraints = g_list_concat(c2box->constraints, temp); + } + }else{ + // v1 of c2 is inner + if(vertex_between(GTS_VERTEX(c2v1), GTS_VERTEX(c2v2), GTS_VERTEX(c1v2))) { + // v2 of c1 is inner + // c2 = c1.v2 -> c2.v2 + temp = insert_constraint_edge(r, l, vx(c1v2), vy(c1v2), 0, vx(c2v2), vy(c2v2), 0, c2box); + c2box->constraints = g_list_concat(c2box->constraints, temp); + }else{ + // v1 of c1 is inner + // c2 = c1.v1 -> c2.v2 + temp = insert_constraint_edge(r, l, vx(c1v1), vy(c1v1), 0, vx(c2v2), vy(c2v2), 0, c2box); + c2box->constraints = g_list_concat(c2box->constraints, temp); + } + } */ + } + else if (vertex_between(edge_v1(c2), edge_v2(c2), edge_v1(c1)) && c1v1 != c2v1 && c1v1 != c2v2) { + unconstrain(l, c1); + unconstrain(l, c2); + rem = 1; + /*v1 of c1 is on c2 */ + printf("v1 of c1 on c2\n"); + + /* replace with 2x constraints */ + temp = insert_constraint_edge(r, l, vx(c2v1), vy(c2v1), 0, vx(c1v1), vy(c1v1), 0, c2box); + c2box->constraints = g_list_concat(c2box->constraints, temp); + temp = insert_constraint_edge(r, l, vx(c2v2), vy(c2v2), 0, vx(c1v1), vy(c1v1), 0, c2box); + c2box->constraints = g_list_concat(c2box->constraints, temp); + + temp = insert_constraint_edge(r, l, vx(c1v1), vy(c1v1), 0, vx(c1v2), vy(c1v2), 0, c1box); + c1box->constraints = g_list_concat(c1box->constraints, temp); + + /* restore c1 + temp = insert_constraint_edge(r, l, vx(tedge_v2(c1)), vy(tedge_v2(c1)), 0, vx(tedge_v1(c1)), vy(tedge_v1(c1)), 0, c1->box); + c2->box->constraints = g_list_concat(c2->box->constraints, temp); */ + + } + else if (vertex_between(edge_v1(c2), edge_v2(c2), edge_v2(c1)) && c1v2 != c2v1 && c1v2 != c2v2) { + unconstrain(l, c1); + unconstrain(l, c2); + rem = 1; + /*v2 of c1 is on c2 */ + printf("v2 of c1 on c2\n"); + + /* replace with 2x constraints */ + temp = insert_constraint_edge(r, l, vx(c2v1), vy(c2v1), 0, vx(c1v2), vy(c1v2), 0, c2box); + c2box->constraints = g_list_concat(c2box->constraints, temp); + temp = insert_constraint_edge(r, l, vx(c2v2), vy(c2v2), 0, vx(c1v2), vy(c1v2), 0, c2box); + c2box->constraints = g_list_concat(c2box->constraints, temp); + + temp = insert_constraint_edge(r, l, vx(c1v1), vy(c1v1), 0, vx(c1v2), vy(c1v2), 0, c1box); + c1box->constraints = g_list_concat(c1box->constraints, temp); + + } + else if (vertex_between(edge_v1(c1), edge_v2(c1), edge_v1(c2)) && c2v1 != c1v1 && c2v1 != c1v2) { + unconstrain(l, c1); + unconstrain(l, c2); + rem = 1; + /*v1 of c2 is on c1 */ + printf("v1 of c2 on c1\n"); + + /* replace with 2x constraints */ + temp = insert_constraint_edge(r, l, vx(c1v1), vy(c1v1), 0, vx(c2v1), vy(c2v1), 0, c1box); + c1box->constraints = g_list_concat(c1box->constraints, temp); + temp = insert_constraint_edge(r, l, vx(c1v2), vy(c1v2), 0, vx(c2v1), vy(c2v1), 0, c1box); + c1box->constraints = g_list_concat(c1box->constraints, temp); + + temp = insert_constraint_edge(r, l, vx(c2v1), vy(c2v1), 0, vx(c2v2), vy(c2v2), 0, c2box); + c2box->constraints = g_list_concat(c2box->constraints, temp); + } + else if (vertex_between(edge_v1(c1), edge_v2(c1), edge_v2(c2)) && c2v2 != c1v1 && c2v2 != c1v2) { + unconstrain(l, c1); + unconstrain(l, c2); + rem = 1; + /*v2 of c2 is on c1 */ + printf("v2 of c2 on c1\n"); + + /* replace with 2x constraints */ + temp = insert_constraint_edge(r, l, vx(c1v1), vy(c1v1), 0, vx(c2v2), vy(c2v2), 0, c1box); + c1box->constraints = g_list_concat(c1box->constraints, temp); + temp = insert_constraint_edge(r, l, vx(c1v2), vy(c1v2), 0, vx(c2v2), vy(c2v2), 0, c1box); + c1box->constraints = g_list_concat(c1box->constraints, temp); + + temp = insert_constraint_edge(r, l, vx(c2v1), vy(c2v1), 0, vx(c2v2), vy(c2v2), 0, c2box); + c2box->constraints = g_list_concat(c2box->constraints, temp); + } + } + if (rem) + goto check_cons_continuation; + + j = j->next; + } + + i = i->next; + } + + i = l->vertices; + while (i) { + /*v = i->data; + if(r->flags & TOPOROUTER_FLAG_DEBUG_CDTS) + fprintf(stderr, "\tadding vertex %f,%f\n", v->p.x, v->p.y); */ + toporouter_vertex_t *v = TOPOROUTER_VERTEX(gts_delaunay_add_vertex(l->surface, (GtsVertex *) i->data, NULL)); + if (v) { + printf("conflict: "); + print_vertex(v); + } + + i = i->next; + } + i = l->constraints; + while (i) { + + /* toporouter_constraint_t *c1 = TOPOROUTER_CONSTRAINT(i->data); + printf("adding cons: "); print_constraint(c1); */ + + GSList *conflicts = gts_delaunay_add_constraint(l->surface, (GtsConstraint *) i->data); + GSList *j = conflicts; + while (j) { + if (TOPOROUTER_IS_CONSTRAINT(j->data)) { + toporouter_constraint_t *c2 = TOPOROUTER_CONSTRAINT(j->data); + + printf("\tconflict: "); + print_constraint(c2); + + } + j = j->next; + } + g_slist_free(conflicts); + + i = i->next; + } + +/* if(rerun) + goto build_cdt_continuation; + fprintf(stderr, "ADDED CONSTRAINTS\n");*/ + gts_allow_floating_vertices = TRUE; + gts_object_destroy(GTS_OBJECT(v1)); + gts_object_destroy(GTS_OBJECT(v2)); + gts_object_destroy(GTS_OBJECT(v3)); + gts_allow_floating_vertices = FALSE; + +/* + { + gpointer data[2]; + gdouble quality = 0.50, area = G_MAXDOUBLE; + guint num = gts_delaunay_conform(l->surface, -1, (GtsEncroachFunc) gts_vertex_encroaches_edge, NULL); + + if (num == 0){ + data[0] = &quality; + data[1] = &area; + num = gts_delaunay_refine(l->surface, -1, (GtsEncroachFunc) gts_vertex_encroaches_edge, NULL, (GtsKeyFunc) triangle_cost, data); + } + } +*/ +#ifdef DEBUG_IMPORT + gts_surface_print_stats(l->surface, stderr); +#endif + +#if 0 + { + char buffer[64]; + FILE *fout2; + sprintf(buffer, "surface%d.gts", l - r->layers); + fout2 = fopen(buffer, "w"); + gts_surface_write(l->surface, fout2); + } +#endif + +} + +gint visited_cmp(gconstpointer a, gconstpointer b) +{ + if (a < b) + return -1; + if (a > b) + return 1; + return 0; +} + +gdouble coord_xangle(gdouble ax, gdouble ay, gdouble bx, gdouble by) +{ + gdouble dx, dy, theta; + + dx = fabs(ax - bx); + dy = fabs(ay - by); + + if (dx < EPSILON) { + theta = M_PI / 2.; + } + else + theta = atan(dy / dx); + + if (by <= ay) { + if (bx < ax) + theta = M_PI - theta; + } + else { + if (bx < ax) + theta += M_PI; + else + theta = (2 * M_PI) - theta; + } + + return theta; +} + +gdouble point_xangle(GtsPoint * a, GtsPoint * b) +{ + gdouble dx, dy, theta; + + dx = fabs(a->x - b->x); + dy = fabs(a->y - b->y); + + if (dx < EPSILON) { + theta = M_PI / 2.; + } + else + theta = atan(dy / dx); + + if (b->y >= a->y) { + if (b->x < a->x) + theta = M_PI - theta; + } + else { + if (b->x < a->x) + theta += M_PI; + else + theta = (2 * M_PI) - theta; + } + + return theta; +} + + +GList *cluster_vertices(toporouter_t * r, toporouter_cluster_t * c) +{ + GList *rval = NULL; + + if (!c) + return NULL; + + FOREACH_CLUSTER(c->netlist->clusters) { + if ((r->flags & TOPOROUTER_FLAG_AFTERRUBIX && cluster->c == c->c) + || (!(r->flags & TOPOROUTER_FLAG_AFTERRUBIX) && cluster == c)) { + FOREACH_BBOX(cluster->boxes) { + if (box->type == LINE) { + g_assert(box->constraints->data); + rval = g_list_prepend(rval, tedge_v1(box->constraints->data)); + rval = g_list_prepend(rval, tedge_v2(box->constraints->data)); + } + else if (box->point) { + rval = g_list_prepend(rval, TOPOROUTER_VERTEX(box->point)); + /*g_assert(vertex_bbox(TOPOROUTER_VERTEX(box->point)) == box); */ + } + else { + printf("WARNING: cluster_vertices: unhandled bbox type\n"); + } + + } + FOREACH_END; + + + } + + } + FOREACH_END; + + return rval; +} + +void print_cluster(toporouter_cluster_t * c) +{ + + if (!c) { + printf("[CLUSTER (NULL)]\n"); + return; + } + + printf("CLUSTER %d: NETLIST = %s STYLE = %s\n", c->c, c->netlist->netlist, c->netlist->style); + + FOREACH_BBOX(c->boxes) { + print_bbox(box); + } + FOREACH_END; +} + + +toporouter_cluster_t *cluster_create(toporouter_t * r, toporouter_netlist_t * netlist) +{ + toporouter_cluster_t *c = (toporouter_cluster_t *) malloc(sizeof(toporouter_cluster_t)); + + c->c = c->pc = netlist->clusters->len; + g_ptr_array_add(netlist->clusters, c); + c->netlist = netlist; + c->boxes = g_ptr_array_new(); + + return c; +} + +toporouter_bbox_t *toporouter_bbox_locate(toporouter_t * r, toporouter_term_t type, void *data, gdouble x, gdouble y, + guint layergroup) +{ + GtsPoint *p = gts_point_new(gts_point_class(), x, y, layergroup); + GSList *boxes = gts_bb_tree_stabbed(r->bboxtree, p), *i = boxes; + + gts_object_destroy(GTS_OBJECT(p)); + + while (i) { + toporouter_bbox_t *box = TOPOROUTER_BBOX(i->data); + + if (box->type == type && box->data == data) { + g_slist_free(boxes); + return box; + } + + i = i->next; + } + + g_slist_free(boxes); + return NULL; +} + +void cluster_join_bbox(toporouter_cluster_t * cluster, toporouter_bbox_t * box) +{ + if (box) { + g_ptr_array_add(cluster->boxes, box); + box->cluster = cluster; + } +} + +toporouter_netlist_t *netlist_create(toporouter_t * r, char *netlist, char *style) +{ + toporouter_netlist_t *nl = (toporouter_netlist_t *) malloc(sizeof(toporouter_netlist_t)); + nl->netlist = netlist; + nl->style = style; + nl->clusters = g_ptr_array_new(); + nl->routes = g_ptr_array_new(); + nl->routed = NULL; + nl->pair = NULL; + g_ptr_array_add(r->netlists, nl); + return nl; +} + +void import_clusters(toporouter_t * r) +{ + pcb_netlist_list_t nets; + pcb_reset_conns(pcb_false); + nets = pcb_rat_collect_subnets(pcb_false); + PCB_NETLIST_LOOP(&nets); + { + if (netlist->NetN > 0) { + toporouter_netlist_t *nl = netlist_create(r, netlist->Net->Connection->menu->Name, netlist->Net->Connection->menu->Style); + + PCB_NET_LOOP(netlist); + { + + toporouter_cluster_t *cluster = cluster_create(r, nl); +#ifdef DEBUG_MERGING + printf("NET:\n"); +#endif + PCB_CONNECTION_LOOP(net); + { + + if (connection->type == PCB_TYPE_LINE) { + pcb_line_t *line = (pcb_line_t *) connection->ptr2; + toporouter_bbox_t *box = toporouter_bbox_locate(r, LINE, line, connection->X, connection->Y, connection->group); + cluster_join_bbox(cluster, box); + +#ifdef DEBUG_MERGING + pcb_printf("\tLINE %#mD\n", connection->X, connection->Y); +#endif + } + else if (connection->type == PCB_TYPE_PAD) { + pcb_pad_t *pad = (pcb_pad_t *) connection->ptr2; + toporouter_bbox_t *box = toporouter_bbox_locate(r, PAD, pad, connection->X, connection->Y, connection->group); + cluster_join_bbox(cluster, box); + +#ifdef DEBUG_MERGING + pcb_printf("\tPAD %#mD\n", connection->X, connection->Y); +#endif + } + else if (connection->type == PCB_TYPE_PIN) { + guint m; + for (m = 0; m < groupcount(); m++) { + pcb_pin_t *pin = (pcb_pin_t *) connection->ptr2; + toporouter_bbox_t *box = toporouter_bbox_locate(r, PIN, pin, connection->X, connection->Y, m); + cluster_join_bbox(cluster, box); + } + +#ifdef DEBUG_MERGING + pcb_printf("\tPIN %#mD\n", connection->X, connection->Y); +#endif + } + else if (connection->type == PCB_TYPE_VIA) { + guint m; + for (m = 0; m < groupcount(); m++) { + pcb_pin_t *pin = (pcb_pin_t *) connection->ptr2; + toporouter_bbox_t *box = toporouter_bbox_locate(r, VIA, pin, connection->X, connection->Y, m); + cluster_join_bbox(cluster, box); + } + +#ifdef DEBUG_MERGING + pcb_printf("\tVIA %#mD\n", connection->X, connection->Y); +#endif + } + else if (connection->type == PCB_TYPE_POLYGON) { + pcb_polygon_t *polygon = (pcb_polygon_t *) connection->ptr2; + toporouter_bbox_t *box = + toporouter_bbox_locate(r, POLYGON, polygon, connection->X, connection->Y, connection->group); + cluster_join_bbox(cluster, box); + +#ifdef DEBUG_MERGING + pcb_printf("\tPOLYGON %#mD\n", connection->X, connection->Y); +#endif + + } + } + PCB_END_LOOP; +#ifdef DEBUG_MERGING + printf("\n"); +#endif + } + PCB_END_LOOP; + + } + } + PCB_END_LOOP; + pcb_netlist_list_free(&nets); +} + +void import_geometry(toporouter_t * r) +{ + toporouter_layer_t *cur_layer; + + int group; + +#ifdef DEBUG_IMPORT + for (group = 0; group < pcb_max_group; group++) { + printf("Group %d: Number %d:\n", group, PCB->LayerGroups.Number[group]); + + for (int entry = 0; entry < PCB->LayerGroups.Number[group]; entry++) { + printf("\tEntry %d\n", PCB->LayerGroups.Entries[group][entry]); + } + } +#endif + /* Allocate space for per layer struct */ + cur_layer = r->layers = (toporouter_layer_t *) malloc(groupcount() * sizeof(toporouter_layer_t)); + + /* Foreach layer, read in pad vertices and constraints, and build CDT */ + for (group = 0; group < pcb_max_group; group++) { +#ifdef DEBUG_IMPORT + printf("*** LAYER GROUP %d ***\n", group); +#endif + if (PCB->LayerGroups.Number[group] > 0) { + cur_layer->vertices = NULL; + cur_layer->constraints = NULL; + +#ifdef DEBUG_IMPORT + printf("reading board constraints from layer %d into group %d\n", PCB->LayerGroups.Entries[group][0], group); +#endif + read_board_constraints(r, cur_layer, PCB->LayerGroups.Entries[group][0]); +#ifdef DEBUG_IMPORT + printf("reading points from layer %d into group %d \n", PCB->LayerGroups.Entries[group][0], group); +#endif + read_points(r, cur_layer, PCB->LayerGroups.Entries[group][0]); + +/*#ifdef DEBUG_IMPORT + printf("reading pads from layer %d into group %d\n", number, group); + #endif*/ + read_pads(r, cur_layer, group); + + PCB_COPPER_GROUP_LOOP(PCB->Data, group) { + +#ifdef DEBUG_IMPORT + printf("reading lines from layer %d into group %d\n", number, group); +#endif + read_lines(r, cur_layer, layer, number); + + } + PCB_END_LOOP; + + + +#ifdef DEBUG_IMPORT + printf("building CDT\n"); +#endif + build_cdt(r, cur_layer); + printf("finished\n"); +/* { + int i; + for(i=0;ilayers[i].surface, buffer, 2048, 2048, 2, NULL, i, NULL); + } + }*/ +#ifdef DEBUG_IMPORT + printf("finished building CDT\n"); +#endif + cur_layer++; + } + } + + r->bboxtree = gts_bb_tree_new(r->bboxes); + + import_clusters(r); + +#ifdef DEBUG_IMPORT + printf("finished import!\n"); +#endif +} + + +gint compare_points(gconstpointer a, gconstpointer b) +{ + GtsPoint *i = GTS_POINT(a); + GtsPoint *j = GTS_POINT(b); + + if (i->x == j->x) { + if (i->y == j->y) + return 0; + if (i->y < j->y) + return -1; + return 1; + } + if (i->x < j->x) + return -1; + return 1; +} + +gint compare_segments(gconstpointer a, gconstpointer b) +{ + if (a == b) + return 0; + if (a < b) + return -1; + return 1; +} + +#define DEBUG_CLUSTER_FIND 1 +toporouter_cluster_t *cluster_find(toporouter_t * r, gdouble x, gdouble y, gdouble z) +{ + GtsPoint *p = gts_point_new(gts_point_class(), x, y, z); + GSList *hits = gts_bb_tree_stabbed(r->bboxtree, p); + toporouter_cluster_t *rval = NULL; + +#ifdef DEBUG_CLUSTER_FIND + printf("FINDING %f,%f,%f\n\n", x, y, z); +#endif + + while (hits) { + toporouter_bbox_t *box = TOPOROUTER_BBOX(hits->data); + +#ifdef DEBUG_CLUSTER_FIND + printf("HIT BOX: "); + print_bbox(box); +#endif + + if (box->layer == (int) z) { + if (box->type != BOARD) { + if (box->type == LINE) { + pcb_line_t *line = (pcb_line_t *) box->data; + gint linewind = coord_wind(line->Point1.X, line->Point1.Y, x, y, line->Point2.X, line->Point2.Y); + + if (line->Point1.X > x - EPSILON && line->Point1.X < x + EPSILON && + line->Point1.Y > y - EPSILON && line->Point1.Y < y + EPSILON) { + rval = box->cluster; + /* break; */ + } + if (line->Point2.X > x - EPSILON && line->Point2.X < x + EPSILON && + line->Point2.Y > y - EPSILON && line->Point2.Y < y + EPSILON) { + rval = box->cluster; + /* break; */ + } + if (!linewind) { + rval = box->cluster; + /* break; */ + } + + } + else if (box->surface) { + + if (gts_point_locate(p, box->surface, NULL)) { + rval = box->cluster; + break; + } + + } + } + } + hits = hits->next; + } + + gts_object_destroy(GTS_OBJECT(p)); + + +#ifdef DEBUG_CLUSTER_FIND + printf("cluster_find: %f,%f,%f: ", x, y, z); + print_cluster(rval); +#endif + + return rval; +} + +gdouble simple_h_cost(toporouter_t * r, toporouter_vertex_t * curpoint, toporouter_vertex_t * destpoint) +{ + gdouble layerpenalty = (vz(curpoint) == vz(destpoint)) ? 0. : r->viacost; + + return gts_point_distance(GTS_POINT(curpoint), GTS_POINT(destpoint)) + layerpenalty; +} + +#define FCOST(x) (x->gcost + x->hcost) +gdouble route_heap_cmp(gpointer item, gpointer data) +{ + return FCOST(TOPOROUTER_VERTEX(item)); +} + +#define closelist_insert(p) closelist = g_list_prepend(closelist, p) + +typedef struct { + toporouter_vertex_t *key; + toporouter_vertex_t *result; +} toporouter_heap_search_data_t; + +void toporouter_heap_search(gpointer data, gpointer user_data) +{ + toporouter_vertex_t *v = TOPOROUTER_VERTEX(data); + toporouter_heap_search_data_t *heap_search_data = (toporouter_heap_search_data_t *) user_data; + if (v == heap_search_data->key) + heap_search_data->result = v; +} + +/* +void +toporouter_heap_color(gpointer data, gpointer user_data) +{ + toporouter_vertex_t *v = TOPOROUTER_VERTEX(data); + v->flags |= (guint) user_data; +} +*/ +static inline gdouble angle_span(gdouble a1, gdouble a2) +{ + if (a1 > a2) + return ((2 * M_PI) - a1 + a2); + return a2 - a1; +} + +gdouble region_span(toporouter_vertex_region_t * region) +{ + gdouble a1, a2; + + g_assert(region->v1 != NULL); + g_assert(region->v2 != NULL); + g_assert(region->origin != NULL); + + a1 = point_xangle(GTS_POINT(region->origin), GTS_POINT(region->v1)); + a2 = point_xangle(GTS_POINT(region->origin), GTS_POINT(region->v2)); + + return angle_span(a1, a2); +} + +gdouble edge_capacity(toporouter_edge_t * e) +{ + return gts_point_distance(GTS_POINT(edge_v1(e)), GTS_POINT(edge_v2(e))); +} + +gdouble edge_flow(toporouter_edge_t * e, toporouter_vertex_t * v1, toporouter_vertex_t * v2, toporouter_vertex_t * dest) +{ + GList *i = edge_routing(e); + toporouter_vertex_t *pv = tedge_v1(e), *v = NULL; + gdouble flow = 0.; + guint waiting = 1; + + if ((pv == v1 || pv == v2) && waiting) { + flow += min_vertex_net_spacing(pv, dest); + pv = dest; + waiting = 0; + } + + g_assert(v1 != v2); + + while (i) { + v = TOPOROUTER_VERTEX(i->data); + + + if (pv == dest) + flow += min_vertex_net_spacing(v, pv); + else + flow += min_spacing(v, pv); + + if ((v == v1 || v == v2) && waiting) { + flow += min_vertex_net_spacing(v, dest); + pv = dest; + waiting = 0; + } + else { + pv = v; + } + i = i->next; + } + + if (pv == dest) + flow += min_vertex_net_spacing(tedge_v2(e), pv); + else + flow += min_spacing(tedge_v2(e), pv); + + return flow; +} + +void print_path(GList * path) +{ + GList *i = path; + + printf("PATH:\n"); + while (i) { + toporouter_vertex_t *v = TOPOROUTER_VERTEX(i->data); +/* printf("[V %f,%f,%f]\n", vx(v), vy(v), vz(v));*/ + print_vertex(v); + + if (v->child && !g_list_find(path, v->child)) + printf("\t CHILD NOT IN LIST\n"); + if (v->parent && !g_list_find(path, v->parent)) + printf("\t parent NOT IN LIST\n"); + i = i->next; + } + + +} + +GList *split_path(GList * path) +{ + toporouter_vertex_t *pv = NULL; + GList *curpath = NULL, *i, *paths = NULL; +#ifdef DEBUG_ROUTE + printf("PATH:\n"); +#endif + i = path; + while (i) { + toporouter_vertex_t *v = TOPOROUTER_VERTEX(i->data); + +#ifdef DEBUG_ROUTE + printf("v = %f,%f ", vx(v), vy(v)); + if (v->parent) + printf("parent = %f,%f ", vx(v->parent), vy(v->parent)); + if (v->child) + printf("child = %f,%f ", vx(v->child), vy(v->child)); + printf("\n"); +#endif +/* printf("***\n"); + if(v) printf("v = %f,%f\n", GTS_POINT(v)->x, GTS_POINT(v)->y); + if(pv) printf("pv = %f,%f\n", GTS_POINT(pv)->x, GTS_POINT(pv)->y);*/ + + + if (pv) + if (GTS_POINT(v)->x == GTS_POINT(pv)->x && GTS_POINT(v)->y == GTS_POINT(pv)->y) { + if (g_list_length(curpath) > 1) + paths = g_list_prepend(paths, curpath); + curpath = NULL; + + pv->child = NULL; + v->parent = NULL; + } + + curpath = g_list_append(curpath, v); + + pv = v; + i = i->next; + } + + if (g_list_length(curpath) > 1) + paths = g_list_prepend(paths, curpath); + + return paths; +} + + + +#define edge_gradient(e) (cartesian_gradient(GTS_POINT(GTS_SEGMENT(e)->v1)->x, GTS_POINT(GTS_SEGMENT(e)->v1)->y, \ + GTS_POINT(GTS_SEGMENT(e)->v2)->x, GTS_POINT(GTS_SEGMENT(e)->v2)->y)) + + +/* sorting into ascending distance from v1 */ +gint routing_edge_insert(gconstpointer a, gconstpointer b, gpointer user_data) +{ + GtsPoint *v1 = GTS_POINT(edge_v1(user_data)); + + if (gts_point_distance2(v1, GTS_POINT(a)) < gts_point_distance2(v1, GTS_POINT(b)) - EPSILON) + return -1; + if (gts_point_distance2(v1, GTS_POINT(a)) > gts_point_distance2(v1, GTS_POINT(b)) + EPSILON) + return 1; +/* + printf("a = %x b = %x\n", (int) a, (int) b); + + printf("WARNING: routing_edge_insert() with same points..\n \ + v1 @ %f,%f\n\ + a @ %f,%f\n\ + b @ %f,%f\n", + v1->x, v1->y, + vx(a), vy(a), + vx(a), vy(b)); + printf("A: "); print_vertex(TOPOROUTER_VERTEX(a)); + printf("B: "); print_vertex(TOPOROUTER_VERTEX(b)); + + TOPOROUTER_VERTEX(a)->flags |= VERTEX_FLAG_RED; + TOPOROUTER_VERTEX(b)->flags |= VERTEX_FLAG_RED; +*/ + return 0; +} + + +toporouter_vertex_t *new_temp_toporoutervertex(gdouble x, gdouble y, toporouter_edge_t * e) +{ + GtsVertexClass *vertex_class = GTS_VERTEX_CLASS(toporouter_vertex_class()); + GList *i = edge_routing(e); + toporouter_vertex_t *r; + + while (i) { + r = TOPOROUTER_VERTEX(i->data); + if (epsilon_equals(vx(r), x) && epsilon_equals(vy(r), y)) { + if (r->flags & VERTEX_FLAG_TEMP) + return r; + } + i = i->next; + } + + r = TOPOROUTER_VERTEX(gts_vertex_new(vertex_class, x, y, vz(edge_v1(e)))); + r->flags |= VERTEX_FLAG_TEMP; + r->routingedge = e; + + if (TOPOROUTER_IS_CONSTRAINT(e)) + TOPOROUTER_CONSTRAINT(e)->routing = g_list_insert_sorted_with_data(edge_routing(e), r, routing_edge_insert, e); + else + e->routing = g_list_insert_sorted_with_data(edge_routing(e), r, routing_edge_insert, e); + + return r; +} + + +/* create vertex on edge e at radius r from v, closest to ref */ +toporouter_vertex_t *new_temp_toporoutervertex_in_segment(toporouter_edge_t * e, toporouter_vertex_t * v, gdouble r, + toporouter_vertex_t * ref) +{ + gdouble m = edge_gradient(e); + toporouter_spoint_t p, np1, np2; +/* toporouter_vertex_t *b = TOPOROUTER_VERTEX((GTS_VERTEX(v) == edge_v1(e)) ? edge_v2(e) : edge_v1(e)); */ + toporouter_vertex_t *rval = NULL; + p.x = vx(v); + p.y = vy(v); + + vertices_on_line(&p, m, r, &np1, &np2); + + if ((pow(np1.x - vx(ref), 2) + pow(np1.y - vy(ref), 2)) < (pow(np2.x - vx(ref), 2) + pow(np2.y - vy(ref), 2))) + rval = new_temp_toporoutervertex(np1.x, np1.y, e); + else + rval = new_temp_toporoutervertex(np2.x, np2.y, e); + + return rval; +} + +gint vertex_keepout_test(toporouter_t * r, toporouter_vertex_t * v) +{ + GList *i = r->keepoutlayers; + while (i) { + gdouble keepout = *((double *) i->data); + if (vz(v) == keepout) + return 1; + i = i->next; + } + return 0; +} + +void +closest_cluster_pair(toporouter_t * r, GList * src_vertices, GList * dest_vertices, toporouter_vertex_t ** a, + toporouter_vertex_t ** b) +{ + GList *i = src_vertices, *j = dest_vertices; + + gdouble min = 0.; + *a = NULL; + *b = NULL; + + i = src_vertices; + while (i) { + toporouter_vertex_t *v1 = TOPOROUTER_VERTEX(i->data); + + if (vertex_keepout_test(r, v1)) { + i = i->next; + continue; + } + + j = dest_vertices; + while (j) { + toporouter_vertex_t *v2 = TOPOROUTER_VERTEX(j->data); + if (vertex_keepout_test(r, v2) || vz(v2) != vz(v1)) { + j = j->next; + continue; + } + + if (!*a) { + *a = v1; + *b = v2; + min = simple_h_cost(r, *a, *b); + } + else { + gdouble tempd = simple_h_cost(r, v1, v2); + if (r->flags & TOPOROUTER_FLAG_GOFAR && tempd > min) { + *a = v1; + *b = v2; + min = tempd; + } + else if (tempd < min) { + *a = v1; + *b = v2; + min = tempd; + } + } + + j = j->next; + } + + i = i->next; + } + +/* g_list_free(src_vertices); + g_list_free(dest_vertices);*/ +} + + +toporouter_vertex_t *closest_dest_vertex(toporouter_t * r, toporouter_vertex_t * v, toporouter_route_t * routedata) +{ + GList /* *vertices = cluster_vertices(r, routedata->dest), */ + * i = routedata->destvertices; + toporouter_vertex_t *closest = NULL; + gdouble closest_distance = 0.; + +/* if(routedata->flags & TOPOROUTER_FLAG_FLEX) i = r->destboxes; */ + + while (i) { + toporouter_vertex_t *cv = TOPOROUTER_VERTEX(i->data); + + if (vz(cv) != vz(v)) { + i = i->next; + continue; + } + + if (!closest) { + closest = cv; + closest_distance = simple_h_cost(r, v, closest); + } + else { + gdouble tempd = simple_h_cost(r, v, cv); + if (r->flags & TOPOROUTER_FLAG_GOFAR && tempd > closest_distance) { + closest = cv; + closest_distance = tempd; + } + else if (tempd < closest_distance) { + closest = cv; + closest_distance = tempd; + } + } + i = i->next; + } + +/* g_list_free(vertices); */ + +#ifdef DEBUG_ROUTE + printf("CLOSEST = %f,%f,%f\n", vx(closest), vy(closest), vz(closest)); +#endif + return closest; +} + +#define toporouter_edge_gradient(e) (cartesian_gradient(vx(edge_v1(e)), vy(edge_v1(e)), vx(edge_v2(e)), vy(edge_v2(e)))) + + +/* returns the capacity of the triangle cut through v */ +gdouble triangle_interior_capacity(GtsTriangle * t, toporouter_vertex_t * v) +{ + toporouter_edge_t *e = TOPOROUTER_EDGE(gts_triangle_edge_opposite(t, GTS_VERTEX(v))); + gdouble x, y, m1, m2, c2, c1; +#ifdef DEBUG_ROUTE + gdouble len; +#endif + + g_assert(e); + + m1 = toporouter_edge_gradient(e); + m2 = perpendicular_gradient(m1); + c2 = (isinf(m2)) ? vx(v) : vy(v) - (m2 * vx(v)); + c1 = (isinf(m1)) ? vx(edge_v1(e)) : vy(edge_v1(e)) - (m1 * vx(edge_v1(e))); + + if (isinf(m2)) + x = vx(v); + else if (isinf(m1)) + x = vx(edge_v1(e)); + else + x = (c2 - c1) / (m1 - m2); + + y = (isinf(m2)) ? vy(edge_v1(e)) : (m2 * x) + c2; + +#ifdef DEBUG_ROUTE + len = gts_point_distance2(GTS_POINT(edge_v1(e)), GTS_POINT(edge_v2(e))); + printf("%f,%f len = %f v = %f,%f\n", x, y, len, vx(v), vy(v)); +#endif + + if (epsilon_equals(x, vx(edge_v1(e))) && epsilon_equals(y, vy(edge_v1(e)))) + return INFINITY; + if (epsilon_equals(x, vx(edge_v2(e))) && epsilon_equals(y, vy(edge_v2(e)))) + return INFINITY; + + if (x >= MIN(vx(edge_v1(e)), vx(edge_v2(e))) && + x <= MAX(vx(edge_v1(e)), vx(edge_v2(e))) && + y >= MIN(vy(edge_v1(e)), vy(edge_v2(e))) && y <= MAX(vy(edge_v1(e)), vy(edge_v2(e)))) + +/* if( (pow(vx(edge_v1(e)) - x, 2) + pow(vy(edge_v1(e)) - y, 2)) < len && (pow(vx(edge_v2(e)) - x, 2) + pow(vy(edge_v2(e)) - y, 2)) < len ) */ + return sqrt(pow(vx(v) - x, 2) + pow(vy(v) - y, 2)); + + return INFINITY; +} + +static inline toporouter_vertex_t *segment_common_vertex(GtsSegment * s1, GtsSegment * s2) +{ + if (!s1 || !s2) + return NULL; + if (s1->v1 == s2->v1) + return TOPOROUTER_VERTEX(s1->v1); + if (s1->v2 == s2->v1) + return TOPOROUTER_VERTEX(s1->v2); + if (s1->v1 == s2->v2) + return TOPOROUTER_VERTEX(s1->v1); + if (s1->v2 == s2->v2) + return TOPOROUTER_VERTEX(s1->v2); + return NULL; +} + +static inline toporouter_vertex_t *route_vertices_common_vertex(toporouter_vertex_t * v1, toporouter_vertex_t * v2) +{ + return segment_common_vertex(GTS_SEGMENT(v1->routingedge), GTS_SEGMENT(v2->routingedge)); +} + + +static inline guint edges_third_edge(GtsSegment * s1, GtsSegment * s2, toporouter_vertex_t ** v1, toporouter_vertex_t ** v2) +{ + if (!s1 || !s2) + return 0; + if (s1->v1 == s2->v1) { + *v1 = TOPOROUTER_VERTEX(s1->v2); + *v2 = TOPOROUTER_VERTEX(s2->v2); + return 1; + } + if (s1->v2 == s2->v1) { + *v1 = TOPOROUTER_VERTEX(s1->v1); + *v2 = TOPOROUTER_VERTEX(s2->v2); + return 1; + } + if (s1->v1 == s2->v2) { + *v1 = TOPOROUTER_VERTEX(s1->v2); + *v2 = TOPOROUTER_VERTEX(s2->v1); + return 1; + } + if (s1->v2 == s2->v2) { + *v1 = TOPOROUTER_VERTEX(s1->v1); + *v2 = TOPOROUTER_VERTEX(s2->v1); + return 1; + } + return 0; +} + +/* returns the flow from e1 to e2, and the flow from the vertex oppisate e1 to + * e1 and the vertex oppisate e2 to e2 */ +gdouble +flow_from_edge_to_edge(GtsTriangle * t, toporouter_edge_t * e1, toporouter_edge_t * e2, + toporouter_vertex_t * common_v, toporouter_vertex_t * curpoint) +{ + gdouble r = 0.; + toporouter_vertex_t *pv = common_v, *v; + toporouter_edge_t *op_edge; + + GList *i = edge_routing(e1); + while (i) { + v = TOPOROUTER_VERTEX(i->data); + + if (v == curpoint) { + r += min_spacing(v, pv); + pv = v; + i = i->next; + continue; + } +/* if(!(v->flags & VERTEX_FLAG_TEMP)) { */ + if ((v->flags & VERTEX_FLAG_ROUTE)) { + if (v->parent) + if (v->parent->routingedge == e2) { + r += min_spacing(v, pv); + pv = v; + i = i->next; + continue; + } + + if (v->child) + if (v->child->routingedge == e2) { + r += min_spacing(v, pv); + pv = v; + i = i->next; + continue; + } + } + i = i->next; + } + + op_edge = TOPOROUTER_EDGE(gts_triangle_edge_opposite(t, GTS_VERTEX(common_v))); + + g_assert(op_edge); + g_assert(e1); + g_assert(e2); + + v = segment_common_vertex(GTS_SEGMENT(e2), GTS_SEGMENT(op_edge)); + g_assert(v); + + /*v = TOPOROUTER_VERTEX(gts_triangle_vertex_opposite(t, GTS_EDGE(e1))); */ + if (v->flags & VERTEX_FLAG_ROUTE && v->parent && v->parent->routingedge) { + if (v->parent->routingedge == e1) + r += min_spacing(v, pv); + } + + v = segment_common_vertex(GTS_SEGMENT(e1), GTS_SEGMENT(op_edge)); + g_assert(v); + + /*v = TOPOROUTER_VERTEX(gts_triangle_vertex_opposite(t, GTS_EDGE(e2))); */ + if (v->flags & VERTEX_FLAG_ROUTE && v->parent && v->parent->routingedge) { + if (v->parent->routingedge == e1) + r += min_spacing(v, pv); + } + + if (TOPOROUTER_IS_CONSTRAINT(op_edge)) { + toporouter_bbox_t *box = vertex_bbox(TOPOROUTER_VERTEX(edge_v1(op_edge))); + r += vertex_net_thickness(v) / 2.; + if (box) { + r += MAX(vertex_net_clearance(v), cluster_clearance(box->cluster)); + r += cluster_thickness(box->cluster) / 2.; + } + else { + r += vertex_net_clearance(v); + + } + } + + return r; +} + + + +guint +check_triangle_interior_capacity(GtsTriangle * t, toporouter_vertex_t * v, toporouter_vertex_t * curpoint, + toporouter_edge_t * op_edge, toporouter_edge_t * adj_edge1, toporouter_edge_t * adj_edge2) +{ + gdouble ic = triangle_interior_capacity(t, v); + gdouble flow = flow_from_edge_to_edge(t, adj_edge1, adj_edge2, v, curpoint); + + if (TOPOROUTER_IS_CONSTRAINT(adj_edge1) || TOPOROUTER_IS_CONSTRAINT(adj_edge2)) + return 1; + + + if (flow > ic) { +#ifdef DEBUG_ROUTE + printf("fail interior capacity flow = %f ic = %f\n", flow, ic); +#endif + return 0; + } + + return 1; +} + +toporouter_vertex_t *edge_routing_next_not_temp(toporouter_edge_t * e, GList * list) +{ + if (!TOPOROUTER_IS_CONSTRAINT(e)) { + while (list) { + toporouter_vertex_t *v = TOPOROUTER_VERTEX(list->data); + if (!(v->flags & VERTEX_FLAG_TEMP)) + return v; + + list = list->next; + } + } + return tedge_v2(e); +} + +toporouter_vertex_t *edge_routing_prev_not_temp(toporouter_edge_t * e, GList * list) +{ + if (!TOPOROUTER_IS_CONSTRAINT(e)) { + while (list) { + toporouter_vertex_t *v = TOPOROUTER_VERTEX(list->data); + if (!(v->flags & VERTEX_FLAG_TEMP)) + return v; + + list = list->prev; + } + } + return tedge_v1(e); +} + +void +edge_adjacent_vertices(toporouter_edge_t * e, toporouter_vertex_t * v, toporouter_vertex_t ** v1, toporouter_vertex_t ** v2) +{ + GList *r = g_list_find(edge_routing(e), v); + + if (v == tedge_v1(e)) { + *v1 = NULL; + *v2 = edge_routing_next_not_temp(e, edge_routing(e)); + } + else if (v == tedge_v2(e)) { + *v1 = edge_routing_prev_not_temp(e, g_list_last(edge_routing(e))); + *v2 = NULL; + } + else { +/* r = g_list_find(r, v);*/ + *v1 = edge_routing_prev_not_temp(e, r); + *v2 = edge_routing_next_not_temp(e, r); + + } + +} + + +GList *candidate_vertices(toporouter_vertex_t * v1, toporouter_vertex_t * v2, toporouter_vertex_t * dest, toporouter_edge_t * e) +{ + gdouble totald, v1ms, v2ms, flow, capacity, ms; + GList *vs = NULL; + + g_assert(v1); + g_assert(v2); + g_assert(dest); + + g_assert(!(v1->flags & VERTEX_FLAG_TEMP)); + g_assert(!(v2->flags & VERTEX_FLAG_TEMP)); +#ifdef DEBUG_ROUTE + printf("starting candidate vertices\n"); + printf("v1 = %f,%f v2 = %f,%f dest = %f,%f\n", vx(v1), vy(v1), vx(v2), vy(v2), vx(dest), vy(dest)); +#endif + totald = gts_point_distance(GTS_POINT(v1), GTS_POINT(v2)); + v1ms = min_spacing(v1, dest); + v2ms = min_spacing(v2, dest); + ms = min_spacing(dest, dest); + flow = TOPOROUTER_IS_CONSTRAINT(e) ? 0. : edge_flow(e, v1, v2, dest); + capacity = edge_capacity(e); + +#ifdef DEBUG_ROUTE + g_assert(totald > 0); + + printf("v1ms = %f v2ms = %f totald = %f ms = %f capacity = %f flow = %f\n", v1ms, v2ms, totald, ms, capacity, flow); +#endif + + if (flow >= capacity) + return NULL; + + + if (v1ms + v2ms + ms >= totald) { + vs = g_list_prepend(vs, new_temp_toporoutervertex((vx(v1) + vx(v2)) / 2., (vy(v1) + vy(v2)) / 2., e)); + } + else { + gdouble x0, y0, x1, y1, d; + + vertex_move_towards_vertex_values(GTS_VERTEX(v1), GTS_VERTEX(v2), v1ms, &x0, &y0); + + vs = g_list_prepend(vs, new_temp_toporoutervertex(x0, y0, e)); + + vertex_move_towards_vertex_values(GTS_VERTEX(v2), GTS_VERTEX(v1), v2ms, &x1, &y1); + + vs = g_list_prepend(vs, new_temp_toporoutervertex(x1, y1, e)); + + d = sqrt(pow(x0 - x1, 2) + pow(y0 - y1, 2)); + + if (ms < d) { +/* guint nint = d / ms; + gdouble dif = d / (nint + 1); */ + gdouble dif = d / 2; + +/* for(guint j=0;jdata); + if (!(v->flags & VERTEX_FLAG_TEMP)) + return i; + + i = i->next; + } + + return NULL; +} + +GList *edge_routing_last_not_temp(toporouter_edge_t * e) +{ + GList *i = edge_routing(e), *last = NULL; + toporouter_vertex_t *v; + + while (i) { + v = TOPOROUTER_VERTEX(i->data); + if (!(v->flags & VERTEX_FLAG_TEMP)) + last = i; + + i = i->next; + } + + return last; +} + +void delete_vertex(toporouter_vertex_t * v) +{ + + if (v->flags & VERTEX_FLAG_TEMP) { + if (v->routingedge) { + if (TOPOROUTER_IS_CONSTRAINT(v->routingedge)) + TOPOROUTER_CONSTRAINT(v->routingedge)->routing = g_list_remove(TOPOROUTER_CONSTRAINT(v->routingedge)->routing, v); + else + v->routingedge->routing = g_list_remove(v->routingedge->routing, v); + } + + gts_object_destroy(GTS_OBJECT(v)); + } +} + +#define edge_is_blocked(e) (TOPOROUTER_IS_EDGE(e) ? (e->flags & EDGE_FLAG_DIRECTCONNECTION) : 0) + +GList *triangle_candidate_points_from_vertex(GtsTriangle * t, toporouter_vertex_t * v, toporouter_vertex_t * dest, + toporouter_route_t * routedata) +{ + toporouter_edge_t *op_e = TOPOROUTER_EDGE(gts_triangle_edge_opposite(t, GTS_VERTEX(v))); + toporouter_vertex_t *vv1, *vv2, *constraintv = NULL; + toporouter_edge_t *e1, *e2; + GList *i; + GList *rval = NULL; + +#ifdef DEBUG_ROUTE + printf("\tTRIANGLE CAND POINT FROM VERTEX\n"); + + g_assert(op_e); +#endif + + e1 = TOPOROUTER_EDGE(gts_vertices_are_connected(GTS_VERTEX(v), edge_v1(op_e))); + e2 = TOPOROUTER_EDGE(gts_vertices_are_connected(GTS_VERTEX(v), edge_v2(op_e))); + + + if (TOPOROUTER_IS_CONSTRAINT(op_e)) { + if (TOPOROUTER_CONSTRAINT(op_e)->box->type == BOARD) { +#ifdef DEBUG_ROUTE + printf("BOARD constraint\n"); +#endif + return NULL; + } + if (constraint_netlist(TOPOROUTER_CONSTRAINT(op_e)) != vertex_netlist(dest)) { /* || TOPOROUTER_CONSTRAINT(op_e)->routing) { */ +#ifdef DEBUG_ROUTE + printf("op_e routing:\n"); + print_edge(op_e); +#endif + return NULL; + } +#ifdef DEBUG_ROUTE + printf("RETURNING CONSTRAINT POING\n"); +#endif + constraintv = new_temp_toporoutervertex_in_segment(op_e, TOPOROUTER_VERTEX(edge_v1(op_e)), + gts_point_distance(GTS_POINT(edge_v1(op_e)), + GTS_POINT(edge_v2(op_e))) / 2., + TOPOROUTER_VERTEX(edge_v2(op_e))); +/* return g_list_prepend(NULL, vv1); */ + + + } + + if (edge_is_blocked(op_e)) { + goto triangle_candidate_points_from_vertex_exit; + } +/* v1 = tedge_v1(op_e); + v2 = tedge_v2(op_e);*/ + + if (v == tedge_v1(e1)) { + i = edge_routing_first_not_temp(e1); + } + else { + i = edge_routing_last_not_temp(e1); + } + + if (i) { + toporouter_vertex_t *temp = TOPOROUTER_VERTEX(i->data); + + if (temp->parent == tedge_v2(op_e) || temp->child == tedge_v2(op_e)) { +#ifdef DEBUG_ROUTE + printf("temp -> op_e->v2\n"); +#endif + goto triangle_candidate_points_from_vertex_exit; + } + if (temp->parent->routingedge == op_e) { + vv1 = temp->parent; +#ifdef DEBUG_ROUTE + printf("vv1->parent\n"); +#endif + + } + else if (temp->child->routingedge == op_e) { + vv1 = temp->child; +#ifdef DEBUG_ROUTE + printf("vv1->child\n"); +#endif + + } + else { + /* must be to e2 */ +#ifdef DEBUG_ROUTE + printf("temp -> e2?\n"); + printf("op_e = %f,%f\t\t%f,%f\n", vx(edge_v1(op_e)), vy(edge_v1(op_e)), vx(edge_v2(op_e)), vy(edge_v2(op_e))); + if (temp->parent->routingedge) + printf("temp->parent->routingedge = %f,%f \t\t %f,%f\n", + vx(edge_v1(temp->parent->routingedge)), vy(edge_v1(temp->parent->routingedge)), + vx(edge_v2(temp->parent->routingedge)), vy(edge_v2(temp->parent->routingedge)) + ); + else + printf("temp->parent->routingedge = NULL\n"); + + if (temp->child->routingedge) + printf("temp->child->routingedge = %f,%f \t\t %f,%f\n", + vx(edge_v1(temp->child->routingedge)), vy(edge_v1(temp->child->routingedge)), + vx(edge_v2(temp->child->routingedge)), vy(edge_v2(temp->child->routingedge)) + ); + else + printf("temp->child->routingedge = NULL\n"); +#endif + goto triangle_candidate_points_from_vertex_exit; + } + + } + else { + vv1 = tedge_v1(op_e); +#ifdef DEBUG_ROUTE + printf("nothing on e1\n"); +#endif + } + + if (v == tedge_v1(e2)) { + i = edge_routing_first_not_temp(e2); + } + else { + i = edge_routing_last_not_temp(e2); + } + + if (i) { + toporouter_vertex_t *temp = TOPOROUTER_VERTEX(i->data); + + if (temp->parent == tedge_v1(op_e) || temp->child == tedge_v1(op_e)) { +#ifdef DEBUG_ROUTE + printf("temp -> op_e->v2\n"); +#endif + goto triangle_candidate_points_from_vertex_exit; + } + + if (temp->parent->routingedge == op_e) { + vv2 = temp->parent; +#ifdef DEBUG_ROUTE + printf("vv2->parent\n"); +#endif + } + else if (temp->child->routingedge == op_e) { + vv2 = temp->child; +#ifdef DEBUG_ROUTE + printf("vv2->child\n"); +#endif + + } + else { + /* must be to e1 */ +#ifdef DEBUG_ROUTE + printf("temp -> e1?\n"); + printf("op_e = %f,%f\t\t%f,%f\n", vx(edge_v1(op_e)), vy(edge_v1(op_e)), vx(edge_v2(op_e)), vy(edge_v2(op_e))); + if (temp->parent->routingedge) + printf("temp->parent->routingedge = %f,%f \t\t %f,%f\n", + vx(edge_v1(temp->parent->routingedge)), vy(edge_v1(temp->parent->routingedge)), + vx(edge_v2(temp->parent->routingedge)), vy(edge_v2(temp->parent->routingedge)) + ); + else + printf("temp->parent->routingedge = NULL\n"); + + if (temp->child->routingedge) + printf("temp->child->routingedge = %f,%f \t\t %f,%f\n", + vx(edge_v1(temp->child->routingedge)), vy(edge_v1(temp->child->routingedge)), + vx(edge_v2(temp->child->routingedge)), vy(edge_v2(temp->child->routingedge)) + ); + else + printf("temp->child->routingedge = NULL\n"); +#endif + goto triangle_candidate_points_from_vertex_exit; + } + + } + else { + vv2 = tedge_v2(op_e); +#ifdef DEBUG_ROUTE + printf("nothing on e2\n"); +#endif + } + +#ifdef DEBUG_ROUTE + printf("size of e1 routing = %d e2 routing = %d op_e routing = %d\n", + g_list_length(edge_routing(e1)), g_list_length(edge_routing(e2)), g_list_length(edge_routing(op_e))); +#endif + + if (constraintv) { +#ifdef DEBUG_ROUTE + print_vertex(constraintv); + printf("constraintv %f,%f returning\n", vx(constraintv), vy(constraintv)); +#endif + return g_list_prepend(NULL, constraintv); + } + + i = edge_routing(op_e); + while (i) { + toporouter_vertex_t *temp = TOPOROUTER_VERTEX(i->data); + + if (temp->parent == v || temp->child == v) { + rval = g_list_concat(rval, candidate_vertices(vv1, temp, dest, op_e)); + vv1 = temp; + } + + i = i->next; + } + + rval = g_list_concat(rval, candidate_vertices(vv1, vv2, dest, op_e)); + + return rval; + + + +triangle_candidate_points_from_vertex_exit: + if (constraintv) /*delete_vertex(constraintv); */ + g_hash_table_insert(routedata->alltemppoints, constraintv, constraintv); + + g_list_free(rval); + + return NULL; +} + +void routedata_insert_temppoints(toporouter_route_t * data, GList * temppoints) +{ + GList *j = temppoints; + while (j) { + g_hash_table_insert(data->alltemppoints, j->data, j->data); + j = j->next; + } +} + + +static inline gint constraint_route_test(toporouter_constraint_t * c, toporouter_route_t * routedata) +{ + if (c->box->cluster && c->box->cluster->netlist == routedata->src->netlist) { + if (c->box->cluster->c == routedata->dest->c || c->box->cluster->c == routedata->src->c) + return 1; + } + return 0; +} + +GList *all_candidates_on_edge(toporouter_edge_t * e, toporouter_route_t * routedata) +{ + GList *rval = NULL; + if (edge_is_blocked(e)) + return NULL; + + if (!TOPOROUTER_IS_CONSTRAINT(e)) { + GList *i = edge_routing(e); + toporouter_vertex_t *pv = tedge_v1(e); + + while (i) { + toporouter_vertex_t *v = TOPOROUTER_VERTEX(i->data); + if (!(v->flags & VERTEX_FLAG_TEMP)) { + rval = g_list_concat(rval, candidate_vertices(pv, v, TOPOROUTER_VERTEX(routedata->destvertices->data), e)); + pv = v; + } + i = i->next; + } + + rval = g_list_concat(rval, candidate_vertices(pv, tedge_v2(e), TOPOROUTER_VERTEX(routedata->destvertices->data), e)); + } + else if (TOPOROUTER_CONSTRAINT(e)->box->type == BOARD) { + return NULL; + } + else if (constraint_route_test(TOPOROUTER_CONSTRAINT(e), routedata)) { + toporouter_vertex_t *consv = + new_temp_toporoutervertex_in_segment(e, tedge_v1(e), tvdistance(tedge_v1(e), tedge_v2(e)) / 2., tedge_v2(e)); + rval = g_list_prepend(rval, consv); +/* g_hash_table_insert(routedata->alltemppoints, consv, consv); */ + } + + return rval; +} + +GList *triangle_all_candidate_points_from_vertex(GtsTriangle * t, toporouter_vertex_t * v, toporouter_route_t * routedata) +{ + toporouter_edge_t *op_e = TOPOROUTER_EDGE(gts_triangle_edge_opposite(t, GTS_VERTEX(v))); + return all_candidates_on_edge(op_e, routedata); +} + +GList *triangle_all_candidate_points_from_edge(toporouter_t * r, GtsTriangle * t, toporouter_edge_t * e, + toporouter_route_t * routedata, toporouter_vertex_t ** dest, + toporouter_vertex_t * curpoint) +{ + toporouter_vertex_t *op_v; + toporouter_edge_t *e1, *e2; + GList *i, *rval = NULL, *rval2 = NULL; + toporouter_vertex_t *boxpoint = NULL; + guint e1intcap, e2intcap; + + op_v = TOPOROUTER_VERTEX(gts_triangle_vertex_opposite(t, GTS_EDGE(e))); + + + if (vertex_bbox(op_v)) + boxpoint = TOPOROUTER_VERTEX(vertex_bbox(op_v)->point); + + if (g_list_find(routedata->destvertices, op_v)) { + rval = g_list_prepend(rval, op_v); + *dest = op_v; + return rval; + } + else if (g_list_find(routedata->destvertices, boxpoint)) { + *dest = boxpoint; + } + else if (g_list_find(routedata->srcvertices, op_v)) { + rval = g_list_prepend(rval, op_v); + } + + e1 = TOPOROUTER_EDGE(gts_vertices_are_connected(GTS_VERTEX(op_v), edge_v1(e))); + e2 = TOPOROUTER_EDGE(gts_vertices_are_connected(GTS_VERTEX(op_v), edge_v2(e))); + + rval = g_list_concat(rval, all_candidates_on_edge(e1, routedata)); + rval = g_list_concat(rval, all_candidates_on_edge(e2, routedata)); + + e1intcap = check_triangle_interior_capacity(t, tedge_v1(e), curpoint, e2, e, e1); + e2intcap = check_triangle_interior_capacity(t, tedge_v2(e), curpoint, e1, e, e2); + + i = rval; + while (i) { + toporouter_vertex_t *v = TOPOROUTER_VERTEX(i->data); + + if (!v->routingedge) + rval2 = g_list_prepend(rval2, v); + else if (v->routingedge == e1 && !(!TOPOROUTER_IS_CONSTRAINT(e1) && !e1intcap)) + rval2 = g_list_prepend(rval2, v); + else if (v->routingedge == e2 && !(!TOPOROUTER_IS_CONSTRAINT(e2) && !e2intcap)) + rval2 = g_list_prepend(rval2, v); + + i = i->next; + } + g_list_free(rval); + + return rval2; +} + +GList *triangle_candidate_points_from_edge(toporouter_t * r, GtsTriangle * t, toporouter_edge_t * e, toporouter_vertex_t * v, + toporouter_vertex_t ** dest, toporouter_route_t * routedata) +{ + toporouter_vertex_t *v1, *v2, *op_v, *vv = NULL, *e1constraintv = NULL, *e2constraintv = NULL; + toporouter_edge_t *e1, *e2; + GList *e1cands = NULL, *e2cands = NULL, *rval = NULL; + guint noe1 = 0, noe2 = 0; + + op_v = TOPOROUTER_VERTEX(gts_triangle_vertex_opposite(t, GTS_EDGE(e))); + + e1 = TOPOROUTER_EDGE(gts_vertices_are_connected(GTS_VERTEX(op_v), edge_v1(e))); + e2 = TOPOROUTER_EDGE(gts_vertices_are_connected(GTS_VERTEX(op_v), edge_v2(e))); + + g_assert(*dest); + + /* v1 is prev dir, v2 is next dir */ + edge_adjacent_vertices(e, v, &v1, &v2); + + if (TOPOROUTER_IS_CONSTRAINT(e1)) { + GList *i = edge_routing(e1); + + if (TOPOROUTER_CONSTRAINT(e1)->box->type == BOARD) { + noe1 = 1; + } + else if (!constraint_route_test(TOPOROUTER_CONSTRAINT(e1), routedata)) { + noe1 = 1; +#ifdef DEBUG_ROUTE + printf("noe1 netlist\n"); +#endif + } + else + if (v1 == tedge_v1(e) || + (v1->parent->routingedge && v1->parent->routingedge == e1) || + (v1->child->routingedge && v1->child->routingedge == e1)) { + e1constraintv = + new_temp_toporoutervertex_in_segment(e1, tedge_v1(e1), + gts_point_distance(GTS_POINT(edge_v1(e1)), GTS_POINT(edge_v2(e1))) / 2., + tedge_v2(e1)); + } + + while (i) { + toporouter_vertex_t *temp = TOPOROUTER_VERTEX(i->data); + + if ((temp->child == tedge_v2(e) || temp->parent == tedge_v2(e)) && !(temp->flags & VERTEX_FLAG_TEMP)) + noe2 = 1; + + i = i->next; + } + + goto triangle_candidate_points_e2; + } + + if (edge_is_blocked(e1)) { + noe1 = 1; + goto triangle_candidate_points_e2; + } + + if (v1 == tedge_v1(e)) { + /* continue up e1 */ + toporouter_vertex_t *vv1, *vv2; + edge_adjacent_vertices(e1, v1, &vv1, &vv2); + +#ifdef DEBUG_ROUTE + printf("v1 == e->v1\n"); +#endif + + if (vv1) { + /* candidates from v1 until vv1 */ + vv = vv1; + } + else { + /* candidates from v1 until vv2 */ + vv = vv2; + } + + if (!e1constraintv) + e1cands = candidate_vertices(v1, vv, *dest, e1); + + if (vv != op_v) { + if (vv->parent == tedge_v2(e) || vv->child == tedge_v2(e)) { +#ifdef DEBUG_ROUTE + printf("noe2 0\n"); +#endif + noe2 = 1; + } + } + + } + else if (v1->parent != op_v && v1->child != op_v) { + toporouter_vertex_t *vv1 = NULL, *vv2 = NULL; + +#ifdef DEBUG_ROUTE + printf("v1 != e->v1\n"); +#endif + + if (v1->parent->routingedge == e1) { + vv1 = v1->parent; +#ifdef DEBUG_ROUTE + printf("v1 parent = e1\n"); +#endif + if (op_v == tedge_v1(e1)) { + /* candidates from v1->parent until prev vertex */ + vv2 = edge_routing_prev_not_temp(e1, g_list_find(edge_routing(e1), v1->parent)->prev); + } + else { + /* candidates from v1->parent until next vertex */ + vv2 = edge_routing_next_not_temp(e1, g_list_find(edge_routing(e1), v1->parent)->next); + } + + } + else if (v1->child->routingedge == e1) { + vv1 = v1->child; +#ifdef DEBUG_ROUTE + printf("v1 child = e1\n"); +#endif + if (op_v == tedge_v1(e1)) { + /* candidates from v1->child until prev vertex */ + vv2 = edge_routing_prev_not_temp(e1, g_list_find(edge_routing(e1), v1->child)->prev); + } + else { + /* candidates from v1->child until next vertex */ + vv2 = edge_routing_next_not_temp(e1, g_list_find(edge_routing(e1), v1->child)->next); + } + + } + else { +#ifdef DEBUG_ROUTE + printf("v1 ? \n"); +#endif + goto triangle_candidate_points_e2; + } + + if (vv1 && vv2) { + if (vv2->parent == tedge_v2(e) || vv2->child == tedge_v2(e)) { +#ifdef DEBUG_ROUTE + printf("noe2 1\n"); +#endif + noe2 = 1; + } + + if (!e1constraintv) + e1cands = candidate_vertices(vv1, vv2, *dest, e1); + + vv = vv2; + } + } + + if (vv && vv == op_v) { + toporouter_vertex_t *boxpoint = NULL; + + if (vertex_bbox(op_v)) + boxpoint = TOPOROUTER_VERTEX(vertex_bbox(op_v)->point); + + if (g_list_find(routedata->destvertices, op_v)) { + rval = g_list_prepend(rval, op_v); + *dest = op_v; + } + else if (g_list_find(routedata->destvertices, boxpoint)) { + *dest = boxpoint; + } + else if (g_list_find(routedata->srcvertices, op_v)) { + rval = g_list_prepend(rval, op_v); + } + } + +triangle_candidate_points_e2: + + if (noe2) { +/* printf("noe2\n"); */ + goto triangle_candidate_points_finish; + } + + if (TOPOROUTER_IS_CONSTRAINT(e2)) { + GList *i = edge_routing(e2); + + if (TOPOROUTER_CONSTRAINT(e2)->box->type == BOARD) { + noe2 = 1; +/* goto triangle_candidate_points_finish; */ + } + else if (!constraint_route_test(TOPOROUTER_CONSTRAINT(e2), routedata)) { +#ifdef DEBUG_ROUTE + printf("noe2 netlist\n"); +#endif + noe2 = 1; +/* goto triangle_candidate_points_finish; */ + } + else if (v2 == tedge_v2(e) || + (v2->parent->routingedge && v2->parent->routingedge == e2) || + (v2->child->routingedge && v2->child->routingedge == e2)) { + + e2constraintv = + new_temp_toporoutervertex_in_segment(e2, tedge_v1(e2), + gts_point_distance(GTS_POINT(edge_v1(e2)), GTS_POINT(edge_v2(e2))) / 2., + tedge_v2(e2)); + + } + + while (i) { + toporouter_vertex_t *temp = TOPOROUTER_VERTEX(i->data); + + if ((temp->child == tedge_v1(e) || temp->parent == tedge_v1(e)) && !(temp->flags & VERTEX_FLAG_TEMP)) + noe1 = 1; + + i = i->next; + } + + + + goto triangle_candidate_points_finish; + } + + if (edge_is_blocked(e2)) { + noe2 = 1; + goto triangle_candidate_points_finish; + } + + if (v2 == tedge_v2(e)) { + /* continue up e2 */ + toporouter_vertex_t *vv1 = NULL, *vv2 = NULL; + edge_adjacent_vertices(e2, v2, &vv1, &vv2); + +#ifdef DEBUG_ROUTE + printf("v2 == e->v2\n"); +#endif + + if (vv1) { + /* candidates from v2 until vv1 */ + vv = vv1; + } + else { + /* candidates from v2 until vv2 */ + vv = vv2; + } + + if (!e2constraintv) + e2cands = candidate_vertices(v2, vv, *dest, e2); + + if (vv != op_v) { + if (vv->parent == tedge_v1(e) || vv->child == tedge_v1(e)) { +#ifdef DEBUG_ROUTE + printf("noe1 0\n"); +#endif + noe1 = 1; + } + } + + } + else if (v2->parent != op_v && v2->child != op_v) { + toporouter_vertex_t *vv1 = NULL, *vv2 = NULL; + +#ifdef DEBUG_ROUTE + printf("v2 == e->v2\n"); +#endif + + if (v2->parent->routingedge == e2) { + vv1 = v2->parent; + if (op_v == tedge_v1(e2)) { + /* candidates from v2->parent until prev vertex */ + vv2 = edge_routing_prev_not_temp(e2, g_list_find(edge_routing(e2), vv1)->prev); + } + else { + /* candidates from v2->parent until next vertex */ + vv2 = edge_routing_next_not_temp(e2, g_list_find(edge_routing(e2), vv1)->next); + } + + } + else if (v2->child->routingedge == e2) { + vv1 = v2->child; + if (op_v == tedge_v1(e2)) { + /* candidates from v2->child until prev vertex */ + vv2 = edge_routing_prev_not_temp(e2, g_list_find(edge_routing(e2), vv1)->prev); + } + else { + /* candidates from v2->child until next vertex */ + vv2 = edge_routing_next_not_temp(e2, g_list_find(edge_routing(e2), vv1)->next); + } + + } + else { + goto triangle_candidate_points_finish; + } + + if (vv1 && vv2) { + if (vv2->parent == tedge_v1(e) || vv2->child == tedge_v1(e)) { +#ifdef DEBUG_ROUTE + printf("noe1 1\n"); +#endif + noe1 = 1; + } + + if (!e2constraintv) + e2cands = candidate_vertices(vv1, vv2, *dest, e2); + } + } + +triangle_candidate_points_finish: + + v1 = segment_common_vertex(GTS_SEGMENT(e), GTS_SEGMENT(e1)); + v2 = segment_common_vertex(GTS_SEGMENT(e), GTS_SEGMENT(e2)); + + if (noe1 || !check_triangle_interior_capacity(t, v1, v, e2, e, e1)) { +#ifdef DEBUG_ROUTE + printf("freeing e1cands\n"); +#endif + routedata_insert_temppoints(routedata, e1cands); + g_list_free(e1cands); + e1cands = NULL; + } + + if (noe2 || !check_triangle_interior_capacity(t, v2, v, e1, e, e2)) { +#ifdef DEBUG_ROUTE + printf("freeing e2cands\n"); +#endif + routedata_insert_temppoints(routedata, e2cands); + g_list_free(e2cands); + e2cands = NULL; + } + + if (!noe1 && e1constraintv) { + e1cands = g_list_prepend(e1cands, e1constraintv); + } + else if (e1constraintv) { + g_hash_table_insert(routedata->alltemppoints, e1constraintv, e1constraintv); +/* delete_vertex(e1constraintv); */ + } + + if (!noe2 && e2constraintv) { + e2cands = g_list_prepend(e2cands, e2constraintv); + } + else if (e2constraintv) { + g_hash_table_insert(routedata->alltemppoints, e2constraintv, e2constraintv); +/* delete_vertex(e2constraintv); */ + } + + if (!noe1 && !noe2) + return g_list_concat(rval, g_list_concat(e1cands, e2cands)); + + return g_list_concat(e1cands, e2cands); +} + +GList *compute_candidate_points(toporouter_t * tr, toporouter_layer_t * l, toporouter_vertex_t * curpoint, + toporouter_route_t * data, toporouter_vertex_t ** closestdest) +{ + GList *r = NULL, *j; + toporouter_edge_t *edge = curpoint->routingedge, *tempedge; + + if (vertex_keepout_test(tr, curpoint)) + goto compute_candidate_points_finish; + + /* direct connection */ +/* if(curpoint == TOPOROUTER_VERTEX(data->src->point)) */ + if ((tempedge = TOPOROUTER_EDGE(gts_vertices_are_connected(GTS_VERTEX(curpoint), GTS_VERTEX(*closestdest))))) { + + if (TOPOROUTER_IS_CONSTRAINT(tempedge)) { + goto compute_candidate_points_finish; + } + else { + if (!tempedge->routing) { + r = g_list_prepend(NULL, *closestdest); + tempedge->flags |= EDGE_FLAG_DIRECTCONNECTION; + goto compute_candidate_points_finish; + } + else { +#ifdef DEBUG_ROUTE + printf("Direct connection, but has routing\n"); +#endif + } + + } + /* if we get to here, there is routing blocking the direct connection, + * continue as per normal */ + } + + /* a real point origin */ + if (!(curpoint->flags & VERTEX_FLAG_TEMP)) { + GSList *triangles, *i; + i = triangles = gts_vertex_triangles(GTS_VERTEX(curpoint), NULL); +#ifdef DEBUG_ROUTE + printf("triangle count = %d\n", g_slist_length(triangles)); +#endif + while (i) { + GtsTriangle *t = GTS_TRIANGLE(i->data); + GList *temppoints; + + if (tr->flags & TOPOROUTER_FLAG_LEASTINVALID) + temppoints = triangle_all_candidate_points_from_vertex(t, curpoint, data); + else + temppoints = triangle_candidate_points_from_vertex(t, curpoint, *closestdest, data); + +#ifdef DEBUG_ROUTE + printf("\treturned %d points\n", g_list_length(temppoints)); +#endif + routedata_insert_temppoints(data, temppoints); + + r = g_list_concat(r, temppoints); + i = i->next; + } + g_slist_free(triangles); + } + else { /* a temp point */ + + int prevwind = vertex_wind(GTS_SEGMENT(edge)->v1, GTS_SEGMENT(edge)->v2, GTS_VERTEX(curpoint->parent)); +/* printf("tempoint\n"); */ + + GSList *i = GTS_EDGE(edge)->triangles; + + while (i) { + GtsVertex *oppv = gts_triangle_vertex_opposite(GTS_TRIANGLE(i->data), GTS_EDGE(edge)); + if (prevwind != vertex_wind(GTS_SEGMENT(edge)->v1, GTS_SEGMENT(edge)->v2, oppv)) { + GList *temppoints; + + if (tr->flags & TOPOROUTER_FLAG_LEASTINVALID) + temppoints = triangle_all_candidate_points_from_edge(tr, GTS_TRIANGLE(i->data), edge, data, closestdest, curpoint); + else + temppoints = triangle_candidate_points_from_edge(tr, GTS_TRIANGLE(i->data), edge, curpoint, closestdest, data); + + j = temppoints; + while (j) { + toporouter_vertex_t *tempj = TOPOROUTER_VERTEX(j->data); + if (tempj->flags & VERTEX_FLAG_TEMP) + g_hash_table_insert(data->alltemppoints, j->data, j->data); +#ifdef DEBUG_ROUTE + else + printf("got cand not a temp\n"); +#endif + j = j->next; + } + r = g_list_concat(r, temppoints); + + break; + } + i = i->next; + } + } + +compute_candidate_points_finish: + + if (vertex_bbox(curpoint) && vertex_bbox(curpoint)->cluster) { + if (vertex_bbox(curpoint)->cluster->c == data->src->c) { + r = g_list_concat(r, g_list_copy(data->srcvertices)); + } + } + + return r; +} + +gboolean temp_point_clean(gpointer key, gpointer value, gpointer user_data) +{ + toporouter_vertex_t *tv = TOPOROUTER_VERTEX(value); + if (tv->flags & VERTEX_FLAG_TEMP) { + if (TOPOROUTER_IS_CONSTRAINT(tv->routingedge)) + TOPOROUTER_CONSTRAINT(tv->routingedge)->routing = g_list_remove(TOPOROUTER_CONSTRAINT(tv->routingedge)->routing, tv); + else + tv->routingedge->routing = g_list_remove(tv->routingedge->routing, tv); + gts_object_destroy(GTS_OBJECT(tv)); + } + return TRUE; +} + +void clean_routing_edges(toporouter_t * r, toporouter_route_t * data) +{ + g_hash_table_foreach_remove(data->alltemppoints, temp_point_clean, NULL); + g_hash_table_destroy(data->alltemppoints); + data->alltemppoints = NULL; +} + +gdouble path_score(toporouter_t * r, GList * path) +{ + gdouble score = 0.; + toporouter_vertex_t *pv = NULL; + toporouter_vertex_t *v0 = NULL; + + if (!path) + return INFINITY; + + v0 = TOPOROUTER_VERTEX(path->data); + + while (path) { + toporouter_vertex_t *v = TOPOROUTER_VERTEX(path->data); + + if (pv) { + score += gts_point_distance(GTS_POINT(pv), GTS_POINT(v)); + if (pv != v0 && vz(pv) != vz(v)) + if (path->next) + score += r->viacost; + + } + + pv = v; + path = path->next; + } + + return score; +} + +void print_vertices(GList * vertices) +{ + while (vertices) { + toporouter_vertex_t *v = TOPOROUTER_VERTEX(vertices->data); + print_vertex(v); + print_bbox(vertex_bbox(v)); + if (vertex_bbox(v)) { + printf("has bbox\n"); + if (vertex_bbox(v)->cluster) + printf("has cluster\n"); + else + printf("no cluster\n"); + } + else + printf("no bbox\n"); + vertices = vertices->next; + } +} + +gint space_edge(gpointer item, gpointer data) +{ + toporouter_edge_t *e = TOPOROUTER_EDGE(item); + GList *i; + gdouble *forces; + guint j; + + if (TOPOROUTER_IS_CONSTRAINT(e)) + return 0; + + if (!edge_routing(e) || !g_list_length(edge_routing(e))) + return 0; + + forces = (gdouble *) malloc(sizeof(double) * g_list_length(edge_routing(e))); + + for (j = 0; j < 100; j++) { + guint k = 0; + guint equilibrium = 1; + + i = edge_routing(e); + while (i) { + toporouter_vertex_t *v = TOPOROUTER_VERTEX(i->data); + gdouble ms, d; + + if (i->prev) { +/* ms = min_net_net_spacing(TOPOROUTER_VERTEX(i->prev->data), v); */ + ms = min_spacing(TOPOROUTER_VERTEX(i->prev->data), v); + d = gts_point_distance(GTS_POINT(i->prev->data), GTS_POINT(v)); + } + else { +/* ms = min_vertex_net_spacing(v, tedge_v1(e)); */ + ms = min_spacing(v, tedge_v1(e)); + d = gts_point_distance(GTS_POINT(edge_v1(e)), GTS_POINT(v)); + } + + if (d < ms) + forces[k] = ms - d; + else + forces[k] = 0.; + + if (i->next) { +/* ms = min_net_net_spacing(TOPOROUTER_VERTEX(i->next->data), v); */ + ms = min_spacing(TOPOROUTER_VERTEX(i->next->data), v); + d = gts_point_distance(GTS_POINT(i->next->data), GTS_POINT(v)); + } + else { +/* ms = min_vertex_net_spacing(v, tedge_v2(e)); */ + ms = min_spacing(v, tedge_v2(e)); + d = gts_point_distance(GTS_POINT(edge_v2(e)), GTS_POINT(v)); + } + + if (d < ms) + forces[k] += d - ms; + + k++; + i = i->next; + } + + k = 0; + i = edge_routing(e); + while (i) { + toporouter_vertex_t *v = TOPOROUTER_VERTEX(i->data); + if (forces[k] > EPSILON || forces[k] < -EPSILON) + equilibrium = 0; + vertex_move_towards_vertex_values(GTS_VERTEX(v), edge_v2(e), forces[k] * 0.1, &(GTS_POINT(v)->x), &(GTS_POINT(v)->y)); + k++; + i = i->next; + } + + if (equilibrium) { +/* printf("reached equilibriium at %d\n", j); */ + break; + } + + } + + free(forces); + return 0; +} + +void swap_vertices(toporouter_vertex_t ** v1, toporouter_vertex_t ** v2) +{ + toporouter_vertex_t *tempv = *v1; + *v1 = *v2; + *v2 = tempv; +} + +void split_edge_routing(toporouter_vertex_t * v, GList ** l1, GList ** l2) +{ + GList *base, *i; + + g_assert(v); + g_assert(v->routingedge); + + base = g_list_find(vrouting(v), v); + + *l1 = g_list_prepend(*l1, tedge_v1(v->routingedge)); + *l2 = g_list_prepend(*l2, tedge_v2(v->routingedge)); + + g_assert(base); + + i = base->next; + while (i) { + if (!(TOPOROUTER_VERTEX(i->data)->flags & VERTEX_FLAG_TEMP)) + *l2 = g_list_prepend(*l2, i->data); + i = i->next; + } + + i = base->prev; + while (i) { + if (!(TOPOROUTER_VERTEX(i->data)->flags & VERTEX_FLAG_TEMP)) + *l1 = g_list_prepend(*l1, i->data); + i = i->prev; + } +} + +GList *vertices_routing_conflicts(toporouter_vertex_t * v, toporouter_vertex_t * pv) +{ + toporouter_edge_t *e; + GList *rval = NULL, *l1 = NULL, *l2 = NULL, *i; + + if (vz(v) != vz(pv)) + return NULL; + g_assert(v != pv); + + if (!v->routingedge && !pv->routingedge) { + e = TOPOROUTER_EDGE(gts_vertices_are_connected(GTS_VERTEX(v), GTS_VERTEX(pv))); + if (!e) + return NULL; + i = edge_routing(e); + while (i) { + rval = g_list_prepend(rval, TOPOROUTER_VERTEX(i->data)->route); + i = i->next; + } + return rval; + } + + if (TOPOROUTER_IS_CONSTRAINT(v->routingedge) && TOPOROUTER_IS_CONSTRAINT(pv->routingedge)) + return NULL; + + if (TOPOROUTER_IS_CONSTRAINT(pv->routingedge)) + swap_vertices(&pv, &v); + + if (!v->routingedge) + swap_vertices(&pv, &v); + + e = v->routingedge; + + split_edge_routing(v, &l1, &l2); + g_assert(l2); + g_assert(l1); + + if (!pv->routingedge) { + toporouter_edge_t *e1, *e2; + e1 = TOPOROUTER_EDGE(gts_vertices_are_connected(GTS_VERTEX(pv), edge_v1(e))); + e2 = TOPOROUTER_EDGE(gts_vertices_are_connected(GTS_VERTEX(pv), edge_v2(e))); + + l1 = g_list_concat(l1, g_list_copy(edge_routing(e1))); + l2 = g_list_concat(l2, g_list_copy(edge_routing(e2))); + + } + else { + GList *pvlist1 = NULL, *pvlist2 = NULL; + toporouter_vertex_t *commonv = route_vertices_common_vertex(v, pv); + + g_assert(commonv); + + split_edge_routing(pv, &pvlist1, &pvlist2); + + if (commonv == tedge_v1(e)) { + toporouter_edge_t *ope; + + if (commonv == tedge_v1(pv->routingedge)) { + l1 = g_list_concat(l1, pvlist1); + l2 = g_list_concat(l2, pvlist2); + ope = TOPOROUTER_EDGE(gts_vertices_are_connected(edge_v2(e), edge_v2(pv->routingedge))); + } + else { + l1 = g_list_concat(l1, pvlist2); + l2 = g_list_concat(l2, pvlist1); + ope = TOPOROUTER_EDGE(gts_vertices_are_connected(edge_v2(e), edge_v1(pv->routingedge))); + } + g_assert(ope); + l2 = g_list_concat(l2, g_list_copy(edge_routing(ope))); + + } + else { + toporouter_edge_t *ope; + if (commonv == tedge_v1(pv->routingedge)) { + l1 = g_list_concat(l1, pvlist2); + l2 = g_list_concat(l2, pvlist1); + ope = TOPOROUTER_EDGE(gts_vertices_are_connected(edge_v1(e), edge_v2(pv->routingedge))); + } + else { + l1 = g_list_concat(l1, pvlist1); + l2 = g_list_concat(l2, pvlist2); + ope = TOPOROUTER_EDGE(gts_vertices_are_connected(edge_v1(e), edge_v1(pv->routingedge))); + } + g_assert(ope); + l1 = g_list_concat(l1, g_list_copy(edge_routing(ope))); + } + } + + i = l1; + while (i) { + toporouter_vertex_t *curv = TOPOROUTER_VERTEX(i->data); + + if (curv->flags & VERTEX_FLAG_ROUTE && (g_list_find(l2, curv->parent) || g_list_find(l2, curv->child))) { + if (!g_list_find(rval, curv->route)) + rval = g_list_prepend(rval, curv->route); + } + i = i->next; + } + i = l2; + while (i) { + toporouter_vertex_t *curv = TOPOROUTER_VERTEX(i->data); + + if (curv->flags & VERTEX_FLAG_ROUTE && (g_list_find(l1, curv->parent) || g_list_find(l1, curv->child))) { + if (!g_list_find(rval, curv->route)) + rval = g_list_prepend(rval, curv->route); + } + i = i->next; + } + + g_list_free(l1); + g_list_free(l2); + + return rval; +} + +gdouble vertices_routing_conflict_cost(toporouter_t * r, toporouter_vertex_t * v, toporouter_vertex_t * pv, guint * n) +{ + GList *conflicts = vertices_routing_conflicts(v, pv), *i; + gdouble penalty = 0.; + + i = conflicts; + while (i) { + (*n) += 1; + penalty += TOPOROUTER_ROUTE(i->data)->score; + i = i->next; + } + g_list_free(conflicts); +/* if(penalty > 0.) printf("conflict penalty of %f with %f,%f %f,%f\n", penalty, vx(v), vy(v), vx(pv), vy(pv)); */ + return penalty; +} + +gdouble +gcost(toporouter_t * r, toporouter_route_t * data, toporouter_vertex_t * srcv, toporouter_vertex_t * v, + toporouter_vertex_t * pv, guint * n, toporouter_netlist_t * pair) +{ + gdouble cost = 0., segcost; + + *n = pv->gn; + + if (g_list_find(data->srcvertices, v)) + return 0.; + + segcost = tvdistance(pv, v); + + if (pair && !TOPOROUTER_IS_CONSTRAINT(v->routingedge) && v->routingedge) { + GList *list = g_list_find(v->routingedge->routing, v); + toporouter_vertex_t *pv = edge_routing_prev_not_temp(v->routingedge, list); + toporouter_vertex_t *nv = edge_routing_next_not_temp(v->routingedge, list); + + if (pv->route && pv->route->netlist == pair) { + } + else if (nv->route && nv->route->netlist == pair) { + } + else { + segcost *= 10.; + } + } + + cost = pv->gcost + segcost; + + if (r->flags & TOPOROUTER_FLAG_LEASTINVALID) { + gdouble conflictcost = 0.; + + if (pv && v != pv && vz(v) == vz(pv)) + conflictcost = vertices_routing_conflict_cost(r, v, pv, n); + + if (!(r->flags & TOPOROUTER_FLAG_DETOUR && *n == 1)) { + cost += conflictcost * (pow(*n, 2)); + } + } + + return cost; +} + +#define vlayer(x) (&r->layers[(int)vz(x)]) + +guint candidate_is_available(toporouter_vertex_t * pv, toporouter_vertex_t * v) +{ + /* TODO: still needed? */ + while (pv) { + if (pv == v) + return 0; + pv = pv->parent; + } + + return 1; +} + +GList *route(toporouter_t * r, toporouter_route_t * data, guint debug) +{ + GtsEHeap *openlist = gts_eheap_new(route_heap_cmp, NULL); + GList *closelist = NULL; + GList *i, *rval = NULL; + toporouter_netlist_t *pair = NULL; + gint count = 0; + + toporouter_vertex_t *srcv = NULL, *destv = NULL, *curpoint = NULL; + toporouter_layer_t *cur_layer; /*, *dest_layer; */ + + g_assert(data->src->c != data->dest->c); + + if (data->destvertices) + g_list_free(data->destvertices); + if (data->srcvertices) + g_list_free(data->srcvertices); + + data->destvertices = cluster_vertices(r, data->dest); + data->srcvertices = cluster_vertices(r, data->src); + + closest_cluster_pair(r, data->srcvertices, data->destvertices, &curpoint, &destv); + + if (!curpoint || !destv) + goto routing_return; + + srcv = curpoint; + cur_layer = vlayer(curpoint); + /*dest_layer = vlayer(destv); */ + + data->path = NULL; + + data->alltemppoints = g_hash_table_new(g_direct_hash, g_direct_equal); + + curpoint->parent = NULL; + curpoint->child = NULL; + curpoint->gcost = 0.; + curpoint->gn = 0; + curpoint->hcost = simple_h_cost(r, curpoint, destv); + + if (data->netlist && data->netlist->pair) { + GList *i = r->routednets; + while (i) { + toporouter_route_t *curroute = TOPOROUTER_ROUTE(i->data); + if (curroute->netlist == data->netlist->pair) { + pair = data->netlist->pair; + break; + } + i = i->next; + } + } + + gts_epcb_heap_insert(openlist, curpoint); + + while (gts_epcb_heap_size(openlist) > 0) { + GList *candidatepoints; + data->curpoint = curpoint; + /*draw_route_status(r, closelist, openlist, curpoint, data, count++); */ + + curpoint = TOPOROUTER_VERTEX(gts_eheap_remove_top(openlist, NULL)); + if (curpoint->parent && !(curpoint->flags & VERTEX_FLAG_TEMP)) { + if (vz(curpoint) != vz(destv)) { + toporouter_vertex_t *tempv; + cur_layer = vlayer(curpoint); /*&r->layers[(int)vz(curpoint)]; */ + tempv = closest_dest_vertex(r, curpoint, data); + if (tempv) { + destv = tempv; + /*dest_layer = vlayer(destv);//&r->layers[(int)vz(destv)]; */ + + } + } + } + +/* destpoint = closest_dest_vertex(r, curpoint, data); + dest_layer = &r->layers[(int)vz(destpoint)];*/ + + if (g_list_find(data->destvertices, curpoint)) { + toporouter_vertex_t *temppoint = curpoint; + srcv = NULL; + destv = curpoint; + + data->path = NULL; + + while (temppoint) { + data->path = g_list_prepend(data->path, temppoint); + if (g_list_find(data->srcvertices, temppoint)) { + srcv = temppoint; + if (r->flags & TOPOROUTER_FLAG_AFTERORDER) + break; + } + temppoint = temppoint->parent; + } + rval = data->path; + data->score = path_score(r, data->path); +#ifdef DEBUG_ROUTE + printf("ROUTE: path score = %f computation cost = %d\n", data->score, count); +#endif + + if (srcv->bbox->cluster != data->src) { + data->src = srcv->bbox->cluster; + } + + if (destv->bbox->cluster != data->dest) { + data->dest = destv->bbox->cluster; + } + goto route_finish; + } + closelist_insert(curpoint); +#ifdef DEBUG_ROUTE + printf("\n\n\n*** ROUTE COUNT = %d\n", count); +#endif + candidatepoints = compute_candidate_points(r, cur_layer, curpoint, data, &destv); + +/*#ifdef DEBUG_ROUTE */ + /********************* + if(debug && !strcmp(data->dest->netlist, " unnamed_net2")) + { + unsigned int mask = ~(VERTEX_FLAG_RED | VERTEX_FLAG_GREEN | VERTEX_FLAG_BLUE); + char buffer[256]; + int j; + + for(j=0;jlayers[j].vertices; + while(i) { + TOPOROUTER_VERTEX(i->data)->flags &= mask; + i = i->next; + } + } + + i = candidatepoints; + while(i) { + TOPOROUTER_VERTEX(i->data)->flags |= VERTEX_FLAG_GREEN; +// printf("flagged a candpoint @ %f,%f\n", +// vx(i->data), vy(i->data)); + i = i->next; + } + + curpoint->flags |= VERTEX_FLAG_BLUE; + if(curpoint->parent) + curpoint->parent->flags |= VERTEX_FLAG_RED; + + + for(j=0;jlayers[j].surface, buffer, 1024, 1024, 2, datas, j, candidatepoints); + g_list_free(datas); + } + } +//#endif + *********************/ + count++; +/* if(count > 100) exit(0);*/ + i = candidatepoints; + while (i) { + toporouter_vertex_t *temppoint = TOPOROUTER_VERTEX(i->data); + if (!g_list_find(closelist, temppoint) && candidate_is_available(curpoint, temppoint)) { /*&& temppoint != curpoint) { */ + toporouter_heap_search_data_t heap_search_data = { temppoint, NULL }; + + guint temp_gn; + gdouble temp_g_cost = gcost(r, data, srcv, temppoint, curpoint, &temp_gn, pair); + + + gts_eheap_foreach(openlist, toporouter_heap_search, &heap_search_data); + + if (heap_search_data.result) { + if (temp_g_cost < temppoint->gcost) { + + temppoint->gcost = temp_g_cost; + temppoint->gn = temp_gn; + + temppoint->parent = curpoint; + curpoint->child = temppoint; + + gts_eheap_update(openlist); + } + } + else { + temppoint->parent = curpoint; + curpoint->child = temppoint; + + temppoint->gcost = temp_g_cost; + temppoint->gn = temp_gn; + + temppoint->hcost = simple_h_cost(r, temppoint, destv); +/* if(cur_layer != dest_layer) temppoint->hcost += r->viacost;*/ + gts_epcb_heap_insert(openlist, temppoint); + } + + } + i = i->next; + } + g_list_free(candidatepoints); + + } +#ifdef DEBUG_ROUTE + printf("ROUTE: could not find path!\n"); +#endif + + data->score = INFINITY; + clean_routing_edges(r, data); + + data->path = NULL; + /*TOPOROUTER_VERTEX(data->src->point)->parent = NULL; + TOPOROUTER_VERTEX(data->src->point)->child = NULL; */ + goto routing_return; + +/* + { + int i; + for(i=0;iroutecount, i); + toporouter_draw_surface(r, r->layers[i].surface, buffer, 1280, 1280, 2, data, i, NULL); + } + r->routecount++; + } + exit(0); +*/ +route_finish: +/* printf(" * finished a*\n");*/ +/* + { + int i; + for(i=0;iroutecount); + toporouter_draw_surface(r, r->layers[i].surface, buffer, 1024, 1024, 2, data, i, NULL); + } + r->routecount++; + } +*/ +/* { + i = data->path; + while(i) { + toporouter_vertex_t *tv = TOPOROUTER_VERTEX(i->data); + + if(tv->routingedge) { + GList *list = g_list_find(edge_routing(tv->routingedge), tv); + toporouter_vertex_t *restartv = NULL, *boxpoint; + + g_assert(list); + + if(!list->next) { + if(vertex_bbox(tedge_v2(tv->routingedge))) + boxpoint = TOPOROUTER_VERTEX(vertex_bbox(tedge_v2(tv->routingedge))->point); + else + boxpoint = NULL; + + if(tedge_v2(tv->routingedge) != srcv && g_list_find(data->srcvertices, tedge_v2(tv->routingedge))) + restartv = tedge_v2(tv->routingedge); + else if(boxpoint != srcv && g_list_find(data->srcvertices, boxpoint)) + restartv = boxpoint; + } + + if(!list->prev) { + if(vertex_bbox(tedge_v1(tv->routingedge))) + boxpoint = TOPOROUTER_VERTEX(vertex_bbox(tedge_v1(tv->routingedge))->point); + else + boxpoint = NULL; + + if(tedge_v1(tv->routingedge) != srcv && g_list_find(data->srcvertices, tedge_v1(tv->routingedge))) + restartv = tedge_v1(tv->routingedge); + else if(boxpoint != srcv && g_list_find(data->srcvertices, boxpoint)) + restartv = boxpoint; + + } + + if(restartv) { + clean_routing_edges(r, data); + gts_epcb_heap_destroy(openlist); + g_list_free(closelist); + openlist = gts_eheap_new(route_heap_cmp, NULL); + closelist = NULL; + g_list_free(data->path); + printf("ROUTING RESTARTING with new src %f,%f,%f\n", vx(restartv), vy(restartv), vz(restartv)); + curpoint = restartv; + goto route_begin; + } + } + + i = i->next; + } + }*/ + + { + toporouter_vertex_t *pv = NULL; + GList *i = data->path; + while (i) { + toporouter_vertex_t *tv = TOPOROUTER_VERTEX(i->data); + + if (pv && g_list_find(data->srcvertices, tv)) { + GList *temp = g_list_copy(i); + g_list_free(data->path); + data->path = temp; + i = data->path; + } + pv = tv; + i = i->next; + } + } + + { + toporouter_vertex_t *pv = NULL; + GList *i = data->path; + while (i) { + toporouter_vertex_t *tv = TOPOROUTER_VERTEX(i->data); + if (tv->flags & VERTEX_FLAG_TEMP) { + tv->flags ^= VERTEX_FLAG_TEMP; + tv->flags |= VERTEX_FLAG_ROUTE; + } + if (pv) + pv->child = tv; + + if (tv->routingedge) + tv->route = data; + +/* if(tv->routingedge && !TOPOROUTER_IS_CONSTRAINT(tv->routingedge)) space_edge(tv->routingedge, NULL);*/ + + pv = tv; + i = i->next; + } + } + + { + toporouter_vertex_t *pv = NULL, *v = NULL; + + GList *i = data->path; + while (i) { + v = TOPOROUTER_VERTEX(i->data); + + if (pv) { + v->parent = pv; + pv->child = v; + } + else { + v->parent = NULL; + } + + pv = v; + i = i->next; + } + + if (v) + v->child = NULL; + } + + clean_routing_edges(r, data); + { + GList *i = data->path; + while (i) { + toporouter_vertex_t *v = TOPOROUTER_VERTEX(i->data); + + if (v->routingedge && !TOPOROUTER_IS_CONSTRAINT(v->routingedge)) + space_edge(v->routingedge, NULL); + i = i->next; + } + } +routing_return: + + g_list_free(data->destvertices); + g_list_free(data->srcvertices); + data->destvertices = NULL; + data->srcvertices = NULL; + gts_epcb_heap_destroy(openlist); + g_list_free(closelist); + + data->alltemppoints = NULL; + + return rval; +} + +/* moves vertex v d units in the direction of vertex p */ +void vertex_move_towards_point(GtsVertex * v, gdouble px, gdouble py, gdouble d) +{ + gdouble dx = px - GTS_POINT(v)->x; + gdouble dy = py - GTS_POINT(v)->y; + gdouble theta = atan(fabs(dy / dx)); + + g_assert(finite(theta)); + + if (dx >= 0.) { + + if (dy >= 0.) { + GTS_POINT(v)->x += d * cos(theta); + GTS_POINT(v)->y += d * sin(theta); + } + else { + GTS_POINT(v)->x += d * cos(theta); + GTS_POINT(v)->y -= d * sin(theta); + } + + } + else { + + if (dy >= 0.) { + GTS_POINT(v)->x -= d * cos(theta); + GTS_POINT(v)->y += d * sin(theta); + } + else { + GTS_POINT(v)->x -= d * cos(theta); + GTS_POINT(v)->y -= d * sin(theta); + } + + } + +} + +/* moves vertex v d units in the direction of vertex p */ +void vertex_move_towards_vertex(GtsVertex * v, GtsVertex * p, gdouble d) +{ + gdouble dx = GTS_POINT(p)->x - GTS_POINT(v)->x; + gdouble dy = GTS_POINT(p)->y - GTS_POINT(v)->y; + gdouble theta = atan(fabs(dy / dx)); + + g_assert(finite(theta)); + + if (dx >= 0.) { + + if (dy >= 0.) { + GTS_POINT(v)->x += d * cos(theta); + GTS_POINT(v)->y += d * sin(theta); + } + else { + GTS_POINT(v)->x += d * cos(theta); + GTS_POINT(v)->y -= d * sin(theta); + } + + } + else { + + if (dy >= 0.) { + GTS_POINT(v)->x -= d * cos(theta); + GTS_POINT(v)->y += d * sin(theta); + } + else { + GTS_POINT(v)->x -= d * cos(theta); + GTS_POINT(v)->y -= d * sin(theta); + } + + } + +} + + +gdouble pathvertex_arcing_through_constraint(toporouter_vertex_t * pathv, toporouter_vertex_t * arcv) +{ + toporouter_vertex_t *v = pathv->child; + + if (!v || !v->routingedge) + return 0.; + + while (v->flags & VERTEX_FLAG_ROUTE && (tedge_v1(v->routingedge) == arcv || tedge_v2(v->routingedge) == arcv)) { + if (TOPOROUTER_IS_CONSTRAINT(v->routingedge)) + return gts_point_distance(GTS_POINT(tedge_v1(v->routingedge)), GTS_POINT(tedge_v2(v->routingedge))); + v = v->child; + } + + v = pathv->parent; + while (v->flags & VERTEX_FLAG_ROUTE && (tedge_v1(v->routingedge) == arcv || tedge_v2(v->routingedge) == arcv)) { + if (TOPOROUTER_IS_CONSTRAINT(v->routingedge)) + return gts_point_distance(GTS_POINT(tedge_v1(v->routingedge)), GTS_POINT(tedge_v2(v->routingedge))); + v = v->parent; + } + + return 0.; +} + +gint vertices_connected(toporouter_vertex_t * a, toporouter_vertex_t * b) +{ + return ((a->route->netlist == b->route->netlist && a->route->src->c == b->route->src->c) ? 1 : 0); +} + +gdouble edge_min_spacing(GList * list, toporouter_edge_t * e, toporouter_vertex_t * v, guint debug) +{ + toporouter_vertex_t *origin; + GList *i = list; + gdouble space = 0.; + toporouter_vertex_t *nextv, *prevv; + /*toporouter_vertex_t *edgev; + gdouble constraint_spacing; */ + + if (!list) + return INFINITY; + +/* printf("\t CMS %f,%f - %f,%f\n", vx(tedge_v1(e)), vy(tedge_v1(e)), vx(tedge_v2(e)), vy(tedge_v2(e))); */ + + prevv = origin = TOPOROUTER_VERTEX(list->data); + +/* print_edge(e); */ + + i = list; + if (gts_point_distance2(GTS_POINT(origin), GTS_POINT(edge_v1(e))) < gts_point_distance2(GTS_POINT(v), GTS_POINT(edge_v1(e)))) { + + /* towards v2 */ + while (i) { + nextv = edge_routing_next(e, i); + if (nextv->route && vertices_connected(nextv, prevv)) { + i = i->next; + continue; + } + if (!(nextv->flags & VERTEX_FLAG_TEMP)) { + gdouble ms = min_spacing(prevv, nextv); + if (nextv == tedge_v2(e)) { + gdouble cms = pathvertex_arcing_through_constraint(TOPOROUTER_VERTEX(i->data), tedge_v2(e)); +/* printf("\t CMS to %f,%f = %f \t ms = %f\n", vx(tedge_v2(e)), vy(tedge_v2(e)), cms, ms); + if(vx(tedge_v2(e)) > -EPSILON && vx(tedge_v2(e)) < EPSILON) { + printf("\t\tPROB: "); + print_vertex(tedge_v2(e)); + }*/ + if (cms > EPSILON) + space += MIN(ms, cms / 2.); + else + space += ms; + } + else + space += ms; + + prevv = nextv; + } +/* printf("%f ", space);*/ + i = i->next; + } + } + else { + + /* towards v1 */ + while (i) { + nextv = edge_routing_prev(e, i); + if (nextv->route && vertices_connected(nextv, prevv)) { + i = i->prev; + continue; + } + if (!(nextv->flags & VERTEX_FLAG_TEMP)) { + gdouble ms = min_spacing(prevv, nextv); + if (nextv == tedge_v1(e)) { + gdouble cms = pathvertex_arcing_through_constraint(TOPOROUTER_VERTEX(i->data), tedge_v1(e)); +/* printf("\t CMS to %f,%f = %f \t ms = %f\n", vx(tedge_v1(e)), vy(tedge_v1(e)), cms, ms); + if(vx(tedge_v1(e)) > -EPSILON && vx(tedge_v1(e)) < EPSILON) { + printf("\t\tPROB: "); + print_vertex(tedge_v1(e)); + }*/ + if (cms > EPSILON) + space += MIN(ms, cms / 2.); + else + space += ms; + } + else + space += ms; + + prevv = nextv; + } +/* printf("%f ", space);*/ + i = i->prev; + } + } + + if (TOPOROUTER_IS_CONSTRAINT(e) && space > gts_point_distance(GTS_POINT(edge_v1(e)), GTS_POINT(edge_v2(e))) / 2.) + space = gts_point_distance(GTS_POINT(edge_v1(e)), GTS_POINT(edge_v2(e))) / 2.; + +/* if(debug) printf("\tedge_min_spacing: %f\n", space);*/ + return space; +} + +/* line segment is 1 & 2, point is 3 + returns 0 if v3 is outside seg +*/ +guint +vertex_line_normal_intersection(gdouble x1, gdouble y1, gdouble x2, gdouble y2, gdouble x3, gdouble y3, gdouble * x, + gdouble * y) +{ + gdouble m1 = cartesian_gradient(x1, y1, x2, y2); + gdouble m2 = perpendicular_gradient(m1); + gdouble c2 = (isinf(m2)) ? x3 : y3 - (m2 * x3); + gdouble c1 = (isinf(m1)) ? x1 : y1 - (m1 * x1); + + if (isinf(m2)) + *x = x3; + else if (isinf(m1)) + *x = x1; + else + *x = (c2 - c1) / (m1 - m2); + + *y = (isinf(m2)) ? y1 : (m2 * (*x)) + c2; + + if (*x >= MIN(x1, x2) - EPSILON && *x <= MAX(x1, x2) + EPSILON && *y >= MIN(y1, y2) - EPSILON && *y <= MAX(y1, y2) + EPSILON) + return 1; + return 0; +} + +void print_toporouter_arc(toporouter_arc_t * arc) +{ +/* GList *i = arc->vs;*/ + + printf("ARC CENTRE: %f,%f ", vx(arc->centre), vy(arc->centre)); /* print_vertex(arc->centre); */ + printf("RADIUS: %f", arc->r); + + if (arc->dir > 0) + printf(" COUNTERCLOCKWISE "); + else if (arc->dir < 0) + printf(" CLOCKWISE "); + else + printf(" COLINEAR(ERROR) "); +/* + printf("\n\tVS: "); + + while(i) { + toporouter_vertex_t *v = TOPOROUTER_VERTEX(i->data); + printf("%f,%f ", vx(v), vy(v)); + + i = i->next; + } +*/ +} + +void toporouter_arc_remove(toporouter_oproute_t * oproute, toporouter_arc_t * arc) +{ + oproute->arcs = g_list_remove(oproute->arcs, arc); + + if (arc->v) + arc->v->arc = NULL; +} + +toporouter_arc_t *toporouter_arc_new(toporouter_oproute_t * oproute, toporouter_vertex_t * v1, toporouter_vertex_t * v2, + toporouter_vertex_t * centre, gdouble r, gint dir) +{ + toporouter_arc_t *arc = TOPOROUTER_ARC(gts_object_new(GTS_OBJECT_CLASS(toporouter_arc_class()))); + arc->centre = centre; + arc->v = v1; + arc->v1 = v1; + arc->v2 = v2; + arc->r = r; + arc->dir = dir; + + if (v1) + v1->arc = arc; + arc->oproute = oproute; + + arc->clearance = NULL; + + return arc; +} + +void path_set_oproute(GList * path, toporouter_oproute_t * oproute) +{ + while (path) { + toporouter_vertex_t *v = TOPOROUTER_VERTEX(path->data); + + if (v->flags & VERTEX_FLAG_ROUTE) + v->oproute = oproute; + + path = path->next; + } +} + +void print_oproute(toporouter_oproute_t * oproute) +{ + GList *i = oproute->arcs; + + printf("Optimized Route:\n"); + printf("\tNetlist:\t\t%s\n\tStyle:\t\t%s\n", oproute->netlist, oproute->style); + /* printf("%s\n", oproute->netlist); */ +/* + i = oproute->term1->zlink; + while(i) { + toporouter_vertex_t *thisv = TOPOROUTER_VERTEX(i->data); + printf("\tNetlist:\t\t%s\n\tStyle:\t\t%s\n", vertex_bbox(thisv)->netlist, vertex_bbox(thisv)->style); + i = i->next; + } +*/ + printf("\t"); + print_vertex(oproute->term1); + printf("\n"); + i = oproute->arcs; + while (i) { + toporouter_arc_t *arc = (toporouter_arc_t *) i->data; + printf("\t"); + print_toporouter_arc(arc); + printf("\n"); + i = i->next; + } + printf("\t"); + print_vertex(oproute->term2); + printf("\n"); +} + +gdouble export_pcb_drawline(guint layer, guint x0, guint y0, guint x1, guint y1, guint thickness, guint clearance) +{ + gdouble d = 0.; + pcb_line_t *line; + line = pcb_line_new_merge(LAYER_PTR(layer), x0, y0, x1, y1, + thickness, clearance, pcb_flag_make(PCB_FLAG_AUTO | (PCB_FLAG_TEST(PCB_CLEARNEWFLAG, PCB) ? PCB_FLAG_CLEARLINE : 0))); + + if (line) { + pcb_undo_add_obj_to_create(PCB_TYPE_LINE, LAYER_PTR(layer), line, line); + d = coord_distance((double) x0, (double) y0, (double) x1, (double) y1) / 100.; + } + return d; +} + +gdouble arc_angle(toporouter_arc_t * arc) +{ + gdouble x0, x1, y0, y1; + + x0 = arc->x0 - vx(arc->centre); + x1 = arc->x1 - vx(arc->centre); + y0 = arc->y0 - vy(arc->centre); + y1 = arc->y1 - vy(arc->centre); + + return fabs(acos(((x0 * x1) + (y0 * y1)) / (sqrt(pow(x0, 2) + pow(y0, 2)) * sqrt(pow(x1, 2) + pow(y1, 2))))); +} + +gdouble export_pcb_drawarc(guint layer, toporouter_arc_t * a, guint thickness, guint clearance) +{ + gdouble sa, da, theta; + gdouble d = 0.; + pcb_arc_t *arc; + gint wind; + + wind = coord_wind(a->x0, a->y0, a->x1, a->y1, vx(a->centre), vy(a->centre)); + + sa = coord_xangle(a->x0, a->y0, vx(a->centre), vy(a->centre)) * 180. / M_PI; + + theta = arc_angle(a); + + if (!a->dir || !wind) + return 0.; + + if (a->dir != wind) + theta = 2. * M_PI - theta; + + da = -a->dir * theta * 180. / M_PI; + + if (da < 1. && da > -1.) + return 0.; + if (da > 359. || da < -359.) + return 0.; + + arc = pcb_arc_new(LAYER_PTR(layer), vx(a->centre), vy(a->centre), a->r, a->r, + sa, da, thickness, clearance, + pcb_flag_make(PCB_FLAG_AUTO | (PCB_FLAG_TEST(PCB_CLEARNEWFLAG, PCB) ? PCB_FLAG_CLEARLINE : 0))); + + if (arc) { + pcb_undo_add_obj_to_create(PCB_TYPE_ARC, LAYER_PTR(layer), arc, arc); + d = a->r * theta / 100.; + } + + return d; +} + +void calculate_term_to_arc(toporouter_vertex_t * v, toporouter_arc_t * arc, guint dir) +{ + gdouble theta, a, b, bx, by, a0x, a0y, a1x, a1y; + gint winddir; + + theta = acos(arc->r / gts_point_distance(GTS_POINT(v), GTS_POINT(arc->centre))); + a = arc->r * sin(theta); + b = arc->r * cos(theta); +#ifdef DEBUG_EXPORT + printf("drawing arc with r %f theta %f d %f centre = %f,%f\n", arc->r, theta, + gts_point_distance(GTS_POINT(v), GTS_POINT(arc->centre)), vx(arc->centre), vy(arc->centre)); +#endif + point_from_point_to_point(arc->centre, v, b, &bx, &by); + + coords_on_line(bx, by, perpendicular_gradient(point_gradient(GTS_POINT(v), GTS_POINT(arc->centre))), a, &a0x, &a0y, &a1x, + &a1y); + + winddir = coord_wind(vx(v), vy(v), a0x, a0y, vx(arc->centre), vy(arc->centre)); + + if (!winddir) { + printf("!winddir @ v %f,%f arc->centre %f,%f\n", vx(v), vy(v), vx(arc->centre), vy(arc->centre)); + /*TODO: fix hack: this shouldn't happen */ + arc->x0 = vx(v); + arc->y0 = vy(v); + arc->x1 = vx(v); + arc->y1 = vy(v); + return; + } + + g_assert(winddir); + + if (dir) + winddir = -winddir; + + if (winddir == arc->dir) { + if (!dir) { + arc->x0 = a0x; + arc->y0 = a0y; + } + else { + arc->x1 = a0x; + arc->y1 = a0y; + } + } + else { + if (!dir) { + arc->x0 = a1x; + arc->y0 = a1y; + } + else { + arc->x1 = a1x; + arc->y1 = a1y; + } + } + +} + + + +/* b1 is the projection in the direction of narc, while b2 is the perpendicular projection*/ +void arc_ortho_projections(toporouter_arc_t * arc, toporouter_arc_t * narc, gdouble * b1, gdouble * b2) +{ + gdouble nax, nay, ax, ay, alen2, c; + gdouble b1x, b1y, b2x, b2y; + +#ifdef DEBUG_EXPORT + printf("arc c = %f,%f narc c = %f,%f arc->0 = %f,%f\n", + vx(arc->centre), vy(arc->centre), vx(narc->centre), vy(narc->centre), arc->x0, arc->y0); +#endif + + nax = vx(narc->centre) - vx(arc->centre); + nay = vy(narc->centre) - vy(arc->centre); + alen2 = pow(nax, 2) + pow(nay, 2); + + + ax = arc->x0 - vx(arc->centre); + ay = arc->y0 - vy(arc->centre); + +#ifdef DEBUG_EXPORT + printf("norm narc = %f,%f - %f\tA=%f,%f\n", nax, nay, sqrt(alen2), ax, ay); +#endif + + c = ((ax * nax) + (ay * nay)) / alen2; + + b1x = c * nax; + b1y = c * nay; + b2x = ax - b1x; + b2y = ay - b1y; + +#ifdef DEBUG_EXPORT + printf("proj = %f,%f perp proj = %f,%f\n", b1x, b1y, b2x, b2y); +#endif + + *b1 = sqrt(pow(b1x, 2) + pow(b1y, 2)); + *b2 = sqrt(pow(b2x, 2) + pow(b2y, 2)); + +} + +guint calculate_arc_to_arc(toporouter_t * ar, toporouter_arc_t * parc, toporouter_arc_t * arc) +{ + gdouble theta, a, b, bx, by, a0x, a0y, a1x, a1y, m, preva, prevb; + gint winddir; + toporouter_arc_t *bigr, *smallr; + + if (parc->r > arc->r) { + bigr = parc; + smallr = arc; + } + else { + bigr = arc; + smallr = parc; + } +#ifdef DEBUG_EXPORT + printf("bigr centre = %f,%f smallr centre = %f,%f\n", vx(bigr->centre), vy(bigr->centre), + vx(smallr->centre), vy(smallr->centre)); +#endif + + m = perpendicular_gradient(point_gradient(GTS_POINT(bigr->centre), GTS_POINT(smallr->centre))); + + if (bigr->centre == smallr->centre) { + + printf("bigr->centre == smallr->centre @ %f,%f\n", vx(smallr->centre), vy(smallr->centre)); + } + + g_assert(bigr->centre != smallr->centre); + + if (parc->dir == arc->dir) { +/*export_arc_straight:*/ + + theta = acos((bigr->r - smallr->r) / gts_point_distance(GTS_POINT(bigr->centre), GTS_POINT(smallr->centre))); + a = bigr->r * sin(theta); + b = bigr->r * cos(theta); + + point_from_point_to_point(bigr->centre, smallr->centre, b, &bx, &by); + + coords_on_line(bx, by, m, a, &a0x, &a0y, &a1x, &a1y); + + winddir = coord_wind(vx(smallr->centre), vy(smallr->centre), a0x, a0y, vx(bigr->centre), vy(bigr->centre)); + + arc_ortho_projections(parc, arc, &prevb, &preva); +/*#ifdef DEBUG_EXPORT */ + if (!winddir) { + + printf("STRAIGHT:\n"); + printf("bigr centre = %f,%f smallr centre = %f,%f\n", vx(bigr->centre), vy(bigr->centre), + vx(smallr->centre), vy(smallr->centre)); + printf("theta = %f a = %f b = %f bigrr = %f d = %f po = %f\n", theta, a, b, bigr->r, + gts_point_distance(GTS_POINT(bigr->centre), GTS_POINT(smallr->centre)), + bigr->r / gts_point_distance(GTS_POINT(bigr->centre), GTS_POINT(smallr->centre))); + printf("bigr-r = %f smallr-r = %f ratio = %f\n", + bigr->r, smallr->r, (bigr->r - smallr->r) / gts_point_distance(GTS_POINT(bigr->centre), + GTS_POINT(smallr->centre))); + printf("preva = %f prevb = %f\n\n", preva, prevb); + + } +/*#endif*/ + g_assert(winddir); + + if (bigr == parc) + winddir = -winddir; + + if (winddir == bigr->dir) { + if (bigr == arc) { + bigr->x0 = a0x; + bigr->y0 = a0y; + } + else { + bigr->x1 = a0x; + bigr->y1 = a0y; + } + } + else { + if (bigr == arc) { + bigr->x0 = a1x; + bigr->y0 = a1y; + } + else { + bigr->x1 = a1x; + bigr->y1 = a1y; + } + } + + a = smallr->r * sin(theta); + b = smallr->r * cos(theta); + +#ifdef DEBUG_EXPORT + printf("a = %f b = %f\n", a, b); +#endif + point_from_point_to_point(smallr->centre, bigr->centre, -b, &bx, &by); + + coords_on_line(bx, by, m, a, &a0x, &a0y, &a1x, &a1y); + + if (winddir == bigr->dir) { + if (bigr == arc) { + smallr->x1 = a0x; + smallr->y1 = a0y; + } + else { + smallr->x0 = a0x; + smallr->y0 = a0y; + } + } + else { + if (bigr == arc) { + smallr->x1 = a1x; + smallr->y1 = a1y; + } + else { + smallr->x0 = a1x; + smallr->y0 = a1y; + } + } + + } + else { + +/*export_arc_twist: */ + + theta = acos((bigr->r + smallr->r) / gts_point_distance(GTS_POINT(bigr->centre), GTS_POINT(smallr->centre))); + a = bigr->r * sin(theta); + b = bigr->r * cos(theta); + + point_from_point_to_point(bigr->centre, smallr->centre, b, &bx, &by); + + coords_on_line(bx, by, m, a, &a0x, &a0y, &a1x, &a1y); + + winddir = coord_wind(vx(smallr->centre), vy(smallr->centre), a0x, a0y, vx(bigr->centre), vy(bigr->centre)); +/*#ifdef DEBUG_EXPORT */ + if (!winddir) { + printf("TWIST:\n"); + printf("theta = %f a = %f b = %f r = %f d = %f po = %f\n", theta, a, b, bigr->r + smallr->r, + gts_point_distance(GTS_POINT(bigr->centre), GTS_POINT(smallr->centre)), + (bigr->r + smallr->r) / gts_point_distance(GTS_POINT(bigr->centre), GTS_POINT(smallr->centre))); + + printf("bigr centre = %f,%f smallr centre = %f,%f\n\n", vx(bigr->centre), vy(bigr->centre), + vx(smallr->centre), vy(smallr->centre)); + + printf("big wind = %d small wind = %d\n", bigr->dir, smallr->dir); + return 1; + } +/*#endif */ +/* if(!winddir) { + smallr->centre->flags |= VERTEX_FLAG_RED; + bigr->centre->flags |= VERTEX_FLAG_GREEN; + //bigr->centre->flags |= VERTEX_FLAG_RED; + { + int i; + for(i=0;ilayers[i].surface, buffer, 2096, 2096, 2, NULL, i, NULL); + } + } + return; + } +*/ + g_assert(winddir); + + if (bigr == parc) + winddir = -winddir; + + if (winddir == bigr->dir) { + if (bigr == arc) { + bigr->x0 = a0x; + bigr->y0 = a0y; + } + else { + bigr->x1 = a0x; + bigr->y1 = a0y; + } + } + else { + if (bigr == arc) { + bigr->x0 = a1x; + bigr->y0 = a1y; + } + else { + bigr->x1 = a1x; + bigr->y1 = a1y; + } + } + + a = smallr->r * sin(theta); + b = smallr->r * cos(theta); + + point_from_point_to_point(smallr->centre, bigr->centre, b, &bx, &by); + + coords_on_line(bx, by, m, a, &a0x, &a0y, &a1x, &a1y); + + winddir = coord_wind(vx(smallr->centre), vy(smallr->centre), a0x, a0y, vx(bigr->centre), vy(bigr->centre)); + + g_assert(winddir); + + if (bigr == parc) + winddir = -winddir; + + if (winddir == smallr->dir) { + if (bigr == arc) { + smallr->x1 = a0x; + smallr->y1 = a0y; + } + else { + smallr->x0 = a0x; + smallr->y0 = a0y; + } + } + else { + if (bigr == arc) { + smallr->x1 = a1x; + smallr->y1 = a1y; + } + else { + smallr->x0 = a1x; + smallr->y0 = a1y; + } + } + + } + + return 0; +} + +void export_oproutes(toporouter_t * ar, toporouter_oproute_t * oproute) +{ + guint layer = PCB->LayerGroups.Entries[oproute->layergroup][0]; + guint thickness = lookup_thickness(oproute->style); + guint clearance = lookup_clearance(oproute->style); + GList *arcs = oproute->arcs; + toporouter_arc_t *arc, *parc = NULL; + + if (!arcs) { + ar->wiring_score += + export_pcb_drawline(layer, vx(oproute->term1), vy(oproute->term1), vx(oproute->term2), vy(oproute->term2), thickness, + clearance); + return; + } + + +/* calculate_term_to_arc(oproute->term1, TOPOROUTER_ARC(arcs->data), 0, layer);*/ + + while (arcs) { + arc = TOPOROUTER_ARC(arcs->data); + + if (parc && arc) { + ar->wiring_score += export_pcb_drawarc(layer, parc, thickness, clearance); + ar->wiring_score += export_pcb_drawline(layer, parc->x1, parc->y1, arc->x0, arc->y0, thickness, clearance); + } + else if (!parc) { + ar->wiring_score += + export_pcb_drawline(layer, vx(oproute->term1), vy(oproute->term1), arc->x0, arc->y0, thickness, clearance); + } + + parc = arc; + arcs = arcs->next; + } + ar->wiring_score += export_pcb_drawarc(layer, arc, thickness, clearance); + ar->wiring_score += export_pcb_drawline(layer, arc->x1, arc->y1, vx(oproute->term2), vy(oproute->term2), thickness, clearance); + +} + + + +void oproute_free(toporouter_oproute_t * oproute) +{ + GList *i = oproute->arcs; + while (i) { + toporouter_arc_t *arc = (toporouter_arc_t *) i->data; + if (arc->centre->flags & VERTEX_FLAG_TEMP) + gts_object_destroy(GTS_OBJECT(arc->centre)); + + i = i->next; + } + + g_list_free(oproute->arcs); + free(oproute); +} + +void oproute_calculate_tof(toporouter_oproute_t * oproute) +{ + GList *arcs = oproute->arcs; + toporouter_arc_t *parc = NULL, *arc; + + oproute->tof = 0.; + + if (!arcs) { + oproute->tof = gts_point_distance(GTS_POINT(oproute->term1), GTS_POINT(oproute->term2)); + return; + } + + while (arcs) { + arc = TOPOROUTER_ARC(arcs->data); + + if (parc && arc) { + oproute->tof += arc_angle(parc) * parc->r; + oproute->tof += sqrt(pow(parc->x1 - arc->x0, 2) + pow(parc->y1 - arc->y0, 2)); + } + else if (!parc) { + oproute->tof += sqrt(pow(arc->x0 - vx(oproute->term1), 2) + pow(arc->y0 - vy(oproute->term1), 2)); + } + + parc = arc; + arcs = arcs->next; + } + + oproute->tof += arc_angle(parc) * parc->r; + oproute->tof += sqrt(pow(arc->x1 - vx(oproute->term2), 2) + pow(arc->y1 - vy(oproute->term2), 2)); + +} + +gdouble +line_line_distance_at_normal(gdouble line1_x1, gdouble line1_y1, + gdouble line1_x2, gdouble line1_y2, + gdouble line2_x1, gdouble line2_y1, gdouble line2_x2, gdouble line2_y2, gdouble x, gdouble y) +{ + gdouble m1 = perpendicular_gradient(cartesian_gradient(line1_x1, line1_y1, line1_x2, line1_y2)); + gdouble m2 = cartesian_gradient(line2_x1, line2_y1, line2_x2, line2_y2); + gdouble c1 = (isinf(m1)) ? x : y - (m1 * x); + gdouble c2 = (isinf(m2)) ? line2_x1 : line2_y1 - (m2 * line2_x1); + + gdouble intx, inty; + + if (isinf(m2)) + intx = line2_x1; + else if (isinf(m1)) + intx = x; + else + intx = (c2 - c1) / (m1 - m2); + + inty = (isinf(m2)) ? (m1 * intx) + c1 : (m2 * intx) + c2; + + return sqrt(pow(x - intx, 2) + pow(y - inty, 2)); +} + +void calculate_serpintine(gdouble delta, gdouble r, gdouble initiala, gdouble * a, guint * nhalfcycles) +{ + gdouble lhalfcycle = 2. * (initiala - r) + (M_PI * r); + guint n; + + printf("lhalfcycle = %f r = %f\n", lhalfcycle, r); + + n = (delta - M_PI * r) / (lhalfcycle - 2. * r) + 1; + *a = (delta + 4. * n * r - n * M_PI * r + 4. * r - M_PI * r) / (2. * n); + *nhalfcycles = n; +} + +gdouble oproute_min_spacing(toporouter_oproute_t * a, toporouter_oproute_t * b) +{ + return lookup_thickness(a->style) / 2. + lookup_thickness(b->style) / 2. + MAX(lookup_clearance(a->style), + lookup_clearance(b->style)); +} + +gdouble vector_angle(gdouble ox, gdouble oy, gdouble ax, gdouble ay, gdouble bx, gdouble by) +{ + gdouble alen = sqrt(pow(ax - ox, 2) + pow(ay - oy, 2)); + gdouble blen = sqrt(pow(bx - ox, 2) + pow(by - oy, 2)); + return acos(((ax - ox) * (bx - ox) + (ay - oy) * (by - oy)) / (alen * blen)); +} + +toporouter_serpintine_t *toporouter_serpintine_new(gdouble x, gdouble y, gdouble x0, gdouble y0, gdouble x1, gdouble y1, + gpointer start, gdouble halfa, gdouble radius, guint nhalfcycles) +{ + toporouter_serpintine_t *serp = (toporouter_serpintine_t *) malloc(sizeof(toporouter_serpintine_t)); + serp->x = x; + serp->y = y; + serp->x0 = x0; + serp->y0 = y0; + serp->x1 = x1; + serp->y1 = y1; + serp->start = start; + serp->halfa = halfa; + serp->radius = radius; + serp->nhalfcycles = nhalfcycles; + serp->arcs = NULL; + return serp; +} + +/*#define DEBUG_RUBBERBAND 1*/ + +gdouble +check_non_intersect_vertex(gdouble x0, gdouble y0, gdouble x1, gdouble y1, toporouter_vertex_t * pathv, + toporouter_vertex_t * arcv, toporouter_vertex_t * opv, gint wind, gint * arcwind, gdouble * arcr, + guint debug) +{ + gdouble ms, line_int_x, line_int_y, x, y, d = 0., m; + gdouble tx0, ty0, tx1, ty1; + gint wind1, wind2; + + g_assert(pathv->routingedge); + + if (TOPOROUTER_IS_CONSTRAINT(pathv->routingedge)) { + gdouble d = tvdistance(tedge_v1(pathv->routingedge), tedge_v2(pathv->routingedge)) / 2.; + ms = min_spacing(pathv, arcv); + if (ms > d) + ms = d; + } + else { + ms = edge_min_spacing(g_list_find(edge_routing(pathv->routingedge), pathv), pathv->routingedge, arcv, debug); + } + + + if (!vertex_line_normal_intersection(x0, y0, x1, y1, vx(arcv), vy(arcv), &line_int_x, &line_int_y)) { + + if (coord_distance2(x0, y0, line_int_x, line_int_y) < coord_distance2(x1, y1, line_int_x, line_int_y)) { + line_int_x = x0; + line_int_y = y0; + } + else { + line_int_x = x1; + line_int_y = y1; + } + + m = perpendicular_gradient(cartesian_gradient(vx(arcv), vy(arcv), line_int_x, line_int_y)); + } + else { + m = cartesian_gradient(x0, y0, x1, y1); + } + + coords_on_line(vx(arcv), vy(arcv), m, 100., &tx0, &ty0, &tx1, &ty1); + + wind1 = coord_wind(tx0, ty0, tx1, ty1, line_int_x, line_int_y); + wind2 = coord_wind(tx0, ty0, tx1, ty1, vx(opv), vy(opv)); + + if (!wind2 || wind1 == wind2) + return -1.; + + if (!wind) { + coords_on_line(line_int_x, line_int_y, perpendicular_gradient(m), ms, &tx0, &ty0, &tx1, &ty1); + if (coord_distance2(tx0, ty0, vx(opv), vy(opv)) < coord_distance2(tx1, ty1, vx(opv), vy(opv))) { + x = tx0; + y = ty0; + } + else { + x = tx1; + y = ty1; + } + } + else { + toporouter_vertex_t *parent = pathv->parent, *child = pathv->child; + guint windtests = 0; + + d = coord_distance(vx(arcv), vy(arcv), line_int_x, line_int_y); + coord_move_towards_coord_values(line_int_x, line_int_y, vx(arcv), vy(arcv), ms + d, &x, &y); + rewind_test: + wind1 = coord_wind(line_int_x, line_int_y, x, y, vx(parent), vy(parent)); + wind2 = coord_wind(line_int_x, line_int_y, x, y, vx(child), vy(child)); + if (wind1 && wind2 && wind1 == wind2) { +/* return -1.;*/ + if (windtests++ == 2) + return -1.; + + if (parent->flags & VERTEX_FLAG_ROUTE) + parent = parent->parent; + if (child->flags & VERTEX_FLAG_ROUTE) + child = child->child; + goto rewind_test; + } + } + + + *arcr = ms; + *arcwind = tvertex_wind(pathv->parent, pathv, arcv); + +#ifdef DEBUG_RUBBERBAND +/*if(debug) + printf("non-int check %f,%f ms %f d %f arcv %f,%f opv %f,%f\n", vx(arcv), vy(arcv), ms, d + ms, + vx(arcv), vy(arcv), vx(opv), vy(opv));*/ +#endif + + return d + ms; +} + +gdouble +check_intersect_vertex(gdouble x0, gdouble y0, gdouble x1, gdouble y1, toporouter_vertex_t * pathv, toporouter_vertex_t * arcv, + toporouter_vertex_t * opv, gint wind, gint * arcwind, gdouble * arcr, guint debug) +{ + gdouble ms, line_int_x, line_int_y, x, y, d = 0.; + + if (TOPOROUTER_IS_CONSTRAINT(pathv->routingedge)) { + gdouble d = tvdistance(tedge_v1(pathv->routingedge), tedge_v2(pathv->routingedge)) / 2.; + ms = min_spacing(pathv, arcv); + if (ms > d) + ms = d; + } + else { + ms = edge_min_spacing(g_list_find(edge_routing(pathv->routingedge), pathv), pathv->routingedge, arcv, debug); + } + + if (!vertex_line_normal_intersection(x0, y0, x1, y1, vx(arcv), vy(arcv), &line_int_x, &line_int_y)) + return -1.; + + d = coord_distance(line_int_x, line_int_y, vx(arcv), vy(arcv)); + + + if (d > ms - EPSILON) + return -1.; + + coord_move_towards_coord_values(vx(arcv), vy(arcv), line_int_x, line_int_y, ms, &x, &y); + + *arcr = ms; + *arcwind = tvertex_wind(pathv->parent, pathv, arcv); +/* *arcwind = coord_wind(x0, y0, x, y, x1, y1);*/ +#ifdef DEBUG_RUBBERBAND +/*if(debug) + printf("int check %f,%f ms %f d %f arcv %f,%f opv %f,%f\n", vx(arcv), vy(arcv), ms, ms - d, + vx(arcv), vy(arcv), vx(opv), vy(opv));*/ +#endif + + return ms - d; +} + +/* returns non-zero if arc has loops */ +guint check_arc_for_loops(gpointer t1, toporouter_arc_t * arc, gpointer t2) +{ + gdouble x0, y0, x1, y1; + + if (TOPOROUTER_IS_VERTEX(t1)) { + x0 = vx(TOPOROUTER_VERTEX(t1)); + y0 = vy(TOPOROUTER_VERTEX(t1)); + } + else { + x0 = TOPOROUTER_ARC(t1)->x1; + y0 = TOPOROUTER_ARC(t1)->y1; + } + + if (TOPOROUTER_IS_VERTEX(t2)) { + x1 = vx(TOPOROUTER_VERTEX(t2)); + y1 = vy(TOPOROUTER_VERTEX(t2)); + } + else { + x1 = TOPOROUTER_ARC(t2)->x0; + y1 = TOPOROUTER_ARC(t2)->y0; + } + + if (coord_intersect_prop(x0, y0, arc->x0, arc->y0, arc->x1, arc->y1, x1, y1)) { +/* || + (arc->x0 > arc->x1 - EPSILON && arc->x0 < arc->x1 + EPSILON && + arc->y0 > arc->y1 - EPSILON && arc->y0 < arc->y1 + EPSILON) + ) {*/ +#ifdef DEBUG_RUBBERBAND + printf("LOOPS %f %f -> %f %f & %f %f -> %f %f\n", x0, y0, arc->x0, arc->y0, arc->x1, arc->y1, x1, y1); +#endif + return 1; + } + return 0; +} + +toporouter_rubberband_arc_t *new_rubberband_arc(toporouter_vertex_t * pathv, toporouter_vertex_t * arcv, gdouble r, gdouble d, + gint wind, GList * list) +{ + toporouter_rubberband_arc_t *rba = (toporouter_rubberband_arc_t *) malloc(sizeof(toporouter_rubberband_arc_t)); + rba->pathv = pathv; + rba->arcv = arcv; + rba->r = r; + rba->d = d; + rba->wind = wind; + rba->list = list; + return rba; +} + +gint compare_rubberband_arcs(toporouter_rubberband_arc_t * a, toporouter_rubberband_arc_t * b) +{ + return b->d - a->d; +} + +void free_list_elements(gpointer data, gpointer user_data) +{ + free(data); +} + + +/* returns the edge opposite v from the triangle facing (x,y), or NULL if v is colinear with an edge between v and a neighbor */ +/* +GtsEdge * +vertex_edge_facing_vertex(GtsVertex *v, gdouble x, gdouble y) +{ + GSList *ts = gts_vertex_triangles(GTS_VERTEX(n), NULL); + GSList *i = ts; + + while(i) { + GtsTriangle *t = GTS_TRIANGLE(i->data); + GtsEdge *e = gts_triangle_edge_opposite(t, v); + + if(coord_wind(vx(edge_v1(e)), vy(edge_v1(e)), vx(v), vy(v), x, y) == vertex_wind(edge_v1(e), v, edge_v2(e)) && + coord_wind(vx(edge_v2(e)), vy(edge_v2(e)), vx(v), vy(v), x, y) == vertex_wind(edge_v2(e), v, edge_v1(e)) + ) { + g_slist_free(ts); + return e; + } + + i = i->next; + } + + g_slist_free(ts); + return NULL; +} +*/ + +gdouble +check_adj_pushing_vertex(toporouter_oproute_t * oproute, gdouble x0, gdouble y0, gdouble x1, gdouble y1, + toporouter_vertex_t * v, gdouble * arcr, gint * arcwind, toporouter_vertex_t ** arc) +{ + GSList *ns = gts_vertex_neighbors(GTS_VERTEX(v), NULL, NULL); + GSList *i = ns; + gdouble maxd = 0.; + + while (i) { + toporouter_vertex_t *n = TOPOROUTER_VERTEX(i->data); + gdouble segintx, seginty; + if (vertex_line_normal_intersection(x0, y0, x1, y1, vx(n), vy(n), &segintx, &seginty)) { + toporouter_edge_t *e = tedge(n, v); + gdouble ms = 0., d = coord_distance(segintx, seginty, vx(n), vy(n)); + /*toporouter_vertex_t *a; */ + toporouter_vertex_t *b; + GList *closestnet = NULL; + + g_assert(e); + + if (v == tedge_v1(e)) { + /*a = tedge_v1(e); */ + b = tedge_v2(e); + closestnet = edge_routing(e); + } + else { + /*a = tedge_v2(e); */ + b = tedge_v1(e); + closestnet = g_list_last(edge_routing(e)); + } + + if (closestnet) { + ms = edge_min_spacing(closestnet, e, b, 0); + ms += min_oproute_net_spacing(oproute, TOPOROUTER_VERTEX(closestnet->data)); + } + else { + ms = min_oproute_vertex_spacing(oproute, b); + } + + if (ms - d > maxd) { + *arcr = ms; + *arc = n; + maxd = ms - d; + if (vx(v) == x0 && vy(v) == y0) { + *arcwind = coord_wind(x0, y0, vx(n), vy(n), x1, y1); + } + else if (vx(v) == x1 && vy(v) == y1) { + *arcwind = coord_wind(x1, y1, vx(n), vy(n), x0, y0); + } + else { + fprintf(stderr, "ERROR: check_adj_pushing_vertex encountered bad vertex v (coordinates don't match)\n"); + } + } + } + + i = i->next; + } + + g_slist_free(ns); + return maxd; +} + + +/* path is t1 path*/ +GList *oproute_rubberband_segment(toporouter_t * r, toporouter_oproute_t * oproute, GList * path, gpointer t1, gpointer t2, + guint debug) +{ + gdouble x0, y0, x1, y1; + toporouter_vertex_t *v1, *v2, *av1, *av2; /* v{1,2} are the vertex terminals of the segment, or arc terminal centres */ + toporouter_arc_t *arc1 = NULL, *arc2 = NULL, *newarc = NULL; /* arc{1,2} are the arc terminals of the segment, if they exist */ + GList *i = path; + GList *list1, *list2; + + GList *arcs = NULL; + toporouter_rubberband_arc_t *max = NULL; + + gdouble d, arcr; + gint v1wind, v2wind, arcwind; + + if (TOPOROUTER_IS_VERTEX(t1)) { + v1 = TOPOROUTER_VERTEX(t1); + x0 = vx(v1); + y0 = vy(v1); + } + else { + g_assert(TOPOROUTER_IS_ARC(t1)); + arc1 = TOPOROUTER_ARC(t1); + v1 = TOPOROUTER_VERTEX(arc1->v1); + x0 = arc1->x1; + y0 = arc1->y1; + } + + if (TOPOROUTER_IS_VERTEX(t2)) { + v2 = TOPOROUTER_VERTEX(t2); + x1 = vx(v2); + y1 = vy(v2); + } + else { + g_assert(TOPOROUTER_IS_ARC(t2)); + arc2 = TOPOROUTER_ARC(t2); + v2 = TOPOROUTER_VERTEX(arc2->v2); + x1 = arc2->x0; + y1 = arc2->y0; + } + +#define TEST_AND_INSERT(z) if(d > EPSILON) arcs = g_list_prepend(arcs, new_rubberband_arc(v, z, arcr, d, arcwind, i)); +#define ARC_CHECKS(z) (!(arc1 && arc1->centre == z) && !(arc2 && arc2->centre == z) && \ + !(TOPOROUTER_IS_VERTEX(t1) && z == v1) && !(TOPOROUTER_IS_VERTEX(t2) && z == v2)) + + if (v1 == v2 || !i->next || TOPOROUTER_VERTEX(i->data) == v2) + return NULL; + +/*#ifdef DEBUG_RUBBERBAND*/ + if (debug) { + printf("\nRB: line %f,%f %f,%f v1 = %f,%f v2 = %f,%f \n ", x0, y0, x1, y1, vx(v1), vy(v1), vx(v2), vy(v2)); +/* if(v1->routingedge) print_edge(v1->routingedge); + if(v2->routingedge) print_edge(v2->routingedge);*/ + + } +/*#endif*/ + + /* check the vectices adjacent to the terminal vectices for push against the segment */ +/*if(TOPOROUTER_IS_VERTEX(t1)) { + toporouter_vertex_t *arcc = NULL; + d = check_adj_pushing_vertex(oproute, x0, y0, x1, y1, v1, &arcr, &arcwind, &arcc); + g_assert(arcc != v1); + if(ARC_CHECKS(arcc) && d > EPSILON) arcs = g_list_prepend(arcs, new_rubberband_arc(v1, arcc, arcr, d, arcwind, path->next)); + } + + if(TOPOROUTER_IS_VERTEX(t2)) { + toporouter_vertex_t *arcc = NULL; + d = check_adj_pushing_vertex(oproute, x0, y0, x1, y1, v2, &arcr, &arcwind, &arcc); + g_assert(arcc != v2); + if(ARC_CHECKS(arcc) && d > EPSILON) arcs = g_list_prepend(arcs, new_rubberband_arc(v2, arcc, arcr, d, arcwind, g_list_last(path)->prev)); + }*/ + + i = i->next; + while (i) { + toporouter_vertex_t *v = TOPOROUTER_VERTEX(i->data); + + if (v == v2 || v == v1 || !v->routingedge) + break; + +#ifdef DEBUG_RUBBERBAND +/* if(debug) + printf("current v %f,%f - edge %f,%f %f,%f\n", vx(v), vy(v), + vx(tedge_v1(v->routingedge)), vy(tedge_v1(v->routingedge)), + vx(tedge_v2(v->routingedge)), vy(tedge_v2(v->routingedge)) + );*/ +#endif + g_assert(v->routingedge); + + v1wind = coord_wind(x0, y0, x1, y1, vx(tedge_v1(v->routingedge)), vy(tedge_v1(v->routingedge))); + v2wind = coord_wind(x0, y0, x1, y1, vx(tedge_v2(v->routingedge)), vy(tedge_v2(v->routingedge))); +/* if(debug) printf("\twinds: %d %d\n", v1wind, v2wind);*/ + if (!v1wind && !v2wind) { + i = i->next; + continue; + } + + + if (v1wind && v2wind && v1wind != v2wind) { /* edge is cutting through the current segment */ + + if (ARC_CHECKS(tedge_v1(v->routingedge))) { /* edge v1 is not the centre of an arc terminal */ + d = + check_intersect_vertex(x0, y0, x1, y1, v, tedge_v1(v->routingedge), tedge_v2(v->routingedge), v1wind, &arcwind, &arcr, + debug); + TEST_AND_INSERT(tedge_v1(v->routingedge)); + } + + if (ARC_CHECKS(tedge_v2(v->routingedge))) { /* edge v2 is not the centre of an arc terminal */ + d = + check_intersect_vertex(x0, y0, x1, y1, v, tedge_v2(v->routingedge), tedge_v1(v->routingedge), v2wind, &arcwind, &arcr, + debug); + TEST_AND_INSERT(tedge_v2(v->routingedge)); + } + } + else { /* edge is on one side of the segment */ + + if (ARC_CHECKS(tedge_v1(v->routingedge))) { /* edge v1 is not the centre of an arc terminal */ + d = + check_non_intersect_vertex(x0, y0, x1, y1, v, tedge_v1(v->routingedge), tedge_v2(v->routingedge), v1wind, &arcwind, + &arcr, debug); + TEST_AND_INSERT(tedge_v1(v->routingedge)); + } + + if (ARC_CHECKS(tedge_v2(v->routingedge))) { /* edge v2 is not the centre of an arc terminal */ + d = + check_non_intersect_vertex(x0, y0, x1, y1, v, tedge_v2(v->routingedge), tedge_v1(v->routingedge), v2wind, &arcwind, + &arcr, debug); + TEST_AND_INSERT(tedge_v2(v->routingedge)); + } + } + + i = i->next; + } + + arcs = g_list_sort(arcs, (GCompareFunc) compare_rubberband_arcs); +/*rubberband_insert_maxarc:*/ + if (!arcs) + return NULL; + max = TOPOROUTER_RUBBERBAND_ARC(arcs->data); + + av2 = max->pathv; + i = max->list->next; + while (i) { + toporouter_vertex_t *v = TOPOROUTER_VERTEX(i->data); + if (v->routingedge && (tedge_v1(v->routingedge) == max->arcv || tedge_v2(v->routingedge) == max->arcv)) { + av2 = v; + i = i->next; + continue; + } + break; + } + + av1 = max->pathv; + i = max->list->prev; + while (i) { + toporouter_vertex_t *v = TOPOROUTER_VERTEX(i->data); + if (v->routingedge && (tedge_v1(v->routingedge) == max->arcv || tedge_v2(v->routingedge) == max->arcv)) { + av1 = v; + i = i->prev; + continue; + } + break; + } +/*#ifdef DEBUG_RUBBERBAND*/ + if (debug) + printf("newarc @ %f,%f \t v1 = %f,%f v2 = %f,%f r = %f\n", vx(max->arcv), vy(max->arcv), vx(av1), vy(av1), vx(av2), vy(av2), + max->r); +/*#endif*/ + newarc = toporouter_arc_new(oproute, av1, av2, max->arcv, max->r, max->wind); + + if (TOPOROUTER_IS_VERTEX(t1)) + calculate_term_to_arc(TOPOROUTER_VERTEX(t1), newarc, 0); + else if (calculate_arc_to_arc(r, TOPOROUTER_ARC(t1), newarc)) { + printf("\tERROR: best: r = %f d = %f\n", max->r, max->d); + printf("\tOPROUTE: %s\n", oproute->netlist); + print_vertex(oproute->term1); + print_vertex(oproute->term2); + return NULL; + } + + if (TOPOROUTER_IS_VERTEX(t2)) + calculate_term_to_arc(TOPOROUTER_VERTEX(t2), newarc, 1); + else if (calculate_arc_to_arc(r, newarc, TOPOROUTER_ARC(t2))) { + printf("\tERROR: best: r = %f d = %f\n", max->r, max->d); + printf("\tOPROUTE: %s\n", oproute->netlist); + print_vertex(oproute->term1); + print_vertex(oproute->term2); + return NULL; + } + +/*if(check_arc_for_loops(t1, newarc, t2)) { + if(arc1 && arc2) calculate_arc_to_arc(r, arc1, arc2); + else if(arc1) calculate_term_to_arc(TOPOROUTER_VERTEX(t2), arc1, 1); + else if(arc2) calculate_term_to_arc(TOPOROUTER_VERTEX(t1), arc2, 0);*/ + +/*#ifdef DEBUG_RUBBERBAND + printf("REMOVING NEW ARC @ %f,%f\n", vx(newarc->centre), vy(newarc->centre)); + TODO: properly remove newarc + #endif*/ + +/* arcs = g_list_remove(arcs, max); + free(max); + goto rubberband_insert_maxarc; + }*/ + + + list1 = oproute_rubberband_segment(r, oproute, path, t1, newarc, debug); + list2 = oproute_rubberband_segment(r, oproute, i->next, newarc, t2, debug); + + if (list1) { + GList *list = g_list_last(list1); + toporouter_arc_t *testarc = TOPOROUTER_ARC(list->data); + toporouter_arc_t *parc = list->prev ? TOPOROUTER_ARC(list->prev->data) : arc1; + gdouble px = parc ? parc->x1 : vx(TOPOROUTER_VERTEX(t1)), py = parc ? parc->y1 : vy(TOPOROUTER_VERTEX(t1)); + + if (coord_intersect_prop(px, py, testarc->x0, testarc->y0, testarc->x1, testarc->y1, newarc->x0, newarc->y0)) { + list1 = g_list_remove(list1, testarc); + if (parc) + calculate_arc_to_arc(r, parc, newarc); + else + calculate_term_to_arc(TOPOROUTER_VERTEX(t1), newarc, 0); +/*#ifdef DEBUG_RUBBERBAND*/ + if (debug) + printf("REMOVING ARC @ %f,%f\n", vx(testarc->centre), vy(testarc->centre)); +/*#endif*/ + } + } + if (list2) { + toporouter_arc_t *testarc = TOPOROUTER_ARC(list2->data); + toporouter_arc_t *narc = list2->next ? TOPOROUTER_ARC(list2->next->data) : arc2; + gdouble nx = narc ? narc->x0 : vx(TOPOROUTER_VERTEX(t2)), ny = narc ? narc->y0 : vy(TOPOROUTER_VERTEX(t2)); + + if (coord_intersect_prop(newarc->x1, newarc->y1, testarc->x0, testarc->y0, testarc->x1, testarc->y1, nx, ny)) { + list2 = g_list_remove(list2, testarc); + if (narc) + calculate_arc_to_arc(r, newarc, narc); + else + calculate_term_to_arc(TOPOROUTER_VERTEX(t2), newarc, 1); + +/*#ifdef DEBUG_RUBBERBAND*/ + if (debug) + printf("REMOVING ARC @ %f,%f\n", vx(testarc->centre), vy(testarc->centre)); +/*#endif*/ + } + } + + g_list_foreach(arcs, free_list_elements, NULL); + g_list_free(arcs); + + return g_list_concat(list1, g_list_prepend(list2, newarc)); +} + +void oproute_check_all_loops(toporouter_t * r, toporouter_oproute_t * oproute) +{ + GList *i; + gpointer t1; + +loopcheck_restart: + t1 = oproute->term1; + i = oproute->arcs; + while (i) { + toporouter_arc_t *arc = TOPOROUTER_ARC(i->data); + gpointer t2 = i->next ? i->next->data : oproute->term2; + + if (check_arc_for_loops(t1, arc, t2)) { + + if (TOPOROUTER_IS_ARC(t1) && TOPOROUTER_IS_ARC(t2)) + calculate_arc_to_arc(r, TOPOROUTER_ARC(t1), TOPOROUTER_ARC(t2)); + else if (TOPOROUTER_IS_ARC(t1)) + calculate_term_to_arc(TOPOROUTER_VERTEX(t2), TOPOROUTER_ARC(t1), 1); + else if (TOPOROUTER_IS_ARC(t2)) + calculate_term_to_arc(TOPOROUTER_VERTEX(t1), TOPOROUTER_ARC(t2), 0); + + oproute->arcs = g_list_remove(oproute->arcs, arc); + goto loopcheck_restart; + } + + t1 = arc; + + i = i->next; + } + +} + +GtsTriangle *opposite_triangle(GtsTriangle * t, toporouter_edge_t * e) +{ + GSList *i = GTS_EDGE(e)->triangles; + + g_assert(e && t); + + while (i) { + if (GTS_TRIANGLE(i->data) != t) + return GTS_TRIANGLE(i->data); + i = i->next; + } + + return NULL; +} + + +void speccut_edge_routing_from_edge(GList * i, toporouter_edge_t * e) +{ + g_assert(TOPOROUTER_IS_EDGE(e)); + while (i) { + toporouter_vertex_t *curv = TOPOROUTER_VERTEX(i->data); + + if (!(curv->flags & VERTEX_FLAG_TEMP)) { + toporouter_vertex_t *newv = tvertex_intersect(curv, curv->parent, tedge_v1(e), tedge_v2(e)); + +/* printf("\nCURV:\n"); + print_vertex(curv); + + printf("CURV child:\n"); + if(curv->child) + print_vertex(curv->child); + else + printf("NULL\n"); + + printf("CURV parent:\n"); + if(curv->parent) + print_vertex(curv->parent); + else + printf("NULL\n");*/ + + if (newv) { + gint index; + newv->flags |= VERTEX_FLAG_ROUTE; + newv->flags |= VERTEX_FLAG_SPECCUT; + e->routing = g_list_insert_sorted_with_data(e->routing, newv, routing_edge_insert, e); + newv->route = curv->route; + newv->oproute = curv->oproute; + newv->routingedge = e; + GTS_POINT(newv)->z = vz(curv); + + newv->parent = curv->parent; + newv->child = curv; + +/* curv->parent = newv;*/ + + index = g_list_index(newv->route->path, curv); + + newv->route->path = g_list_insert(newv->route->path, newv, index); + + + if (newv->oproute) + newv->oproute->path = newv->route->path; + } + + if (!(curv->child->routingedge)) { + newv = tvertex_intersect(curv, curv->child, tedge_v1(e), tedge_v2(e)); + + if (newv) { + gint index; + newv->flags |= VERTEX_FLAG_ROUTE; + newv->flags |= VERTEX_FLAG_SPECCUT; + e->routing = g_list_insert_sorted_with_data(e->routing, newv, routing_edge_insert, e); + newv->route = curv->route; + newv->oproute = curv->oproute; + newv->routingedge = e; + GTS_POINT(newv)->z = vz(curv); + + newv->parent = curv; + newv->child = curv->child; + +/* curv->child = newv;*/ + + index = g_list_index(newv->route->path, curv); + + newv->route->path = g_list_insert(newv->route->path, newv, index + 1); + + + if (newv->oproute) + newv->oproute->path = newv->route->path; + } + + } + + } + i = i->next; + } + +} + +void speccut_edge_patch_links(toporouter_edge_t * e) +{ + GList *i = e->routing; + g_assert(TOPOROUTER_IS_EDGE(e)); + while (i) { + toporouter_vertex_t *v = TOPOROUTER_VERTEX(i->data); + v->parent->child = v; + v->child->parent = v; + i = i->next; + } +} + +gint +check_speccut(toporouter_oproute_t * oproute, toporouter_vertex_t * v1, toporouter_vertex_t * v2, toporouter_edge_t * e, + toporouter_edge_t * e1, toporouter_edge_t * e2) +{ + GtsTriangle *t, *opt; + toporouter_vertex_t *opv, *opv2; + toporouter_edge_t *ope1, *ope2; + gdouble cap, flow, line_int_x, line_int_y; + + if (TOPOROUTER_IS_CONSTRAINT(e)) + return 0; + + if (!(t = gts_triangle_use_edges(GTS_EDGE(e), GTS_EDGE(e1), GTS_EDGE(e2)))) { + printf("check_speccut: NULL t\n"); + return 0; + } + + if (!(opt = opposite_triangle(t, e))) { +/* printf("check_speccut: NULL opt\n");*/ + return 0; + } + + if (!(opv = segment_common_vertex(GTS_SEGMENT(e1), GTS_SEGMENT(e2)))) { + printf("check_speccut: NULL opv\n"); + return 0; + } + + if (!(opv2 = TOPOROUTER_VERTEX(gts_triangle_vertex_opposite(opt, GTS_EDGE(e))))) { + printf("check_speccut: NULL opv2\n"); + return 0; + } + + /*TODO: shifting it out of the way would be better */ + if (e->routing) { + GList *i = e->routing; + while (i) { + toporouter_vertex_t *ev = TOPOROUTER_VERTEX(i->data); + if (!tvertex_wind(opv, ev, opv2)) + return 0; + i = i->next; + } + + } + + ope1 = tedge(opv2, tedge_v1(e)); + ope2 = tedge(opv2, tedge_v2(e)); + + /*this fixes the weird pad exits in r8c board + if(TOPOROUTER_IS_CONSTRAINT(ope1)) return 0; */ + if (TOPOROUTER_IS_CONSTRAINT(ope2)) + return 0; + + if (!tvertex_wind(opv2, tedge_v1(e), opv)) + return 0; + if (!tvertex_wind(opv2, tedge_v2(e), opv)) + return 0; + + if (!vertex_line_normal_intersection(vx(tedge_v1(e)), vy(tedge_v1(e)), + vx(tedge_v2(e)), vy(tedge_v2(e)), vx(opv2), vy(opv2), &line_int_x, &line_int_y)) + return 0; + + +/* return 0; + if(vertex_line_normal_intersection(tev1x(e), tev1y(e), tev2x(e), tev2y(e), vx(opv), vy(opv), &line_int_x, &line_int_y)) + return 0;*/ + + g_assert(opt && opv2); + + /* this is just temp, for the purposes of determining flow */ + if (tedge_v1(ope1) == opv2) { + if (TOPOROUTER_IS_CONSTRAINT(ope1)) + TOPOROUTER_CONSTRAINT(ope1)->routing = g_list_append(TOPOROUTER_CONSTRAINT(ope1)->routing, v1); + else + ope1->routing = g_list_append(ope1->routing, v1); + } + else { + if (TOPOROUTER_IS_CONSTRAINT(ope1)) + TOPOROUTER_CONSTRAINT(ope1)->routing = g_list_prepend(TOPOROUTER_CONSTRAINT(ope1)->routing, v1); + else + ope1->routing = g_list_prepend(ope1->routing, v1); + } + + cap = triangle_interior_capacity(opt, opv2); + flow = flow_from_edge_to_edge(opt, tedge(opv2, tedge_v1(e)), tedge(opv2, tedge_v2(e)), opv2, v1); + + /* temp v1 removed */ + if (TOPOROUTER_IS_CONSTRAINT(ope1)) + TOPOROUTER_CONSTRAINT(ope1)->routing = g_list_remove(TOPOROUTER_CONSTRAINT(ope1)->routing, v1); + else + ope1->routing = g_list_remove(ope1->routing, v1); + + if (flow >= cap) { + toporouter_edge_t *newe = + TOPOROUTER_EDGE(gts_edge_new(GTS_EDGE_CLASS(toporouter_edge_class()), GTS_VERTEX(opv), GTS_VERTEX(opv2))); + + speccut_edge_routing_from_edge(edge_routing(e1), newe); + speccut_edge_routing_from_edge(edge_routing(e2), newe); + speccut_edge_routing_from_edge(edge_routing(ope1), newe); + speccut_edge_routing_from_edge(edge_routing(ope2), newe); + + speccut_edge_patch_links(newe); +/* + printf("SPECCUT WITH v %f,%f for seg %f,%f %f,%f detected\n", vx(opv2), vy(opv2), + vx(v1), vy(v1), + vx(v2), vy(v2)); + printf("\tflow %f cap %f\n", flow, cap); + print_edge(newe); + */ + if (newe->routing) + return 1; + } + + + return 0; +} + + +gint oproute_path_speccut(toporouter_oproute_t * oproute) +{ + GList *i; + toporouter_vertex_t *pv; +path_speccut_restart: + i = oproute->path; + pv = NULL; + while (i) { + toporouter_vertex_t *v = TOPOROUTER_VERTEX(i->data); + + + if (pv && (v->routingedge || pv->routingedge) && !(pv->flags & VERTEX_FLAG_SPECCUT) && !(v->flags & VERTEX_FLAG_SPECCUT)) { + + if (!v->routingedge) { + if (check_speccut + (oproute, pv, v, tedge(tedge_v1(pv->routingedge), v), pv->routingedge, tedge(tedge_v2(pv->routingedge), v))) + goto path_speccut_restart; + if (check_speccut + (oproute, pv, v, tedge(tedge_v2(pv->routingedge), v), pv->routingedge, tedge(tedge_v1(pv->routingedge), v))) + goto path_speccut_restart; + } + else if (!pv->routingedge) { + if (check_speccut + (oproute, v, pv, tedge(tedge_v1(v->routingedge), pv), v->routingedge, tedge(tedge_v2(v->routingedge), pv))) + goto path_speccut_restart; + if (check_speccut + (oproute, v, pv, tedge(tedge_v2(v->routingedge), pv), v->routingedge, tedge(tedge_v1(v->routingedge), pv))) + goto path_speccut_restart; + } + else { + toporouter_vertex_t *v1 = NULL, *v2 = NULL; + edges_third_edge(GTS_SEGMENT(v->routingedge), GTS_SEGMENT(pv->routingedge), &v1, &v2); + if (check_speccut(oproute, v, pv, tedge(v1, v2), v->routingedge, pv->routingedge)) + goto path_speccut_restart; + } + } + + + pv = v; + i = i->next; + } + + return 0; +} + +toporouter_oproute_t *oproute_rubberband(toporouter_t * r, GList * path) +{ + toporouter_oproute_t *oproute = (toporouter_oproute_t *) malloc(sizeof(toporouter_oproute_t)); + + g_assert(path); + + oproute->term1 = TOPOROUTER_VERTEX(path->data); + oproute->term2 = TOPOROUTER_VERTEX(g_list_last(path)->data); + oproute->arcs = NULL; + oproute->style = vertex_bbox(oproute->term1)->cluster->netlist->style; + oproute->netlist = vertex_bbox(oproute->term1)->cluster->netlist->netlist; + oproute->layergroup = vz(oproute->term1); + oproute->path = path; + oproute->serp = NULL; + + oproute->term1->parent = NULL; + oproute->term2->child = NULL; + + path_set_oproute(path, oproute); + +/* if(!strcmp(oproute->netlist, " unnamed_net1")) */ + oproute_path_speccut(oproute); + +#ifdef DEBUG_RUBBERBAND + if (!strcmp(oproute->netlist, " VCC3V3") && vx(oproute->term1) == 95700. && vy(oproute->term1) == 70800. && + vx(oproute->term2) == 196700. && vy(oproute->term2) == 67300.) { +/* printf("OPROUTE %s - %f,%f %f,%f\n", oproute->netlist, vx(oproute->term1), vy(oproute->term1), vx(oproute->term2), vy(oproute->term2)); + print_path(path);*/ + oproute->arcs = oproute_rubberband_segment(r, oproute, path, oproute->term1, oproute->term2, 1); + } + else +#endif + oproute->arcs = oproute_rubberband_segment(r, oproute, path, oproute->term1, oproute->term2, 0); + + oproute_check_all_loops(r, oproute); + return oproute; + +} + +void toporouter_export(toporouter_t * r) +{ + GList *i = r->routednets; + GList *oproutes = NULL; + + while (i) { + toporouter_route_t *routedata = TOPOROUTER_ROUTE(i->data); + toporouter_oproute_t *oproute = oproute_rubberband(r, routedata->path); + oproutes = g_list_prepend(oproutes, oproute); + i = i->next; + } + + i = oproutes; + while (i) { + toporouter_oproute_t *oproute = (toporouter_oproute_t *) i->data; + export_oproutes(r, oproute); + oproute_free(oproute); + i = i->next; + } + + pcb_message(PCB_MSG_INFO, _("Reticulating splines... successful\n\n")); + pcb_message(PCB_MSG_INFO, _("Wiring cost: %f inches\n"), r->wiring_score / 1000.); + printf("Wiring cost: %f inches\n", r->wiring_score / 1000.); + + g_list_free(oproutes); + +} + +toporouter_route_t *routedata_create(void) +{ + toporouter_route_t *routedata = (toporouter_route_t *) malloc(sizeof(toporouter_route_t)); + routedata->netlist = NULL; + routedata->alltemppoints = NULL; + routedata->path = NULL; + routedata->curpoint = NULL; + routedata->pscore = routedata->score = 0.; + routedata->flags = 0; + routedata->src = routedata->dest = NULL; + routedata->psrc = routedata->pdest = NULL; + routedata->ppath = routedata->topopath = NULL; + + routedata->ppathindices = NULL; + + routedata->destvertices = routedata->srcvertices = NULL; + return routedata; +} + +/* +void +print_routedata(toporouter_route_t *routedata) +{ + GList *srcvertices = cluster_vertices(routedata->src); + GList *destvertices = cluster_vertices(routedata->dest); + + printf("ROUTEDATA:\n"); + printf("SRCVERTICES:\n"); + print_vertices(srcvertices); + printf("DESTVERTICES:\n"); + print_vertices(destvertices); + + g_list_free(srcvertices); + g_list_free(destvertices); +}*/ + +toporouter_route_t *import_route(toporouter_t * r, pcb_rat_t * line) +{ + toporouter_route_t *routedata = routedata_create(); + + routedata->src = cluster_find(r, line->Point1.X, line->Point1.Y, line->group1); + routedata->dest = cluster_find(r, line->Point2.X, line->Point2.Y, line->group2); + + if (!routedata->src) + printf("couldn't locate src\n"); + if (!routedata->dest) + printf("couldn't locate dest\n"); + + if (!routedata->src || !routedata->dest) { + pcb_printf("PROBLEM: couldn't locate rat src or dest for rat %#mD, %d -> %#mD, %d\n", + line->Point1.X, line->Point1.Y, line->group1, line->Point2.X, line->Point2.Y, line->group2); + free(routedata); + return NULL; + } + + routedata->netlist = routedata->src->netlist; + + g_assert(routedata->src->netlist == routedata->dest->netlist); + + g_ptr_array_add(r->routes, routedata); + g_ptr_array_add(routedata->netlist->routes, routedata); + + r->failednets = g_list_prepend(r->failednets, routedata); + + return routedata; +} + +void delete_route(toporouter_route_t * routedata, guint destroy) +{ + GList *i = routedata->path; + toporouter_vertex_t *pv = NULL; + + while (i) { + toporouter_vertex_t *tv = TOPOROUTER_VERTEX(i->data); + + g_assert(tv); + + if (tv && pv && !(tv->flags & VERTEX_FLAG_ROUTE) && !(pv->flags & VERTEX_FLAG_ROUTE)) { + toporouter_edge_t *e = TOPOROUTER_EDGE(gts_vertices_are_connected(GTS_VERTEX(tv), GTS_VERTEX(pv))); + + if (e && (e->flags & EDGE_FLAG_DIRECTCONNECTION)) { + e->flags ^= EDGE_FLAG_DIRECTCONNECTION; + } + } + pv = tv; + i = i->next; + } + + i = routedata->path; + while (i) { + toporouter_vertex_t *tv = TOPOROUTER_VERTEX(i->data); + + tv->parent = NULL; + tv->child = NULL; + + if (tv->routingedge) { + if (TOPOROUTER_IS_CONSTRAINT(tv->routingedge)) + TOPOROUTER_CONSTRAINT(tv->routingedge)->routing = g_list_remove(TOPOROUTER_CONSTRAINT(tv->routingedge)->routing, tv); + else + tv->routingedge->routing = g_list_remove(tv->routingedge->routing, tv); + if (destroy) + gts_object_destroy(GTS_OBJECT(tv)); + } + + i = i->next; + } + + if (routedata->path) + g_list_free(routedata->path); + routedata->path = NULL; + routedata->curpoint = NULL; + routedata->score = INFINITY; + routedata->alltemppoints = NULL; +} + +/* remove route can be later reapplied */ +void remove_route(GList * path) +{ + GList *i = path; + + while (i) { + toporouter_vertex_t *tv = TOPOROUTER_VERTEX(i->data); + + tv->parent = NULL; + tv->child = NULL; + +/* if(tv->flags & VERTEX_FLAG_ROUTE) g_assert(tv->route == routedata);*/ + + if (tv->routingedge) { + + if (TOPOROUTER_IS_CONSTRAINT(tv->routingedge)) + TOPOROUTER_CONSTRAINT(tv->routingedge)->routing = g_list_remove(TOPOROUTER_CONSTRAINT(tv->routingedge)->routing, tv); + else + tv->routingedge->routing = g_list_remove(tv->routingedge->routing, tv); + } + i = i->next; + } + +} + +gint apply_route(GList * path, toporouter_route_t * routedata) +{ + GList *i = path; + toporouter_vertex_t *pv = NULL; + gint count = 0; + + if (!path) + return 0; +/* g_assert(path);*/ + + while (i) { + toporouter_vertex_t *tv = TOPOROUTER_VERTEX(i->data); + + if (tv->routingedge) { + if (TOPOROUTER_IS_CONSTRAINT(tv->routingedge)) + TOPOROUTER_CONSTRAINT(tv->routingedge)->routing = + g_list_insert_sorted_with_data(TOPOROUTER_CONSTRAINT(tv->routingedge)->routing, tv, routing_edge_insert, + tv->routingedge); + else + tv->routingedge->routing = g_list_insert_sorted_with_data(tv->routingedge->routing, + tv, routing_edge_insert, tv->routingedge); + + count++; + } + + if (pv) { + pv->child = tv; + tv->parent = pv; + } + + if (tv->flags & VERTEX_FLAG_ROUTE) + g_assert(tv->route == routedata); + + pv = tv; + i = i->next; + } + + TOPOROUTER_VERTEX(path->data)->parent = NULL; + pv->child = NULL; + + return count; +} + + +gint compare_routedata_ascending(gconstpointer a, gconstpointer b) +{ + toporouter_route_t *ra = (toporouter_route_t *) a; + toporouter_route_t *rb = (toporouter_route_t *) b; + return ra->score - rb->score; +} + +void print_costmatrix(gdouble * m, guint n) +{ + guint i; + printf("COST MATRIX:\n"); + for (i = 0; i < n; i++) { + guint j; + for (j = 0; j < n; j++) { + printf("%f ", m[(i * n) + j]); + } + printf("\n"); + } +} + + +static inline void init_cost_matrix(gdouble * m, guint n) +{ + guint i; + for (i = 0; i < n; i++) { + guint j; + for (j = 0; j < n; j++) { + m[(i * n) + j] = INFINITY; + } + } +} + + +toporouter_netscore_t *netscore_create(toporouter_t * r, toporouter_route_t * routedata, guint n, guint id) +{ + toporouter_netscore_t *netscore = (toporouter_netscore_t *) malloc(sizeof(toporouter_netscore_t)); + GList *path = route(r, routedata, 0); + guint i; + + netscore->id = id; + + netscore->routedata = routedata; + routedata->detourscore = netscore->score = routedata->score; + + if (!finite(routedata->detourscore)) { + printf("WARNING: !finite(detourscore)\n"); + print_cluster(routedata->src); + print_cluster(routedata->dest); + return NULL; + } + + netscore->pairwise_nodetour = (guint *) malloc(n * sizeof(guint)); + + for (i = 0; i < n; i++) { + netscore->pairwise_nodetour[i] = 0; + } + + netscore->pairwise_detour_sum = 0.; + netscore->pairwise_fails = 0; + + netscore->r = r; + + if (path) { + routedata->topopath = g_list_copy(routedata->path); + delete_route(routedata, 0); + } + + return netscore; +} + +static inline void netscore_destroy(toporouter_netscore_t * netscore) +{ + free(netscore->pairwise_nodetour); + free(netscore); +} + +void print_netscores(GPtrArray * netscores) +{ + toporouter_netscore_t **i; + printf("NETSCORES: \n\n"); + printf(" %15s %15s %15s\n----------------------------------------------------\n", "Score", "Detour Sum", + "Pairwise Fails"); + + for (i = (toporouter_netscore_t **) netscores->pdata; i < (toporouter_netscore_t **) netscores->pdata + netscores->len; i++) { +#ifdef DEBUG_NETSCORES + printf("%4d %15f %15f %15d %15x\n", (*i)->id, (*i)->score, (*i)->pairwise_detour_sum, (*i)->pairwise_fails, (guint) * i); +#endif + } + + printf("\n"); +} + +void netscore_pairwise_calculation(toporouter_netscore_t * netscore, GPtrArray * netscores) +{ + toporouter_netscore_t **i; + toporouter_netscore_t **netscores_base = (toporouter_netscore_t **) (netscores->pdata); + toporouter_route_t *temproutedata = routedata_create(); + + /*route(netscore->r, netscore->routedata, 0); */ + apply_route(netscore->routedata->topopath, netscore->routedata); + + for (i = netscores_base; i < netscores_base + netscores->len; i++) { + + if (!netscore->pairwise_nodetour[i - netscores_base] && *i != netscore + && (*i)->routedata->netlist != netscore->routedata->netlist) { + + temproutedata->src = (*i)->routedata->src; + temproutedata->dest = (*i)->routedata->dest; + + route(netscore->r, temproutedata, 0); + + if (temproutedata->score == (*i)->score) { + netscore->pairwise_nodetour[i - netscores_base] = 1; + (*i)->pairwise_nodetour[netscore->id] = 1; + } + else if (!finite(temproutedata->score)) { + netscore->pairwise_fails += 1; + } + else { + netscore->pairwise_detour_sum += temproutedata->score - (*i)->score; + } + + delete_route(temproutedata, 1); + } + + } + +/* delete_route(netscore->routedata, 1);*/ + remove_route(netscore->routedata->topopath); + + free(temproutedata); +} + +gint netscore_pairwise_size_compare(toporouter_netscore_t ** a, toporouter_netscore_t ** b) +{ + /* infinite scores are last */ + if (!finite((*a)->score) && !finite((*b)->score)) + return 0; + if (finite((*a)->score) && !finite((*b)->score)) + return -1; + if (finite((*b)->score) && !finite((*a)->score)) + return 1; + + /* order by pairwise fails */ + if ((*a)->pairwise_fails < (*b)->pairwise_fails) + return -1; + if ((*b)->pairwise_fails < (*a)->pairwise_fails) + return 1; + + /* order by pairwise detour */ + if ((*a)->pairwise_detour_sum < (*b)->pairwise_detour_sum) + return -1; + if ((*b)->pairwise_detour_sum < (*a)->pairwise_detour_sum) + return 1; + + /* order by score */ + if ((*a)->score < (*b)->score) + return -1; + if ((*b)->score < (*a)->score) + return 1; + + return 0; +} + +gint netscore_pairwise_compare(toporouter_netscore_t ** a, toporouter_netscore_t ** b) +{ + /* infinite scores are last */ + if (!finite((*a)->score) && !finite((*b)->score)) + return 0; + if (finite((*a)->score) && !finite((*b)->score)) + return -1; + if (finite((*b)->score) && !finite((*a)->score)) + return 1; + + /* order by pairwise fails */ + if ((*a)->pairwise_fails < (*b)->pairwise_fails) + return -1; + if ((*b)->pairwise_fails < (*a)->pairwise_fails) + return 1; + + /* order by pairwise detour */ + if ((*a)->pairwise_detour_sum < (*b)->pairwise_detour_sum) + return -1; + if ((*b)->pairwise_detour_sum < (*a)->pairwise_detour_sum) + return 1; + + return 0; +} + +guint order_nets_preroute_greedy(toporouter_t * r, GList * nets, GList ** rnets) +{ + gint len = g_list_length(nets); + GPtrArray *netscores = g_ptr_array_sized_new(len); + guint failcount = 0; + + while (nets) { + toporouter_netscore_t *ns = netscore_create(r, TOPOROUTER_ROUTE(nets->data), len, failcount++); + if (ns) + g_ptr_array_add(netscores, ns); + nets = nets->next; + } + + failcount = 0; + + g_ptr_array_foreach(netscores, (GFunc) netscore_pairwise_calculation, netscores); + + g_ptr_array_sort(netscores, (GCompareFunc) r->netsort); + +#ifdef DEBUG_ORDERING + print_netscores(netscores); +#endif + + *rnets = NULL; + FOREACH_NETSCORE(netscores) { + *rnets = g_list_prepend(*rnets, netscore->routedata); + if (!finite(netscore->score)) + failcount++; + netscore_destroy(netscore); + } + FOREACH_END; + + g_ptr_array_free(netscores, TRUE); + + return failcount; +} + +toporouter_vertex_t *edge_closest_vertex(toporouter_edge_t * e, toporouter_vertex_t * v) +{ + GList *i = v->routingedge ? edge_routing(v->routingedge) : NULL; + gdouble closestd = 0.; + toporouter_vertex_t *closestv = NULL; + + while (i) { + toporouter_vertex_t *ev = TOPOROUTER_VERTEX(i->data); + gdouble tempd = gts_point_distance2(GTS_POINT(ev), GTS_POINT(v)); + + if (!closestv || (tempd < closestd)) { + closestd = tempd; + closestv = ev; + } + + i = i->next; + } + + return closestv; +} + +void snapshot(toporouter_t * r, char *name, GList * datas) +{ + { + int i; + for (i = 0; i < groupcount(); i++) { + char buffer[256]; + sprintf(buffer, "route-%s-%d.png", name, i); + toporouter_draw_surface(r, r->layers[i].surface, buffer, 2048, 2048, 2, datas, i, NULL); + } + } +} + +/* +gdouble +route_conflict(toporouter_t *r, toporouter_route_t *route, guint *n) +{ + GList *i = route->path; + toporouter_vertex_t *pv = NULL; + gdouble cost = 0.; + + while(i) { + toporouter_vertex_t *v = TOPOROUTER_VERTEX(i->data); + if(pv && vz(v) == vz(pv)) + cost += vertices_routing_conflict_cost(r, v, pv, n); + pv = v; + i = i->next; + } + + return cost; +} +*/ +GList *route_conflicts(toporouter_route_t * route) +{ + GList *conflicts = NULL, *i = route->path; + toporouter_vertex_t *pv = NULL; + + while (i) { + toporouter_vertex_t *v = TOPOROUTER_VERTEX(i->data); + + if (pv && vz(pv) == vz(v)) { + GList *temp = vertices_routing_conflicts(pv, v), *j; + + j = temp; + while (j) { + toporouter_route_t *conroute = TOPOROUTER_ROUTE(j->data); + if (!g_list_find(conflicts, conroute)) + conflicts = g_list_prepend(conflicts, conroute); + j = j->next; + } + + if (temp) + g_list_free(temp); + } + + pv = v; + i = i->next; + } + return conflicts; +} + +gint spread_edge(gpointer item, gpointer data) +{ + toporouter_edge_t *e = TOPOROUTER_EDGE(item); + toporouter_vertex_t *v; + gdouble spacing, s; + GList *i; + + if (TOPOROUTER_IS_CONSTRAINT(e)) + return 0; + + i = edge_routing(e); + + if (!g_list_length(i)) + return 0; + + if (g_list_length(i) == 1) { + v = TOPOROUTER_VERTEX(i->data); + GTS_POINT(v)->x = (vx(edge_v1(e)) + vx(edge_v2(e))) / 2.; + GTS_POINT(v)->y = (vy(edge_v1(e)) + vy(edge_v2(e))) / 2.; + return 0; + } + + s = spacing = (gts_point_distance(GTS_POINT(edge_v1(e)), GTS_POINT(edge_v2(e)))) / (g_list_length(i) + 1); + + while (i) { + v = TOPOROUTER_VERTEX(i->data); + vertex_move_towards_vertex_values(edge_v1(e), edge_v2(e), s, &(GTS_POINT(v)->x), &(GTS_POINT(v)->y)); + + s += spacing; + i = i->next; + } + + return 0; +} + +void route_checkpoint(toporouter_route_t * route, toporouter_route_t * temproute) +{ + GList *i = g_list_last(route->path); + gint n = g_list_length(route->path); + + if (route->ppathindices) + free(route->ppathindices); + route->ppathindices = (gint *) malloc(sizeof(gint) * n); + +/* n = 0;*/ + while (i) { + toporouter_vertex_t *v = TOPOROUTER_VERTEX(i->data); + n--; + + if (v->routingedge) { + GList *j = g_list_find(edge_routing(v->routingedge), v)->prev; + gint tempindex = g_list_index(edge_routing(v->routingedge), v); + + while (j) { + if (TOPOROUTER_VERTEX(j->data)->route == temproute) + tempindex--; + j = j->prev; + } + + route->ppathindices[n] = tempindex; + + if (TOPOROUTER_IS_CONSTRAINT(v->routingedge)) + TOPOROUTER_CONSTRAINT(v->routingedge)->routing = g_list_remove(TOPOROUTER_CONSTRAINT(v->routingedge)->routing, v); + else + v->routingedge->routing = g_list_remove(v->routingedge->routing, v); + } + + i = i->prev; + } + + route->pscore = route->score; + route->ppath = route->path; + remove_route(route->path); + route->path = NULL; + route->psrc = route->src; + route->pdest = route->dest; +/*route->src->pc = route->src->c; + route->dest->pc = route->dest->c;*/ +} + +void route_restore(toporouter_route_t * route) +{ + GList *i; + toporouter_vertex_t *pv = NULL; + gint n = 0; + + g_assert(route->ppath); + g_assert(route->ppathindices); + + route->path = route->ppath; + i = route->ppath; + while (i) { + toporouter_vertex_t *v = TOPOROUTER_VERTEX(i->data); + + if (v->routingedge) { + if (TOPOROUTER_IS_CONSTRAINT(v->routingedge)) + TOPOROUTER_CONSTRAINT(v->routingedge)->routing = + g_list_insert(TOPOROUTER_CONSTRAINT(v->routingedge)->routing, v, route->ppathindices[n]); + else + v->routingedge->routing = g_list_insert(v->routingedge->routing, v, route->ppathindices[n]); + + /* space_edge(v->routingedge, NULL); */ + } + + if (pv) { + pv->child = v; + v->parent = pv; + } + + n++; + pv = v; + i = i->next; + } + + route->score = route->pscore; + route->src = route->psrc; + route->dest = route->pdest; +/*route->src->c = route->src->pc; + route->dest->c = route->dest->pc;*/ + +} + +void cluster_merge(toporouter_route_t * routedata) +{ + gint oldc = routedata->dest->c, newc = routedata->src->c; + + FOREACH_CLUSTER(routedata->netlist->clusters) { + if (cluster->c == oldc) + cluster->c = newc; + } + FOREACH_END; + +} + +void netlist_recalculate(toporouter_netlist_t * netlist, GList * ignore) +{ + GList *i = g_list_last(netlist->routed); + gint n = netlist->clusters->len - 1; + + FOREACH_CLUSTER(netlist->clusters) { + cluster->c = n--; + } FOREACH_END; + + while (i) { + if (!ignore || !g_list_find(ignore, i->data)) + cluster_merge(TOPOROUTER_ROUTE(i->data)); + i = i->prev; + } + +} + +void netlists_recalculate(GList * netlists, GList * ignore) +{ + GList *i = netlists; + while (i) { + netlist_recalculate(TOPOROUTER_NETLIST(i->data), ignore); + i = i->next; + } +} + +void netlists_rollback(GList * netlists) +{ +/* netlists_recalculate(netlists, NULL);*/ + while (netlists) { + toporouter_netlist_t *netlist = TOPOROUTER_NETLIST(netlists->data); + + FOREACH_CLUSTER(netlist->clusters) { + cluster->c = cluster->pc; + } + FOREACH_END; + + netlists = netlists->next; + } +} + +void print_netlist(toporouter_netlist_t * netlist) +{ + + printf("NETLIST %s: ", netlist->netlist); + + FOREACH_CLUSTER(netlist->clusters) { + printf("%d ", cluster->c); + + } FOREACH_END; + printf("\n"); +} + +#define REMOVE_ROUTING(x) x->netlist->routed = g_list_remove(x->netlist->routed, x); \ + r->routednets = g_list_remove(r->routednets, x); \ + r->failednets = g_list_prepend(r->failednets, x) + +#define INSERT_ROUTING(x) x->netlist->routed = g_list_prepend(x->netlist->routed, x); \ + r->routednets = g_list_prepend(r->routednets, x); \ + r->failednets = g_list_remove(r->failednets, x) + +gint roar_route(toporouter_t * r, toporouter_route_t * routedata, gint threshold) +{ + gint intfails = 0; + GList *netlists = NULL, *routed = NULL; + + g_assert(!routedata->path); + + if (routedata->src->c == routedata->dest->c) { + printf("ERROR: attempt to route already complete route\n"); + g_assert(routedata->src->c != routedata->dest->c); + } + + routedata->src->pc = routedata->src->c; + routedata->dest->pc = routedata->dest->c; + routedata->psrc = routedata->src; + routedata->pdest = routedata->dest; + + r->flags |= TOPOROUTER_FLAG_LEASTINVALID; + if (route(r, routedata, 0)) { + GList *conflicts, *j; + + INSERT_ROUTING(routedata); + + conflicts = route_conflicts(routedata); + cluster_merge(routedata); + + r->flags &= ~TOPOROUTER_FLAG_LEASTINVALID; + + j = conflicts; + while (j) { + toporouter_route_t *conflict = TOPOROUTER_ROUTE(j->data); + if (!g_list_find(netlists, conflict->netlist)) + netlists = g_list_prepend(netlists, conflict->netlist); + + route_checkpoint(conflict, routedata); + + REMOVE_ROUTING(conflict); + j = j->next; + } + + netlists = g_list_prepend(netlists, routedata->netlist); + netlists_recalculate(netlists, NULL); + + j = conflicts; + while (j) { + toporouter_route_t *conflict = TOPOROUTER_ROUTE(j->data); + g_assert(conflict->src->c != conflict->dest->c); + if (route(r, conflict, 0)) { + cluster_merge(conflict); + + routed = g_list_prepend(routed, conflict); + + INSERT_ROUTING(conflict); + + netlist_recalculate(conflict->netlist, NULL); + + } + else { + if (++intfails >= threshold) { + GList *i = routed; + while (i) { + toporouter_route_t *intconflict = TOPOROUTER_ROUTE(i->data); + REMOVE_ROUTING(intconflict); + delete_route(intconflict, 1); + i = i->next; + } + delete_route(routedata, 1); + i = g_list_last(conflicts); + while (i) { + toporouter_route_t *intconflict = TOPOROUTER_ROUTE(i->data); + + route_restore(intconflict); + INSERT_ROUTING(intconflict); + + i = i->prev; + } + REMOVE_ROUTING(routedata); + intfails = 0; + netlists_recalculate(netlists, NULL); + goto roar_route_end; + } + + } + j = j->next; + } + + + netlists_recalculate(netlists, NULL); + + intfails--; + roar_route_end: + g_list_free(conflicts); + g_list_free(netlists); + + } + else { + r->flags &= ~TOPOROUTER_FLAG_LEASTINVALID; + } + + g_list_free(routed); + return intfails; +} + +gint roar_router(toporouter_t * r, gint failcount, gint threshold) +{ + guint j; + gint pfailcount = failcount + 1; + + pcb_message(PCB_MSG_INFO, _("ROAR router: ")); + for (j = 0; j < 6; j++) { + GList *failed = g_list_copy(r->failednets), *k = failed; + + k = failed; + while (k) { + failcount += roar_route(r, TOPOROUTER_ROUTE(k->data), threshold); + k = k->next; + } + g_list_free(failed); + + printf("\tROAR pass %d - %d routed - %d failed\n", j, g_list_length(r->routednets), g_list_length(r->failednets)); + + if (!failcount || failcount >= pfailcount) { + pcb_message(PCB_MSG_INFO, _("%d nets remaining\n"), failcount); + break; + } + pcb_message(PCB_MSG_INFO, _("%d -> "), failcount); + pfailcount = failcount; + } + + return failcount; +} + +gint route_detour_compare(toporouter_route_t ** a, toporouter_route_t ** b) +{ + return ((*b)->score - (*b)->detourscore) - ((*a)->score - (*a)->detourscore); +} + + + +void roar_detour_route(toporouter_t * r, toporouter_route_t * data) +{ + gdouble pscore = data->score, nscore = 0.; + GList *netlists = NULL; + + route_checkpoint(data, NULL); + + REMOVE_ROUTING(data); + + netlists = g_list_prepend(NULL, data->netlist); + netlists_recalculate(netlists, NULL); + + r->flags |= TOPOROUTER_FLAG_LEASTINVALID; + if (route(r, data, 0)) { + GList *conflicts, *j; + + nscore = data->score; + conflicts = route_conflicts(data); + + INSERT_ROUTING(data); + + r->flags &= ~TOPOROUTER_FLAG_LEASTINVALID; + + j = conflicts; + while (j) { + toporouter_route_t *conflict = TOPOROUTER_ROUTE(j->data); + + if (!g_list_find(netlists, conflict->netlist)) + netlists = g_list_prepend(netlists, conflict->netlist); + pscore += conflict->score; + + route_checkpoint(conflict, NULL); + REMOVE_ROUTING(conflict); + + j = j->next; + } + netlists_recalculate(netlists, NULL); + + j = conflicts; + while (j) { + toporouter_route_t *conflict = TOPOROUTER_ROUTE(j->data); + + if (route(r, conflict, 0)) { + cluster_merge(conflict); + INSERT_ROUTING(conflict); + nscore += conflict->score; + } + else { + j = j->prev; + goto roar_detour_route_rollback_int; + } + j = j->next; + } + + if (nscore > pscore) { + j = g_list_last(conflicts); + roar_detour_route_rollback_int: + REMOVE_ROUTING(data); + + while (j) { + toporouter_route_t *conflict = TOPOROUTER_ROUTE(j->data); + REMOVE_ROUTING(conflict); + delete_route(conflict, 1); + j = j->prev; + } + + j = g_list_last(conflicts); + while (j) { + toporouter_route_t *conflict = TOPOROUTER_ROUTE(j->data); + route_restore(conflict); + INSERT_ROUTING(conflict); + j = j->prev; + } + delete_route(data, 1); + + goto roar_detour_route_rollback_exit; + + } + + g_list_free(conflicts); + } + else { + r->flags &= ~TOPOROUTER_FLAG_LEASTINVALID; + roar_detour_route_rollback_exit: + route_restore(data); + INSERT_ROUTING(data); + } + netlists_recalculate(netlists, NULL); + + g_list_free(netlists); +} + +void detour_router(toporouter_t * r) +{ + GList *i = r->routednets; + guint n = g_list_length(r->routednets); + GPtrArray *scores = g_ptr_array_sized_new(n); + + while (i) { + toporouter_route_t *curroute = TOPOROUTER_ROUTE(i->data); + curroute->score = path_score(r, curroute->path); + g_ptr_array_add(scores, i->data); + i = i->next; + } + + g_ptr_array_sort(scores, (GCompareFunc) route_detour_compare); + + r->flags |= TOPOROUTER_FLAG_DETOUR; + + { + toporouter_route_t **i; + for (i = (toporouter_route_t **) scores->pdata; i < (toporouter_route_t **) scores->pdata + scores->len; i++) { + toporouter_route_t *curroute = (*i); + + if (finite(curroute->score) && finite(curroute->detourscore)) { +/* printf("%15s %15f \t %8f,%8f - %8f,%8f\n", (*i)->src->netlist + 2, (*i)->score - (*i)->detourscore, + vx(curroute->mergebox1->point), vy(curroute->mergebox1->point), + vx(curroute->mergebox2->point), vy(curroute->mergebox2->point));*/ + + if (curroute->score - curroute->detourscore > 1000.) { + roar_detour_route(r, curroute); + } + else + break; + + } + } + } + printf("\n"); + + r->flags ^= TOPOROUTER_FLAG_DETOUR; + + g_ptr_array_free(scores, TRUE); + +} + +gint rubix_router(toporouter_t * r, gint failcount) +{ + GList *i, *ordering; + order_nets_preroute_greedy(r, r->failednets, &ordering); + + i = ordering; + while (i) { + toporouter_route_t *data = TOPOROUTER_ROUTE(i->data); + + if (route(r, data, 0)) { + INSERT_ROUTING(data); + cluster_merge(data); + failcount--; + } + + i = i->next; + } + + g_list_free(ordering); + + return failcount; +} + +guint hybrid_router(toporouter_t * r) +{ + guint i; + gint failcount = g_list_length(r->failednets); + r->flags |= TOPOROUTER_FLAG_AFTERORDER; + r->flags |= TOPOROUTER_FLAG_AFTERRUBIX; + failcount = rubix_router(r, failcount); + + pcb_message(PCB_MSG_INFO, _("RUBIX router: %d nets remaining\n"), failcount); + printf("RUBIX router: %d nets remaining\n", failcount); + + r->flags |= TOPOROUTER_FLAG_GOFAR; + + for (i = 0; i < 6 && failcount; i++) { + if (i % 2 == 1) { + failcount = roar_router(r, failcount, 5); + /* printf("THRESH 5\n"); */ + } + else { + failcount = roar_router(r, failcount, 2); + /* printf("THRESH 2\n"); */ + } + + detour_router(r); + } + + failcount = roar_router(r, failcount, 2); + detour_router(r); + + return failcount; +} + +void parse_arguments(toporouter_t * r, int argc, char **argv) +{ + int i, tempint; + guint group; + + for (i = 0; i < argc; i++) { + if (sscanf(argv[i], "viacost=%d", &tempint)) { + r->viacost = (double) tempint; + } + else if (sscanf(argv[i], "l%d", &tempint)) { + gdouble *layer = (gdouble *) malloc(sizeof(gdouble)); + *layer = (double) tempint; + r->keepoutlayers = g_list_prepend(r->keepoutlayers, layer); + } + } + + for (group = 0; group < pcb_max_group; group++) + for (i = 0; i < PCB->LayerGroups.Number[group]; i++) + pcb_layer_id_t lid = PCB->LayerGroups.Entries[group][i]; + flg = pcb_layer_flag(lid); + if ((lid & PCB_LYT_COPPER) && !(PCB->Data->Layer[lid].On)) { + gdouble *layer = (gdouble *) malloc(sizeof(gdouble)); + *layer = (double) group; + r->keepoutlayers = g_list_prepend(r->keepoutlayers, layer); + } + +} + +toporouter_t *toporouter_new(void) +{ + toporouter_t *r = (toporouter_t *) calloc(1, sizeof(toporouter_t)); + time_t ltime; + + gettimeofday(&r->starttime, NULL); + + r->netsort = netscore_pairwise_compare; + + r->destboxes = NULL; + r->consumeddestboxes = NULL; + + r->paths = NULL; + + r->layers = NULL; + r->flags = 0; + r->viamax = 3; + r->viacost = 10000.; + r->stublength = 300.; + r->serpintine_half_amplitude = 1500.; + + r->wiring_score = 0.; + + r->bboxes = NULL; + r->bboxtree = NULL; + + r->netlists = g_ptr_array_new(); + r->routes = g_ptr_array_new(); + + r->keepoutlayers = NULL; + + r->routednets = NULL; + r->failednets = NULL; + + ltime = time(NULL); + + gts_predicates_init(); + + pcb_message(PCB_MSG_INFO, _("Topological Autorouter\n")); + pcb_message(PCB_MSG_INFO, _("Started %s"), asctime(localtime(<ime))); + pcb_message(PCB_MSG_INFO, _("-------------------------------------\n")); + pcb_message(PCB_MSG_INFO, _("Copyright 2009 Anthony Blake (tonyb33@gmail.com)\n\n")); + return r; +} + +void acquire_twonets(toporouter_t * r) +{ + PCB_RAT_LOOP(PCB->Data); + if (PCB_FLAG_TEST(PCB_FLAG_SELECTED, line)) + import_route(r, line); + PCB_END_LOOP; + + if (!r->routes->len) { + PCB_RAT_LOOP(PCB->Data); + import_route(r, line); + PCB_END_LOOP; + } +} + +toporouter_netlist_t *find_netlist_by_name(toporouter_t * r, char *name) +{ + FOREACH_NETLIST(r->netlists) { + if (!strcmp(netlist->netlist, name)) + return netlist; + } + FOREACH_END; + return NULL; +} + +gint toporouter_set_pair(toporouter_t * r, toporouter_netlist_t * n1, toporouter_netlist_t * n2) +{ + if (!n1 || !n2) + return 0; + n1->pair = n2; + n2->pair = n1; + return 1; +} + +static int toporouter(int argc, char **argv, pcb_coord_t x, pcb_coord_t y) +{ + toporouter_t *r = toporouter_new(); + parse_arguments(r, argc, argv); + import_geometry(r); + acquire_twonets(r); + +/*if(!toporouter_set_pair(r, find_netlist_by_name(r, " DRAM_DQS_N"), find_netlist_by_name(r, " DRAM_DQS"))) { + printf("Couldn't associate pair\n"); + }*/ + + hybrid_router(r); +/* + for(gint i=0;ilayers[i].surface, space_edge, NULL); + } + { + int i; + for(i=0;ilayers[i].surface, buffer, 1024, 1024, 2, NULL, i, NULL); + } + } +*/ + toporouter_export(r); + toporouter_free(r); + + pcb_undo_save_serial(); + pcb_rats_destroy(pcb_false); + pcb_undo_restore_serial(); + pcb_rat_add_all(pcb_false, NULL); + pcb_undo_restore_serial(); + pcb_undo_inc_serial(); + pcb_redraw(); + + return 0; +} + +static int escape(int argc, char **argv, pcb_coord_t x, pcb_coord_t y) +{ + guint dir, viax, viay; + gdouble pitch, length, dx, dy; + + if (argc != 1) + return 0; + + dir = atoi(argv[0]); + + + PCB_PAD_ALL_LOOP(PCB->Data); + { + if (PCB_FLAG_TEST(PCB_FLAG_SELECTED, pad)) { + pcb_pin_t *via; + pcb_line_t *line; + + pcb_pad_t *pad0 = element->Pad->data; + pcb_pad_t *pad1 = g_list_next(element->Pad)->data; + + pitch = sqrt(pow(abs(pad0->Point1.X - pad1->Point1.X), 2) + pow(abs(pad0->Point1.Y - pad1->Point1.Y), 2)); + length = sqrt(pow(pitch, 2) + pow(pitch, 2)) / 2.; + + dx = length * sin(M_PI / 4.); + dy = length * cos(M_PI / 4.); + + switch (dir) { + case 1: + viax = pad->Point1.X - dx; + viay = pad->Point1.Y + dy; + break; + case 3: + viax = pad->Point1.X + dx; + viay = pad->Point1.Y + dy; + break; + case 9: + viax = pad->Point1.X + dx; + viay = pad->Point1.Y - dy; + break; + case 7: + viax = pad->Point1.X - dx; + viay = pad->Point1.Y - dy; + break; + case 2: + viax = pad->Point1.X; + viay = pad->Point1.Y + (pitch / 2); + break; + case 8: + viax = pad->Point1.X; + viay = pad->Point1.Y - (pitch / 2); + break; + case 4: + viax = pad->Point1.X - (pitch / 2); + viay = pad->Point1.Y; + break; + case 6: + viax = pad->Point1.X + (pitch / 2); + viay = pad->Point1.Y; + break; + default: + printf("ERROR: escape() with bad direction (%d)\n", dir); + return 1; + } + + if ((via = pcb_via_new(PCB->Data, viax, viay, + Settings.ViaThickness, 2 * Settings.Clearance, + 0, Settings.ViaDrillingHole, NULL, pcb_no_flags())) != NULL) { + pcb_undo_add_obj_to_create(PCB_TYPE_VIA, via, via, via); +/* if (gui->shift_is_pressed ()) + pcb_chg_obj_thermal(PCB_TYPE_VIA, via, via, via, PCB->ThermStyle);*/ + DrawVia(via); + if ((line = pcb_line_new_merge(CURRENT, pad->Point1.X + 1., pad->Point1.Y + 1., viax + 1., viay + 1., + Settings.LineThickness, 2 * Settings.Clearance, pcb_no_flags()))) { + + pcb_undo_add_obj_to_create(PCB_TYPE_LINE, CURRENT, line, line); + DrawLine(CURRENT, line); + + } + + } + + } + } + PCB_END_LOOP; + PCB_END_LOOP; + + pcb_undo_inc_serial(); + pcb_draw(); + return 0; +} + +static pcb_hid_action_t toporouter_action_list[] = { + {"Escape", "Select a set of pads", escape, "Pad escape", "Escape()"}, + {"Toporouter", "Select net(s)", toporouter, "Topological autorouter", "Toporouter()"} +}; + +PCB_REGISTER_ACTIONS(toporouter_action_list, toporouter_cookie) + +static void hid_toporouter_uninit(void) +{ + pcb_hid_remove_actions_by_cookie(toporouter_cookie); +} + +pcb_uninit_t hid_toporouter_init() +{ + register_toporouter_action_list(); + return hid_toporouter_uninit; +} Index: toporouter/src_plugins/toporouter/toporouter.h =================================================================== --- toporouter/src_plugins/toporouter/toporouter.h (nonexistent) +++ toporouter/src_plugins/toporouter/toporouter.h (revision 6803) @@ -0,0 +1,489 @@ +/* + * COPYRIGHT + * + * Topological Autorouter for + * PCB, interactive printed circuit board design + * Copyright (C) 2009 Anthony Blake + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Contact addresses for email: + * Anthony Blake, tonyb33@gmail.com + * + */ + +#ifndef PCB_TOPOROUTER_H +#define PCB_TOPOROUTER_H + +#include +#include +#include "data.h" +#include "macro.h" +#include "../autoroute/autoroute.h" +#include "box.h" +#include "draw.h" +#include "error.h" +#include "find.h" +#include "heap.h" +#include "rtree.h" +#include "polygon.h" +#include "rats.h" +#include "remove.h" +#include "obj_pinvia_therm.h" +#include "undo.h" +#include "config.h" + +#include "gts.h" + +#include +#include + +#include + +#define TOPOROUTER_FLAG_VERBOSE (1<<0) +#define TOPOROUTER_FLAG_HARDDEST (1<<1) +#define TOPOROUTER_FLAG_HARDSRC (1<<2) +#define TOPOROUTER_FLAG_MATCH (1<<3) +#define TOPOROUTER_FLAG_LAYERHINT (1<<4) +#define TOPOROUTER_FLAG_LEASTINVALID (1<<5) +#define TOPOROUTER_FLAG_AFTERORDER (1<<6) +#define TOPOROUTER_FLAG_AFTERRUBIX (1<<7) +#define TOPOROUTER_FLAG_GOFAR (1<<8) +#define TOPOROUTER_FLAG_DETOUR (1<<9) + +/* Define to 1 to enable toporouter graphical output - wait, we don't link cairo?! */ +#define TOPO_OUTPUT_ENABLED 0 + + +#if TOPO_OUTPUT_ENABLED +#include +#endif + +#define EPSILON 0.0001f + +/*#define DEBUG_ROAR 1*/ + +#define coord_distance(a,b,c,d) sqrt(pow(a-c,2)+pow(b-d,2)) +#define coord_distance2(a,b,c,d) (pow(a-c,2)+pow(b-d,2)) + +#define tvdistance(a,b) sqrt(pow(vx(a)-vx(b),2)+pow(vy(a)-vy(b),2)) +#define tvdistance2(a,b) (pow(vx(a)-vx(b),2)+pow(vy(a)-vy(b),2)) + +#define edge_v1(e) (GTS_SEGMENT(e)->v1) +#define edge_v2(e) (GTS_SEGMENT(e)->v2) +#define tedge_v1(e) (TOPOROUTER_VERTEX(GTS_SEGMENT(e)->v1)) +#define tedge_v2(e) (TOPOROUTER_VERTEX(GTS_SEGMENT(e)->v2)) + +#define tedge(v1,v2) TOPOROUTER_EDGE(gts_vertices_are_connected(GTS_VERTEX(v1), GTS_VERTEX(v2))) + +#define edge_routing(e) (TOPOROUTER_IS_CONSTRAINT(e) ? TOPOROUTER_CONSTRAINT(e)->routing : e->routing) +#define vrouting(v) (edge_routing(v->routingedge)) + +#define edge_routing_next(e,list) ((list->next) ? TOPOROUTER_VERTEX(list->next->data) : TOPOROUTER_VERTEX(edge_v2(e))) +#define edge_routing_prev(e,list) ((list->prev) ? TOPOROUTER_VERTEX(list->prev->data) : TOPOROUTER_VERTEX(edge_v1(e))) + +#define vx(v) (GTS_POINT(v)->x) +#define vy(v) (GTS_POINT(v)->y) +#define vz(v) (GTS_POINT(v)->z) + +#define close_enough_xy(a,b) (vx(a) > vx(b) - EPSILON && vx(a) < vx(b) + EPSILON && vy(a) > vy(b) - EPSILON && vy(a) < vy(b) + EPSILON) + +#define tev1x(e) (vx(tedge_v1(e)) +#define tev1y(e) (vy(tedge_v1(e)) +#define tev1z(e) (vz(tedge_v1(e)) +#define tev2x(e) (vx(tedge_v2(e)) +#define tev2y(e) (vy(tedge_v2(e)) +#define tev2z(e) (vz(tedge_v2(e)) + +#define tvertex_intersect(a,b,c,d) (TOPOROUTER_VERTEX(vertex_intersect(GTS_VERTEX(a),GTS_VERTEX(b),GTS_VERTEX(c),GTS_VERTEX(d)))) + +#define TOPOROUTER_IS_BBOX(obj) (gts_object_is_from_class (obj, toporouter_bbox_class ())) +#define TOPOROUTER_BBOX(obj) GTS_OBJECT_CAST (obj, toporouter_bbox_t, toporouter_bbox_class ()) +#define TOPOROUTER_BBOX_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass, toporouter_bbox_class_t, toporouter_bbox_class ()) + +typedef enum { + PAD, + PIN, + VIA, + ARC, + VIA_SHADOW, + LINE, + OTHER, + BOARD, + EXPANSION_AREA, + POLYGON, + TEMP +} toporouter_term_t; + +struct _toporouter_bbox_t { + GtsBBox b; + + toporouter_term_t type; + void *data; + int layer; + + GtsSurface *surface; + GtsTriangle *enclosing; + + GList *constraints; + GtsPoint *point, *realpoint; + +/* char *netlist, *style;*/ + + struct _toporouter_cluster_t *cluster; + +}; + +struct _toporouter_bbox_class_t { + GtsBBoxClass parent_class; +}; + +typedef struct _toporouter_bbox_t toporouter_bbox_t; +typedef struct _toporouter_bbox_class_t toporouter_bbox_class_t; + +#define TOPOROUTER_IS_EDGE(obj) (gts_object_is_from_class (obj, toporouter_edge_class ())) +#define TOPOROUTER_EDGE(obj) GTS_OBJECT_CAST (obj, toporouter_edge_t, toporouter_edge_class ()) +#define TOPOROUTER_EDGE_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass, toporouter_edge_class_t, toporouter_edge_class ()) + +#define EDGE_FLAG_DIRECTCONNECTION (1<<0) + +struct _toporouter_edge_t { + GtsEdge e; + /*pcb_netlist_t *netlist; */ + + guint flags; + + GList *routing; +}; + +struct _toporouter_edge_class_t { + GtsEdgeClass parent_class; +}; + +typedef struct _toporouter_edge_t toporouter_edge_t; +typedef struct _toporouter_edge_class_t toporouter_edge_class_t; + +#define TOPOROUTER_IS_VERTEX(obj) (gts_object_is_from_class (obj, toporouter_vertex_class ())) +#define TOPOROUTER_VERTEX(obj) GTS_OBJECT_CAST (obj, toporouter_vertex_t, toporouter_vertex_class ()) +#define TOPOROUTER_VERTEX_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass, toporouter_vertex_class_t, toporouter_vertex_class ()) + +#define VERTEX_FLAG_VIZ (1<<1) +#define VERTEX_FLAG_CCW (1<<2) +#define VERTEX_FLAG_CW (1<<3) +#define VERTEX_FLAG_RED (1<<4) +#define VERTEX_FLAG_GREEN (1<<5) +#define VERTEX_FLAG_BLUE (1<<6) +#define VERTEX_FLAG_TEMP (1<<7) +#define VERTEX_FLAG_ROUTE (1<<8) +#define VERTEX_FLAG_FAKE (1<<10) +#define VERTEX_FLAG_SPECCUT (1<<11) + +struct _toporouter_vertex_t { + GtsVertex v; + /*GList *boxes; */ + struct _toporouter_bbox_t *bbox; + + struct _toporouter_vertex_t *parent; + struct _toporouter_vertex_t *child; + + toporouter_edge_t *routingedge; + + guint flags; + + gdouble gcost, hcost; + guint gn; + + struct _toporouter_arc_t *arc; + + struct _toporouter_oproute_t *oproute; + struct _toporouter_route_t *route; + + gdouble thickness; + +}; + +struct _toporouter_vertex_class_t { + GtsVertexClass parent_class; +}; + +typedef struct _toporouter_vertex_t toporouter_vertex_t; +typedef struct _toporouter_vertex_class_t toporouter_vertex_class_t; + +#define TOPOROUTER_IS_CONSTRAINT(obj) (gts_object_is_from_class (obj, toporouter_constraint_class ())) +#define TOPOROUTER_CONSTRAINT(obj) GTS_OBJECT_CAST (obj, toporouter_constraint_t, toporouter_constraint_class ()) +#define TOPOROUTER_CONSTRAINT_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass, toporouter_constraint_class_t, toporouter_constraint_class ()) + +struct _toporouter_constraint_t { + GtsConstraint c; + toporouter_bbox_t *box; + GList *routing; +}; + +struct _toporouter_constraint_class_t { + GtsConstraintClass parent_class; +}; + +typedef struct { + gdouble x, y; +} toporouter_spoint_t; + +typedef struct _toporouter_constraint_t toporouter_constraint_t; +typedef struct _toporouter_constraint_class_t toporouter_constraint_class_t; + +typedef struct { + GtsSurface *surface; +/* GtsTriangle *t;*/ +/* GtsVertex *v1, *v2, *v3;*/ + + GList *vertices; + GList *constraints; + GList *edges; + +} toporouter_layer_t; + +#define TOPOROUTER_VERTEX_REGION(x) ((toporouter_vertex_region_t *)x) +typedef struct { + + GList *points; + toporouter_vertex_t *v1, *v2; + toporouter_vertex_t *origin; + +} toporouter_vertex_region_t; + +struct _toporouter_rubberband_arc_t { + toporouter_vertex_t *pathv, *arcv; + gdouble r, d; + gint wind; + GList *list; +}; + +typedef struct _toporouter_rubberband_arc_t toporouter_rubberband_arc_t; +#define TOPOROUTER_RUBBERBAND_ARC(x) ((toporouter_rubberband_arc_t *)x) + +struct _toporouter_route_t { + + struct _toporouter_netlist_t *netlist; + + struct _toporouter_cluster_t *src, *dest; + struct _toporouter_cluster_t *psrc, *pdest; + + gdouble score, detourscore; + + toporouter_vertex_t *curpoint; + GHashTable *alltemppoints; + + GList *path; + + guint flags; + + GList *destvertices, *srcvertices; + + GList *topopath; + + gdouble pscore; + GList *ppath; + + gint *ppathindices; +}; + +typedef struct _toporouter_route_t toporouter_route_t; + +#define TOPOROUTER_ROUTE(x) ((toporouter_route_t *)x) + +struct _toporouter_netlist_t { + GPtrArray *clusters, *routes; + char *netlist, *style; + GList *routed; + + struct _toporouter_netlist_t *pair; +}; + +typedef struct _toporouter_netlist_t toporouter_netlist_t; + +#define TOPOROUTER_NETLIST(x) ((toporouter_netlist_t *)x) + +struct _toporouter_cluster_t { + gint c, pc; + GPtrArray *boxes; + toporouter_netlist_t *netlist; +}; + +typedef struct _toporouter_cluster_t toporouter_cluster_t; + +#define TOPOROUTER_CLUSTER(x) ((toporouter_cluster_t *)x) + +#define TOPOROUTER_OPROUTE(x) ((toporouter_oproute_t *)x) + +#define oproute_next(a,b) (b->next ? TOPOROUTER_ARC(b->next->data) : a->term2) +#define oproute_prev(a,b) (b->prev ? TOPOROUTER_ARC(b->prev->data) : a->term1) + +#define TOPOROUTER_SERPINTINE(x) ((toporouter_serpintine_t *)x) + +struct _toporouter_serpintine_t { + GList *arcs; + gdouble x, y; + gdouble x0, y0, x1, y1; + + gpointer start; + gdouble halfa, radius; + guint nhalfcycles; + +}; +typedef struct _toporouter_serpintine_t toporouter_serpintine_t; + +struct _toporouter_oproute_t { + GList *arcs; + toporouter_vertex_t *term1, *term2; + char *style; + char *netlist; + guint layergroup; + gdouble tof; + GList *path; + + toporouter_serpintine_t *serp; +}; + +typedef struct _toporouter_oproute_t toporouter_oproute_t; + + +#define TOPOROUTER_IS_ARC(obj) (gts_object_is_from_class (obj, toporouter_arc_class())) +#define TOPOROUTER_ARC(obj) GTS_OBJECT_CAST (obj, toporouter_arc_t, toporouter_arc_class()) +#define TOPOROUTER_ARC_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass, toporouter_arc_class_t, toporouter_arc_class()) + +struct _toporouter_arc_t { + GtsObject object; + + gdouble x0, y0, x1, y1; + toporouter_vertex_t *centre, *v; + gdouble r; + gint dir; + + GList *clearance; + + toporouter_oproute_t *oproute; + + toporouter_vertex_t *v1, *v2; +}; + +struct _toporouter_arc_class_t { + GtsObjectClass parent_class; + gboolean binary; +}; + +typedef struct _toporouter_arc_t toporouter_arc_t; +typedef struct _toporouter_arc_class_t toporouter_arc_class_t; + +typedef struct _toporouter_t toporouter_t; + + + +typedef struct { + guint id; + + guint *pairwise_nodetour; + gdouble pairwise_detour_sum; + gdouble score; + guint pairwise_fails; + + toporouter_route_t *routedata; + + toporouter_t *r; + +} toporouter_netscore_t; + +#define TOPOROUTER_NETSCORE(x) ((toporouter_netscore_t *)x) + +struct _toporouter_t { + GSList *bboxes; + GNode *bboxtree; + + toporouter_layer_t *layers; + + GList *paths; + + GList *keepoutlayers; + + guint flags; + + GList *destboxes, *consumeddestboxes; + + /* settings: */ + guint viamax; + gdouble viacost; + gdouble stublength; + gdouble serpintine_half_amplitude; + + gdouble wiring_score; + + GPtrArray *routes; + GPtrArray *netlists; + + GList *routednets, *failednets; + + gint(*netsort) (toporouter_netscore_t **, toporouter_netscore_t **); + + struct timeval starttime; + + FILE *debug; +}; + +typedef gint(*oproute_adjseg_func) + + (toporouter_t *, + GList **, GList **, guint *, gdouble, gdouble, gdouble, gdouble, toporouter_oproute_t *, toporouter_oproute_t *); + +typedef struct { +#ifdef CAIRO_H + cairo_t *cr; + cairo_surface_t *surface; +#endif + + double s; /* scale factor */ + + int mode; + void *data; + + char *filename; + double iw, ih; /* image dimensions */ +} drawing_context_t; + +#define FOREACH_CLUSTER(clusters) do { \ + toporouter_cluster_t **i; \ + for(i = ((toporouter_cluster_t **)clusters->pdata) + clusters->len - 1; i >= (toporouter_cluster_t **)clusters->pdata && clusters->len > 0; --i) { \ + toporouter_cluster_t *cluster = *i; + +#define FOREACH_BBOX(boxes) do { \ + toporouter_bbox_t **i; \ + for(i = ((toporouter_bbox_t **)boxes->pdata) + boxes->len - 1; i >= (toporouter_bbox_t **)boxes->pdata && boxes->len > 0; --i) { \ + toporouter_bbox_t *box = *i; + +#define FOREACH_ROUTE(routes) do { \ + toporouter_route_t **i; \ + for(i = ((toporouter_route_t **)routes->pdata) + routes->len - 1; i >= (toporouter_route_t **)routes->pdata && routes->len > 0; --i) { \ + toporouter_route_t *routedata = *i; + +#define FOREACH_NETSCORE(netscores) do { \ + toporouter_netscore_t **i; \ + for(i = ((toporouter_netscore_t **)netscores->pdata) + netscores->len - 1; i >= (toporouter_netscore_t **)netscores->pdata && netscores->len > 0; --i) { \ + toporouter_netscore_t *netscore = *i; + +#define FOREACH_NETLIST(netlists) do { \ + toporouter_netlist_t **i; \ + for(i = ((toporouter_netlist_t **)netlists->pdata) + netlists->len - 1; i >= (toporouter_netlist_t **)netlists->pdata && netlists->len > 0; --i) { \ + toporouter_netlist_t *netlist = *i; + +#define FOREACH_END }} while(0) + +#endif /* PCB_TOPOROUTER_H */