static void gts_triangulate_test_stuff() { gdouble v[4][3] = { {0.0, 0.0, 0.0}, {0.0, 3.0, 0.0}, {3.0, 3.0, 0.0}, {1.0, 1.0, 0.0} }; int i; GtsVertex *vtx[4]; GCList *polygon = NULL; GtsVector orient; GCList *p; for (i = 0; i < 4; ++i) { vtx[i] = gts_vertex_new(gts_vertex_class(), v[i][0],v[i][1],v[i][2]); polygon = g_clist_append(polygon, vtx[i]); } g_assert(g_clist_length(polygon)); g_assert(gts_polygon_orientation(polygon, orient)); g_assert(orient[0] == 0 && orient[1] == 0 && orient[2] < 0); p = g_clist_next(polygon); g_assert(is_convex(GTS_POINT(p->prev->data), GTS_POINT(p->data),GTS_POINT(p->next->data), orient)); g_assert(is_inside(GTS_POINT(vtx[0]), GTS_POINT(vtx[1]), GTS_POINT(vtx[2]), GTS_POINT(vtx[3]))); g_assert(!is_inside(GTS_POINT(vtx[0]), GTS_POINT(vtx[1]), GTS_POINT(vtx[3]), GTS_POINT(vtx[2]))); g_clist_free(p); for (i = 0; i < 4; ++i) gts_object_destroy(GTS_OBJECT(vtx[i])); }
static void prepend (GtsListFace * f, gray ** g, guint i, guint j) { f->points = g_slist_prepend (f->points, gts_vertex_new (gts_vertex_class (), i, j, g[j][i])); }
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)); }
void gts_triangulate_convex_polygon_test() { int i; gdouble v[5][3] = { {0.0, 0.0, 1.0}, {0.0, 2.0, 2.0}, {2.0, 3.0, 1.0}, {4.0, 1.0, 1.0}, {2.0, 0.0, 0.0} }; GtsEdgePool *pool = gts_edge_pool_new(gts_edge_pool_class()); GtsVertex *vtx[5]; GCList *polygon = NULL; GCList *wired = NULL; GtsSurfaceStats sstats; for (i = 0; i < 5; ++i) { vtx[i] = gts_vertex_new(gts_vertex_class(), v[i][0],v[i][1],v[i][2]); polygon = g_clist_append(polygon, vtx[i]); } polygon = g_clist_append(polygon, vtx[0]); polygon = g_clist_append(polygon, vtx[1]); wired = g_clist_append(wired, vtx[0]); wired = g_clist_append(wired, vtx[1]); wired = g_clist_append(wired, vtx[0]); wired = g_clist_append(wired, vtx[2]); GtsSurface *s = gts_surface_new(gts_surface_class(), gts_face_class(), gts_edge_class(), gts_vertex_class()); gts_triangulate_convex_polygon(s, pool, wired); g_assert(gts_surface_face_number(s) == 0); gts_triangulate_convex_polygon(s, pool, polygon); g_message("gts_surface_face_number = %d", gts_surface_face_number(s)); g_assert(gts_surface_face_number(s) == 3); gts_surface_stats(s, &sstats); g_assert(sstats.n_incompatible_faces == 0); g_assert(sstats.n_duplicate_faces == 0); g_assert(sstats.n_duplicate_edges == 0); g_assert(sstats.n_boundary_edges == 5); g_assert(sstats.n_non_manifold_edges == 0); g_assert(sstats.faces_per_edge.min == 1); g_assert(sstats.faces_per_edge.max == 2); g_message("Triangulate convex polygon PASS"); gts_object_destroy(GTS_OBJECT(pool)); }
GtsVertex* get_vertex(const k3d::uint_t Vertex) { if(!gts_vertices[Vertex]) { const k3d::point3 mesh_point = (*mesh_points)[Vertex]; gts_vertices[Vertex] = gts_vertex_new(gts_vertex_class(), mesh_point[0], mesh_point[1], mesh_point[2]); } return gts_vertices[Vertex]; }
/** * 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_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); }
/* the file format is the classic GTS file format but only the vertex and * edge sections are read. */ static guint read_list (GPtrArray * vertices, GtsFifo * constraints, GtsEdgeClass * edge_class, FILE * fptr) { guint nv, ne, nt, i; guint line = 1; g_return_val_if_fail (vertices != NULL, 1); g_return_val_if_fail (constraints != NULL, 1); g_return_val_if_fail (edge_class != NULL, 1); g_return_val_if_fail (fptr != NULL, 1); if (fscanf (fptr, "%u %u %u", &nv, &ne, &nt) != 3) return line; line++; g_ptr_array_set_size (vertices, nv); i = 1; while (i <= nv) { gdouble x, y, z; if (fscanf (fptr, "%lf %lf %lf", &x, &y, &z) != 3) return line; line++; g_ptr_array_index (vertices, i++ - 1) = gts_vertex_new (gts_vertex_class (), x, y, z); } i = 1; while (i <= ne) { guint iv1, iv2; if (fscanf (fptr, "%u %u", &iv1, &iv2) != 2 || iv1 <= 0 || iv1 > nv || iv2 <= 0 || iv2 > nv) return line; line++; gts_fifo_push (constraints, gts_edge_new (edge_class, g_ptr_array_index (vertices, iv1 - 1), g_ptr_array_index (vertices, iv2 - 1))); i++; } return 0; }
static PyObject * new_(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyObject *o; PygtsObject *obj; guint alloc_gtsobj = TRUE; /* Parse the args */ if(kwds) { o = PyDict_GetItemString(kwds,"alloc_gtsobj"); if(o==Py_False) { alloc_gtsobj = FALSE; } if(o!=NULL) { PyDict_DelItemString(kwds, "alloc_gtsobj"); } } if(kwds) { Py_INCREF(Py_False); PyDict_SetItemString(kwds,"alloc_gtsobj", Py_False); } /* Chain up */ obj = PYGTS_OBJECT(PygtsPointType.tp_new(type,args,kwds)); /* Allocate the gtsobj (if needed) */ if( alloc_gtsobj ) { obj->gtsobj = GTS_OBJECT(gts_vertex_new(gts_vertex_class(),0,0,0)); if( obj->gtsobj == NULL ) { PyErr_SetString(PyExc_MemoryError, "could not create Vertex"); return NULL; } /* Create the parent GtsSegment */ if( (obj->gtsobj_parent=parent(GTS_VERTEX(obj->gtsobj))) == NULL ) { gts_object_destroy(obj->gtsobj); obj->gtsobj = NULL; return NULL; } pygts_object_register(obj); } return (PyObject*)obj; }
static CartesianGrid * cartesian_grid_read (GtsVertexClass * klass, guint * line) { CartesianGrid * grid; guint nx, ny, line_number = 1, i, j; g_return_val_if_fail (klass != NULL, NULL); if (scanf ("%u %u", &nx, &ny) != 2) { if (line) *line = line_number; return NULL; } line_number++; grid = cartesian_grid_new (nx, ny); for (i = 0; i < nx; i++) for (j = 0; j < ny; j++) { gdouble x, y, z; if (scanf ("%lf %lf %lf", &x, &y, &z) != 3) { cartesian_grid_destroy (grid, TRUE); if (line) *line = line_number; return NULL; } if (z != 0.) grid->vertices[i][j] = gts_vertex_new (klass, x, y, z); if (x > grid->xmax) grid->xmax = x; if (x < grid->xmin) grid->xmin = x; if (y > grid->ymax) grid->ymax = y; if (y < grid->ymin) grid->ymin = y; line_number++; } return grid; }
static GtsObject * parent(GtsVertex *v1) { GtsPoint *p1; GtsVertex *v2; GtsSegment *p; /* Create another Vertex */ p1 = GTS_POINT(v1); if( (v2 = gts_vertex_new(pygts_parent_vertex_class(),p1->x,p1->y,p1->z+1)) == NULL ) { PyErr_SetString(PyExc_MemoryError, "could not create parent"); return NULL; } /* Create and return the parent */ if( (p = gts_segment_new(pygts_parent_segment_class(),v1,v2)) == NULL ) { PyErr_SetString(PyExc_MemoryError, "could not create parent"); gts_object_destroy(GTS_OBJECT(v2)); return NULL; } return GTS_OBJECT(p); }
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; }
void gts_polygon_triangulate_test() { int i; gdouble v[12][3] = { {0.0, 0.0, 0.0}, {0.0, 3.0, 0.0}, {3.0, 3.0, 0.0}, {3.0, 1.0, 0.0}, {4.0, 1.0, 0.0}, {4.0, 3.0, 0.0}, {5.0, 3.0, 0.0}, {5.0, 0.0, 0.0}, {2.0, 0.0, 0.0}, {2.0, 2.0, 0.0}, {1.0, 2.0, 0.0}, {1.0, 0.0, 0.0}, }; GtsVector normal = {0,0,0}; GtsVertex *vtx[12]; GCList *polygon = NULL; GCList *triangle = NULL; GtsSurface *s, *s2; GtsEdgePool *pool = gts_edge_pool_new(gts_edge_pool_class()); gts_triangulate_test_stuff(); for (i = 0; i < 12; ++i) { vtx[i] = gts_vertex_new(gts_vertex_class(), v[i][0],v[i][1],v[i][2]); polygon = g_clist_append(polygon, vtx[i]); } for (i = 0; i < 3; ++i) { triangle = g_clist_prepend(triangle, vtx[i]); } s = gts_surface_new(gts_surface_class(), gts_face_class(), gts_edge_class(), gts_vertex_class()); s2 = gts_surface_new(gts_surface_class(), gts_face_class(), gts_edge_class(), gts_vertex_class()); // triangulate the polygon by using its own orientation polygon = gts_surface_add_polygon(s, pool, polygon, normal); // do some test ? g_assert(gts_surface_face_number(s) == 10); g_debug("area = %f", gts_surface_area(s)); g_assert(gts_surface_area(s) == 11.0); gts_object_destroy(GTS_OBJECT(pool)); // cleanup g_clist_free(triangle); g_clist_free(polygon); gts_object_destroy(GTS_OBJECT(s)); gts_object_destroy(GTS_OBJECT(s2)); g_message("Triangulate PASSED"); }
PygtsVertex * pygts_vertex_from_sequence(PyObject *tuple) { guint i,N; gdouble x=0,y=0,z=0; PyObject *obj; GtsVertex *v; PygtsVertex *vertex; /* Convert list into tuple */ if(PyList_Check(tuple)) { tuple = PyList_AsTuple(tuple); } else { Py_INCREF(tuple); } if(!PyTuple_Check(tuple)) { Py_DECREF(tuple); PyErr_SetString(PyExc_TypeError,"expected a list or tuple of vertices"); return NULL; } /* Get the tuple size */ if( (N = PyTuple_Size(tuple)) > 3 ) { PyErr_SetString(PyExc_RuntimeError, "expected a list or tuple of up to three floats"); Py_DECREF(tuple); return NULL; } /* Get the coordinates */ for(i=0;i<N;i++) { obj = PyTuple_GET_ITEM(tuple,i); if(!PyFloat_Check(obj) && !PyLong_Check(obj)) { PyErr_SetString(PyExc_TypeError,"expected a list or tuple of floats"); Py_DECREF(tuple); return NULL; } if(i==0) { if(PyFloat_Check(obj)) x = PyFloat_AsDouble(obj); else x = (double)PyLong_AsLong(obj); } if(i==1) { if(PyFloat_Check(obj)) y = PyFloat_AsDouble(obj); else y = (double)PyLong_AsLong(obj); } if(i==2) { if(PyFloat_Check(obj)) z = PyFloat_AsDouble(obj); else z = (double)PyLong_AsLong(obj); } } Py_DECREF(tuple); /* Create the vertex */ if( (v = gts_vertex_new(gts_vertex_class(),x,y,z)) == NULL ) { PyErr_SetString(PyExc_MemoryError,"could not create Vertex"); } if( (vertex = pygts_vertex_new(v)) == NULL ) { gts_object_destroy(GTS_OBJECT(v)); return NULL; } return vertex; }
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; }
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; }
void add_vertex(const k3d::point3& Coordinates, k3d::uint_t Vertices[4], k3d::uint_t Edges[4], double Weights[4], k3d::uint_t& NewVertex) { NewVertex = gts_vertices.size(); gts_vertices.push_back(gts_vertex_new(gts_vertex_class(), Coordinates[0], Coordinates[1], Coordinates[2])); }
int main (int argc, char * argv[]) { int c = 0; gboolean verbose = FALSE; GtsSurface * s; CostFunc cost_func = (CostFunc) height_cost; gpointer cost_data = NULL; GtsStopFunc stop_func = NULL; gpointer stop_data = NULL; StopOptions stop = NUMBER; guint number = 0; gdouble cmin = 0.0; gboolean logcost = FALSE; gboolean flat = FALSE; gdouble relative = 0.; gboolean keep_enclosing = FALSE; gboolean closed = FALSE; if (!setlocale (LC_ALL, "POSIX")) g_warning ("cannot set locale to POSIX"); while (c != EOF) { #ifdef HAVE_GETOPT_LONG static struct option long_options[] = { {"closed", no_argument, NULL, 'C'}, {"keep", no_argument, NULL, 'k'}, {"relative", required_argument, NULL, 'r'}, {"flat", no_argument, NULL, 'f'}, {"log", no_argument, NULL, 'l'}, {"number", required_argument, NULL, 'n'}, {"cost", required_argument, NULL, 'c'}, {"help", no_argument, NULL, 'h'}, {"verbose", no_argument, NULL, 'v'}, { NULL } }; int option_index = 0; switch ((c = getopt_long (argc, argv, "hvn:c:lfr:kC", long_options, &option_index))) { #else /* not HAVE_GETOPT_LONG */ switch ((c = getopt (argc, argv, "hvn:c:lfr:kC"))) { #endif /* not HAVE_GETOPT_LONG */ case 'C': /* closed */ closed = TRUE; keep_enclosing = TRUE; break; case 'k': /* keep */ keep_enclosing = TRUE; break; case 'r': /* relative */ cost_func = (CostFunc) relative_height_cost; relative = strtod (optarg, NULL); cost_data = &relative; if (relative <= 0.) { fprintf (stderr, "happrox: argument for -r option must be strictly positive\n"); return 1; } break; case 'f': /* flat file */ flat = TRUE; break; case 'l': /* log cost */ logcost = TRUE; break; case 'n': /* stop by number */ stop = NUMBER; number = strtol (optarg, NULL, 0); break; case 'c': /* stop by cost */ stop = COST; cmin = strtod (optarg, NULL); break; case 'h': /* help */ fprintf (stderr, "Usage: happrox [OPTION]... < [input.pgm|input] > output.gts\n" "Returns a simplified triangulation of a set of points using\n" "algorithm III of Garland and Heckbert (1995).\n" "\n" " -n N, --number=N stop the refinement process if the number of\n" " vertices is larger than N\n" " -c C, --cost=C stop the refinement process if the cost of insertion\n" " of a vertex is smaller than C\n" " -f --flat input is a flat file with three x,y,z columns\n" " (default is PGM file)\n" " -r Z --relative=Z use relative height cost for all heights larger than Z\n" " -k --keep keep enclosing triangle\n" " -C --closed close the surface\n" " -l --log log evolution of cost\n" " -v, --verbose display surface statistics\n" " -h, --help display this help and exit\n" "\n" "Report bugs to %s\n", GTS_MAINTAINER); return 0; break; case 'v': verbose = TRUE; break; case '?': /* wrong options */ fprintf (stderr, "Try `happrox --help' for more information.\n"); return 1; } } switch (stop) { case NUMBER: if (verbose) stop_func = (GtsStopFunc) stop_number_verbose; else stop_func = (GtsStopFunc) stop_number; if (logcost) stop_func = (GtsStopFunc) stop_number_log_cost; stop_data = &number; break; case COST: if (verbose) stop_func = (GtsStopFunc) stop_cost_verbose; else stop_func = (GtsStopFunc) stop_cost; stop_data = &cmin; break; default: g_assert_not_reached (); } if (flat) { GSList * points = NULL; guint n = 0; gdouble x, y, z; while (scanf ("%lf %lf %lf", &x, &y, &z) == 3) { points = g_slist_prepend (points, gts_vertex_new (gts_vertex_class (), x, y, z)); n++; } if (verbose) fprintf (stderr, "happrox: %d vertices\n", n); s = happrox_list (points, keep_enclosing, closed, cost_func, cost_data, stop_func, stop_data); } else { gint width, height; gray ** g, maxgray; g = pgm_readpgm (stdin, &width, &height, &maxgray); if (verbose) fprintf (stderr, "happrox: width: %d height: %d maxgray: %d\n", width, height, maxgray); s = happrox (g, width, height, cost_func, cost_data, stop_func, stop_data); } if (verbose) { fputc ('\n', stderr); gts_surface_print_stats (s, stderr); } gts_surface_write (s, stdout); return 0; }
/* 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; }