static GtsSurface * happrox_list (GSList * points, gboolean keep_enclosing, gboolean closed, CostFunc cost_func, gpointer cost_data, GtsStopFunc stop_func, gpointer stop_data) { GtsSurface * s = gts_surface_new (gts_surface_class (), GTS_FACE_CLASS (list_face_class ()), gts_edge_class (), gts_vertex_class ()); GtsTriangle * t; GtsVertex * w1, * w2, * w3; GtsListFace * f; /* creates enclosing triangle */ t = gts_triangle_enclosing (gts_triangle_class (), points, 10.); gts_triangle_vertices (t, &w1, &w2, &w3); GTS_POINT (w1)->z = GTS_POINT (w2)->z = GTS_POINT (w3)->z = keep_enclosing ? -10. : -1e30; f = GTS_LIST_FACE (gts_face_new (s->face_class, t->e1, t->e2, t->e3)); gts_surface_add_face (s, GTS_FACE (f)); f->points = points; /* refine surface */ surface_hf_refine (s, cost_func, cost_data, stop_func, stop_data); /* destroy unused vertices */ gts_surface_foreach_face (s, (GtsFunc) destroy_unused, NULL); /* destroy enclosing triangle */ if (!keep_enclosing) { gts_allow_floating_vertices = TRUE; gts_object_destroy (GTS_OBJECT (w1)); gts_object_destroy (GTS_OBJECT (w2)); gts_object_destroy (GTS_OBJECT (w3)); gts_allow_floating_vertices = FALSE; } else if (closed) { GSList * l = gts_surface_boundary (s); GtsFace * f; g_assert (g_slist_length (l) == 3); f = gts_face_new (s->face_class, l->data, l->next->data, l->next->next->data); gts_surface_add_face (s, f); if (!gts_face_is_compatible (f, s)) gts_triangle_revert (GTS_TRIANGLE (f)); g_slist_free (l); gts_object_destroy (GTS_OBJECT (t)); } else gts_object_destroy (GTS_OBJECT (t)); return s; }
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)); } }
/** * 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)); }
static void add_face_from_polygon_corner(GtsSurface *s, GtsEdgePool *pool, GtsVertex *v1, GtsVertex *v2, GtsVertex *v3) { GtsEdge *e1 = gts_edge_pool_new_edge(pool, s->edge_class, v1, v2); GtsEdge *e2 = gts_edge_pool_new_edge(pool, s->edge_class, v2, v3); GtsEdge *e3 = gts_edge_pool_new_edge(pool, s->edge_class, v3, v1); GtsFace *f = gts_face_new(s->face_class, e1, e2, e3); gts_surface_add_face(s,f); }
void add_triangle(k3d::uint_t Vertices[3], k3d::uint_t Edges[3]) { GtsVertex* const vertex1 = get_vertex(Vertices[0]); GtsVertex* const vertex2 = get_vertex(Vertices[1]); GtsVertex* const vertex3 = get_vertex(Vertices[2]); GtsEdge* const edge1 = gts_edge_new(gts_edge_class(), vertex1, vertex2); GtsEdge* const edge2 = gts_edge_new(gts_edge_class(), vertex2, vertex3); GtsEdge* const edge3 = gts_edge_new(gts_edge_class(), vertex3, vertex1); GtsFace* const face = gts_face_new(gts_face_class(), edge1, edge2, edge3); gts_surface_add_face(gts_surface, face); }
static void edge_swap (GtsEdge * e, GtsSurface * s, GtsEHeap * heap) { GSList * i; GtsTriangle * t1 = NULL, * t2 = NULL; GtsVertex * v1, * v2, * v3, * v4; GtsEdge * e1, * e2, * e3, * e4; i = e->triangles; while (i) { if (GTS_IS_FACE (i->data)) { if (!t1) t1 = i->data; else if (!t2) t2 = i->data; else g_assert_not_reached (); } i = i->next; } g_assert (t1 && t2); gts_triangle_vertices_edges (t1, e, &v1, &v2, &v3, &e, &e3, &e4); gts_triangle_vertices_edges (t2, e, &v2, &v1, &v4, &e, &e1, &e2); gts_object_destroy (GTS_OBJECT (e)); e = gts_edge_new (s->edge_class, v3, v4); gts_surface_add_face (s, gts_face_new (s->face_class, e, e4, e1)); gts_surface_add_face (s, gts_face_new (s->face_class, e, e2, e3)); HEAP_INSERT_EDGE (heap, e); HEAP_REMOVE_EDGE (heap, e1); HEAP_INSERT_EDGE (heap, e1); HEAP_REMOVE_EDGE (heap, e2); HEAP_INSERT_EDGE (heap, e2); HEAP_REMOVE_EDGE (heap, e3); HEAP_INSERT_EDGE (heap, e3); HEAP_REMOVE_EDGE (heap, e4); HEAP_INSERT_EDGE (heap, e4); }
static GtsObject * parent(GtsFace *face) { GtsSurface *p; p = gts_surface_new(gts_surface_class(), gts_face_class(), gts_edge_class(), gts_vertex_class()); if( p == NULL ) { PyErr_SetString(PyExc_MemoryError, "could not create parent"); return NULL; } gts_surface_add_face(p,face); return GTS_OBJECT(p); }
void ofxGtsSurface::addFace(ofPoint &p1, ofPoint &p2, ofPoint &p3) { if(!loaded) { setup(); } GtsVertex *v1 = gts_vertex_new(surface->vertex_class, p1.x, p1.y, p1.z); GtsVertex *v2 = gts_vertex_new(surface->vertex_class, p2.x, p2.y, p2.z); GtsVertex *v3 = gts_vertex_new(surface->vertex_class, p3.x, p3.y, p3.z); GtsEdge *e1 = gts_edge_new(surface->edge_class, v1, v2); GtsEdge *e2 = gts_edge_new(surface->edge_class, v2, v3); GtsEdge *e3 = gts_edge_new(surface->edge_class, v3, v1); gts_surface_add_face(surface, gts_face_new(surface->face_class, e1, e2, e3)); }
static GtsSurface * happrox (gray ** g, gint width, gint height, CostFunc cost_func, gpointer cost_data, GtsStopFunc stop_func, gpointer stop_data) { GtsSurface * s = gts_surface_new (gts_surface_class (), GTS_FACE_CLASS (list_face_class ()), gts_edge_class (), gts_vertex_class ()); GtsVertex * v1 = gts_vertex_new (s->vertex_class, 0., 0., g[0][0]); GtsVertex * v2 = gts_vertex_new (s->vertex_class, 0., height - 1, g[height - 1][0]); GtsVertex * v3 = gts_vertex_new (s->vertex_class, width - 1, 0., g[0][width - 1]); GtsVertex * v4 = gts_vertex_new (s->vertex_class, width - 1, height - 1, g[height - 1][width - 1]); guint i, j; GSList * corners = NULL; GtsTriangle * t; GtsVertex * w1, * w2, * w3; GtsListFace * f; /* creates enclosing triangle */ corners = g_slist_prepend (corners, v1); corners = g_slist_prepend (corners, v2); corners = g_slist_prepend (corners, v3); corners = g_slist_prepend (corners, v4); t = gts_triangle_enclosing (gts_triangle_class (), corners, 100.); g_slist_free (corners); gts_triangle_vertices (t, &w1, &w2, &w3); f = GTS_LIST_FACE (gts_face_new (s->face_class, t->e1, t->e2, t->e3)); gts_surface_add_face (s, GTS_FACE (f)); /* add PGM vertices (corners excepted) to point list of f */ for (i = 1; i < width - 1; i++) { for (j = 1; j < height - 1; j++) prepend (f, g, i, j); prepend (f, g, i, 0); prepend (f, g, i, height - 1); } for (j = 1; j < height - 1; j++) { prepend (f, g, 0, j); prepend (f, g, width - 1, j); } pgm_freearray (g, height); /* add four corners to initial triangulation */ g_assert (gts_delaunay_add_vertex_to_face (s, v1, GTS_FACE (f)) == NULL); f = GTS_LIST_FACE (gts_point_locate (GTS_POINT (v2), s, NULL)); g_assert (gts_delaunay_add_vertex_to_face (s, v2, GTS_FACE (f)) == NULL); f = GTS_LIST_FACE (gts_point_locate (GTS_POINT (v3), s, NULL)); g_assert (gts_delaunay_add_vertex_to_face (s, v3, GTS_FACE (f)) == NULL); f = GTS_LIST_FACE (gts_point_locate (GTS_POINT (v4), s, NULL)); g_assert (gts_delaunay_add_vertex_to_face (s, v4, GTS_FACE (f)) == NULL); /* refine surface */ surface_hf_refine (s, cost_func, cost_data, stop_func, stop_data); /* destroy unused vertices */ gts_surface_foreach_face (s, (GtsFunc) destroy_unused, NULL); /* destroy enclosing triangle */ gts_allow_floating_vertices = TRUE; gts_object_destroy (GTS_OBJECT (w1)); gts_object_destroy (GTS_OBJECT (w2)); gts_object_destroy (GTS_OBJECT (w3)); gts_allow_floating_vertices = FALSE; return s; }
static GtsSurface * cartesian_grid_triangulate_holes (CartesianGrid * grid, GtsSurface * s) { GtsVertex * v1, * v2, * v3, * v4; GtsEdge * e1, * e2, * e3, * e4, * e5; gdouble w, h; GtsSurface * box; GSList * constraints = NULL, * vertices = NULL, * i; gpointer data[2]; g_return_val_if_fail (grid != NULL, NULL); g_return_val_if_fail (s != NULL, NULL); /* build enclosing box */ w = grid->xmax - grid->xmin; h = grid->ymax - grid->ymin; v1 = gts_vertex_new (s->vertex_class, grid->xmin - w, grid->ymin - h, 0.); v2 = gts_vertex_new (s->vertex_class, grid->xmax + w, grid->ymin - h, 0.); v3 = gts_vertex_new (s->vertex_class, grid->xmax + w, grid->ymax + h, 0.); v4 = gts_vertex_new (s->vertex_class, grid->xmin - w, grid->ymax + h, 0.); e1 = gts_edge_new (s->edge_class, v1, v2); e2 = gts_edge_new (s->edge_class, v2, v3); e3 = gts_edge_new (s->edge_class, v3, v4); e4 = gts_edge_new (s->edge_class, v4, v1); e5 = gts_edge_new (s->edge_class, v1, v3); box = gts_surface_new (GTS_SURFACE_CLASS (GTS_OBJECT (s)->klass), s->face_class, s->edge_class, s->vertex_class); gts_surface_add_face (box, gts_face_new (s->face_class, e1, e2, e5)); gts_surface_add_face (box, gts_face_new (s->face_class, e3, e4, e5)); /* build vertex and constraint list from the boundaries of the input surface s */ data[0] = s; data[1] = &constraints; gts_surface_foreach_edge (s, (GtsFunc) build_constraint_list, data); vertices = gts_vertices_from_segments (constraints); /* triangulate holes */ i = vertices; while (i) { g_assert (!gts_delaunay_add_vertex (box, i->data, NULL)); i = i->next; } g_slist_free (vertices); i = constraints; while (i) { g_assert (!gts_delaunay_add_constraint (box, i->data)); i = i->next; } /* destroy corners of the enclosing box */ gts_allow_floating_vertices = TRUE; gts_object_destroy (GTS_OBJECT (v1)); gts_object_destroy (GTS_OBJECT (v2)); gts_object_destroy (GTS_OBJECT (v3)); gts_object_destroy (GTS_OBJECT (v4)); gts_allow_floating_vertices = FALSE; /* remove parts of the mesh which are not holes */ i = constraints; while (i) { edge_mark_as_hole (i->data, box); i = i->next; } g_slist_free (constraints); /* remove marked and duplicate faces */ gts_surface_foreach_face_remove (box, (GtsFunc) face_is_marked, NULL); /* box now contains only the triangulated holes. */ return box; }
int main (int argc, char * argv[]) { guint i, j, nx, ny; gdouble cosa, sina; GtsSurface * surface; GSList * l, * vertices = NULL; GtsTriangle * t; GtsVertex * v1, * v2, * v3; GTimer * timer; if (argc != 4) { fprintf (stderr, "usage: cartesian nx ny angle\n"); return 0; } nx = strtol (argv[1], NULL, 0); ny = strtol (argv[2], NULL, 0); cosa = cos (strtod (argv[3], NULL)); sina = sin (strtod (argv[3], NULL)); timer = g_timer_new (); g_timer_start (timer); for (i = 0; i < nx; i++) { gdouble x = (gdouble) i/(gdouble) (nx - 1); for (j = 0; j < ny; j++) { gdouble y = (gdouble) j/(gdouble) (nx - 1); vertices = g_slist_prepend (vertices, gts_vertex_new (gts_vertex_class (), cosa*x - sina*y, sina*x + cosa*y, 0.)); } } t = gts_triangle_enclosing (gts_triangle_class (), vertices, 100.); gts_triangle_vertices (t, &v1, &v2, &v3); 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)); g_timer_stop (timer); fprintf (stderr, "Input: %g s\n", g_timer_elapsed (timer, NULL)); g_timer_reset (timer); g_timer_start (timer); l = vertices; while (l) { g_assert (gts_delaunay_add_vertex (surface, l->data, NULL) == NULL); l = l->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_timer_stop (timer); fprintf (stderr, "Triangulation: %g s speed: %.0f vertex/s\n", g_timer_elapsed (timer, NULL), g_slist_length (vertices)/g_timer_elapsed (timer, NULL)); g_timer_reset (timer); g_timer_start (timer); gts_surface_write (surface, stdout); g_timer_stop (timer); fprintf (stderr, "Output: %g s\n", g_timer_elapsed (timer, NULL)); if (gts_delaunay_check (surface)) { fprintf (stderr, "WARNING: surface is not Delaunay\n"); return 0; } return 1; }
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; }
int spheroidsToSTL(const string& out, const shared_ptr<DemField>& dem, Real tol, const string& solid, int mask, bool append, bool clipCell, bool merge){ if(tol==0 || isnan(tol)) throw std::runtime_error("tol must be non-zero."); #ifndef WOO_GTS if(merge) throw std::runtime_error("woo.triangulated.spheroidsToSTL: merge=True only possible in builds with the 'gts' feature."); #endif // first traversal to find reference radius auto particleOk=[&](const shared_ptr<Particle>&p){ return (mask==0 || (p->mask & mask)) && (p->shape->isA<Sphere>() || p->shape->isA<Ellipsoid>() || p->shape->isA<Capsule>()); }; int numTri=0; if(tol<0){ LOG_DEBUG("tolerance is negative, taken as relative to minimum radius."); Real minRad=Inf; for(const auto& p: *dem->particles){ if(particleOk(p)) minRad=min(minRad,p->shape->equivRadius()); } if(isinf(minRad) || isnan(minRad)) throw std::runtime_error("Minimum radius not found (relative tolerance specified); no matching particles?"); tol=-minRad*tol; LOG_DEBUG("Minimum radius "<<minRad<<"."); } LOG_DEBUG("Triangulation tolerance is "<<tol); std::ofstream stl(out,append?(std::ofstream::app|std::ofstream::binary):std::ofstream::binary); // binary better, anyway if(!stl.good()) throw std::runtime_error("Failed to open output file "+out+" for writing."); Scene* scene=dem->scene; if(!scene) throw std::logic_error("DEM field has not associated scene?"); // periodicity, cache that for later use AlignedBox3r cell; /* wasteful memory-wise, but we need to store the whole triangulation in case *merge* is in effect, when it is only an intermediary result and will not be output as-is */ vector<vector<Vector3r>> ppts; vector<vector<Vector3i>> ttri; vector<Particle::id_t> iid; for(const auto& p: *dem->particles){ if(!particleOk(p)) continue; const auto sphere=dynamic_cast<Sphere*>(p->shape.get()); const auto ellipsoid=dynamic_cast<Ellipsoid*>(p->shape.get()); const auto capsule=dynamic_cast<Capsule*>(p->shape.get()); vector<Vector3r> pts; vector<Vector3i> tri; if(sphere || ellipsoid){ Real r=sphere?sphere->radius:ellipsoid->semiAxes.minCoeff(); // 1 is for icosahedron int tess=ceil(M_PI/(5*acos(1-tol/r))); LOG_DEBUG("Tesselation level for #"<<p->id<<": "<<tess); tess=max(tess,0); auto uSphTri(CompUtils::unitSphereTri20(/*0 for icosahedron*/max(tess-1,0))); const auto& uPts=std::get<0>(uSphTri); // unit sphere point coords pts.resize(uPts.size()); const auto& node=(p->shape->nodes[0]); Vector3r scale=(sphere?sphere->radius*Vector3r::Ones():ellipsoid->semiAxes); for(size_t i=0; i<uPts.size(); i++){ pts[i]=node->loc2glob(uPts[i].cwiseProduct(scale)); } tri=std::get<1>(uSphTri); // this makes a copy, but we need out own for capsules } if(capsule){ #ifdef WOO_VTK int subdiv=max(4.,ceil(M_PI/(acos(1-tol/capsule->radius)))); std::tie(pts,tri)=VtkExport::triangulateCapsule(static_pointer_cast<Capsule>(p->shape),subdiv); #else throw std::runtime_error("Triangulation of capsules is (for internal and entirely fixable reasons) only available when compiled with the 'vtk' features."); #endif } // do not write out directly, store first for later ppts.push_back(pts); ttri.push_back(tri); LOG_TRACE("#"<<p->id<<" triangulated: "<<tri.size()<<","<<pts.size()<<" faces,vertices."); if(scene->isPeriodic){ // make sure we have aabb, in skewed coords and such if(!p->shape->bound){ // this is a bit ugly, but should do the trick; otherwise we would recompute all that ourselves here if(sphere) Bo1_Sphere_Aabb().go(p->shape); else if(ellipsoid) Bo1_Ellipsoid_Aabb().go(p->shape); else if(capsule) Bo1_Capsule_Aabb().go(p->shape); } assert(p->shape->bound); const AlignedBox3r& box(p->shape->bound->box); AlignedBox3r cell(Vector3r::Zero(),scene->cell->getSize()); // possibly in skewed coords // central offset Vector3i off0; scene->cell->canonicalizePt(p->shape->nodes[0]->pos,off0); // computes off0 Vector3i off; // offset from the original cell //cerr<<"#"<<p->id<<" at "<<p->shape->nodes[0]->pos.transpose()<<", off0="<<off0<<endl; for(off[0]=off0[0]-1; off[0]<=off0[0]+1; off[0]++) for(off[1]=off0[1]-1; off[1]<=off0[1]+1; off[1]++) for(off[2]=off0[2]-1; off[2]<=off0[2]+1; off[2]++){ Vector3r dx=scene->cell->intrShiftPos(off); //cerr<<" off="<<off.transpose()<<", dx="<<dx.transpose()<<endl; AlignedBox3r boxOff(box); boxOff.translate(dx); //cerr<<" boxOff="<<boxOff.min()<<";"<<boxOff.max()<<" | cell="<<cell.min()<<";"<<cell.max()<<endl; if(boxOff.intersection(cell).isEmpty()) continue; // copy the entire triangulation, offset by dx vector<Vector3r> pts2(pts); for(auto& p: pts2) p+=dx; vector<Vector3i> tri2(tri); // same topology ppts.push_back(pts2); ttri.push_back(tri2); LOG_TRACE(" offset "<<off.transpose()<<": #"<<p->id<<": "<<tri2.size()<<","<<pts2.size()<<" faces,vertices."); } } } if(!merge){ LOG_DEBUG("Will export (unmerged) "<<ppts.size()<<" particles to STL."); stl<<"solid "<<solid<<"\n"; for(size_t i=0; i<ppts.size(); i++){ const auto& pts(ppts[i]); const auto& tri(ttri[i]); LOG_TRACE("Exporting "<<i<<" with "<<tri.size()<<" faces."); for(const Vector3i& t: tri){ Vector3r pp[]={pts[t[0]],pts[t[1]],pts[t[2]]}; // skip triangles which are entirely out of the canonical periodic cell if(scene->isPeriodic && clipCell && (!scene->cell->isCanonical(pp[0]) && !scene->cell->isCanonical(pp[1]) && !scene->cell->isCanonical(pp[2]))) continue; numTri++; Vector3r n=(pp[1]-pp[0]).cross(pp[2]-pp[1]).normalized(); stl<<" facet normal "<<n.x()<<" "<<n.y()<<" "<<n.z()<<"\n"; stl<<" outer loop\n"; for(auto p: {pp[0],pp[1],pp[2]}){ stl<<" vertex "<<p[0]<<" "<<p[1]<<" "<<p[2]<<"\n"; } stl<<" endloop\n"; stl<<" endfacet\n"; } } stl<<"endsolid "<<solid<<"\n"; stl.close(); return numTri; } #if WOO_GTS /***** Convert all triangulation to GTS surfaces, find their distances, isolate connected components, merge these components incrementally and write to STL *****/ // total number of points const size_t N(ppts.size()); // bounds for collision detection struct Bound{ Bound(Real _coord, int _id, bool _isMin): coord(_coord), id(_id), isMin(_isMin){}; Bound(): coord(NaN), id(-1), isMin(false){}; // just for allocation Real coord; int id; bool isMin; bool operator<(const Bound& b) const { return coord<b.coord; } }; vector<Bound> bounds[3]={vector<Bound>(2*N),vector<Bound>(2*N),vector<Bound>(2*N)}; /* construct GTS surface objects; all objects must be deleted explicitly! */ vector<GtsSurface*> ssurf(N); vector<vector<GtsVertex*>> vvert(N); vector<vector<GtsEdge*>> eedge(N); vector<AlignedBox3r> boxes(N); for(size_t i=0; i<N; i++){ LOG_TRACE("** Creating GTS surface for #"<<i<<", with "<<ttri[i].size()<<" faces, "<<ppts[i].size()<<" vertices."); AlignedBox3r box; // new surface object ssurf[i]=gts_surface_new(gts_surface_class(),gts_face_class(),gts_edge_class(),gts_vertex_class()); // copy over all vertices vvert[i].reserve(ppts[i].size()); eedge[i].reserve(size_t(1.5*ttri[i].size())); // each triangle consumes 1.5 edges, for closed surfs for(size_t v=0; v<ppts[i].size(); v++){ vvert[i].push_back(gts_vertex_new(gts_vertex_class(),ppts[i][v][0],ppts[i][v][1],ppts[i][v][2])); box.extend(ppts[i][v]); } // create faces, and create edges on the fly as needed std::map<std::pair<int,int>,int> edgeIndices; for(size_t t=0; t<ttri[i].size(); t++){ //const Vector3i& t(ttri[i][t]); //LOG_TRACE("Face with vertices "<<ttri[i][t][0]<<","<<ttri[i][t][1]<<","<<ttri[i][t][2]); Vector3i eIxs; for(int a:{0,1,2}){ int A(ttri[i][t][a]), B(ttri[i][t][(a+1)%3]); auto AB=std::make_pair(min(A,B),max(A,B)); auto ABI=edgeIndices.find(AB); if(ABI==edgeIndices.end()){ // this edge not created yet edgeIndices[AB]=eedge[i].size(); // last index eIxs[a]=eedge[i].size(); //LOG_TRACE(" New edge #"<<eIxs[a]<<": "<<A<<"--"<<B<<" (length "<<(ppts[i][A]-ppts[i][B]).norm()<<")"); eedge[i].push_back(gts_edge_new(gts_edge_class(),vvert[i][A],vvert[i][B])); } else { eIxs[a]=ABI->second; //LOG_TRACE(" Found edge #"<<ABI->second<<" for "<<A<<"--"<<B); } } //LOG_TRACE(" New face: edges "<<eIxs[0]<<"--"<<eIxs[1]<<"--"<<eIxs[2]); GtsFace* face=gts_face_new(gts_face_class(),eedge[i][eIxs[0]],eedge[i][eIxs[1]],eedge[i][eIxs[2]]); gts_surface_add_face(ssurf[i],face); } // make sure the surface is OK if(!gts_surface_is_orientable(ssurf[i])) LOG_ERROR("Surface of #"+to_string(iid[i])+" is not orientable (expect troubles)."); if(!gts_surface_is_closed(ssurf[i])) LOG_ERROR("Surface of #"+to_string(iid[i])+" is not closed (expect troubles)."); assert(!gts_surface_is_self_intersecting(ssurf[i])); // copy bounds LOG_TRACE("Setting bounds of surf #"<<i); boxes[i]=box; for(int ax:{0,1,2}){ bounds[ax][2*i+0]=Bound(box.min()[ax],/*id*/i,/*isMin*/true); bounds[ax][2*i+1]=Bound(box.max()[ax],/*id*/i,/*isMin*/false); } } /* broad-phase collision detection between GTS surfaces only those will be probed with exact algorithms below and merged if needed */ for(int ax:{0,1,2}) std::sort(bounds[ax].begin(),bounds[ax].end()); vector<Bound>& bb(bounds[0]); // run the search along x-axis, does not matter really std::list<std::pair<int,int>> int0; // broad-phase intersections for(size_t i=0; i<2*N; i++){ if(!bb[i].isMin) continue; // only start with lower bound // go up to the upper bound, but handle overflow safely (no idea why it would happen here) as well for(size_t j=i+1; j<2*N && bb[j].id!=bb[i].id; j++){ if(bb[j].isMin) continue; // this is handled by symmetry #if EIGEN_VERSION_AT_LEAST(3,2,5) if(!boxes[bb[i].id].intersects(boxes[bb[j].id])) continue; // no intersection along all axes #else // old, less elegant if(boxes[bb[i].id].intersection(boxes[bb[j].id]).isEmpty()) continue; #endif int0.push_back(std::make_pair(min(bb[i].id,bb[j].id),max(bb[i].id,bb[j].id))); LOG_TRACE("Broad-phase collision "<<int0.back().first<<"+"<<int0.back().second); } } /* narrow-phase collision detection between GTS surface this must be done via gts_surface_inter_new, since gts_surface_distance always succeeds */ std::list<std::pair<int,int>> int1; for(const std::pair<int,int> ij: int0){ LOG_TRACE("Testing narrow-phase collision "<<ij.first<<"+"<<ij.second); #if 0 GtsRange gr1, gr2; gts_surface_distance(ssurf[ij.first],ssurf[ij.second],/*delta ??*/(gfloat).2,&gr1,&gr2); if(gr1.min>0 && gr2.min>0) continue; LOG_TRACE(" GTS reports collision "<<ij.first<<"+"<<ij.second<<" (min. distances "<<gr1.min<<", "<<gr2.min); #else GtsSurface *s1(ssurf[ij.first]), *s2(ssurf[ij.second]); GNode* t1=gts_bb_tree_surface(s1); GNode* t2=gts_bb_tree_surface(s2); GtsSurfaceInter* I=gts_surface_inter_new(gts_surface_inter_class(),s1,s2,t1,t2,/*is_open_1*/false,/*is_open_2*/false); GSList* l=gts_surface_intersection(s1,s2,t1,t2); // list of edges describing intersection int n1=g_slist_length(l); // extra check by looking at number of faces of the intersected surface #if 1 GtsSurface* s12=gts_surface_new(gts_surface_class(),gts_face_class(),gts_edge_class(),gts_vertex_class()); gts_surface_inter_boolean(I,s12,GTS_1_OUT_2); gts_surface_inter_boolean(I,s12,GTS_2_OUT_1); int n2=gts_surface_face_number(s12); gts_object_destroy(GTS_OBJECT(s12)); #endif gts_bb_tree_destroy(t1,TRUE); gts_bb_tree_destroy(t2,TRUE); gts_object_destroy(GTS_OBJECT(I)); g_slist_free(l); if(n1==0) continue; #if 1 if(n2==0){ LOG_ERROR("n1==0 but n2=="<<n2<<" (no narrow-phase collision)"); continue; } #endif LOG_TRACE(" GTS reports collision "<<ij.first<<"+"<<ij.second<<" ("<<n<<" edges describe the intersection)"); #endif int1.push_back(ij); } /* connected components on the graph: graph nodes are 0…(N-1), graph edges are in int1 see http://stackoverflow.com/a/37195784/761090 */ typedef boost::subgraph<boost::adjacency_list<boost::vecS,boost::vecS,boost::undirectedS,boost::property<boost::vertex_index_t,int>,boost::property<boost::edge_index_t,int>>> Graph; Graph graph(N); for(const auto& ij: int1) boost::add_edge(ij.first,ij.second,graph); vector<size_t> clusters(boost::num_vertices(graph)); size_t numClusters=boost::connected_components(graph,clusters.data()); for(size_t n=0; n<numClusters; n++){ // beginning cluster #n // first, count how many surfaces are in this cluster; if 1, things are easier int numThisCluster=0; int cluster1st=-1; for(size_t i=0; i<N; i++){ if(clusters[i]!=n) continue; numThisCluster++; if(cluster1st<0) cluster1st=(int)i; } GtsSurface* clusterSurf=NULL; LOG_DEBUG("Cluster "<<n<<" has "<<numThisCluster<<" surfaces."); if(numThisCluster==1){ clusterSurf=ssurf[cluster1st]; } else { clusterSurf=ssurf[cluster1st]; // surface of the cluster itself LOG_TRACE(" Initial cluster surface from "<<cluster1st<<"."); /* composed surface */ for(size_t i=0; i<N; i++){ if(clusters[i]!=n || ((int)i)==cluster1st) continue; LOG_TRACE(" Adding "<<i<<" to the cluster"); // ssurf[i] now belongs to cluster #n // trees need to be rebuild every time anyway, since the merged surface keeps changing in every cycle //if(gts_surface_face_number(clusterSurf)==0) LOG_ERROR("clusterSurf has 0 faces."); //if(gts_surface_face_number(ssurf[i])==0) LOG_ERROR("Surface #"<<i<<" has 0 faces."); GNode* t1=gts_bb_tree_surface(clusterSurf); GNode* t2=gts_bb_tree_surface(ssurf[i]); GtsSurfaceInter* I=gts_surface_inter_new(gts_surface_inter_class(),clusterSurf,ssurf[i],t1,t2,/*is_open_1*/false,/*is_open_2*/false); GtsSurface* merged=gts_surface_new(gts_surface_class(),gts_face_class(),gts_edge_class(),gts_vertex_class()); gts_surface_inter_boolean(I,merged,GTS_1_OUT_2); gts_surface_inter_boolean(I,merged,GTS_2_OUT_1); gts_object_destroy(GTS_OBJECT(I)); gts_bb_tree_destroy(t1,TRUE); gts_bb_tree_destroy(t2,TRUE); if(gts_surface_face_number(merged)==0){ LOG_ERROR("Cluster #"<<n<<": 0 faces after fusing #"<<i<<" (why?), adding #"<<i<<" separately!"); // this will cause an extra 1-particle cluster to be created clusters[i]=numClusters; numClusters+=1; } else { // not from global vectors (cleanup at the end), explicit delete! if(clusterSurf!=ssurf[cluster1st]) gts_object_destroy(GTS_OBJECT(clusterSurf)); clusterSurf=merged; } } } #if 0 LOG_TRACE(" GTS surface cleanups..."); pygts_vertex_cleanup(clusterSurf,.1*tol); // cleanup 10× smaller than tolerance pygts_edge_cleanup(clusterSurf); pygts_face_cleanup(clusterSurf); #endif LOG_TRACE(" STL: cluster "<<n<<" output"); stl<<"solid "<<solid<<"_"<<n<<"\n"; /* output cluster to STL here */ _gts_face_to_stl_data data(stl,scene,clipCell,numTri); gts_surface_foreach_face(clusterSurf,(GtsFunc)_gts_face_to_stl,(gpointer)&data); stl<<"endsolid\n"; if(clusterSurf!=ssurf[cluster1st]) gts_object_destroy(GTS_OBJECT(clusterSurf)); } // this deallocates also edges and vertices for(size_t i=0; i<ssurf.size(); i++) gts_object_destroy(GTS_OBJECT(ssurf[i])); return numTri; #endif /* WOO_GTS */ }
int main (int argc, char * argv[]) { GtsSurface * surface = gts_surface_new (gts_surface_class (), gts_face_class (), gts_edge_class (), gts_vertex_class ()); GtsVertex * v1 = gts_vertex_new (gts_vertex_class (), 0, 0, 0); GtsVertex * v2 = gts_vertex_new (gts_vertex_class (), 0, -0.5, 1); GtsVertex * v3 = gts_vertex_new (gts_vertex_class (), 0, 0.5, 1); GtsVertex * v4 = gts_vertex_new (gts_vertex_class (), 1, 0, 0); GtsVertex * v5 = gts_vertex_new (gts_vertex_class (), 0, -0.5, -1); GtsVertex * v6 = gts_vertex_new (gts_vertex_class (), 1, -0.5, -1); GtsVertex * v7 = gts_vertex_new (gts_vertex_class (), 1, 0.5, -1); GtsVertex * v8 = gts_vertex_new (gts_vertex_class (), 1, -0.5, 1); GtsVertex * v9 = gts_vertex_new (gts_vertex_class (), 1, 0.5, 1); GtsVertex * v10 = gts_vertex_new (gts_vertex_class (), 0, 0.5, -1); GtsEdge * e1 = gts_edge_new (gts_edge_class (), v1, v2); GtsEdge * e2 = gts_edge_new (gts_edge_class (), v1, v3); GtsEdge * e3 = gts_edge_new (gts_edge_class (), v2, v3); GtsEdge * e4 = gts_edge_new (gts_edge_class (), v4, v5); GtsEdge * e5 = gts_edge_new (gts_edge_class (), v4, v6); GtsEdge * e6 = gts_edge_new (gts_edge_class (), v5, v6); GtsEdge * e7 = gts_edge_new (gts_edge_class (), v1, v4); GtsEdge * e8 = gts_edge_new (gts_edge_class (), v1, v5); GtsEdge * e9 = gts_edge_new (gts_edge_class (), v6, v7); GtsEdge * e10 = gts_edge_new (gts_edge_class (), v4, v7); GtsEdge * e11 = gts_edge_new (gts_edge_class (), v1, v7); GtsEdge * e12 = gts_edge_new (gts_edge_class (), v8, v9); GtsEdge * e13 = gts_edge_new (gts_edge_class (), v4, v9); GtsEdge * e14 = gts_edge_new (gts_edge_class (), v4, v8); GtsEdge * e15 = gts_edge_new (gts_edge_class (), v5, v10); GtsEdge * e16 = gts_edge_new (gts_edge_class (), v1, v10); GtsEdge * e17 = gts_edge_new (gts_edge_class (), v10, v7); GtsEdge * e18 = gts_edge_new (gts_edge_class (), v1, v8); GtsEdge * e19 = gts_edge_new (gts_edge_class (), v2, v8); GtsEdge * e20 = gts_edge_new (gts_edge_class (), v4, v3); GtsEdge * e21 = gts_edge_new (gts_edge_class (), v3, v9); GtsFace * f1 = gts_face_new (gts_face_class (), e1, e2, e3); GtsFace * f2 = gts_face_new (gts_face_class (), e4, e5, e6); GtsFace * f3 = gts_face_new (gts_face_class (), e7, e4, e8); GtsFace * f4 = gts_face_new (gts_face_class (), e9, e5, e10); GtsFace * f5 = gts_face_new (gts_face_class (), e11, e10, e7); GtsFace * f6 = gts_face_new (gts_face_class (), e12, e13, e14); GtsFace * f7 = gts_face_new (gts_face_class (), e15, e16, e8); GtsFace * f8 = gts_face_new (gts_face_class (), e17, e11, e16); GtsFace * f9 = gts_face_new (gts_face_class (), e18, e14, e7); GtsFace * f10 = gts_face_new (gts_face_class (), e19, e18, e1); GtsFace * f11 = gts_face_new (gts_face_class (), e20, e13, e21); GtsFace * f12 = gts_face_new (gts_face_class (), e7, e20, e2); GtsVertex * v; GtsVolumeOptimizedParams params = { 0.5, 0.5, 0.0 }; GtsSplit * vs; gts_surface_add_face (surface, f1); gts_surface_add_face (surface, f2); gts_surface_add_face (surface, f3); gts_surface_add_face (surface, f4); gts_surface_add_face (surface, f5); gts_surface_add_face (surface, f6); gts_surface_add_face (surface, f7); gts_surface_add_face (surface, f8); gts_surface_add_face (surface, f9); gts_surface_add_face (surface, f10); gts_surface_add_face (surface, f11); gts_surface_add_face (surface, f12); g_assert (gts_edge_collapse_is_valid (e7)); v = gts_volume_optimized_vertex (e7, gts_vertex_class (), ¶ms); vs = gts_split_new (gts_split_class (), v, GTS_OBJECT (GTS_SEGMENT (e7)->v1), GTS_OBJECT (GTS_SEGMENT (e7)->v2)); gts_split_collapse (vs, gts_edge_class (), NULL); gts_surface_write (surface, stdout); return 0; }