static void bb_tree_free (GNode * tree, gboolean free_leaves) { GNode * i; g_return_if_fail (tree != NULL); if (!free_leaves && tree->children == NULL) /* leaf node */ return; gts_object_destroy (tree->data); i = tree->children; while (i) { bb_tree_free (i, free_leaves); i = i->next; } }
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 gboolean gfs_init_stokes_wave_event (GfsEvent * event, GfsSimulation * sim) { if ((* GFS_EVENT_CLASS (GTS_OBJECT_CLASS (gfs_init_stokes_wave_class ())->parent_class)->event) (event, sim)) { GfsVariable ** velocity = gfs_domain_velocity (GFS_DOMAIN (sim)); GfsVariable * t = gfs_variable_from_name (GFS_DOMAIN (sim)->variables, "T"); g_assert (velocity); g_assert (t); gfs_domain_cell_traverse (GFS_DOMAIN (sim), FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1, (FttCellTraverseFunc) init_velocity, velocity); GfsSurface * surface = GFS_SURFACE (gts_object_new (GTS_OBJECT_CLASS (gfs_surface_class ()))); surface->f = gfs_function_spatial_new (gfs_function_spatial_class (), stokes_height); gfs_object_simulation_set (surface->f, sim); gfs_domain_init_fraction (GFS_DOMAIN (sim), GFS_GENERIC_SURFACE (surface), t); gts_object_destroy (GTS_OBJECT (surface)); return TRUE; } return FALSE; }
static void cartesian_grid_destroy (CartesianGrid * g, gboolean destroy_vertices) { g_return_if_fail (g != NULL); if (destroy_vertices) { guint i, j; gts_allow_floating_vertices = TRUE; for (i = 0; i < g->nx; i++) for (j = 0; j < g->ny; j++) if (g->vertices[i][j]) gts_object_destroy (GTS_OBJECT (g->vertices[i][j])); gts_allow_floating_vertices = FALSE; } free2D ((void **) g->vertices, g->nx); g_free (g); }
/** * gts_bb_tree_segment_distance: * @tree: a bounding box tree. * @s: a #GtsSegment. * @distance: a #GtsBBoxDistFunc. * @delta: spatial scale of the sampling to be used. * @range: a #GtsRange to be filled with the results. * * Given a segment @s, points are sampled regularly on its length * using @delta as increment. The distance from each of these points * to the closest object of @tree is computed using @distance and the * gts_bb_tree_point_distance() function. The fields of @range are * filled with the number of points sampled, the minimum, average and * maximum value and the standard deviation. */ void gts_bb_tree_segment_distance (GNode * tree, GtsSegment * s, gdouble (*distance) (GtsPoint *, gpointer), gdouble delta, GtsRange * range) { GtsPoint * p1, * p2, * p; GtsVector p1p2; gdouble l, t, dt; guint i, n; g_return_if_fail (tree != NULL); g_return_if_fail (s != NULL); g_return_if_fail (distance != NULL); g_return_if_fail (delta > 0.); g_return_if_fail (range != NULL); p1 = GTS_POINT (s->v1); p2 = GTS_POINT (s->v2); gts_vector_init (p1p2, p1, p2); gts_range_init (range); p = GTS_POINT (gts_object_new (GTS_OBJECT_CLASS (gts_point_class()))); l = sqrt (gts_vector_scalar (p1p2, p1p2)); n = (guint) (l/delta + 1); dt = 1.0/(gdouble) n; t = 0.0; for (i = 0; i <= n; i++, t += dt) { p->x = p1->x + t*p1p2[0]; p->y = p1->y + t*p1p2[1]; p->z = p1->z + t*p1p2[2]; gts_range_add_value (range, gts_bb_tree_point_distance (tree, p, distance, NULL)); } gts_object_destroy (GTS_OBJECT (p)); gts_range_update (range); }
static void 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 void recursive_bisection (GtsWGraph * wg, guint np, guint ntry, guint mmax, guint nmin, gfloat imbalance, GSList ** list) { if (np == 0) *list = g_slist_prepend (*list, wg); else { GtsGraphBisection * bg = gts_graph_bisection_new (wg, ntry, mmax, nmin, imbalance); GtsGraph * g1 = bg->g1; GtsGraph * g2 = bg->g2; gts_object_destroy (GTS_OBJECT (wg)); gts_graph_bisection_destroy (bg, FALSE); recursive_bisection (GTS_WGRAPH (g1), np - 1, ntry, mmax, nmin, imbalance, list); recursive_bisection (GTS_WGRAPH (g2), np - 1, ntry, mmax, nmin, imbalance, list); } }
/** * gts_edges_merge: * @edges: a list of #GtsEdge. * * For each edge in @edges check if it is duplicated (as * returned by gts_edge_is_duplicate()). If it is replace it by its * duplicate, destroy it and remove it from the list. * * Returns: the updated @edges list. */ GList * gts_edges_merge (GList * edges) { GList * i = edges; /* we want to control edge destruction */ gts_allow_floating_edges = TRUE; while (i) { GtsEdge * e = i->data; GtsEdge * de = gts_edge_is_duplicate (e); if (de) { GList * next = i->next; edges = g_list_remove_link (edges, i); g_list_free_1 (i); i = next; gts_edge_replace (e, de); gts_object_destroy (GTS_OBJECT (e)); } else i = i->next; } gts_allow_floating_edges = FALSE;; return edges; }
static 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); }
/** * gts_graph_bisection_new: * @wg: a #GtsWGraph. * @ntry: the number of tries for the graph growing algorithm. * @mmax: the number of unsucessful moves for the refinement algorithm. * @nmin: the minimum number of nodes of the coarsest graph. * @imbalance: the maximum relative imbalance allowed between the * weights of both halves of the partition. * * An implementation of a multilevel bisection algorithm as presented * in Karypis and Kumar (1997). A multilevel hierarchy of graphs is * created using the #GtsPGraph object. The bisection of the coarsest * graph is created using the gts_graph_ggg_bisection() function. The * graph is then uncoarsened using gts_pgraph_down() and at each level * the bisection is refined using gts_graph_bisection_bkl_refine(). * * Returns: a new #GtsGraphBisection of @wg. */ GtsGraphBisection * gts_graph_bisection_new (GtsWGraph * wg, guint ntry, guint mmax, guint nmin, gfloat imbalance) { GtsGraph * g; GtsPGraph * pg; GtsGraphBisection * bg; gdouble cost; g_return_val_if_fail (wg != NULL, NULL); g = GTS_GRAPH (wg); pg = gts_pgraph_new (gts_pgraph_class (), g, gts_gnode_split_class (), gts_wgnode_class (), gts_wgedge_class (), nmin); bg = gts_graph_ggg_bisection (g, ntry); #ifdef DEBUG fprintf (stderr, "before size: %5d weight: %5g cuts: %5d cweight: %5g\n", gts_container_size (GTS_CONTAINER (bg->g1)), gts_graph_weight (bg->g1), gts_graph_edges_cut (bg->g1), gts_graph_edges_cut_weight (bg->g1)); g_assert (gts_graph_bisection_check (bg)); #endif while ((cost = gts_graph_bisection_bkl_refine (bg, mmax, imbalance))) { #ifdef DEBUG fprintf (stderr, " cost: %g\n", cost); g_assert (gts_graph_bisection_check (bg)); #endif } #ifdef DEBUG fprintf (stderr, "after size: %5d weight: %5g cuts: %5d cweight: %5g\n", gts_container_size (GTS_CONTAINER (bg->g1)), gts_graph_weight (bg->g1), gts_graph_edges_cut (bg->g1), gts_graph_edges_cut_weight (bg->g1)); #endif while (gts_pgraph_down (pg, (GtsFunc) bisection_children, bg)) { #ifdef DEBUG fprintf (stderr, "before size: %5d weight: %5g cuts: %5d cweight: %5g\n", gts_container_size (GTS_CONTAINER (bg->g1)), gts_graph_weight (bg->g1), gts_graph_edges_cut (bg->g1), gts_graph_edges_cut_weight (bg->g1)); #endif while ((cost = gts_graph_bisection_bkl_refine (bg, mmax, imbalance))) { #ifdef DEBUG fprintf (stderr, " cost: %g\n", cost); g_assert (gts_graph_bisection_check (bg)); #endif } #ifdef DEBUG fprintf (stderr, "after size: %5d weight: %5g cuts: %5d cweight: %5g\n", gts_container_size (GTS_CONTAINER (bg->g1)), gts_graph_weight (bg->g1), gts_graph_edges_cut (bg->g1), gts_graph_edges_cut_weight (bg->g1)); #endif } gts_object_destroy (GTS_OBJECT (pg)); return bg; }
static PyObject * new_(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyObject *o; PygtsObject *obj; GtsSegment *tmp; GtsObject *segment=NULL; PyObject *v1_=NULL,*v2_=NULL; PygtsVertex *v1,*v2; guint alloc_gtsobj = TRUE; guint N; /* 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); } /* Allocate the gtsobj (if needed) */ if( alloc_gtsobj ) { /* Parse the args */ if( (N = PyTuple_Size(args)) < 2 ) { PyErr_SetString(PyExc_TypeError,"expected two Vertices"); return NULL; } v1_ = PyTuple_GET_ITEM(args,0); v2_ = PyTuple_GET_ITEM(args,1); /* Convert to PygtsObjects */ if(!pygts_vertex_check(v1_)) { PyErr_SetString(PyExc_TypeError,"expected two Vertices"); return NULL; } if(!pygts_vertex_check(v2_)) { PyErr_SetString(PyExc_TypeError,"expected two Vertices"); return NULL; } v1 = PYGTS_VERTEX(v1_); v2 = PYGTS_VERTEX(v2_); /* Error check */ if(PYGTS_OBJECT(v1)->gtsobj == PYGTS_OBJECT(v2)->gtsobj) { PyErr_SetString(PyExc_ValueError,"Vertices are identical"); return NULL; } /* Create the GtsSegment */ segment = GTS_OBJECT(gts_segment_new(gts_segment_class(), GTS_VERTEX(v1->gtsobj), GTS_VERTEX(v2->gtsobj))); if( segment == NULL ) { PyErr_SetString(PyExc_MemoryError, "could not create Segment"); return NULL; } /* Check for duplicate */ tmp = gts_segment_is_duplicate(GTS_SEGMENT(segment)); if( tmp != NULL ) { gts_object_destroy(segment); segment = GTS_OBJECT(tmp); } /* If corresponding PyObject found in object table, we are done */ if( (obj=(PygtsObject*)g_hash_table_lookup(obj_table,segment)) != NULL ) { Py_INCREF(obj); return (PyObject*)obj; } } /* Chain up */ obj = PYGTS_OBJECT(PygtsObjectType.tp_new(type,args,kwds)); if( alloc_gtsobj ) { obj->gtsobj = segment; pygts_object_register(PYGTS_OBJECT(obj)); } return (PyObject*)obj; }
/* set - compute set operations between surfaces */ int main (int argc, char * argv[]) { GtsSurface * s1, * s2, * s3; GtsSurfaceInter * si; GNode * tree1, * tree2; FILE * fptr; GtsFile * fp; int c = 0; gboolean verbose = TRUE; gboolean inter = FALSE; gboolean check_self_intersection = FALSE; gchar * operation, * file1, * file2; gboolean closed = TRUE, is_open1, is_open2; if (!setlocale (LC_ALL, "POSIX")) g_warning ("cannot set locale to POSIX"); /* parse options using getopt */ while (c != EOF) { #ifdef HAVE_GETOPT_LONG static struct option long_options[] = { {"inter", no_argument, NULL, 'i'}, {"self", no_argument, NULL, 's'}, {"help", no_argument, NULL, 'h'}, {"verbose", no_argument, NULL, 'v'} }; int option_index = 0; switch ((c = getopt_long (argc, argv, "hvis", long_options, &option_index))) { #else /* not HAVE_GETOPT_LONG */ switch ((c = getopt (argc, argv, "hvis"))) { #endif /* not HAVE_GETOPT_LONG */ case 's': /* self */ check_self_intersection = TRUE; break; case 'i': /* inter */ inter = TRUE; break; case 'v': /* verbose */ verbose = FALSE; break; case 'h': /* help */ fprintf (stderr, "Usage: set [OPTION] OPERATION FILE1 FILE2\n" "Compute set operations between surfaces, where OPERATION is either.\n" "union, inter, diff.\n" "\n" " -i --inter output an OOGL (Geomview) representation of the curve\n" " intersection of the surfaces\n" " -s --self checks that the surfaces are not self-intersecting\n" " if one of them is, the set of self-intersecting faces\n" " is written (as a GtsSurface) on standard output\n" " -v --verbose do not print statistics about the surface\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 `set --help' for more information.\n"); return 1; /* failure */ } } if (optind >= argc) { /* missing OPERATION */ fprintf (stderr, "set: missing OPERATION\n" "Try `set --help' for more information.\n"); return 1; /* failure */ } operation = argv[optind++]; if (optind >= argc) { /* missing FILE1 */ fprintf (stderr, "set: missing FILE1\n" "Try `set --help' for more information.\n"); return 1; /* failure */ } file1 = argv[optind++]; if (optind >= argc) { /* missing FILE2 */ fprintf (stderr, "set: missing FILE2\n" "Try `set --help' for more information.\n"); return 1; /* failure */ } file2 = argv[optind++]; /* open first file */ if ((fptr = fopen (file1, "rt")) == NULL) { fprintf (stderr, "set: can not open file `%s'\n", file1); return 1; } /* reads in first surface file */ s1 = GTS_SURFACE (gts_object_new (GTS_OBJECT_CLASS (gts_surface_class ()))); fp = gts_file_new (fptr); if (gts_surface_read (s1, fp)) { fprintf (stderr, "set: `%s' is not a valid GTS surface file\n", file1); fprintf (stderr, "%s:%d:%d: %s\n", file1, fp->line, fp->pos, fp->error); return 1; } gts_file_destroy (fp); fclose (fptr); /* open second file */ if ((fptr = fopen (file2, "rt")) == NULL) { fprintf (stderr, "set: can not open file `%s'\n", file2); return 1; } /* reads in second surface file */ s2 = GTS_SURFACE (gts_object_new (GTS_OBJECT_CLASS (gts_surface_class ()))); fp = gts_file_new (fptr); if (gts_surface_read (s2, fp)) { fprintf (stderr, "set: `%s' is not a valid GTS surface file\n", file2); fprintf (stderr, "%s:%d:%d: %s\n", file2, fp->line, fp->pos, fp->error); return 1; } gts_file_destroy (fp); fclose (fptr); /* display summary information about both surfaces */ if (verbose) { gts_surface_print_stats (s1, stderr); gts_surface_print_stats (s2, stderr); } /* check that the surfaces are orientable manifolds */ if (!gts_surface_is_orientable (s1)) { fprintf (stderr, "set: surface `%s' is not an orientable manifold\n", file1); return 1; } if (!gts_surface_is_orientable (s2)) { fprintf (stderr, "set: surface `%s' is not an orientable manifold\n", file2); return 1; } /* check that the surfaces are not self-intersecting */ if (check_self_intersection) { GtsSurface * self_intersects; self_intersects = gts_surface_is_self_intersecting (s1); if (self_intersects != NULL) { fprintf (stderr, "set: surface `%s' is self-intersecting\n", file1); if (verbose) gts_surface_print_stats (self_intersects, stderr); gts_surface_write (self_intersects, stdout); gts_object_destroy (GTS_OBJECT (self_intersects)); return 1; } self_intersects = gts_surface_is_self_intersecting (s2); if (self_intersects != NULL) { fprintf (stderr, "set: surface `%s' is self-intersecting\n", file2); if (verbose) gts_surface_print_stats (self_intersects, stderr); gts_surface_write (self_intersects, stdout); gts_object_destroy (GTS_OBJECT (self_intersects)); return 1; } } /* build bounding box tree for first surface */ tree1 = gts_bb_tree_surface (s1); is_open1 = gts_surface_volume (s1) < 0. ? TRUE : FALSE; /* build bounding box tree for second surface */ tree2 = gts_bb_tree_surface (s2); is_open2 = gts_surface_volume (s2) < 0. ? TRUE : FALSE; si = gts_surface_inter_new (gts_surface_inter_class (), s1, s2, tree1, tree2, is_open1, is_open2); g_assert (gts_surface_inter_check (si, &closed)); if (!closed) { fprintf (stderr, "set: the intersection of `%s' and `%s' is not a closed curve\n", file1, file2); return 1; } s3 = gts_surface_new (gts_surface_class (), gts_face_class (), gts_edge_class (), gts_vertex_class ()); if (!strcmp (operation, "union")) { gts_surface_inter_boolean (si, s3, GTS_1_OUT_2); gts_surface_inter_boolean (si, s3, GTS_2_OUT_1); } else if (!strcmp (operation, "inter")) { gts_surface_inter_boolean (si, s3, GTS_1_IN_2); gts_surface_inter_boolean (si, s3, GTS_2_IN_1); } else if (!strcmp (operation, "diff")) { gts_surface_inter_boolean (si, s3, GTS_1_OUT_2); gts_surface_inter_boolean (si, s3, GTS_2_IN_1); gts_surface_foreach_face (si->s2, (GtsFunc) gts_triangle_revert, NULL); gts_surface_foreach_face (s2, (GtsFunc) gts_triangle_revert, NULL); } else { fprintf (stderr, "set: operation `%s' unknown\n" "Try `set --help' for more information.\n", operation); return 1; } /* check that the resulting surface is not self-intersecting */ if (check_self_intersection) { GtsSurface * self_intersects; self_intersects = gts_surface_is_self_intersecting (s3); if (self_intersects != NULL) { fprintf (stderr, "set: the resulting surface is self-intersecting\n"); if (verbose) gts_surface_print_stats (self_intersects, stderr); gts_surface_write (self_intersects, stdout); gts_object_destroy (GTS_OBJECT (self_intersects)); return 1; } } /* display summary information about the resulting surface */ if (verbose) gts_surface_print_stats (s3, stderr); /* write resulting surface to standard output */ if (inter) { printf ("LIST {\n"); g_slist_foreach (si->edges, (GFunc) write_edge, stdout); printf ("}\n"); } else { GTS_POINT_CLASS (gts_vertex_class ())->binary = TRUE; gts_surface_write (s3, stdout); } /* destroy surfaces */ gts_object_destroy (GTS_OBJECT (s1)); gts_object_destroy (GTS_OBJECT (s2)); gts_object_destroy (GTS_OBJECT (s3)); gts_object_destroy (GTS_OBJECT (si)); /* destroy bounding box trees (including bounding boxes) */ gts_bb_tree_destroy (tree1, TRUE); gts_bb_tree_destroy (tree2, TRUE); return 0; }
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[]) { gboolean verbose = FALSE; gboolean triangulate_holes = TRUE; gboolean write_holes = FALSE; int c = 0; CartesianGrid * grid; guint line = 0; GtsSurface * s; GTimer * timer; if (!setlocale (LC_ALL, "POSIX")) g_warning ("cannot set locale to POSIX"); /* parse options using getopt */ while (c != EOF) { #ifdef HAVE_GETOPT_LONG static struct option long_options[] = { {"holes", no_argument, NULL, 'H'}, {"keep", no_argument, NULL, 'k'}, {"help", no_argument, NULL, 'h'}, {"verbose", no_argument, NULL, 'v'}, { NULL } }; int option_index = 0; switch ((c = getopt_long (argc, argv, "hvHk", long_options, &option_index))) { #else /* not HAVE_GETOPT_LONG */ switch ((c = getopt (argc, argv, "hvHk"))) { #endif /* not HAVE_GETOPT_LONG */ case 'H': /* holes */ write_holes = TRUE; break; case 'k': /* keep */ triangulate_holes = FALSE; break; case 'v': /* verbose */ verbose = TRUE; break; case 'h': /* help */ fprintf (stderr, "Usage: cartesian [OPTION] < FILE\n" "Triangulates vertices of a regular cartesian mesh\n" "(possibly containing holes)\n" "\n" " -k --keep keep holes\n" " -H --holes write holes only\n" " -v --verbose print statistics about the surface\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 `cartesian --help' for more information.\n"); return 1; /* failure */ } } grid = cartesian_grid_read (gts_vertex_class (), &line); if (grid == NULL) { fprintf (stderr, "cartesian: file on standard input is not a valid cartesian grid\n" "error at line %u\n", line); return 1; } timer = g_timer_new (); g_timer_start (timer); s = gts_surface_new (gts_surface_class (), gts_face_class (), gts_edge_class (), gts_vertex_class ()); cartesian_grid_triangulate (grid, s); if (triangulate_holes) { GtsSurface * holes = cartesian_grid_triangulate_holes (grid, s); if (write_holes) { gts_object_destroy (GTS_OBJECT (s)); s = holes; } else gts_surface_merge (s, holes); } g_timer_stop (timer); if (verbose) { gts_surface_print_stats (s, stderr); fprintf (stderr, "# Triangulation time: %g s speed: %.0f vertex/s\n", g_timer_elapsed (timer, NULL), gts_surface_vertex_number (s)/g_timer_elapsed (timer, NULL)); } gts_surface_write (s, stdout); return 0; /* success */ }
/** * gts_graph_bfgg_bisection: * @g: a #GtsGraph. * @ntry: the number of randomly selected initial seeds. * * An implementation of a "Breadth-First Graph Growing" algorithm. * * @ntry randomly chosen seeds are used and the best partition is retained. * * Returns: a new #GtsGraphBisection of @g. */ GtsGraphBisection * gts_graph_bfgg_bisection (GtsGraph * g, guint ntry) { gfloat size, bestcost = G_MAXFLOAT, smin; GtsGraph * bestg1 = NULL, * bestg2 = NULL; GtsEHeap * degree_heap; GtsGNode * seed; GtsGraphBisection * bg; g_return_val_if_fail (g != NULL, NULL); bg = g_malloc (sizeof (GtsGraphBisection)); bg->g = g; size = gts_graph_weight (g)/2.; smin = 0.9*size; degree_heap = gts_eheap_new ((GtsKeyFunc) degree_cost, g); gts_eheap_freeze (degree_heap); gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) add_seed, degree_heap); gts_eheap_thaw (degree_heap); while (ntry && ((seed = gts_eheap_remove_top (degree_heap, NULL)))) { GtsGraph * g1, * g2; GtsGNode * n; gdouble cost; GtsGraphTraverse * t = gts_graph_traverse_new (g, seed, GTS_BREADTH_FIRST, TRUE); g1 = gts_graph_new (GTS_GRAPH_CLASS (GTS_OBJECT (g)->klass), g->node_class, g->edge_class); g2 = gts_graph_new (GTS_GRAPH_CLASS (GTS_OBJECT (g)->klass), g->node_class, g->edge_class); while ((n = gts_graph_traverse_next (t))) if (gts_graph_weight (g1) + gts_gnode_weight (n) <= size) { gts_container_add (GTS_CONTAINER (g1), GTS_CONTAINEE (n)); GTS_OBJECT (n)->reserved = n; } gts_graph_traverse_destroy (t); gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) add_unused, g2); cost = gts_graph_edges_cut_weight (g1); if (!bestg1 || (cost < bestcost && gts_graph_weight (g1) >= smin)) { if (bestg1) bestcost = cost; if (bestg1) gts_object_destroy (GTS_OBJECT (bestg1)); if (bestg2) gts_object_destroy (GTS_OBJECT (bestg2)); bestg1 = g1; bestg2 = g2; } else { gts_object_destroy (GTS_OBJECT (g1)); gts_object_destroy (GTS_OBJECT (g2)); } ntry--; } gts_eheap_destroy (degree_heap); #ifdef DEBUG fprintf (stderr, "bestcost: %5g g1: %5g|%5d g2: %5g|%5d\n", bestcost, gts_graph_weight (bestg1), gts_container_size (GTS_CONTAINER (bestg1)), gts_graph_weight (bestg2), gts_container_size (GTS_CONTAINER (bestg2))); #endif bg->g1 = bestg1; bg->g2 = bestg2; /* boundary nodes */ bg->bg1 = g_hash_table_new (NULL, NULL); gts_container_foreach (GTS_CONTAINER (bg->g1), (GtsFunc) boundary_node1, bg); bg->bg2 = g_hash_table_new (NULL, NULL); gts_container_foreach (GTS_CONTAINER (bg->g2), (GtsFunc) boundary_node2, bg); return bg; }
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"); }
/** * gts_hsurface_new: * @klass: a #GtsHSurfaceClass. * @hsplit_class: a #GtsHSplitClass. * @psurface: a #GtsPSurface. * @expand_key: a #GtsKeyFunc used to order the priority heap of expandable * #GtsHSplit. * @expand_data: data to be passed to @expand_key. * @collapse_key: a #GtsKeyFunc used to order the priority heap of collapsable * #GtsHSplit. * @collapse_data: data to be passed to @collapsed_key. * * Returns: a new #GtsHSurface, hierarchical extension of @psurface * and using #GtsHSplit of class @hsplit_class. Note that @psurface is * destroyed in the process. */ GtsHSurface * gts_hsurface_new (GtsHSurfaceClass * klass, GtsHSplitClass * hsplit_class, GtsPSurface * psurface, GtsKeyFunc expand_key, gpointer expand_data, GtsKeyFunc collapse_key, gpointer collapse_data) { GtsHSurface * hsurface; g_return_val_if_fail (klass != NULL, NULL); g_return_val_if_fail (hsplit_class != NULL, NULL); g_return_val_if_fail (psurface != NULL, NULL); g_return_val_if_fail (expand_key != NULL, NULL); g_return_val_if_fail (collapse_key != NULL, NULL); hsurface = GTS_HSURFACE (gts_object_new (GTS_OBJECT_CLASS (klass))); hsurface->s = psurface->s; hsurface->expandable = gts_eheap_new (expand_key, expand_data); hsurface->collapsable = gts_eheap_new (collapse_key, collapse_data); g_ptr_array_set_size (hsurface->split, psurface->split->len); while (gts_psurface_remove_vertex (psurface)) ; while (psurface->pos) { GtsSplit * vs = g_ptr_array_index (psurface->split, psurface->pos - 1); GtsHSplit * hs = gts_hsplit_new (hsplit_class, vs); g_ptr_array_index (hsurface->split, psurface->pos - 1) = hs; psurface->pos--; hs->parent = GTS_OBJECT (vs)->reserved; if (hs->parent) { GtsSplit * vsp = GTS_SPLIT (hs->parent); if (vsp->v1 == GTS_OBJECT (vs)) { g_assert (vsp->v2 != GTS_OBJECT (vs)); vsp->v1 = GTS_OBJECT (hs); } else { g_assert (vsp->v2 == GTS_OBJECT (vs)); vsp->v2 = GTS_OBJECT (hs); } } else hsurface->roots = g_slist_prepend (hsurface->roots, hs); hs->nchild = 0; if (GTS_IS_SPLIT (vs->v1)) GTS_OBJECT (vs->v1)->reserved = hs; else hs->nchild++; if (GTS_IS_SPLIT (vs->v2)) GTS_OBJECT (vs->v2)->reserved = hs; else hs->nchild++; gts_split_expand (vs, psurface->s, psurface->s->edge_class); if (hs->nchild == 2) HEAP_INSERT_HSPLIT (hsurface->collapsable, hs); } hsurface->nvertex = gts_surface_vertex_number (hsurface->s); gts_object_destroy (GTS_OBJECT (psurface)); return hsurface; }
/* inside - check if points are inside a surface */ int main (int argc, char * argv[]) { GtsSurface * s1; GNode * tree1; GtsPoint P; FILE * fptr; GtsFile * fp; int c = 0, cnt = 0; double x,y,z; gboolean verbose = FALSE; gchar * file1, * file2; gboolean is_open1, is_inside; if (!setlocale (LC_ALL, "POSIX")) g_warning ("cannot set locale to POSIX"); /* parse options using getopt */ while (c != EOF) { #ifdef HAVE_GETOPT_LONG static struct option long_options[] = { {"help", no_argument, NULL, 'h'}, {"verbose", no_argument, NULL, 'v'} }; int option_index = 0; switch ((c = getopt_long (argc, argv, "shv", long_options, &option_index))) { #else /* not HAVE_GETOPT_LONG */ switch ((c = getopt (argc, argv, "shv"))) { #endif /* not HAVE_GETOPT_LONG */ case 'v': /* verbose */ verbose = TRUE; break; case 'h': /* help */ fprintf (stderr, "Usage: gtsinside [OPTION] FILE1 FILE2\n" "Test whether points are inside a closed surface.\n" "FILE1 is a surface file. FILE2 is a text file where each line\n" "contains the three coordinates of a point, separated with blanks.\n" "\n" " -v --verbose print statistics about the surface\n" " -h --help display this help and exit\n" "\n" "Reports bugs to %s\n", "https://savannah.nongnu.org/projects/pyformex/"); return 0; /* success */ break; case '?': /* wrong options */ fprintf (stderr, "Try `gtsinside --help' for more information.\n"); return 1; /* failure */ } } if (optind >= argc) { /* missing FILE1 */ fprintf (stderr, "gtsinside: missing FILE1\n" "Try `inside --help' for more information.\n"); return 1; /* failure */ } file1 = argv[optind++]; if (optind >= argc) { /* missing FILE2 */ fprintf (stderr, "gtsinside: missing FILE2\n" "Try `gtsinside --help' for more information.\n"); return 1; /* failure */ } file2 = argv[optind++]; /* open first file */ if ((fptr = fopen (file1, "rt")) == NULL) { fprintf (stderr, "gtsinside: can not open file `%s'\n", file1); return 1; } /* reads in first surface file */ s1 = GTS_SURFACE (gts_object_new (GTS_OBJECT_CLASS (gts_surface_class ()))); fp = gts_file_new (fptr); if (gts_surface_read (s1, fp)) { fprintf (stderr, "gtsinside: `%s' is not a valid GTS surface file\n", file1); fprintf (stderr, "%s:%d:%d: %s\n", file1, fp->line, fp->pos, fp->error); return 1; } gts_file_destroy (fp); fclose (fptr); /* open second file */ if ((fptr = fopen (file2, "rt")) == NULL) { fprintf (stderr, "gtsinside: can not open file `%s'\n", file2); return 1; } /* display summary information about the surface */ if (verbose) { gts_surface_print_stats (s1, stderr); } /* check that the surface is an orientable manifold */ if (!gts_surface_is_orientable (s1)) { fprintf (stderr, "gtsinside: surface `%s' is not an orientable manifold\n", file1); return 1; } /* build bounding box tree for the surface */ tree1 = gts_bb_tree_surface (s1); is_open1 = gts_surface_volume (s1) < 0. ? TRUE : FALSE; /* scan file 2 for points and determine if it they are inside surface */ while ( fscanf(fptr, "%lg %lg %lg", &x, &y, &z) == 3 ) { P.x = x; P.y = y; P.z = z; is_inside = gts_point_is_inside_surface(&P,tree1,is_open1); if (is_inside) printf("%d\n",cnt); //printf("Point %d: %lf, %lf, %lf: %d\n",cnt,P.x,P.y,P.z,is_inside); cnt++; } if ( !feof(fptr) ) { fprintf (stderr, "gtsinside: error while reading points from file `%s'\n", file2); return 1; } fclose (fptr); /* destroy surface */ gts_object_destroy (GTS_OBJECT (s1)); /* destroy bounding box tree (including bounding boxes) */ gts_bb_tree_destroy (tree1, TRUE); return 0; }
static void gfs_refine_destroy (GtsObject * o) { gts_object_destroy (GTS_OBJECT (GFS_REFINE (o)->maxlevel)); (* GTS_OBJECT_CLASS (gfs_refine_class ())->parent_class->destroy) (o); }
/** * gts_graph_bubble_partition: * @g: a #GtsGraph. * @np: number of partitions. * @niter: the maximum number of iterations. * @step_info: a #GtsFunc or %NULL. * @data: user data to pass to @step_info. * * An implementation of the "bubble partitioning algorithm" of * Diekmann, Preis, Schlimbach and Walshaw (2000). The maximum number * of iteration on the positions of the graph growing seeds is * controlled by @niter. * * If not %NULL @step_info is called after each iteration on the seeds * positions passing the partition (a GSList) as argument. * * Returns: a list of @np new #GtsGraph representing the partition. */ GSList * gts_graph_bubble_partition (GtsGraph * g, guint np, guint niter, GtsFunc step_info, gpointer data) { GSList * list = NULL, * seeds = NULL; GtsGNode * seed = NULL; guint min = G_MAXINT/2 - 1; gpointer info[3]; GtsGraph * g1; gboolean changed = TRUE; g_return_val_if_fail (g != NULL, NULL); g_return_val_if_fail (np > 0, NULL); info[0] = &seed; info[1] = g; info[2] = &min; gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) find_smallest_degree, info); if (seed == NULL) return NULL; g1 = GTS_GRAPH (gts_object_new (GTS_OBJECT (g)->klass)); gts_container_add (GTS_CONTAINER (g1), GTS_CONTAINEE (seed)); list = g_slist_prepend (list, g1); GTS_OBJECT (g1)->reserved = seed; seeds = g_slist_prepend (seeds, seed); while (--np && seed) if ((seed = gts_graph_farthest (g, seeds))) { g1 = GTS_GRAPH (gts_object_new (GTS_OBJECT (g)->klass)); gts_container_add (GTS_CONTAINER (g1), GTS_CONTAINEE (seed)); list = g_slist_prepend (list, g1); GTS_OBJECT (g1)->reserved = seed; seeds = g_slist_prepend (seeds, seed); } g_slist_free (seeds); partition_update (list, g); while (changed && niter--) { GSList * i; changed = FALSE; i = list; while (i) { GtsGraph * g1 = i->data; GtsGNode * seed = GTS_OBJECT (g1)->reserved; GtsGNode * new_seed = graph_new_seed (g1, seed); if (new_seed != seed) { changed = TRUE; GTS_OBJECT (g1)->reserved = new_seed; } i = i->next; } if (changed) { i = list; while (i) { GtsGraph * g1 = i->data; GtsGNode * seed = GTS_OBJECT (g1)->reserved; gts_object_destroy (GTS_OBJECT (g1)); i->data = g1 = GTS_GRAPH (gts_object_new (GTS_OBJECT (g)->klass)); gts_container_add (GTS_CONTAINER (g1), GTS_CONTAINEE (seed)); GTS_OBJECT (g1)->reserved = seed; i = i->next; } partition_update (list, g); if (step_info) (* step_info) (list, data); } } g_slist_foreach (list, (GFunc) gts_object_reset_reserved, NULL); return list; }
/** * gts_graph_ggg_bisection: * @g: a #GtsGraph. * @ntry: the number of randomly selected initial seeds. * * An implementation of the "Greedy Graph Growing" algorithm of * Karypis and Kumar (1997). * * @ntry randomly chosen seeds are used and the best partition is retained. * * Returns: a new #GtsGraphBisection of @g. */ GtsGraphBisection * gts_graph_ggg_bisection (GtsGraph * g, guint ntry) { gfloat size, bestcost = G_MAXFLOAT, smin; GtsGraph * bestg1 = NULL, * bestg2 = NULL; gboolean balanced = FALSE; GtsEHeap * degree_heap; GtsGNode * seed; GtsGraphBisection * bg; g_return_val_if_fail (g != NULL, NULL); bg = g_malloc (sizeof (GtsGraphBisection)); bg->g = g; size = gts_graph_weight (g)/2.; smin = 0.9*size; degree_heap = gts_eheap_new ((GtsKeyFunc) degree_cost, g); gts_eheap_freeze (degree_heap); gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) add_seed, degree_heap); gts_eheap_thaw (degree_heap); while (ntry && ((seed = gts_eheap_remove_top (degree_heap, NULL)))) { GtsGraph * g1, * g2; GtsGNode * n; gdouble cost; gpointer data[2]; GtsEHeap * heap; g1 = gts_graph_new (GTS_GRAPH_CLASS (GTS_OBJECT (g)->klass), g->node_class, g->edge_class); g2 = gts_graph_new (GTS_GRAPH_CLASS (GTS_OBJECT (g)->klass), g->node_class, g->edge_class); data[0] = g; data[1] = g1; heap = gts_eheap_new ((GtsKeyFunc) node_cost, data); gts_container_add (GTS_CONTAINER (g1), GTS_CONTAINEE (seed)); GTS_OBJECT (seed)->reserved = seed; gts_gnode_foreach_neighbor (seed, g, (GtsFunc) add_neighbor, heap); while ((n = gts_eheap_remove_top (heap, &cost))) if (gts_graph_weight (g1) + gts_gnode_weight (n) <= size) { gts_container_add (GTS_CONTAINER (g1), GTS_CONTAINEE (n)); GTS_OBJECT (n)->reserved = n; gts_gnode_foreach_neighbor (n, g, (GtsFunc) add_neighbor, heap); } else GTS_OBJECT (n)->reserved = NULL; gts_eheap_destroy (heap); gts_container_foreach (GTS_CONTAINER (g), (GtsFunc) add_unused, g2); cost = gts_graph_edges_cut_weight (g1); if (!bestg1 || (!balanced && gts_graph_weight (g1) >= smin) || (cost < bestcost && gts_graph_weight (g1) >= smin)) { if (bestg1) bestcost = cost; if (bestg1) gts_object_destroy (GTS_OBJECT (bestg1)); if (bestg2) gts_object_destroy (GTS_OBJECT (bestg2)); bestg1 = g1; bestg2 = g2; if (gts_graph_weight (g1) >= smin) balanced = TRUE; } else { gts_object_destroy (GTS_OBJECT (g1)); gts_object_destroy (GTS_OBJECT (g2)); } ntry--; } gts_eheap_destroy (degree_heap); #ifdef DEBUG fprintf (stderr, "bestcost: %5g g1: %5g|%5d g2: %5g|%5d\n", bestcost, gts_graph_weight (bestg1), gts_container_size (GTS_CONTAINER (bestg1)), gts_graph_weight (bestg2), gts_container_size (GTS_CONTAINER (bestg2))); #endif g_assert (bestg1 != NULL); bg->g1 = bestg1; g_assert (bestg2 != NULL); bg->g2 = bestg2; /* boundary nodes */ bg->bg1 = g_hash_table_new (NULL, NULL); gts_container_foreach (GTS_CONTAINER (bg->g1), (GtsFunc) boundary_node1, bg); bg->bg2 = g_hash_table_new (NULL, NULL); gts_container_foreach (GTS_CONTAINER (bg->g2), (GtsFunc) boundary_node2, bg); return bg; }
extern field_t* field_read_gfs(const char* file) { int argc = 0; gfs_init(&argc,NULL); FILE *st = fopen(file,"r"); if (!st) { fprintf(stderr,"failed to open %s\n",file); return NULL; } GtsFile *fp = gts_file_new(st); GfsSimulation *sim = gfs_simulation_read(fp); if (!sim) { fprintf(stderr, "file %s not a valid simulation file\n" "line %d:%d: %s\n", file, fp->line, fp->pos, fp->error); return NULL; } gts_file_destroy(fp); fclose(st); GfsDomain *gdom = GFS_DOMAIN(sim); /* find the bounding box */ bbox_t *bb = NULL; gfs_domain_cell_traverse(gdom, FTT_PRE_ORDER, FTT_TRAVERSE_NON_LEAFS, 0, (FttCellTraverseFunc)ftt_bbox, &bb); if (!bb) { fprintf(stderr,"failed to determine bounding box\n"); return NULL; } #ifdef FRG_DEBUG fprintf(stdout, "%g %g %g %g\n", bb->x.min, bb->x.max, bb->y.min, bb->y.max); #endif /* tree depth and discretisation size */ int depth = gfs_domain_depth(gdom), nw = (int)(POW2(depth)*bbox_width(*bb)), nh = (int)(POW2(depth)*bbox_height(*bb)); /* shave bbox for node-aligned rather than pixel */ double shave = bbox_width(*bb)/(2.0*nw); bb->x.min += shave; bb->x.max -= shave; bb->y.min += shave; bb->y.max -= shave; /* create & intialise meshes */ bilinear_t* B[2]; int i; for (i=0 ; i<2 ; i++) { if ((B[i] = bilinear_new()) == NULL) return NULL; if (bilinear_dimension(nw,nh,*bb,B[i]) != ERROR_OK) return NULL; } ftts_t ftts; ftts.B = B; ftts.depth = depth; if ((ftts.u = gfs_variable_from_name(gdom->variables,"U")) == NULL) { fprintf(stderr,"no variable U\n"); return NULL; } if ((ftts.v = gfs_variable_from_name(gdom->variables,"V")) == NULL) { fprintf(stderr,"no variable V\n"); return NULL; } gfs_domain_cell_traverse(gdom, FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1, (FttCellTraverseFunc)ftt_sample, &ftts); /* clean up */ free(bb); gts_object_destroy(GTS_OBJECT(sim)); /* results */ field_t* F = malloc(sizeof(field_t)); if (!F) return NULL; F->u = B[0]; F->v = B[1]; F->k = NULL; return F; }
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; }
void pygts_edge_cleanup(GtsSurface *s) { GSList *edges = NULL; GSList *i, *ii, *cur, *parents=NULL; PygtsEdge *edge; GtsEdge *e, *duplicate; g_return_if_fail(s != NULL); /* build list of edges */ gts_surface_foreach_edge(s, (GtsFunc)build_list, &edges); /* remove degenerate and duplicate edges. Note: we could use gts_edges_merge() to remove the duplicates and then remove the degenerate edges but it is more efficient to do everything at once (and it's more pedagogical too ...) */ /* We want to control manually the destruction of edges */ gts_allow_floating_edges = TRUE; i = edges; while(i) { e = (GtsEdge*)i->data; if(GTS_SEGMENT(e)->v1 == GTS_SEGMENT(e)->v2) { /* edge is degenerate */ if( !g_hash_table_lookup(obj_table,GTS_OBJECT(e)) ) { /* destroy e */ gts_object_destroy(GTS_OBJECT(e)); } } else { if((duplicate = gts_edge_is_duplicate(e))) { /* Detach and save any parent triangles */ if( (edge = PYGTS_EDGE(g_hash_table_lookup(obj_table,GTS_OBJECT(e)))) !=NULL ) { ii = e->triangles; while(ii!=NULL) { cur = ii; ii = g_slist_next(ii); if(PYGTS_IS_PARENT_TRIANGLE(cur->data)) { e->triangles = g_slist_remove_link(e->triangles, cur); parents = g_slist_prepend(parents,cur->data); g_slist_free_1(cur); } } } /* replace e with its duplicate */ gts_edge_replace(e, duplicate); /* Reattach the parent segments */ if( edge != NULL ) { ii = parents; while(ii!=NULL) { e->triangles = g_slist_prepend(e->triangles, ii->data); ii = g_slist_next(ii); } g_slist_free(parents); parents = NULL; } if( !g_hash_table_lookup(obj_table,GTS_OBJECT(e)) ) { /* destroy e */ gts_object_destroy(GTS_OBJECT (e)); } } } i = g_slist_next(i); } /* don't forget to reset to default */ gts_allow_floating_edges = FALSE; /* free list of edges */ g_slist_free (edges); }
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; } }
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 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; }
/* This function is modified from the original in GTS in order to avoid * deallocating any objects referenced by the live-objects table. The * approach is similar to what is used for replace() in vertex.c. */ GList* pygts_vertices_merge(GList* vertices, gdouble epsilon, gboolean (* check) (GtsVertex *, GtsVertex *)) { GPtrArray *array; GList *i, *next; GNode *kdtree; GtsVertex *v; GtsBBox *bbox; GSList *selected, *j; GtsVertex *sv; PygtsObject *obj; PygtsVertex *vertex=NULL; GSList *parents=NULL, *ii,*cur; g_return_val_if_fail(vertices != NULL, 0); array = g_ptr_array_new(); i = vertices; while (i) { g_ptr_array_add(array, i->data); i = g_list_next(i); } kdtree = gts_kdtree_new(array, NULL); g_ptr_array_free(array, TRUE); i = vertices; while(i) { v = (GtsVertex*)i->data; if (!GTS_OBJECT(v)->reserved) { /* Do something only if v is active */ /* build bounding box */ bbox = gts_bbox_new(gts_bbox_class(), v, GTS_POINT(v)->x - epsilon, GTS_POINT(v)->y - epsilon, GTS_POINT(v)->z - epsilon, GTS_POINT(v)->x + epsilon, GTS_POINT(v)->y + epsilon, GTS_POINT(v)->z + epsilon); /* select vertices which are inside bbox using kdtree */ j = selected = gts_kdtree_range(kdtree, bbox, NULL); while(j) { sv = (GtsVertex*)j->data; if( sv!=v && !GTS_OBJECT(sv)->reserved && (!check||(*check)(sv, v)) ) { /* sv is not v and is active */ if( (obj = (PygtsObject*)g_hash_table_lookup(obj_table,GTS_OBJECT(sv))) !=NULL ) { vertex = PYGTS_VERTEX(obj); /* Detach and save any parent segments */ ii = sv->segments; while(ii!=NULL) { cur = ii; ii = g_slist_next(ii); if(PYGTS_IS_PARENT_SEGMENT(cur->data)) { sv->segments = g_slist_remove_link(sv->segments, cur); parents = g_slist_prepend(parents,cur->data); g_slist_free_1(cur); } } } gts_vertex_replace(sv, v); GTS_OBJECT(sv)->reserved = sv; /* mark sv as inactive */ /* Reattach the parent segments */ if( vertex != NULL ) { ii = parents; while(ii!=NULL) { sv->segments = g_slist_prepend(sv->segments, ii->data); ii = g_slist_next(ii); } g_slist_free(parents); parents = NULL; } vertex = NULL; } j = g_slist_next(j); } g_slist_free(selected); gts_object_destroy(GTS_OBJECT(bbox)); } i = g_list_next(i); } gts_kdtree_destroy(kdtree); /* destroy inactive vertices and removes them from list */ /* we want to control vertex destruction */ gts_allow_floating_vertices = TRUE; i = vertices; while (i) { v = (GtsVertex*)i->data; next = g_list_next(i); if(GTS_OBJECT(v)->reserved) { /* v is inactive */ if( g_hash_table_lookup(obj_table,GTS_OBJECT(v))==NULL ) { gts_object_destroy(GTS_OBJECT(v)); } else { GTS_OBJECT(v)->reserved = 0; } vertices = g_list_remove_link(vertices, i); g_list_free_1(i); } i = next; } gts_allow_floating_vertices = FALSE; return vertices; }
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; }