static void cartesian_grid_triangulate (CartesianGrid * g, GtsSurface * s) { gint i, j; GtsVertex *** v; g_return_if_fail (g != NULL); g_return_if_fail (s != NULL); v = g->vertices; for (i = 0; i < g->nx - 1; i++) for (j = 0; j < g->ny - 1; j++) if (v[i][j]) { if (v[i][j+1]) { if (v[i+1][j+1]) { GtsEdge * e1 = new_edge (v[i][j+1], v[i][j]); GtsEdge * e2 = gts_edge_new (GTS_EDGE_CLASS (gts_constraint_class ()), v[i][j], v[i+1][j+1]); GtsEdge * e3 = new_edge (v[i+1][j+1], v[i][j+1]); gts_surface_add_face (s, gts_face_new (s->face_class, e1, e2, e3)); if (v[i+1][j]) { e1 = new_edge (v[i+1][j], v[i+1][j+1]); e3 = new_edge (v[i][j], v[i+1][j]); gts_surface_add_face (s, gts_face_new (s->face_class, e1, e2, e3)); } } else if (v[i+1][j]) { GtsEdge * e1 = new_edge (v[i][j+1], v[i][j]); GtsEdge * e2 = new_edge (v[i][j], v[i+1][j]); GtsEdge * e3 = gts_edge_new (GTS_EDGE_CLASS (gts_constraint_class ()), v[i+1][j], v[i][j+1]); gts_surface_add_face (s, gts_face_new (s->face_class, e1, e2, e3)); } } else if (v[i+1][j] && v[i+1][j+1]) { GtsEdge * e1 = new_edge (v[i][j], v[i+1][j]); GtsEdge * e2 = new_edge (v[i+1][j], v[i+1][j+1]); GtsEdge * e3 = gts_edge_new (GTS_EDGE_CLASS (gts_constraint_class ()), v[i+1][j+1], v[i][j]); gts_surface_add_face (s, gts_face_new (s->face_class, e1, e2, e3)); } } else if (v[i][j+1] && v[i+1][j+1] && v[i+1][j]) { GtsEdge * e1 = new_edge (v[i+1][j], v[i+1][j+1]); GtsEdge * e2 = new_edge (v[i+1][j+1], v[i][j+1]); GtsEdge * e3 = gts_edge_new (GTS_EDGE_CLASS (gts_constraint_class ()), v[i][j+1], v[i+1][j]); gts_surface_add_face (s, gts_face_new (s->face_class, e1, e2, e3)); } }
void ofxGtsSurface::setup(string filename) { FILE * fptr; GtsFile * fp; string filePath = ofToDataPath(filename); /* open first file */ if ((fptr = fopen (filePath.c_str(), "rt")) == NULL) { ofLog(OF_LOG_ERROR, "Cannot open file: " + filePath); return; } /* reads in first surface file */ surface = gts_surface_new(GTS_SURFACE_CLASS(gts_surface_class()), GTS_FACE_CLASS(gts_nface_class()), GTS_EDGE_CLASS(gts_nedge_class()), GTS_VERTEX_CLASS(gts_nvertex_class())); fp = gts_file_new(fptr); if (gts_surface_read (surface, fp)) { ofLog(OF_LOG_ERROR, filePath + " is not a valid GTS surface file"); loaded = false; gts_object_destroy(GTS_OBJECT(surface)); } else { ofLog(OF_LOG_NOTICE, "Gts surface file read: " + filePath); loaded = true; } gts_file_destroy (fp); fclose (fptr); }
static GtsEdge * new_edge (GtsVertex * v1, GtsVertex * v2) { GtsSegment * s = gts_vertices_are_connected (v1, v2); return s == NULL ? gts_edge_new (GTS_EDGE_CLASS (gts_constraint_class ()), v1, v2) : GTS_EDGE (s); }
void ofxGtsSurface::setup() { surface = gts_surface_new(GTS_SURFACE_CLASS(gts_surface_class()), GTS_FACE_CLASS(gts_nface_class()), GTS_EDGE_CLASS(gts_nedge_class()), GTS_VERTEX_CLASS(gts_nvertex_class())); loaded = true; }
void ofxGtsSurface::createBoolean(ofxGtsSurface &source, ofxGtsSurface &result, BooleanOperation operation) { result.surface = gts_surface_new(GTS_SURFACE_CLASS(gts_surface_class()), GTS_FACE_CLASS(gts_nface_class()), GTS_EDGE_CLASS(gts_nedge_class()), GTS_VERTEX_CLASS(gts_nvertex_class())); switch(operation) { case BOOLEAN_INTERSECTION: gts_surface_inter_boolean(si, result.surface, GTS_1_IN_2); gts_surface_inter_boolean(si, result.surface, GTS_2_IN_1); result.loaded = true; break; case BOOLEAN_UNION: gts_surface_inter_boolean(si, result.surface, GTS_1_OUT_2); gts_surface_inter_boolean(si, result.surface, GTS_2_OUT_1); result.loaded = true; break; case BOOLEAN_DIFFERENCE: gts_surface_inter_boolean(si, result.surface, GTS_1_OUT_2); gts_surface_inter_boolean(si, result.surface, GTS_2_IN_1); gts_surface_foreach_face(si->s2, (GtsFunc)gts_triangle_revert, NULL); gts_surface_foreach_face(source.surface, (GtsFunc)gts_triangle_revert, NULL); result.loaded = true; break; case BOOLEAN_REVERSE_DIFFERENCE: // TODO: Reverse difference can cause crashes, is there a way to catch them? gts_surface_inter_boolean(si, result.surface, GTS_2_OUT_1); gts_surface_inter_boolean(si, result.surface, GTS_1_IN_2); gts_surface_foreach_face(si->s1, (GtsFunc)gts_triangle_revert, NULL); gts_surface_foreach_face(surface, (GtsFunc)gts_triangle_revert, NULL); result.loaded = true; break; } }
int main (int argc, char * argv[]) { GPtrArray * vertices; GtsFifo * edges; guint i, line; GtsTriangle * t; GtsVertex * v1, * v2, * v3; GtsSurface * surface; gboolean keep_hull = TRUE; gboolean verbose = FALSE; gboolean add_constraints = TRUE; gboolean remove_holes = FALSE; gboolean check_delaunay = FALSE; gboolean conform = FALSE; gboolean refine = FALSE; gboolean split_constraints = FALSE; gboolean randomize = FALSE; gboolean remove_duplicates = FALSE; gint steiner_max = -1; gdouble quality = 0., area = G_MAXDOUBLE; int c = 0, status = 0; const char * fname = NULL; GTimer * timer; /* parse options using getopt */ while (c != EOF) { #ifdef HAVE_GETOPT_LONG static struct option long_options[] = { {"duplicates", no_argument, NULL, 'd'}, {"help", no_argument, NULL, 'h'}, {"verbose", no_argument, NULL, 'v'}, {"randomize", no_argument, NULL, 'r'}, {"hull", no_argument, NULL, 'b'}, {"noconst", no_argument, NULL, 'e'}, {"holes", no_argument, NULL, 'H'}, {"split", no_argument, NULL, 'S'}, {"check", no_argument, NULL, 'c'}, {"files", required_argument, NULL, 'f'}, {"conform", no_argument, NULL, 'o'}, {"steiner", required_argument, NULL, 's'}, {"quality", required_argument, NULL, 'q'}, {"area", required_argument, NULL, 'a'} }; int option_index = 0; switch ((c = getopt_long (argc, argv, "hvbecf:os:q:a:HSrd", long_options, &option_index))) { #else /* not HAVE_GETOPT_LONG */ switch ((c = getopt (argc, argv, "hvbecf:os:q:a:HSrd"))) { #endif /* not HAVE_GETOPT_LONG */ case 'd': /* duplicates */ remove_duplicates = TRUE; break; case 'b': /* do not keep convex hull */ keep_hull = FALSE; break; case 'e': /* do not add constrained edges */ add_constraints = FALSE; break; case 'H': /* remove holes */ remove_holes = TRUE; break; case 'S': /* split constraints */ split_constraints = TRUE; break; case 'r': /* randomize */ randomize = TRUE; break; case 'c': /* check Delaunay property */ check_delaunay = TRUE; break; case 'f': /* generates files */ fname = optarg; break; case 'v': /* verbose */ verbose = TRUE; break; case 'o': /* conform */ conform = TRUE; break; case 's': /* steiner */ steiner_max = atoi (optarg); break; case 'q': /* quality */ conform = TRUE; refine = TRUE; quality = atof (optarg); break; case 'a': /* area */ conform = TRUE; refine = TRUE; area = atof (optarg); break; case 'h': /* help */ fprintf (stderr, "Usage: delaunay [OPTION] < file.gts\n" "Construct the constrained Delaunay triangulation of the input\n" "\n" " -b --hull do not keep convex hull\n" " -e --noconst do not add constrained edges\n" " -S --split split constraints (experimental)\n" " -H --holes remove holes from the triangulation\n" " -d --duplicates remove duplicate vertices\n" " -r --randomize shuffle input vertex list\n" " -c --check check Delaunay property\n" " -f FNAME --files=FNAME generate evolution files\n" " -o --conform generate conforming triangulation\n" " -s N --steiner=N maximum number of Steiner points for\n" " conforming triangulation (default is no limit)\n" " -q Q --quality=Q Set the minimum acceptable face quality\n" " -a A --area=A Set the maximum acceptable face area\n" " -v --verbose print statistics about the triangulation\n" " -h --help display this help and exit\n" "\n" "Reports bugs to %s\n", GTS_MAINTAINER); return 0; /* success */ break; case '?': /* wrong options */ fprintf (stderr, "Try `delaunay --help' for more information.\n"); return 1; /* failure */ } } /* read file => two lists: vertices and constraints */ edges = gts_fifo_new (); vertices = g_ptr_array_new (); if (add_constraints) /* the edge class is a GtsConstraintClass */ line = read_list (vertices, edges, GTS_EDGE_CLASS (gts_constraint_class ()), stdin); else /* the edge class is a "normal" edge: GtsEdgeClass */ line = read_list (vertices, edges, gts_edge_class (), stdin); if (line > 0) { fprintf (stderr, "delaunay: error in input file at line %u\n", line); return 1; } timer = g_timer_new (); g_timer_start (timer); if (randomize) shuffle_array (vertices); /* create triangle enclosing all the vertices */ { GSList * list = NULL; for (i = 0; i < vertices->len; i++) list = g_slist_prepend (list, g_ptr_array_index (vertices, i)); t = gts_triangle_enclosing (gts_triangle_class (), list, 100.); g_slist_free (list); } gts_triangle_vertices (t, &v1, &v2, &v3); /* create surface with one face: the enclosing triangle */ surface = gts_surface_new (gts_surface_class (), gts_face_class (), gts_edge_class (), gts_vertex_class ()); gts_surface_add_face (surface, gts_face_new (gts_face_class (), t->e1, t->e2, t->e3)); /* add vertices */ for (i = 0; i < vertices->len; i++) { GtsVertex * v1 = g_ptr_array_index (vertices, i); GtsVertex * v = gts_delaunay_add_vertex (surface, v1, NULL); g_assert (v != v1); if (v != NULL) { if (!remove_duplicates) { fprintf (stderr, "delaunay: duplicate vertex (%g,%g) in input file\n", GTS_POINT (v)->x, GTS_POINT (v)->y); return 1; /* Failure */ } else gts_vertex_replace (v1, v); } if (fname) { static guint nf = 1; char s[80]; FILE * fp; g_snprintf (s, 80, "%s.%u", fname, nf++); fp = fopen (s, "wt"); gts_surface_write_oogl (surface, fp); fclose (fp); if (check_delaunay && gts_delaunay_check (surface)) { fprintf (stderr, "delaunay: triangulation is not Delaunay\n"); return 1; } } } g_ptr_array_free (vertices, TRUE); /* add remaining constraints */ if (add_constraints) gts_fifo_foreach (edges, (GtsFunc) add_constraint, surface); /* destroy enclosing triangle */ 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; if (!keep_hull) gts_delaunay_remove_hull (surface); if (remove_holes) delaunay_remove_holes (surface); if (split_constraints) { gpointer data[2]; data[0] = surface; data[1] = edges; gts_fifo_foreach (edges, (GtsFunc) split_constraint, data); } if (conform) { guint encroached_number = gts_delaunay_conform (surface, steiner_max, (GtsEncroachFunc) gts_vertex_encroaches_edge, NULL); if (encroached_number == 0 && refine) { guint unrefined_number; gpointer data[2]; data[0] = &quality; data[1] = &area; unrefined_number = gts_delaunay_refine (surface, steiner_max, (GtsEncroachFunc) gts_vertex_encroaches_edge, NULL, (GtsKeyFunc) triangle_cost, data); if (verbose && unrefined_number > 0) fprintf (stderr, "delaunay: ran out of Steiner points (max: %d) during refinement\n" "%d unrefined faces left\n", steiner_max, unrefined_number); } else if (verbose && encroached_number > 0) fprintf (stderr, "delaunay: ran out of Steiner points (max: %d) during conforming\n" "Delaunay triangulation: %d encroached constraints left\n", steiner_max, encroached_number); } g_timer_stop (timer); if (verbose) { gts_surface_print_stats (surface, stderr); fprintf (stderr, "# Triangulation time: %g s speed: %.0f vertex/s\n", g_timer_elapsed (timer, NULL), gts_surface_vertex_number (surface)/g_timer_elapsed (timer, NULL)); } if (check_delaunay && gts_delaunay_check (surface)) { fprintf (stderr, "delaunay: triangulation is not Delaunay\n"); status = 1; /* failure */ } /* write triangulation */ gts_surface_write (surface, stdout); return status; }
static void gts_constraint_split (GtsConstraint * c, GtsSurface * s, GtsFifo * fifo) { GSList * i; GtsVertex * v1, * v2; GtsEdge * e; g_return_if_fail (c != NULL); g_return_if_fail (s != NULL); v1 = GTS_SEGMENT (c)->v1; v2 = GTS_SEGMENT (c)->v2; e = GTS_EDGE (c); 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 (gts_point_orientation (GTS_POINT (v1), GTS_POINT (v2), GTS_POINT (v)) == 0.) { GSList * j = e->triangles; GtsFace * f1 = NULL; GtsEdge * e1, * e2; /* replaces edges with constraints */ gts_triangle_vertices_edges (GTS_TRIANGLE (f), e, &v1, &v2, &v, &e, &e1, &e2); if (!GTS_IS_CONSTRAINT (e1)) { GtsEdge * ne1 = gts_edge_new (GTS_EDGE_CLASS (GTS_OBJECT (c)->klass), v2, v); gts_edge_replace (e1, ne1); gts_object_destroy (GTS_OBJECT (e1)); e1 = ne1; if (fifo) gts_fifo_push (fifo, e1); } if (!GTS_IS_CONSTRAINT (e2)) { GtsEdge * ne2 = gts_edge_new (GTS_EDGE_CLASS (GTS_OBJECT (c)->klass), v, v1); gts_edge_replace (e2, ne2); gts_object_destroy (GTS_OBJECT (e2)); e2 = ne2; if (fifo) gts_fifo_push (fifo, e2); } /* look for face opposite */ while (j && !f1) { if (GTS_IS_FACE (j->data) && gts_face_has_parent_surface (j->data, s)) f1 = j->data; j = j->next; } if (f1) { /* c is not a boundary of s */ GtsEdge * e3, * e4, * e5; GtsVertex * v3; gts_triangle_vertices_edges (GTS_TRIANGLE (f1), e, &v1, &v2, &v3, &e, &e3, &e4); e5 = gts_edge_new (s->edge_class, v, v3); gts_surface_add_face (s, gts_face_new (s->face_class, e5, e2, e3)); gts_surface_add_face (s, gts_face_new (s->face_class, e5, e4, e1)); gts_object_destroy (GTS_OBJECT (f1)); } gts_object_destroy (GTS_OBJECT (f)); return; } } i = i->next; } }
/* tri: * Main entry point to using GTS for triangulation. * Input is npt points with x and y coordinates stored either separately * in x[] and y[] (sepArr != 0) or consecutively in x[] (sepArr == 0). * Optionally, the input can include nsegs line segments, whose endpoint * indices are supplied in segs[2*i] and segs[2*i+1] yielding a constrained * triangulation. * * The return value is the corresponding gts surface, which can be queries for * the triangles and line segments composing the triangulation. */ static GtsSurface* tri(double *x, double *y, int npt, int *segs, int nsegs, int sepArr) { int i; GtsSurface *surface; GVertex **vertices = N_GNEW(npt, GVertex *); GtsEdge **edges = N_GNEW(nsegs, GtsEdge*); GSList *list = NULL; GtsVertex *v1, *v2, *v3; GtsTriangle *t; GtsVertexClass *vcl = (GtsVertexClass *) g_vertex_class(); GtsEdgeClass *ecl = GTS_EDGE_CLASS (gts_constraint_class ()); if (sepArr) { for (i = 0; i < npt; i++) { GVertex *p = (GVertex *) gts_vertex_new(vcl, x[i], y[i], 0); p->idx = i; vertices[i] = p; } } else { for (i = 0; i < npt; i++) { GVertex *p = (GVertex *) gts_vertex_new(vcl, x[2*i], x[2*i+1], 0); p->idx = i; vertices[i] = p; } } /* N.B. Edges need to be created here, presumably before the * the vertices are added to the face. In particular, they cannot * be added created and added vi gts_delaunay_add_constraint() below. */ for (i = 0; i < nsegs; i++) { edges[i] = gts_edge_new(ecl, (GtsVertex *) (vertices[ segs[ 2 * i]]), (GtsVertex *) (vertices[ segs[ 2 * i + 1]])); } for (i = 0; i < npt; i++) list = g_slist_prepend(list, vertices[i]); t = gts_triangle_enclosing(gts_triangle_class(), list, 100.); g_slist_free(list); gts_triangle_vertices(t, &v1, &v2, &v3); surface = gts_surface_new(gts_surface_class(), (GtsFaceClass *) g_face_class(), gts_edge_class(), gts_vertex_class()); gts_surface_add_face(surface, gts_face_new(gts_face_class(), t->e1, t->e2, t->e3)); for (i = 0; i < npt; i++) { GtsVertex *v1 = (GtsVertex *) vertices[i]; GtsVertex *v = gts_delaunay_add_vertex(surface, v1, NULL); /* if v != NULL, it is a previously added pt with the same * coordinates as v1, in which case we replace v1 with v */ if (v) { /* agerr (AGWARN, "Duplicate point %d %d\n", i, ((GVertex*)v)->idx); */ gts_vertex_replace (v1, v); } } for (i = 0; i < nsegs; i++) { gts_delaunay_add_constraint(surface,GTS_CONSTRAINT(edges[i])); } /* destroy enclosing triangle */ gts_allow_floating_vertices = TRUE; gts_allow_floating_edges = TRUE; /* gts_object_destroy(GTS_OBJECT(v1)); gts_object_destroy(GTS_OBJECT(v2)); gts_object_destroy(GTS_OBJECT(v3)); */ destroy(v1); destroy(v2); destroy(v3); gts_allow_floating_edges = FALSE; gts_allow_floating_vertices = FALSE; if (nsegs) delaunay_remove_holes(surface); free (edges); free(vertices); return surface; }