├── src ├── read_obj.h ├── trimesh.h ├── make_signed_distance.h ├── optimize_tet_mesh.h ├── sdf.h ├── match_features.h ├── make_tet_mesh.h ├── geometry_queries.h ├── tet_quality.h ├── trimesh.cpp ├── tet_quality.cpp ├── read_obj.cpp ├── tet_mesh.h ├── sdf.cpp ├── geometry_queries.cpp ├── feature.h ├── gluvi.h ├── array3.h ├── make_signed_distance.cpp ├── main.cpp ├── util.h ├── vec.h ├── optimize_tet_mesh.cpp └── view_tet.cpp ├── meshes ├── cube.obj ├── cube.feat ├── joint.feat ├── sphere.obj ├── joint.obj ├── block.feat └── fandisk.feat ├── .gitignore ├── LICENSE ├── Makefile.defs ├── TODO ├── Makefile └── README /src/read_obj.h: -------------------------------------------------------------------------------- 1 | #ifndef READ_OBJ_H 2 | #define READ_OBJ_H 3 | 4 | #include 5 | #include "vec.h" 6 | 7 | bool 8 | read_objfile(std::vector& x, 9 | std::vector& tri, 10 | const char *filename_format, ...); 11 | 12 | bool 13 | write_objfile(const std::vector& x, 14 | const std::vector& tri, 15 | const char *filename_format, ...); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /meshes/cube.obj: -------------------------------------------------------------------------------- 1 | v 0 0 0 2 | v 0 0 1 3 | v 0 1 0 4 | v 0 1 1 5 | v 1 0 0 6 | v 1 0 1 7 | v 1 1 0 8 | v 1 1 1 9 | vt 0 0 10 | vt 0 0 11 | vt 0 0 12 | vt 0 0 13 | vt 0 0 14 | vt 0 0 15 | vt 0 0 16 | vt 0 0 17 | f 3/1 4/2 8/3 18 | f 3/1 1/4 2/5 19 | f 4/2 2/5 6/6 20 | f 8/3 6/6 5/7 21 | f 2/5 1/4 5/7 22 | f 1/4 3/1 7/8 23 | f 8/3 7/8 3/1 24 | f 2/5 4/2 3/1 25 | f 6/6 8/3 4/2 26 | f 5/7 7/8 8/3 27 | f 5/7 6/6 2/5 28 | f 7/8 5/7 1/4 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | #*.obj 6 | obj/* 7 | obj_debug/* 8 | 9 | # Precompiled Headers 10 | *.gch 11 | *.pch 12 | 13 | # Compiled Dynamic libraries 14 | *.so 15 | *.dylib 16 | *.dll 17 | 18 | # Fortran module files 19 | *.mod 20 | 21 | # Compiled Static libraries 22 | *.lai 23 | *.la 24 | *.a 25 | *.lib 26 | 27 | # Executables 28 | *.exe 29 | *.out 30 | *.app 31 | quartet 32 | quartet_release 33 | view_tet 34 | view_tet_release 35 | 36 | -------------------------------------------------------------------------------- /src/trimesh.h: -------------------------------------------------------------------------------- 1 | #ifndef TRIMESH_H 2 | #define TRIMESH_H 3 | 4 | #include "vec.h" 5 | #include 6 | 7 | 8 | class TriMesh 9 | { 10 | public: 11 | struct HalfEdge; 12 | 13 | struct Vertex 14 | { 15 | Vec3f p; 16 | HalfEdge* e; 17 | }; 18 | 19 | struct Face 20 | { 21 | HalfEdge* e; 22 | }; 23 | 24 | struct HalfEdge 25 | { 26 | Vertex* v; 27 | Face* f; 28 | HalfEdge* prev, *next; 29 | HalfEdge* sym; 30 | }; 31 | 32 | std::vector vertices; 33 | std::vector faces; 34 | std::vector edges; 35 | 36 | TriMesh(const std::vector& x, 37 | const std::vector& tri); 38 | }; 39 | 40 | #endif 41 | 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, crawforddoran <> 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | -------------------------------------------------------------------------------- /src/make_signed_distance.h: -------------------------------------------------------------------------------- 1 | #ifndef MAKE_SIGNED_DISTANCE_H 2 | #define MAKE_SIGNED_DISTANCE_H 3 | 4 | #include "sdf.h" 5 | #include "vec.h" 6 | 7 | // Create a signed distance field for a water-tight triangle mesh (absolute 8 | // distances will be pretty good even for arbitrary triangle soup, but 9 | // correct signs rely on having a closed mesh). 10 | // Input tri is a list of triangles in the mesh, and x is the positions of the 11 | // vertices; 12 | // Variable sdf should already be initialized with the desired origin, grid 13 | // size and grid dimensions. 14 | // Its values will be set up with the signed distance field. 15 | void 16 | make_signed_distance(const std::vector &tri, 17 | const std::vector &x, 18 | SDF &sdf); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /meshes/cube.feat: -------------------------------------------------------------------------------- 1 | feat 12 2 | 2 3 | 0.000000 0.000000 0.000000 4 | 1.000000 0.000000 0.000000 5 | 2 6 | 0.000000 0.000000 0.000000 7 | 0.000000 0.000000 1.000000 8 | 2 9 | 0.000000 0.000000 0.000000 10 | 0.000000 1.000000 0.000000 11 | 2 12 | 0.000000 0.000000 1.000000 13 | 1.000000 0.000000 1.000000 14 | 2 15 | 0.000000 0.000000 1.000000 16 | 0.000000 1.000000 1.000000 17 | 2 18 | 0.000000 1.000000 0.000000 19 | 0.000000 1.000000 1.000000 20 | 2 21 | 0.000000 1.000000 0.000000 22 | 1.000000 1.000000 0.000000 23 | 2 24 | 0.000000 1.000000 1.000000 25 | 1.000000 1.000000 1.000000 26 | 2 27 | 1.000000 0.000000 0.000000 28 | 1.000000 1.000000 0.000000 29 | 2 30 | 1.000000 0.000000 0.000000 31 | 1.000000 0.000000 1.000000 32 | 2 33 | 1.000000 0.000000 1.000000 34 | 1.000000 1.000000 1.000000 35 | 2 36 | 1.000000 1.000000 0.000000 37 | 1.000000 1.000000 1.000000 38 | -------------------------------------------------------------------------------- /Makefile.defs: -------------------------------------------------------------------------------- 1 | # This file is included into the main Makefile, providing it with 2 | # local machine settings. 3 | # Most of it is platform-specific compilers, flags, and libraries. 4 | # See below examples for which variables should be defined. 5 | 6 | # For example, on a Linux PC this will likely work: 7 | DEPEND = g++ 8 | CC = g++ -Wall 9 | RELEASE_FLAGS = -O3 -DNDEBUG -funroll-loops 10 | DEBUG_FLAGS = -g 11 | LINK = g++ 12 | LINK_LIBS = -lm 13 | VIEWER_LIBS = -lGL -lGLU -lglut 14 | 15 | # Should work for Mingw (with freeglut installed) 16 | #DEPEND = g++ 17 | #CC = g++ -Wall 18 | #RELEASE_FLAGS = -O3 -DNDEBUG -funroll-loops 19 | #DEBUG_FLAGS = -g 20 | #LINK = g++ 21 | #LINK_LIBS = -lm 22 | #VIEWER_LIBS = -lopengl32 -lglu32 -lfreeglut 23 | 24 | # On Mac OS X (on a G5), this probably will work: 25 | #DEPEND = g++ 26 | #CC = g++ -Wall 27 | #RELEASE_FLAGS = -DNDEBUG -fast # add -mcpu=7450 for a G4, or -mcpu=750 for a G3 since -fast enables 970(G5) instructions by default 28 | #DEBUG_FLAGS = -g 29 | #LINK = g++ 30 | #LINK_LIBS = -lm -lobjc 31 | #VIEWER_LIBS = -framework OpenGL -framework GLUT 32 | -------------------------------------------------------------------------------- /src/optimize_tet_mesh.h: -------------------------------------------------------------------------------- 1 | #ifndef OPTIMIZE_TET_MESH_H 2 | #define OPTIMIZE_TET_MESH_H 3 | 4 | #include "tet_mesh.h" 5 | #include "sdf.h" 6 | #include "feature.h" 7 | #include 8 | #include 9 | 10 | 11 | // Optimize tetrahedral mesh to improve tet quality. 12 | // mesh - the tetrahedral mesh to optimize 13 | // sdf - the signed distance field representing the object the tet mesh 14 | // should reproduce 15 | // boundary_verts - the indices of the vertices on the boundary of the 16 | // tet mesh 17 | // featureSet - The set of features that the tet mesh is constrained to match 18 | // feature_endpoints - the indices of the vertices constrained to feature 19 | // endpoints. These vertices will not be moved. 20 | // feature_edge_verts - maps vertex indices to the feature to which they 21 | // they should be constrained 22 | float 23 | optimize_tet_mesh(TetMesh& mesh, 24 | const SDF& sdf, 25 | const std::vector& boundary_verts, 26 | const FeatureSet& featureSet, 27 | const std::vector& feature_endpoints, 28 | const std::map& vertex_feature_map); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /src/sdf.h: -------------------------------------------------------------------------------- 1 | #ifndef SDF_H 2 | #define SDF_H 3 | 4 | #include "array3.h" 5 | #include "vec.h" 6 | 7 | 8 | // 9 | // Signed Distance Field 10 | // 11 | // Scalar field storing the shortest distance to an implicit surface 12 | // (which is therefore found at the zero-isosurface). 13 | // The field has a negative sign inside the surface and positive sign outside. 14 | // The scalar field phi is stored as a 3-dimensional array of values, and 15 | // trilinear interpolation is provided for arbitrary spatial lookups. 16 | // 17 | struct SDF 18 | { 19 | Array3f phi; 20 | const Vec3f origin; 21 | const float dx; 22 | 23 | // Constructor 24 | // origin is the origin of the 3D array. 25 | // dx is the grid spacing in 3D space 26 | // ni*nj*nk are the dimensions of the grid. 27 | SDF(const Vec3f& origin, 28 | const float dx, 29 | int ni, int nj, int nk); 30 | 31 | // Evaluate the scalar field at the given point using 32 | // trilinear interpolation. 33 | float operator() (const Vec3f& x) const; 34 | 35 | // Compute the gradient vector of the field at the given point. 36 | Vec3f gradient(const Vec3f& x) const; 37 | 38 | // Compute the "normal" vector of the field at the given point. 39 | // This is equivalent to the normalized gradient vector. 40 | Vec3f normal(const Vec3f& x) const; 41 | 42 | // Project the given point x onto the zero-isosurface of the field. 43 | // Note that this is only accurate if x is already near the isosurface. 44 | Vec3f projectToIsosurface(const Vec3f& x) const; 45 | 46 | private: 47 | const float over_dx; // 1/dx 48 | }; 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /src/match_features.h: -------------------------------------------------------------------------------- 1 | #ifndef MATCH_FEATURES_H 2 | #define MATCH_FEATURES_H 3 | 4 | #include "tet_mesh.h" 5 | #include "feature.h" 6 | #include "vec.h" 7 | #include 8 | #include 9 | 10 | 11 | // Modify the given TetMesh to match the features in the given FeatureSet. 12 | // mesh - the TetMesh to be modified 13 | // featureSet - the set of features to be matched by the mesh 14 | // dx - the resolution of the mesh. Features below this resolution are not 15 | // guaranteed to be matched accurately. 16 | // boundary_verts - indices of the boundary vertices of the mesh 17 | // boundary_tris - triangles of the boundary mesh, stored as indices into the 18 | // vertices of the mesh 19 | // feature_endpoints - on exit, stores the indices of the mesh vertices that 20 | // are now constrained to feature endpoints. 21 | // vertex_feature_map - on exit, stores a mapping of vertex indices to the 22 | // indices of the features to which they are now 23 | // constrained 24 | // unsafe - if true, allow feature matching operations that might invert 25 | // tetrahedra 26 | // Returns false iff the endpoints of the features could not be resolved, 27 | // indicating algorithm failure. 28 | bool 29 | match_features(TetMesh& mesh, 30 | FeatureSet& featureSet, 31 | float dx, 32 | const std::vector& boundary_verts, 33 | const std::vector& boundary_tris, 34 | std::vector& feature_endpoints, 35 | std::map& vertex_feature_map, 36 | bool unsafe = false); 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /src/make_tet_mesh.h: -------------------------------------------------------------------------------- 1 | #ifndef MAKE_TET_MESH_H 2 | #define MAKE_TET_MESH_H 3 | 4 | #include "sdf.h" 5 | #include "tet_mesh.h" 6 | #include "feature.h" 7 | 8 | // From a signed distance function sdf, create a tetrahedral mesh of 9 | // its interior. 10 | // We use an isosurface stuffing approach starting from an acute lattice. 11 | // mesh - The tetrahedral mesh to be constructed. 12 | // sdf - The signed distance field from which the mesh will be built 13 | // optimize - if true, the mesh will be optimized after construction 14 | // outputIntermediateMeshes - if true, the mesh will be output at various 15 | // intermediate stages of the mesh generation 16 | // process. 17 | void 18 | make_tet_mesh(TetMesh& mesh, 19 | const SDF& sdf, 20 | bool optimize = true, 21 | bool outputIntermediateMeshes = false, 22 | bool unsafeFeatureMatching = false); 23 | 24 | 25 | // Create tetrahedral mesh as above that also incorporates the 26 | // sharp features found in the given featureSet. 27 | // sdf - The signed distance field from which the mesh will be built 28 | // mesh - The tetrahedral mesh to be constructed. 29 | // featureSet - A set of features (points and edges) that will be 30 | // matched as closely as possible by the constructed tet mesh. 31 | // optimize - if true, the mesh will be optimized after construction 32 | // outputIntermediateMeshes - if true, the mesh will be output at various 33 | // intermediate stages of the mesh generation 34 | // process. 35 | void 36 | make_tet_mesh(TetMesh& mesh, 37 | const SDF& sdf, 38 | FeatureSet& featureSet, 39 | bool optimize = true, 40 | bool outputIntermediateMeshes = false, 41 | bool unsafeFeatureMatching = false); 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /src/geometry_queries.h: -------------------------------------------------------------------------------- 1 | #ifndef GEOMETRY_QUERIES_H 2 | #define GEOMETRY_QUERIES_H 3 | 4 | #include "vec.h" 5 | 6 | 7 | // Initializes values needed for exact arithmetic. 8 | // Call this before calling orientation predicates! 9 | double initialize_exact(); 10 | 11 | // find distance x0 is from segment x1-x2 12 | float 13 | point_segment_distance(const Vec3f &x0, 14 | const Vec3f &x1, 15 | const Vec3f &x2); 16 | 17 | // find distance x0 is from triangle x1-x2-x3 18 | float 19 | point_triangle_distance(const Vec3f &x0, 20 | const Vec3f &x1, 21 | const Vec3f &x2, 22 | const Vec3f &x3); 23 | 24 | // Calculate twice signed area of triangle (0,0)-(x1,y1)-(x2,y2) and return an 25 | // SOS-determined sign (-1, +1, or 0 only if it's a truly degenerate triangle) 26 | int 27 | orientation(double x1, double y1, 28 | double x2, double y2, 29 | double &twice_signed_area); 30 | 31 | // Robust test of (x0,y0) in the triangle (x1,y1)-(x2,y2)-(x3,y3), with SOS 32 | // determination of edge and corner cases. 33 | // If true is returned, the barycentric coordinates are set in a,b,c; 34 | // if false is returned, they could be partially overwritten - do not trust. 35 | bool point_in_triangle_2d(double x0, double y0, 36 | double x1, double y1, 37 | double x2, double y2, 38 | double x3, double y3, 39 | double& a, double& b, double& c); 40 | 41 | 42 | // Calculate six times the signed area of the tetrahedron 43 | // (a_x,a_y,a_z)-(b_x,b_y,b_z)-(c_x,c_y,c_z)-(0,0,0) 44 | // and return an SOS-determined sign 45 | // (-1, +1, or 0 only if the tet is truly degenerate). 46 | int orientation3D(double a_x, double a_y, double a_z, 47 | double b_x, double b_y, double b_z, 48 | double c_x, double c_y, double c_z, 49 | double d_x, double d_y, double d_z, 50 | double &six_signed_volume); 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /src/tet_quality.h: -------------------------------------------------------------------------------- 1 | #ifndef TET_QUALITY_H 2 | #define TET_QUALITY_H 3 | 4 | #include "tet_mesh.h" 5 | 6 | // 7 | // Implementations of different quality metric for tetrahedra. 8 | // All quality metrics should conform to the following: 9 | // - Equilateral tetrahedra should have a quality measure of 1.0, which is 10 | // the maximum (ie: quality measure should be normalized). 11 | // - Degenerate tetrahedra should have a quality measure of zero 12 | // - Inverted tetrahedra should have a negative quality measure. 13 | // 14 | // Ideally these measures should also be scale-invariant. 15 | // 16 | // This gets its own file (ie: is not part of the Tet or TetMesh classes) 17 | // because we want to be able to use these quality metrics interchangeably. 18 | // One user might want to optimize based on max dihedral angle, whereas 19 | // another might want to use aspect ratio. 20 | // 21 | // So ideally, client code should be able to choose which quality function 22 | // (or maybe function object/functor in the future) they want to use. 23 | // Then any code that calls compute_tet_quality will use the chosen 24 | // quality function without needing to know about it. 25 | // 26 | 27 | // 28 | // Quality metrics 29 | // 30 | 31 | // Largest dihedral angle 32 | float 33 | max_dihedral_angle(const Tet& tet); 34 | 35 | // Minimum sine of dihedral angle 36 | float 37 | min_sine_dihedral_angle(const Tet& tet); 38 | 39 | // Smallest face angle 40 | float 41 | min_face_angle(const Tet& tet); 42 | 43 | // Aspect ratio is the longest edge length divided by the shortest altitude 44 | float 45 | aspect_ratio(const Tet& tet); 46 | 47 | 48 | // 49 | // Abstract quality metric function. 50 | // 51 | 52 | // Compute a quality metric for the given tetrahedron. 53 | // The quality metric has an optimal (normalized) value of 1.0. 54 | // Degenerate tets have a quality value of 0.0, and inverted tets have 55 | // a negative quality value. 56 | inline float 57 | compute_tet_quality(const Tet& tet) { 58 | return aspect_ratio(tet); 59 | 60 | // TODO: Appropriate design pattern to allow plugging in 61 | // different metrics as desired. 62 | } 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /src/trimesh.cpp: -------------------------------------------------------------------------------- 1 | #include "trimesh.h" 2 | 3 | #include 4 | 5 | 6 | TriMesh::TriMesh(const std::vector& x, 7 | const std::vector& tri) 8 | : vertices(x.size()), faces(tri.size()), edges(tri.size()*3) 9 | { 10 | for (size_t v = 0; v < vertices.size(); ++v) 11 | { 12 | vertices[v].p = x[v]; 13 | } 14 | 15 | // For keeping track of edges that we haven't paired off yet. 16 | std::map,int> unmatchedEdges; 17 | std::map,int>::iterator edgeItr; 18 | 19 | for (size_t f = 0; f < faces.size(); ++f) 20 | { 21 | const Vec3i& t = tri[f]; 22 | HalfEdge* e = &(edges[3*f]); 23 | 24 | for (int i = 0; i < 3; ++i) 25 | { 26 | // Associate vertex and edge. 27 | e[i].v = &(vertices[t[i]]); 28 | e[i].v->e = &(e[i]); 29 | 30 | // Associate edge with face. 31 | e[i].f = &(faces[f]); 32 | 33 | // Link up edges. 34 | e[i].prev = &(e[(i+2)%3]); 35 | e[i].next = &(e[(i+1)%3]); 36 | 37 | // Match symmetric edges 38 | std::pair tempEdge(t[i], t[(i+1)%3]); 39 | if (tempEdge.first > tempEdge.second) 40 | { 41 | // Lower vertex index always comes first. 42 | tempEdge.first = tempEdge.second; 43 | tempEdge.second = t[i]; 44 | } 45 | edgeItr = unmatchedEdges.find(tempEdge); 46 | if (edgeItr == unmatchedEdges.end()) 47 | { 48 | // The pair to this edge has not been found yet. 49 | // Save the index of the current half-edge for later. 50 | unmatchedEdges[tempEdge] = 3*f+i; 51 | } 52 | else 53 | { 54 | // The pair was already found, pair it with the current edge. 55 | e[i].sym = &(edges[edgeItr->second]); 56 | e[i].sym->sym = &(e[i]); 57 | unmatchedEdges.erase(edgeItr); 58 | } 59 | } 60 | 61 | // Associate face with the first edge. 62 | e[0].f->e = &(e[0]); 63 | } 64 | } 65 | 66 | 67 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | - A better build setup (eg: cmake) 2 | 3 | - Iteratively alternate between optimization and progressive feature matching 4 | 5 | - Alternative feature matching strategies 6 | - Move neighbors when snapping vertex (ie: energy/spring model, 7 | displacement kernel). 8 | - Snap lattice vertices to features before doing isosurface stuffing. 9 | - OR Snap lattice vertices to feature endpoints (not full features) 10 | before doing isosurface stuffing. 11 | 12 | - Ensure optimality of path search 13 | - Right now not guaranteed to be optimal because the tet inversion test 14 | depends on the path taken to a particular vertex, not just the edges 15 | and weights of the graph. 16 | 17 | - Use tet quality metrics to choose optimal paths during edge snapping 18 | (instead of just minimizing the distance that vertices are moved). 19 | - This may require Bellman-Ford (for negative edge weights), because 20 | it's possible that snapping a vertex could actually improve the 21 | tet that it's a part of (our distance-minimizing algorithm 22 | assumes that moving a vertex will always make the tet worse). 23 | 24 | - Improve mesh data structure efficiency 25 | - ie: adjacency list for triangle mesh, O(1) neighbor lookup, improve 26 | efficiency of graph search in match_features 27 | - Efficient TriMesh class created (half-edge data structures), 28 | but it isn't applicable to the mesh we've been using that 29 | indexes into the vertices of a TetMesh. 30 | 31 | 32 | Less important stuff: 33 | - Better tet-mesh data structures 34 | - More efficient traversal of edges, triangles. 35 | 36 | - Tet Quality measures 37 | - Design pattern for plugging in arbitrary quality measure? 38 | 39 | - FeatureSet::consolidate() 40 | 41 | - Allow topology changes when matching features. 42 | - If unable to find path between two vertices separated by two boundary 43 | triangles, do an "edge split" or "edge flip" on the edge separating 44 | them. 45 | 46 | - Curvature for automatic feature detection to detect feature points. 47 | 48 | - Additional misc optimizations 49 | 50 | - Handle more mesh file formats 51 | - .OFF (input, triangles) 52 | - .ELE (output, tets) 53 | 54 | -------------------------------------------------------------------------------- /src/tet_quality.cpp: -------------------------------------------------------------------------------- 1 | #include "tet_quality.h" 2 | 3 | 4 | static const float SQRT2 = sqrt(2.0); 5 | static const float PI_3_INV = 3.0/M_PI; 6 | static const float OPT_DIHEDRAL = asin(2.0*sqrt(2.0)/3.0); 7 | 8 | 9 | float 10 | max_dihedral_angle(const Tet& tet) 11 | { 12 | int orient = tet.orientation(); 13 | 14 | if (orient == 0) 15 | { 16 | return 0.0; 17 | } 18 | 19 | std::vector angles; 20 | tet.dihedralAngles(angles); 21 | float maxAngle = angles[0]; 22 | for (size_t i = 1; i < angles.size(); ++i) 23 | { 24 | if (angles[i] > maxAngle) 25 | { 26 | maxAngle = angles[i]; 27 | } 28 | } 29 | 30 | // maxAngle will be between OPT_DIHEDRAL and M_PI. 31 | // Normalize to the range (0,1) and flip so that 0 corresponds to M_PI 32 | // and 1 corresponds to OPT_DIHEDRAL. 33 | float quality = 1.0 - (maxAngle-OPT_DIHEDRAL)/(M_PI-OPT_DIHEDRAL); 34 | 35 | if (orient < 0) 36 | { 37 | quality *= -1.0; 38 | } 39 | 40 | return quality; 41 | } 42 | 43 | 44 | float 45 | min_sine_dihedral_angle(const Tet& tet) 46 | { 47 | int orient = tet.orientation(); 48 | if (orient == 0) 49 | { 50 | return 0.0; 51 | } 52 | 53 | std::vector angles; 54 | tet.dihedralAngles(angles); 55 | float minSin = sin(angles[0]); 56 | for (size_t i = 1; i < angles.size(); ++i) 57 | { 58 | float sinAngle = sin(angles[i]); 59 | if (minSin > sinAngle) 60 | { 61 | minSin = sinAngle; 62 | } 63 | } 64 | 65 | if (orient < 0) 66 | { 67 | minSin *= -1.0; 68 | } 69 | 70 | return minSin; 71 | } 72 | 73 | 74 | float 75 | min_face_angle(const Tet& tet) 76 | { 77 | int orient = tet.orientation(); 78 | if (orient == 0) 79 | { 80 | return 0.0; 81 | } 82 | 83 | std::vector angles; 84 | tet.faceAngles(angles); 85 | float minAngle = angles[0]; 86 | for (size_t i = 1; i < angles.size(); ++i) 87 | { 88 | if (angles[i] < minAngle) 89 | { 90 | minAngle = angles[i]; 91 | } 92 | } 93 | 94 | // Normalize 95 | minAngle *= PI_3_INV; 96 | 97 | // Compute orientation to determine sign of result. 98 | if (orient < 0) 99 | { 100 | minAngle *= -1.0; 101 | } 102 | 103 | return minAngle; 104 | } 105 | 106 | 107 | float 108 | aspect_ratio(const Tet& tet) 109 | { 110 | // Want to minimize aspect ratio, so invert. 111 | // Normalize by sqrt(2). 112 | return SQRT2 / tet.aspectRatio(); 113 | } 114 | 115 | 116 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for quartet 2 | # 3 | # This Makefile depends on a file called "Makefile.defs" which contains 4 | # platform-specific definitions. 5 | # 6 | # To build quartet, first run "make depend" (which relies on g++ version 3.1 or 7 | # higher) and then either "make" (equivalent to "make debug") or "make release". 8 | # 9 | # This is for GNU make; other versions of make may not run correctly. 10 | 11 | MAIN_PROGRAM = quartet 12 | SRC = predicates.cpp \ 13 | geometry_queries.cpp \ 14 | sdf.cpp \ 15 | trimesh.cpp \ 16 | tet_mesh.cpp \ 17 | feature.cpp \ 18 | read_obj.cpp \ 19 | tet_quality.cpp \ 20 | match_features.cpp \ 21 | optimize_tet_mesh.cpp \ 22 | make_signed_distance.cpp \ 23 | make_tet_mesh.cpp \ 24 | main.cpp 25 | 26 | VIEWER_PROGRAM = view_tet 27 | VIEWER_SRC = gluvi.cpp \ 28 | view_tet.cpp \ 29 | feature.cpp \ 30 | read_obj.cpp \ 31 | predicates.cpp \ 32 | geometry_queries.cpp \ 33 | tet_mesh.cpp \ 34 | tet_quality.cpp 35 | 36 | 37 | SRC_ALL = $(SRC) $(VIEWER_SRC) 38 | 39 | include Makefile.defs 40 | 41 | # object files 42 | RELEASE_OBJ = $(patsubst %.cpp,obj/%.o,$(notdir $(SRC))) 43 | VIEWER_RELEASE_OBJ = $(patsubst %.cpp,obj/%.o,$(notdir $(VIEWER_SRC))) 44 | DEBUG_OBJ = $(patsubst %.cpp,obj_debug/%.o,$(notdir $(SRC))) 45 | VIEWER_DEBUG_OBJ = $(patsubst %.cpp,obj_debug/%.o,$(notdir $(VIEWER_SRC))) 46 | 47 | .PHONY: all 48 | all: debug 49 | 50 | # how to make the main target (debug mode, the default) 51 | $(MAIN_PROGRAM): $(DEBUG_OBJ) 52 | $(LINK) $(DEBUG_LINKFLAGS) -o $@ $^ $(LINK_LIBS) 53 | 54 | # how to make the main target (release mode) 55 | $(MAIN_PROGRAM)_release: $(RELEASE_OBJ) 56 | $(LINK) $(RELEASE_LINKFLAGS) -o $@ $^ $(LINK_LIBS) 57 | 58 | # how to make the viewer application (debug mode, the default) 59 | $(VIEWER_PROGRAM): $(VIEWER_DEBUG_OBJ) 60 | $(LINK) $(DEBUG_LINKFLAGS) -o $@ $^ $(LINK_LIBS) $(VIEWER_LIBS) 61 | 62 | # how to make the viewer application (release mode) 63 | $(VIEWER_PROGRAM)_release: $(VIEWER_RELEASE_OBJ) 64 | $(LINK) $(RELEASE_LINKFLAGS) -o $@ $^ $(LINK_LIBS) $(VIEWER_LIBS) 65 | 66 | # how to compile the predicates.cpp source file 67 | # This is different because optimization must be disabled 68 | # (at least, according to the TetGen source... not sure if it's true or not) 69 | obj/predicates.o: src/predicates.cpp 70 | $(CC) -c $(RELEASE_FLAGS) -O0 -o $@ $^ 71 | obj_debug/predicates.o: src/predicates.cpp 72 | $(CC) -c $(DEBUG_FLAGS) -O0 -o $@ $^ 73 | 74 | 75 | .PHONY: release 76 | release: $(MAIN_PROGRAM)_release $(VIEWER_PROGRAM)_release 77 | 78 | .PHONY: debug 79 | debug: $(MAIN_PROGRAM) $(VIEWER_PROGRAM) 80 | 81 | # how to compile each file 82 | .SUFFIXES: 83 | obj/%.o: 84 | $(CC) -c $(RELEASE_FLAGS) -o $@ $< 85 | obj_debug/%.o: 86 | $(CC) -c $(DEBUG_FLAGS) -o $@ $< 87 | 88 | # cleaning up 89 | .PHONY: clean 90 | clean: 91 | -rm -f obj/*.o $(MAIN_PROGRAM) $(VIEWER_PROGRAM) obj_debug/*.o $(MAIN_PROGRAM)_release $(VIEWER_PROGRAM)_release *core 92 | 93 | # dependencies are automatically generated 94 | .PHONY: depend 95 | depend: 96 | -mkdir obj 97 | -rm -f obj/depend 98 | $(foreach srcfile,$(SRC_ALL),$(DEPEND) -MM src/$(srcfile) -MT $(patsubst %.cpp,obj/%.o,$(notdir $(srcfile))) >> obj/depend;) 99 | -mkdir obj_debug 100 | -rm -f obj_debug/depend 101 | $(foreach srcfile,$(SRC_ALL),$(DEPEND) -MM src/$(srcfile) -MT $(patsubst %.cpp,obj_debug/%.o,$(notdir $(srcfile))) >> obj_debug/depend;) 102 | 103 | -include obj/depend 104 | -include obj_debug/depend 105 | -------------------------------------------------------------------------------- /src/read_obj.cpp: -------------------------------------------------------------------------------- 1 | #include "read_obj.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define LINESIZE 1024 // maximum line size when reading .OBJ files 8 | 9 | static bool 10 | read_int(const char *s, 11 | int &value, 12 | bool &leading_slash, 13 | int &position) 14 | { 15 | leading_slash=false; 16 | for(position=0; s[position]!=0; ++position){ 17 | switch(s[position]){ 18 | case '/': 19 | leading_slash=true; 20 | break; 21 | case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': 22 | goto found_int; 23 | } 24 | } 25 | return false; 26 | 27 | found_int: 28 | value=0; 29 | for(;; ++position){ 30 | switch(s[position]){ 31 | case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': 32 | value=10*value+s[position]-'0'; 33 | break; 34 | default: 35 | return true; 36 | } 37 | } 38 | return true; // should never get here, but keeps compiler happy 39 | } 40 | 41 | static void 42 | read_face_list(const char *s, 43 | std::vector &vertex_list) 44 | { 45 | vertex_list.clear(); 46 | int v, skip; 47 | bool leading_slash; 48 | for(int i=0;;){ 49 | if(read_int(s+i, v, leading_slash, skip)){ 50 | if(!leading_slash) 51 | vertex_list.push_back(v-1); // correct for 1-based index 52 | // vertex_list.push_back(v); // correct for 0-based index 53 | i+=skip; 54 | }else 55 | break; 56 | } 57 | } 58 | 59 | bool 60 | read_objfile(std::vector& x, 61 | std::vector& tri, 62 | const char *filename_format, ...) 63 | { 64 | va_list ap; 65 | va_start(ap, filename_format); 66 | #ifdef WIN32 67 | #define FILENAMELENGTH 256 68 | char filename[FILENAMELENGTH]; 69 | _vsnprintf(filename, FILENAMELENGTH, filename_format, ap); 70 | std::ifstream input(filename, std::ifstream::binary); 71 | #else 72 | char *filename; 73 | if (vasprintf(&filename, filename_format, ap) == -1) { 74 | return false; 75 | } 76 | std::ifstream input(filename, std::ifstream::binary); 77 | std::free(filename); 78 | #endif 79 | va_end(ap); 80 | 81 | if(!input.good()) return false; 82 | 83 | x.clear(); 84 | tri.clear(); 85 | 86 | char line[LINESIZE]; 87 | std::vector vertex_list; 88 | while(input.good()){ 89 | input.getline(line, LINESIZE); 90 | switch(line[0]){ 91 | case 'v': // vertex data 92 | if(line[1]==' '){ 93 | Vec3f new_vertex; 94 | std::sscanf(line+2, "%f %f %f", &new_vertex[0], 95 | &new_vertex[1], 96 | &new_vertex[2]); 97 | x.push_back(new_vertex); 98 | } 99 | break; 100 | case 'f': // face data 101 | if(line[1]==' '){ 102 | read_face_list(line+2, vertex_list); 103 | for(int j=0; j<(int)vertex_list.size()-2; ++j) 104 | tri.push_back(Vec3i(vertex_list[0], 105 | vertex_list[j+1], 106 | vertex_list[j+2])); 107 | } 108 | break; 109 | } 110 | } 111 | return true; 112 | } 113 | 114 | 115 | bool 116 | write_objfile(const std::vector& x, 117 | const std::vector& tri, 118 | const char *filename_format, ...) 119 | { 120 | va_list ap; 121 | va_start(ap, filename_format); 122 | #ifdef WIN32 123 | #define FILENAMELENGTH 256 124 | char filename[FILENAMELENGTH]; 125 | _vsnprintf(filename, FILENAMELENGTH, filename_format, ap); 126 | std::ofstream output(filename); 127 | #else 128 | char *filename; 129 | if (vasprintf(&filename, filename_format, ap) == -1) { 130 | return false; 131 | } 132 | std::ofstream output(filename); 133 | std::free(filename); 134 | #endif 135 | va_end(ap); 136 | 137 | if(!output.good()) return false; 138 | 139 | for (size_t v = 0; v < x.size(); ++v) 140 | { 141 | output << "v " << x[v][0] << " " << x[v][1] << " " << x[v][2] 142 | << std::endl; 143 | } 144 | for (size_t f = 0; f < tri.size(); ++f) 145 | { 146 | output << "f " << tri[f][0]+1 << " " 147 | << tri[f][1]+1 << " " 148 | << tri[f][2]+1 149 | << std::endl; 150 | } 151 | 152 | return true; 153 | } 154 | 155 | -------------------------------------------------------------------------------- /src/tet_mesh.h: -------------------------------------------------------------------------------- 1 | #ifndef TET_MESH_H 2 | #define TET_MESH_H 3 | 4 | #include "vec.h" 5 | #include 6 | 7 | // 8 | // Data structure representing a single tetrahedron. 9 | // 10 | struct Tet 11 | { 12 | Vec3f v[4]; // The four vertices of the tetrahedron. 13 | 14 | Tet() { } 15 | Tet(const Vec3f& v0, const Vec3f& v1, const Vec3f& v2, const Vec3f& v3); 16 | 17 | Vec3f& operator[] (int i) { return v[i]; } 18 | const Vec3f& operator[] (int i) const { return v[i]; } 19 | 20 | int orientation(double &sixSignedVolume) const; 21 | int orientation() const; 22 | 23 | float volume() const; 24 | float aspectRatio() const; 25 | 26 | void dihedralAngles(std::vector& angles) const; 27 | void faceAngles(std::vector& angles) const; 28 | 29 | float maxEdgeLength() const; 30 | }; 31 | 32 | 33 | // 34 | // Data structure representing a tetrahedral mesh. 35 | // 36 | struct TetMesh 37 | { 38 | // Default constructor 39 | TetMesh() : dirty(false) {} 40 | // Copy constructor 41 | TetMesh(const TetMesh& mesh) : v(mesh.v), t(mesh.t), dirty(true) {} 42 | // Construct from vertices and tet indices 43 | TetMesh(const std::vector& verts, 44 | const std::vector& tets) : 45 | v(verts), t(tets), dirty(true) {} 46 | 47 | // Returns the position of the vertex stored at index i. 48 | Vec3f& V(int i) { return v[i]; } 49 | const Vec3f& V(int i) const { return v[i]; } 50 | 51 | // Returns the indices of the tetrahedron stored at index i. 52 | Vec4i& T(int i) { dirty = true; return t[i]; } 53 | const Vec4i& T(int i) const { return t[i]; } 54 | 55 | // Returns the list of this tet mesh's vertices. 56 | std::vector& verts() { dirty = true; return v; } 57 | const std::vector& verts() const { return v; } 58 | 59 | // Returns the list of this tet mesh's tetrahedron indices. 60 | std::vector& tets() { dirty = true; return t; } 61 | const std::vector& tets() const { return t; } 62 | 63 | // Returns the number of vertices in the tet mesh. 64 | size_t vSize() const { return v.size(); } 65 | 66 | // Returns the number of tetrahedra in the mesh. 67 | size_t tSize() const { return t.size(); } 68 | 69 | // Returns the tetrahedron stored at index t. 70 | Tet getTet(int t) const; 71 | 72 | // Fills the given list with all indices of all the vertices 73 | // that are adjacent to the given vertex. 74 | void getAdjacentVertices(int vertex, std::vector& vertices) const; 75 | // Fills the given list with all the tets incident to the given vertex. 76 | void getIncidentTets(int vertex, std::vector& tets) const; 77 | // Fills the given list with the indices of tets incident to vertex. 78 | void getIncidentTetIndices(int vertex, std::vector& tetIndices) const; 79 | 80 | // Clean up the mesh, getting rid of unused vertices. 81 | // Tetrahedral indices adjusted accordingly. 82 | void compactMesh(); 83 | 84 | // Extract boundary vertices and triangles from the tet mesh. 85 | // Vertices will be returned in boundary_verts as indices into the 86 | // tet mesh vertex list v. 87 | // Triangles are stored in boundary_tris. These are also indices into v, 88 | // NOT as indices into boundary_verts!!! 89 | // Assumes that the tet mesh is well-formed and closed... 90 | void getBoundary(std::vector& boundary_verts, 91 | std::vector& boundary_tris) const; 92 | 93 | // Extract boundary vertices and triangles from the tet mesh. 94 | // Vertices will be copied into boundary_verts. 95 | // Triangles are stored in boundary_tris. These are indices into 96 | // boundary_verts. 97 | // Assumes that the tet mesh is well-formed and closed... 98 | void getBoundary(std::vector& boundary_verts, 99 | std::vector& boundary_tris) const; 100 | 101 | // Write this TetMesh to file with the given name. 102 | // The output file will be of the .TET format. 103 | bool writeToFile(const char* filename) const; 104 | 105 | // Compute information about this TetMesh (dihedral angles, tet volumes, 106 | // etc.) and write it to the file with the given name. 107 | bool writeInfoToFile(const char* filename) const; 108 | 109 | private: 110 | std::vector v; 111 | std::vector t; 112 | 113 | // Stores the indices of the tetrahedra incident to each vertex. 114 | mutable std::vector< std::vector > incidenceMap; 115 | // Indicates whether the incidence map may need to be rebuilt. 116 | mutable bool dirty; 117 | 118 | // Re-create the incidence map from the current v and t lists. 119 | // This is const because we need to be able to call it from 120 | // getIncidentTets(), which is const. 121 | // incidenceMap and dirty are mutable as a result. 122 | void buildIncidenceMap() const; 123 | }; 124 | 125 | #endif 126 | -------------------------------------------------------------------------------- /src/sdf.cpp: -------------------------------------------------------------------------------- 1 | #include "sdf.h" 2 | 3 | 4 | SDF::SDF(const Vec3f &origin_, 5 | const float dx_, 6 | int ni, int nj, int nk) 7 | : origin(origin_), dx(dx_), over_dx(1.0f/dx_) 8 | { 9 | assert(ni >= 2 && nj >= 2 && nk >= 2); 10 | 11 | // Initialize phi to be upper bound on distance. 12 | phi.assign(ni, nj, nk, (ni+nj+nk)*dx); 13 | } 14 | 15 | 16 | float 17 | SDF::operator()(const Vec3f& x) const 18 | { 19 | float fi = (x[0] - origin[0])*over_dx, 20 | fj = (x[1] - origin[1])*over_dx, 21 | fk = (x[2] - origin[2])*over_dx; 22 | 23 | float u = fi - std::floor(fi), 24 | v = fj - std::floor(fj), 25 | w = fk - std::floor(fk); 26 | 27 | int i = static_cast(fi), 28 | j = static_cast(fj), 29 | k = static_cast(fk); 30 | 31 | if (i < 0) { i = 0; u = 0; } else if (i > phi.ni-2) { i = phi.ni-2; u=1; } 32 | if (j < 0) { j = 0; v = 0; } else if (j > phi.nj-2) { j = phi.nj-2; v=1; } 33 | if (k < 0) { k = 0; w = 0; } else if (k > phi.nk-2) { k = phi.nk-2; w=1; } 34 | 35 | return (1-u)*( (1-v)*( (1-w)*phi(i,j,k) + w*phi(i,j,k+1) ) 36 | + v*( (1-w)*phi(i,j+1,k) + w*phi(i,j+1,k+1) ) ) 37 | + u*( (1-v)*( (1-w)*phi(i+1,j,k) + w*phi(i+1,j,k+1) ) 38 | + v*( (1-w)*phi(i+1,j+1,k) + w*phi(i+1,j+1,k+1) ) ); 39 | } 40 | 41 | 42 | Vec3f 43 | SDF::gradient(const Vec3f& x) const 44 | { 45 | float fi = (x[0] - origin[0])*over_dx, 46 | fj = (x[1] - origin[1])*over_dx, 47 | fk = (x[2] - origin[2])*over_dx; 48 | 49 | float u = fi - std::floor(fi), 50 | v = fj - std::floor(fj), 51 | w = fk - std::floor(fk); 52 | 53 | int i = static_cast(fi), 54 | j = static_cast(fj), 55 | k = static_cast(fk); 56 | 57 | if (i < 0) { i = 0; u = 0; } else if (i > phi.ni-2) { i = phi.ni-2; u=1; } 58 | if (j < 0) { j = 0; v = 0; } else if (j > phi.nj-2) { j = phi.nj-2; v=1; } 59 | if (k < 0) { k = 0; w = 0; } else if (k > phi.nk-2) { k = phi.nk-2; w=1; } 60 | 61 | // Compute interpolated finite differences in each dimension. 62 | Vec3f diff; 63 | 64 | diff[0] = -(1-v) * ( (1-w)*phi(i,j,k) + w*phi(i,j,k+1) ) 65 | - v * ( (1-w)*phi(i,j+1,k) + w*phi(i,j+1,k+1) ) 66 | +(1-v) * ( (1-w)*phi(i+1,j,k) + w*phi(i+1,j,k+1) ) 67 | + v * ( (1-w)*phi(i+1,j+1,k) + w*phi(i+1,j+1,k+1) ); 68 | diff[1] = (1-u) *( -((1-w)*phi(i,j,k) + w*phi(i,j,k+1)) 69 | + ((1-w)*phi(i,j+1,k) + w*phi(i,j+1,k+1)) ) 70 | + u *( -((1-w)*phi(i+1,j,k) + w*phi(i+1,j,k+1)) 71 | + ((1-w)*phi(i+1,j+1,k) + w*phi(i+1,j+1,k+1)) ); 72 | diff[2] = (1-u) * ( (1-v)*(phi(i,j,k+1) - phi(i,j,k)) 73 | + v * (phi(i,j+1,k+1) - phi(i,j+1,k)) ) 74 | + u * ( (1-v)*(phi(i+1,j,k+1) - phi(i+1,j,k)) 75 | + v * (phi(i+1,j+1,k+1) - phi(i+1,j+1,k)) ); 76 | 77 | return diff * over_dx; 78 | } 79 | 80 | 81 | Vec3f 82 | SDF::normal(const Vec3f& x) const 83 | { 84 | Vec3f g = gradient(x); 85 | return g / (mag(g) + 1e-30); // Avoid divide-by-zero 86 | } 87 | 88 | 89 | Vec3f 90 | SDF::projectToIsosurface(const Vec3f& x) const 91 | { 92 | float s0 = 0, // zero'th step 93 | d0 = (*this)(x); // zero'th value 94 | if (d0 == 0) 95 | return x; // Already on the isosurface. 96 | 97 | Vec3f g = gradient(x); // Search direction 98 | float g2 = mag2(g); 99 | if (g2 == 0) 100 | return x; // Give up if we're stuck. 101 | 102 | float tol = 1e-3*std::fabs(d0); 103 | 104 | float s = clamp(-d0/g2, -dx, dx); // first step 105 | float d = (*this)(x+s*g); // first value 106 | 107 | // Search outward until a sign change is found 108 | for (int steps = 0; steps < 5; ++steps) { 109 | if (std::fabs(d) <= tol) // converged? 110 | return x+s*g; 111 | if (d0 < 0 && d > 0) // found sign change? 112 | goto refine_projection; 113 | if (d0 > 0 && d < 0) // found sign change? 114 | goto refine_projection; 115 | // otherwise search forward 116 | d0 = d; 117 | s0 = s; 118 | s *= 1.25; // expand outwards a bit 119 | d = (*this)(x+s*g); 120 | } 121 | // If we make it here, we failed to find a sign change... 122 | if (std::fabs(d0) < std::fabs(d)) 123 | return x; 124 | else 125 | return x+s*g; 126 | 127 | refine_projection: 128 | // Secant guess at step size 129 | s = (d*s0 - d0*s) / (d-d0); 130 | #ifndef NDEBUG 131 | float d_new = (*this)(x+s*g); 132 | if (std::fabs(d_new) > 100.0*tol && tol > 1e-5) { 133 | std::cerr << "projectToIsosurface failed? sdf=" << d_new 134 | << ", tol=" << tol << ", ratio=" 135 | << std::fabs(d_new)/tol << std::endl 136 | << "x={" << x << "}, gradient={" << g << "}" << std::endl; 137 | } 138 | #endif 139 | return x+s*g; 140 | } 141 | 142 | 143 | -------------------------------------------------------------------------------- /src/geometry_queries.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "geometry_queries.h" 3 | 4 | // External functions defined in predicates.cpp 5 | extern double exactinit(); 6 | extern double orient3d(double* pa, double* pb, double* pc, double* pd); 7 | 8 | 9 | double 10 | initialize_exact() 11 | { 12 | return exactinit(); 13 | } 14 | 15 | float 16 | point_segment_distance(const Vec3f &x0, 17 | const Vec3f &x1, 18 | const Vec3f &x2) 19 | { 20 | Vec3f dx(x2-x1); 21 | double m2=mag2(dx); 22 | // find parameter value of closest point on segment 23 | float s12=(float)(dot(x2-x0, dx)/m2); 24 | if(s12<0){ 25 | s12=0; 26 | }else if(s12>1){ 27 | s12=1; 28 | } 29 | // and find the distance 30 | return dist(x0, s12*x1+(1-s12)*x2); 31 | } 32 | 33 | float 34 | point_triangle_distance(const Vec3f &x0, 35 | const Vec3f &x1, 36 | const Vec3f &x2, 37 | const Vec3f &x3) 38 | { 39 | // first find barycentric coordinates of closest point on infinite plane 40 | Vec3f x13(x1-x3), x23(x2-x3), x03(x0-x3); 41 | float m13=mag2(x13), m23=mag2(x23), d=dot(x13,x23); 42 | float invdet=1.f/max(m13*m23-d*d,1e-30f); 43 | float a=dot(x13,x03), b=dot(x23,x03); 44 | // the barycentric coordinates themselves 45 | float w23=invdet*(m23*a-d*b); 46 | float w31=invdet*(m13*b-d*a); 47 | float w12=1-w23-w31; 48 | if(w23>=0 && w31>=0 && w12>=0){ // if we're inside the triangle 49 | return dist(x0, w23*x1+w31*x2+w12*x3); 50 | }else{ // we have to clamp to one of the edges 51 | if(w23>0) // this rules out edge 2-3 for us 52 | return min(point_segment_distance(x0,x1,x2), 53 | point_segment_distance(x0,x1,x3)); 54 | else if(w31>0) // this rules out edge 1-3 55 | return min(point_segment_distance(x0,x1,x2), 56 | point_segment_distance(x0,x2,x3)); 57 | else // w12 must be >0, ruling out edge 1-2 58 | return min(point_segment_distance(x0,x1,x3), 59 | point_segment_distance(x0,x2,x3)); 60 | } 61 | } 62 | 63 | // Find x+y=a+b, but with |x|>|y| (or both zero) non-overlapping in bits. 64 | static void 65 | two_sum(double a, double b, double& x, double& y) 66 | { 67 | x=a+b; 68 | double z=x-a; 69 | y=(a-(x-z))+(b-z); 70 | } 71 | 72 | // Find x+y=a with x and y using only half the mantissa bits. 73 | static void 74 | split(double a, double& x, double& y) 75 | { 76 | double c=134217729*a; 77 | x=c-(c-a); 78 | y=a-x; 79 | } 80 | 81 | // Find x+y=a*b (exactly) with |x|>|y| (or both zero) nonoverlapping in bits. 82 | static void 83 | two_product(double a, 84 | double b, 85 | double& x, 86 | double& y) 87 | { 88 | x=a*b; 89 | double a1, a2, b1, b2; 90 | split(a, a1, a2); 91 | split(b, b1, b2); 92 | y=a2*b2-(((x-a1*b1)-a2*b1)-a1*b2); 93 | } 94 | 95 | int 96 | orientation(double x1, double y1, 97 | double x2, double y2, 98 | double &twice_signed_area) 99 | { 100 | twice_signed_area=y1*x2-x1*y2; 101 | double bound=5*DBL_EPSILON*(std::fabs(y1*x2)+std::fabs(x1*y2)); 102 | if(std::fabs(twice_signed_area)0) return 1; 121 | else if(twice_signed_area<0) return -1; 122 | else if(y2>y1) return 1; 123 | else if(y2x2) return 1; 125 | else if(x1 0.0) return 1; 171 | else return 0; 172 | } 173 | 174 | 175 | -------------------------------------------------------------------------------- /src/feature.h: -------------------------------------------------------------------------------- 1 | #ifndef FEATURE_H 2 | #define FEATURE_H 3 | 4 | #include "trimesh.h" 5 | #include "vec.h" 6 | #include 7 | #include 8 | 9 | // 10 | // A Feature is a point or connected sequence of edges that we wish to 11 | // preserve when meshing a 3D model. 12 | // 13 | struct Feature 14 | { 15 | // The vertices that define the feature when connected by line segments. 16 | std::vector vertices; 17 | 18 | // Returns true iff this feature is a single point. 19 | bool isPoint() const { return vertices.size() == 1; } 20 | 21 | // Return the endpoints of the feature. 22 | Vec3f getEndpoint1() const { return vertices[0]; } 23 | Vec3f getEndpoint2() const { return vertices[vertices.size()-1]; } 24 | 25 | // Returns the point on the feature closest to the given point p. 26 | Vec3f projectToFeature(const Vec3f &p) const; 27 | 28 | // Project the given point p onto the feature and return the tangent 29 | // to the feature at that point. 30 | Vec3f getTangent(const Vec3f &p) const; 31 | }; 32 | 33 | 34 | // 35 | // FeatureSet stores and maintains a set of features. 36 | // 37 | // The following invariant is enforced at all times: 38 | // - No two features share the same vertex on their interiors. 39 | // In other words, features may only share vertices if those vertices are 40 | // their endpoints. 41 | // 42 | // The FeatureSet can consolidate its features by combining vertices that 43 | // are within a given distance threshold. 44 | // 45 | // The FeatureSet can be initialized by passing in Features and lists of 46 | // Features, or it can be initialized by reading a .FEAT file. 47 | // 48 | // Features can be retrieved from the set by numerical index. 49 | // Note that modifying the FeatureSet in any way can invalidate previous 50 | // indices into the FeatureSet. 51 | // 52 | // In addition, the FeatureSet can provide the set of all endpoints of its 53 | // features. These vertices are returned with no duplicates. 54 | // 55 | class FeatureSet 56 | { 57 | public: 58 | // Default constructor 59 | FeatureSet() {} 60 | 61 | // Constructor 62 | // f is a list of Features to add to the set. 63 | FeatureSet(const std::vector& f); 64 | 65 | // Add the given list of features to the set. 66 | void addFeatures(const std::vector& features); 67 | 68 | // Add the given feature to the set. 69 | void addFeature(const Feature& feature); 70 | 71 | // Initialize this FeatureSet based on the given file. 72 | // The file should be of the .FEAT format, 73 | // ie: it should contain lists of vertices each prefaced by an 74 | // integer indicating the number of vertices in the current feature. 75 | // Returns true iff the file was read successfully. 76 | bool readFromFile(const char *filename_format, ...); 77 | 78 | // Write this FeatureSet to a file with the given name. 79 | // Ideally, this should be a .FEAT file. 80 | // The output format will match the description above. 81 | bool writeToFile(const char *filename_format, ...); 82 | 83 | // Combine vertices that are within the given distance threshold apart. 84 | // If this causes any features to share vertices, split those features 85 | // at those vertices. 86 | void consolidate(float dist = 0.0f); 87 | 88 | // Returns the number of features in the set. 89 | unsigned int numFeatures() const { return indices.size(); } 90 | 91 | // Copies the feature at index i in to the given feature parameter. 92 | void getFeature(unsigned int i, Feature& feature) const; 93 | 94 | // Copies the features in the set into the given features parameter. 95 | void getFeatures(std::vector& features) const; 96 | 97 | // Copies a list of feature endpoints into the given endpoints parameter. 98 | // The endpoints will not be duplicated; if a vertex is the endpoint of 99 | // multiple features it will only be returned once. 100 | void getFeatureEndpoints(std::vector& endpoints) const; 101 | 102 | // Merges the two given feature endpoints by moving endpoint2 to endpoint1 103 | // and replacing all references to endpoint2 with references to endpoint1. 104 | // The given endpoints must be valid endpoints in the FeatureSet. 105 | // Ideally they should come from a call to getFeatureEndpoints, 106 | // and should not be modified before being passed to this function. 107 | void mergeEndpoints(const Vec3f& endpoint1, const Vec3f& endpoint2, 108 | std::vector& newEndpoints); 109 | 110 | // Process the given triangle mesh to find sharp features and add them to 111 | // this FeatureSet. 112 | // Uses the given angle_threshold to determine which edges and vertices are 113 | // "sharp". 114 | void autoDetectFeatures(const TriMesh& mesh, 115 | float angle_threshold_degrees); 116 | 117 | private: 118 | // The set of vertices in the features of the FeatureSet. 119 | std::vector vertices; 120 | 121 | // List of features as indices into the vertex list (vertices). 122 | // indices[f][v] gives the index of the v'th vertex of the f'th feature. 123 | std::vector< std::vector > indices; 124 | 125 | // Map of vertex positions to their index in vertices. 126 | std::map vertexMap; 127 | 128 | // Examine the feature at the given index and check if it forms a loop. 129 | // A loop is a feature that starts and ends at the same vertex. 130 | // If the feature is degenerate (ie all vertices are the same), 131 | // convert it into a point. 132 | // If the is a loop and the entire feature lies within the given 133 | // collapseRadius, collapse the feature to a point. 134 | // If the feature is a loop and extends outside the collapseRadius, 135 | // split it in half. 136 | // Returns true iff the feature is split and a new feature is added. 137 | bool checkForLoop(int fIndex, float collapseRadiusSquared = 0.0f); 138 | 139 | // Find the features in the set containing the given index in its interior. 140 | // Split that feature in two at that index. 141 | // Repeat until the given index is only found at the endpoints of features. 142 | // This may increase the number of features in the FeatureSet, 143 | // but the number of vertices remains the same. 144 | void splitFeatures(int vIndex); 145 | }; 146 | 147 | #endif 148 | -------------------------------------------------------------------------------- /src/gluvi.h: -------------------------------------------------------------------------------- 1 | #ifndef GLUVI_H 2 | #define GLUVI_H 3 | 4 | #include 5 | #include 6 | 7 | #ifdef __APPLE__ 8 | #include // why does Apple have to put glut.h here... 9 | #else 10 | #include // ...when everyone else puts it here? 11 | #endif 12 | 13 | #include "vec.h" 14 | 15 | namespace Gluvi{ 16 | 17 | /* 18 | For portions of the code which export RIB commands (e.g. from cameras): 19 | 20 | The RenderMan (R) Interface Procedures and Protocol are: 21 | Copyright 1988, 1989, Pixar 22 | All Rights Reserved 23 | */ 24 | 25 | // the camera capability for Gluvi 26 | struct Camera 27 | { 28 | virtual ~Camera(void) {} 29 | virtual void click(int button, int state, int x, int y) = 0; 30 | virtual void drag(int x, int y) = 0; 31 | virtual void return_to_default(void) = 0; 32 | //@@@ add these to be called by a user glutKeyboardFunc() thing so that 33 | //@@@ cameras can easily add keyboard shortcuts for e.g. return_to_default, transformation, etc. 34 | //virtual void navigation_keyboard_handler(unsigned char key, int x, int y) = 0; 35 | //virtual void navigation_special_key_handler(int key, int x, int y) = 0; 36 | virtual void gl_transform(void) = 0; 37 | virtual void export_rib(std::ostream &output) = 0; 38 | virtual void display_screen(void) {} // in case the camera needs to show anything on screen 39 | }; 40 | 41 | struct Target3D : public Camera 42 | { 43 | float target[3], dist; 44 | float heading, pitch; 45 | float default_target[3], default_dist; 46 | float default_heading, default_pitch; 47 | float fovy; 48 | float near_clip_factor, far_clip_factor; 49 | enum {INACTIVE, ROTATE, TRUCK, DOLLY} action_mode; 50 | int oldmousex, oldmousey; 51 | 52 | Target3D(float target_[3]=0, float dist_=1, float heading_=0, float pitch_=0, float fovy_=45, 53 | float near_clip_factor_=0.01, float far_clip_factor_=100); 54 | void click(int button, int state, int x, int y); 55 | void drag(int x, int y); 56 | void return_to_default(void); 57 | void transform_mouse(int x, int y, float ray_origin[3], float ray_direction[3]); 58 | void get_viewing_direction(float direction[3]); 59 | void gl_transform(void); 60 | void export_rib(std::ostream &output); 61 | }; 62 | 63 | // same as above, but with orthographic projection 64 | struct TargetOrtho3D : public Camera 65 | { 66 | float target[3], dist; 67 | float heading, pitch; 68 | float default_target[3], default_dist; 69 | float default_heading, default_pitch; 70 | float height_factor; 71 | float near_clip_factor, far_clip_factor; 72 | enum {INACTIVE, ROTATE, TRUCK, DOLLY} action_mode; // @@@@ WHAT ABOUT ZOOMING??? IS WIDTH ALWAYS A FUNCTION OF DIST? 73 | int oldmousex, oldmousey; 74 | 75 | TargetOrtho3D(float target_[3]=0, float dist_=1, float heading_=0, float pitch_=0, float height_factor_=1, 76 | float near_clip_factor_=0.01, float far_clip_factor_=100); 77 | void click(int button, int state, int x, int y); 78 | void drag(int x, int y); 79 | void return_to_default(void); 80 | void transform_mouse(int x, int y, float ray_origin[3], float ray_direction[3]); 81 | void get_viewing_direction(float direction[3]); 82 | void gl_transform(void); 83 | void export_rib(std::ostream &output); 84 | }; 85 | 86 | struct PanZoom2D : public Camera 87 | { 88 | float bottom, left, height; 89 | float default_bottom, default_left, default_height; 90 | enum {INACTIVE, PAN, ZOOM_IN, ZOOM_OUT} action_mode; 91 | int oldmousex, oldmousey; 92 | bool moved_since_mouse_down; // to distinuish simple clicks from drags 93 | int clickx, clicky; 94 | 95 | PanZoom2D(float bottom_=0, float left_=0, float height_=1); 96 | void click(int button, int state, int x, int y); 97 | void drag(int x, int y); 98 | void return_to_default(void); 99 | void transform_mouse(int x, int y, float coords[2]); 100 | void gl_transform(void); 101 | void export_rib(std::ostream &output); 102 | void display_screen(void); 103 | }; 104 | 105 | // overlaid user-interface widgets 106 | struct Widget 107 | { 108 | int dispx, dispy, width, height; // set in display() 109 | 110 | virtual ~Widget() {} 111 | virtual void display(int x, int y) = 0; 112 | virtual bool click(int state, int x, int y) { return false; } // returns true if click handled by widget 113 | virtual void drag(int x, int y) {} 114 | }; 115 | 116 | struct StaticText : public Widget 117 | { 118 | const char *text; 119 | 120 | StaticText(const char *text_); 121 | void display(int x, int y); 122 | }; 123 | 124 | struct Button : public Widget 125 | { 126 | enum {UNINVOLVED, SELECTED, HIGHLIGHTED} status; 127 | const char *text; 128 | int minwidth; 129 | 130 | Button(const char *text_, int minwidth_=0); 131 | void display(int x, int y); 132 | bool click(int state, int x, int y); 133 | void drag(int x, int y); 134 | virtual void action() {} 135 | }; 136 | 137 | struct Slider : public Widget 138 | { 139 | enum {UNINVOLVED, SELECTED} status; 140 | const char *text; 141 | int length, justify; 142 | int position; 143 | int scrollxmin, scrollxmax, scrollymin, scrollymax; 144 | int clickx; 145 | 146 | Slider(const char *text_, int length_=100, int position_=0, int justify_=0); 147 | void display(int x, int y); 148 | bool click(int state, int x, int y); 149 | void drag(int x, int y); 150 | virtual void action() {} 151 | }; 152 | 153 | struct WidgetList : public Widget 154 | { 155 | int indent; 156 | bool hidden; 157 | std::vector list; 158 | int downclicked_member; 159 | 160 | WidgetList(int listindent_=12, bool hidden_=false); 161 | void display(int x, int y); 162 | bool click(int state, int x, int y); 163 | void drag(int x, int y); 164 | }; 165 | 166 | // display callback 167 | extern void (*userDisplayFunc)(void); 168 | 169 | // mouse callbacks for events that Gluvi ignores (control not pressed, or mouse not on an active widget) 170 | extern void (*userMouseFunc)(int button, int state, int x, int y); 171 | extern void (*userDragFunc)(int x, int y); 172 | 173 | // user is free to do their own callbacks for everything else except glutReshape() 174 | 175 | // additional helpful functions 176 | void ppm_screenshot(const char *filename_format, ...); 177 | void sgi_screenshot(const char *filename_format, ...); 178 | void set_generic_lights(void); 179 | void set_generic_material(float r, float g, float b, GLenum face=GL_FRONT_AND_BACK); 180 | void set_matte_material(float r, float g, float b, GLenum face=GL_FRONT_AND_BACK); 181 | //@@@@@@@ USEFUL FUNCTIONALITY: 182 | void draw_3d_arrow(const float base[3], const float point[3], float arrow_head_length=0); 183 | void draw_2d_arrow(const Vec2f base, const Vec2f point, float arrow_head_length); 184 | void draw_coordinate_grid(float size=1, int spacing=10); 185 | void draw_text(const float point[3], const char *text, int fontsize=12); 186 | 187 | // call init first thing 188 | void init(const char *windowtitle, int *argc, char **argv); 189 | 190 | // the Gluvi state 191 | extern Camera *camera; 192 | extern WidgetList root; 193 | extern int winwidth, winheight; 194 | 195 | // then after setting the Gluvi state and doing any of your own set-up, call run() 196 | void run(void); 197 | 198 | }; // end of namespace 199 | 200 | #endif 201 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Quartet 2 | ******* 3 | 4 | A tetrahedral mesh generator based on Jonathon Shewchuk's isosurface stuffing 5 | algorithm in combination with the A15 acute tetrahedral tile. 6 | 7 | written by: 8 | Robert Bridson (www.cs.ubc.ca/~rbridson) 9 | Crawford Doran (www.crawforddoran.com) 10 | -------------------------------------- 11 | 12 | 13 | Overview 14 | ========== 15 | 16 | Quartet converts a watertight triangle mesh into a high-quality uniform 17 | tetrahedral mesh that closely approximates the geometry, up to the curvature 18 | implied by the specified grid sampling (dx). Optionally, the tetrahedral mesh 19 | can be warped to match sharp features on the input, and also improved by a 20 | quality optimization pass. 21 | 22 | 23 | Building Quartet 24 | ================= 25 | 26 | 1) It may be necessary to edit Makefile.defs in order to ensure that 27 | platform-specific variables are set correctly. These include the compiler, 28 | linker, and all flags associated with them, including libraries. 29 | 2) Run 'make depend' 30 | 3) Run 'make' and/or 'make release' ('make' is equivalent to 'make debug') 31 | 32 | 33 | Running Quartet 34 | ================== 35 | 36 | Quartet is run from the command line with the following syntax: 37 | 38 | quartet [-f ] [-a ] 39 | [-s ] [-i] [-o] 40 | 41 | - is the OBJ file containing the surface to be converted to a 42 | tetrahedral mesh. 43 | - is the grid spacing used in the isosurface stuffing algorithm. The 44 | generated tetrahedra will be approximately this size. 45 | - is a path to the file where the resulting tetrahedral mesh will 46 | be saved. 47 | 48 | The optional flags have the following effects: 49 | -f -- The given file contains lists of 50 | points and edges indicating sharp corners and ridges 51 | in the input. The output mesh will resolve these 52 | features as well as possible. 53 | -a -- Auto-detects sharp features in the input. Edges with 54 | a dihedral angle smaller than degrees 55 | will be resolved in the output as well as possible. 56 | In addition, these features will be written to a file 57 | called 'auto.feat' (which can then be fed to the -f 58 | option in future executions). 59 | -s -- The boundary of the resulting tetrahedral mesh is 60 | written to . This is largely for 61 | convenience of visualizing the result. 62 | -i -- Intermediate stages of the mesh generation process are 63 | saved to disk in files named 'N_*_.tet' where N is the 64 | numbering of the steps. 65 | -o -- Perform quality optimization on the resulting 66 | tetrahedral mesh. 67 | 68 | Example inputs can be found in the 'meshes' directory. For the inputs with 69 | sharp features, corresponding .feat files are provided. 70 | 71 | Example usages: 72 | quartet dragon.obj 0.01 dragon_01.tet -o -s dragon.obj 73 | quartet fandisk.obj 0.1 fandisk_1.tet -f fandisk.feat 74 | quartet block.obj 0.5 block_5.tet -a 110.0 -o 75 | 76 | 77 | Running view_tet 78 | ================== 79 | 80 | We have included an OpenGL viewer application for visualizing resulting tet 81 | meshes. The viewer is called view_tet, and can be called with the following 82 | syntax: 83 | 84 | view_tet [-s] [-f ] [-p (x|y|z)]* 85 | [-w ] 86 | 87 | Here, is the TET file produced by quartet containing the tet mesh. 88 | 89 | The optional flags have the following effects: 90 | -s -- Assume that is actually 91 | and read it in as a surface 92 | mesh from an OBJ file. 93 | -f -- Displays the sharp features in the file 94 | alongside the mesh. 95 | -p (x|y|z) -- Specifies a plane to cut the mesh so the 96 | viewer can see "inside" of it. The plane is 97 | specified by which coordinate axis is its 98 | normal and its position along that axis. 99 | Multiple planes can be specified with multiple 100 | occurrences of this flag. 101 | -w num_tets -- Draw the mesh so that the 102 | worst-quality tetrahedra are highlighted. 103 | 104 | Example usages: 105 | view_tet block.tet -p x0.0 106 | view_tet dragon.obj -s 107 | view_tet fandisk.tet -f fandisk.feat -w 10 108 | 109 | Once view_tet is running, the viewer can be controlled with the following 110 | inputs: 111 | 112 | Shift+LeftMouseButton -- Rotate camera 113 | Shift+RightMouseButton -- Zoom camera 114 | Shift+MiddleMouseButton -- Pan camera 115 | 116 | 117 | 118 | Source Code overview 119 | ====================== 120 | 121 | The following files make up the quartet source: 122 | 123 | Utilities / Data Structures: 124 | 125 | util.h 126 | - a bunch of small useful functions 127 | 128 | vec.h 129 | - a convenience wrapper around fixed-length arrays (for vectors in 3D etc.) 130 | 131 | array3.h 132 | - a convenience wrapper around STL vectors to get 3D arrays 133 | 134 | sdf.h/cpp 135 | - a signed-distance-field data structure for working with level sets 136 | 137 | read_obj.h/cpp 138 | - code to read in a Wavefront .OBJ file 139 | 140 | trimesh.h/cpp 141 | - a simple half-edge triangle mesh data structure 142 | 143 | tet_mesh.h/cpp 144 | - an index-based tetrahedral mesh data structure. Maintains incidence 145 | relationships as necessary for fast adjacency queries. 146 | 147 | features.h/cpp 148 | - Data structures representing sharp features of a triangle mesh and 149 | aggregations of those features (called FeatureSets) 150 | 151 | 152 | Meshing Functions: 153 | 154 | geometry_queries.h/cpp 155 | - some simple and not-so-simple geometric operations needed for creating 156 | signed distance functions from triangle meshes. 157 | Uses Jonathan Shewchuk's robust geometric predicates code, found in 158 | predicates.cpp (http://www.cs.cmu.edu/~quake/robust.html). 159 | 160 | make_signed_distance.h/cpp 161 | - code for robustly creating a fairly accurate signed distance function 162 | of a watertight triangle mesh, sampled on a regular 3D grid. 163 | 164 | make_tet_mesh.h/cpp 165 | - code to create a quality tetrahedral mesh of the interior of a level set 166 | 167 | tet_quality.h/cpp 168 | - functions for measuring quality of tetrahedra based on various metrics 169 | 170 | match_features.h/cpp 171 | - code to restore desired sharp features onto a tetrahedral mesh 172 | 173 | optimize_tet_mesh.h/cpp 174 | - code to improve the overall quality of a given tetrahedral mesh. Uses as 175 | much data from the meshing step (ie: sdf, sharp features, etc.) as possible. 176 | 177 | main.cpp 178 | - quartet command-line program 179 | 180 | 181 | Viewer Application: 182 | 183 | gluvi.h/cpp 184 | - code for creating a simple generic OpenGL viewer application 185 | 186 | view_tet.cpp 187 | - view_tet main code file for viewing tet meshes 188 | 189 | -------------------------------------------------------------------------------- /src/array3.h: -------------------------------------------------------------------------------- 1 | #ifndef ARRAY3_H 2 | #define ARRAY3_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | template > 9 | struct Array3 10 | { 11 | // STL-friendly typedefs 12 | 13 | typedef typename ArrayT::iterator iterator; 14 | typedef typename ArrayT::const_iterator const_iterator; 15 | typedef typename ArrayT::size_type size_type; 16 | typedef long difference_type; 17 | typedef T& reference; 18 | typedef const T& const_reference; 19 | typedef T value_type; 20 | typedef T* pointer; 21 | typedef const T* const_pointer; 22 | typedef typename ArrayT::reverse_iterator reverse_iterator; 23 | typedef typename ArrayT::const_reverse_iterator const_reverse_iterator; 24 | 25 | // the actual representation 26 | 27 | int ni, nj, nk; 28 | ArrayT a; 29 | 30 | // the interface 31 | 32 | Array3(void) 33 | : ni(0), nj(0), nk(0) 34 | {} 35 | 36 | Array3(int ni_, int nj_, int nk_) 37 | : ni(ni_), nj(nj_), nk(nk_), a(ni_*nj_*nk_) 38 | { assert(ni_>=0 && nj_>=0 && nk_>=0); } 39 | 40 | Array3(int ni_, int nj_, int nk_, ArrayT& a_) 41 | : ni(ni_), nj(nj_), nk(nk_), a(a_) 42 | { assert(ni_>=0 && nj_>=0 && nk_>=0); } 43 | 44 | Array3(int ni_, int nj_, int nk_, const T& value) 45 | : ni(ni_), nj(nj_), nk(nk_), a(ni_*nj_*nk_, value) 46 | { assert(ni_>=0 && nj_>=0 && nk_>=0); } 47 | 48 | Array3(int ni_, int nj_, int nk_, const T& value, size_type max_n_) 49 | : ni(ni_), nj(nj_), nk(nk_), a(ni_*nj_*nk_, value, max_n_) 50 | { assert(ni_>=0 && nj_>=0 && nk_>=0); } 51 | 52 | Array3(int ni_, int nj_, int nk_, T* data_) 53 | : ni(ni_), nj(nj_), nk(nk_), a(ni_*nj_*nk_, data_) 54 | { assert(ni_>=0 && nj_>=0 && nk_>=0); } 55 | 56 | Array3(int ni_, int nj_, int nk_, T* data_, size_type max_n_) 57 | : ni(ni_), nj(nj_), nk(nk_), a(ni_*nj_*nk_, data_, max_n_) 58 | { assert(ni_>=0 && nj_>=0 && nk_>=0); } 59 | 60 | ~Array3(void) 61 | { 62 | #ifndef NDEBUG 63 | ni=nj=0; 64 | #endif 65 | } 66 | 67 | const T& operator()(int i, int j, int k) const 68 | { 69 | assert(i>=0 && i=0 && j=0 && k=0 && i=0 && j=0 && k& x) const 80 | { return ni==x.ni && nj==x.nj && nk==x.nk && a==x.a; } 81 | 82 | bool operator!=(const Array3& x) const 83 | { return ni!=x.ni || nj!=x.nj || nk!=x.nk || a!=x.a; } 84 | 85 | bool operator<(const Array3& x) const 86 | { 87 | if(nix.ni) return false; 88 | if(njx.nj) return false; 89 | if(nkx.nk) return false; 90 | return a(const Array3& x) const 94 | { 95 | if(ni>x.ni) return true; else if(nix.nj) return true; else if(njx.nk) return true; else if(nkx.a; 99 | } 100 | 101 | bool operator<=(const Array3& x) const 102 | { 103 | if(nix.ni) return false; 104 | if(njx.nj) return false; 105 | if(nkx.nk) return false; 106 | return a<=x.a; 107 | } 108 | 109 | bool operator>=(const Array3& x) const 110 | { 111 | if(ni>x.ni) return true; else if(nix.nj) return true; else if(njx.nk) return true; else if(nk=x.a; 115 | } 116 | 117 | void assign(const T& value) 118 | { a.assign(value); } 119 | 120 | void assign(int ni_, int nj_, int nk_, const T& value) 121 | { 122 | a.assign(ni_*nj_*nk_, value); 123 | ni=ni_; 124 | nj=nj_; 125 | nk=nk_; 126 | } 127 | 128 | void assign(int ni_, int nj_, int nk_, const T* copydata) 129 | { 130 | a.assign(ni_*nj_*nk_, copydata); 131 | ni=ni_; 132 | nj=nj_; 133 | nk=nk_; 134 | } 135 | 136 | const T& at(int i, int j, int k) const 137 | { 138 | assert(i>=0 && i=0 && j=0 && k=0 && i=0 && j=0 && k=0 && nj_>=0 && nk_>=0); 225 | a.resize(ni_*nj_*nk_); 226 | ni=ni_; 227 | nj=nj_; 228 | nk=nk_; 229 | } 230 | 231 | void resize(int ni_, int nj_, int nk_, const T& value) 232 | { 233 | assert(ni_>=0 && nj_>=0 && nk_>=0); 234 | a.resize(ni_*nj_*nk_, value); 235 | ni=ni_; 236 | nj=nj_; 237 | nk=nk_; 238 | } 239 | 240 | void set_zero(void) 241 | { a.set_zero(); } 242 | 243 | size_type size(void) const 244 | { return a.size(); } 245 | 246 | void swap(Array3& x) 247 | { 248 | std::swap(ni, x.ni); 249 | std::swap(nj, x.nj); 250 | std::swap(nk, x.nk); 251 | a.swap(x.a); 252 | } 253 | 254 | void trim(void) 255 | { a.trim(); } 256 | }; 257 | 258 | // some common arrays 259 | 260 | typedef Array3 Array3d; 261 | typedef Array3 Array3f; 262 | typedef Array3 Array3i; 263 | 264 | #endif 265 | -------------------------------------------------------------------------------- /src/make_signed_distance.cpp: -------------------------------------------------------------------------------- 1 | #include "make_signed_distance.h" 2 | #include "geometry_queries.h" 3 | 4 | 5 | // Comment out to use a faster, less accurate point-tri distance measure. 6 | #define EXACT_POINT_TRI_DISTANCE 7 | 8 | 9 | static void 10 | check_neighbour(const std::vector &tri, 11 | const std::vector &x, 12 | Array3f &phi, 13 | Array3i &closest_tri, 14 | const Vec3f &gx, 15 | int i0, int j0, int k0, 16 | int i1, int j1, int k1) 17 | { 18 | if(closest_tri(i1,j1,k1) >= 0) { 19 | int p, q, r; assign(tri[closest_tri(i1,j1,k1)], p, q, r); 20 | 21 | #ifdef EXACT_POINT_TRI_DISTANCE 22 | // This is the more accurate, exact distance-to-tri routine 23 | float d = point_triangle_distance(gx, x[p], x[q], x[r]); 24 | #else 25 | // This is a fast approximation, using the triangle centroid 26 | float d = dist(gx, 0.3333333333333333333f*(x[p]+x[q]+x[r])); 27 | #endif 28 | 29 | if(d < phi(i0,j0,k0)) { 30 | phi(i0,j0,k0) = d; 31 | closest_tri(i0,j0,k0) = closest_tri(i1,j1,k1); 32 | } 33 | } 34 | } 35 | 36 | 37 | static void 38 | sweep(const std::vector &tri, 39 | const std::vector &x, 40 | SDF &sdf, 41 | Array3i &closest_tri, 42 | int di, int dj, int dk) 43 | { 44 | // figure out loop bounds from direction (di,dj,dk) 45 | int i0, i1; 46 | if (di>0) { i0=1; i1=sdf.phi.ni; } 47 | else { i0=sdf.phi.ni-2; i1=-1; } 48 | int j0, j1; 49 | if (dj>0) { j0=1; j1=sdf.phi.nj; } 50 | else { j0=sdf.phi.nj-2; j1=-1; } 51 | int k0, k1; 52 | if (dk>0) { k0=1; k1=sdf.phi.nk; } 53 | else { k0=sdf.phi.nk-2; k1=-1; } 54 | // sweep through the grid propagating closest triangle information 55 | int i, j, k; 56 | for(k=k0; k!=k1; k+=dk) for(j=j0; j!=j1; j+=dj) for(i=i0; i!=i1; i+=di) { 57 | Vec3f gx(i*sdf.dx+sdf.origin[0], 58 | j*sdf.dx+sdf.origin[1], 59 | k*sdf.dx+sdf.origin[2]); 60 | check_neighbour(tri,x, sdf.phi, closest_tri, gx, i,j,k, i-di,j, k); 61 | check_neighbour(tri,x, sdf.phi, closest_tri, gx, i,j,k, i, j-dj,k); 62 | check_neighbour(tri,x, sdf.phi, closest_tri, gx, i,j,k, i-di,j-dj,k); 63 | check_neighbour(tri,x, sdf.phi, closest_tri, gx, i,j,k, i, j, k-dk); 64 | check_neighbour(tri,x, sdf.phi, closest_tri, gx, i,j,k, i-di,j, k-dk); 65 | check_neighbour(tri,x, sdf.phi, closest_tri, gx, i,j,k, i, j-dj,k-dk); 66 | check_neighbour(tri,x, sdf.phi, closest_tri, gx, i,j,k, i-di,j-dj,k-dk); 67 | } 68 | } 69 | 70 | 71 | void 72 | make_signed_distance(const std::vector &tri, 73 | const std::vector &x, 74 | SDF &sdf) 75 | { 76 | // prepare an array to store (approximate) closest triangle to grid points 77 | Array3i closest_tri(sdf.phi.ni, sdf.phi.nj, sdf.phi.nk, -1); 78 | // prepare an array to count intersections of triangles with grid edges: 79 | // intersection_count(i,j,k) is # of tri intersections in (i-1,i]x{j}x{k} 80 | Array3i intersection_count(sdf.phi.ni, sdf.phi.nj, sdf.phi.nk, 0); 81 | 82 | // Pre-compute double-precision dx and 1/dx. 83 | double Ddx = static_cast(sdf.dx); 84 | double _Ddx = 1.0 / Ddx; 85 | float _dx = static_cast(_Ddx); 86 | // Pre-compute double-precision origin. 87 | double o0 = static_cast(sdf.origin[0]); 88 | double o1 = static_cast(sdf.origin[1]); 89 | double o2 = static_cast(sdf.origin[2]); 90 | 91 | // initialize correct distances near the mesh and count intersections 92 | std::cout<<" rasterizing triangles"<sdf.phi.ni-1) i0=sdf.phi.ni-1; 105 | if (i1<0) i1=0; else if (i1>sdf.phi.ni-1) i1=sdf.phi.ni-1; 106 | if (j0<0) j0=0; else if (j0>sdf.phi.nj-1) j0=sdf.phi.nj-1; 107 | if (j1<0) j1=0; else if (j1>sdf.phi.nj-1) j1=sdf.phi.nj-1; 108 | if (k0<0) k0=0; else if (k0>sdf.phi.nk-1) k0=sdf.phi.nk-1; 109 | if (k1<0) k1=0; else if (k1>sdf.phi.nk-1) k1=sdf.phi.nk-1; 110 | // do distances and intersection counts in this chunk 111 | for(k=k0; k<=k1; ++k) for(j=j0; j<=j1; ++j) for(i=i0; i<=i1; ++i) { 112 | Vec3f gx(i*sdf.dx+sdf.origin[0], 113 | j*sdf.dx+sdf.origin[1], 114 | k*sdf.dx+sdf.origin[2]); 115 | float d = point_triangle_distance(gx, x[p], x[q], x[r]); 116 | if (d < sdf.phi(i,j,k)) { 117 | sdf.phi(i,j,k) = d; 118 | closest_tri(i,j,k) = t; 119 | } 120 | } 121 | // and do intersection counts 122 | for(int k=k0; k<=k1; ++k) for(int j=j0; j<=j1; ++j) { 123 | double gy=j*Ddx+sdf.origin[1], gz=k*Ddx+sdf.origin[2]; 124 | double a, b, c; 125 | if (point_in_triangle_2d(gy, gz, 126 | x[p][1], x[p][2], 127 | x[q][1], x[q][2], 128 | x[r][1], x[r][2], a, b, c)) { 129 | // estimate x coordinate of intersection 130 | double xint = a*x[p][0] + b*x[q][0] + c*x[r][0]; 131 | // convert to a grid coordinate 132 | int i_interval=(int)(std::ceil((xint-sdf.origin[0])*_dx)); 133 | // intersection is in (i_interval-1,i_interval] 134 | if (i_interval < 0) 135 | ++intersection_count(0, j, k); 136 | else if (i_interval < sdf.phi.ni) 137 | ++intersection_count(i_interval, j, k); 138 | // ignore intersections beyond the +x side of the grid 139 | } 140 | } 141 | } 142 | 143 | // fill in the rest of the distances with fast sweeping 144 | // (a single sweep is adequate to get rough approximation everywhere) 145 | std::cout<<" sweeping to rest of grid"< 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | int 13 | main(int argc, 14 | char **argv) 15 | { 16 | // 17 | // Parse input arguments 18 | // 19 | 20 | bool usage = false, failure = false; 21 | bool feature = false, automatic = false, surface = false, 22 | intermediate = false, optimize = false, unsafe = false; 23 | char* featureFile = NULL, *surfaceFile = NULL; 24 | float angleThreshold; 25 | 26 | if (argc < 4 || argc > 12) { 27 | usage = true; 28 | failure = true; 29 | } 30 | 31 | for (int arg = 4; arg < argc && !failure; ++arg) { 32 | if (strcmp(argv[arg], "-f") == 0) { 33 | if (feature) { 34 | std::printf("Error: already specified feature file\n"); 35 | usage = true; 36 | failure = true; 37 | break; 38 | } 39 | ++arg; 40 | if (arg >= argc || argv[arg][0] == '-') { 41 | std::printf("Error: Expected feature file after -f\n"); 42 | usage = true; 43 | --arg; 44 | } else { 45 | feature = true; 46 | featureFile = argv[arg]; 47 | } 48 | 49 | } else if (strcmp(argv[arg], "-a") == 0) { 50 | if (automatic) { 51 | std::printf("Error: already specified automatic feature " 52 | "generation\n"); 53 | usage = true; 54 | failure = true; 55 | break; 56 | } 57 | ++arg; 58 | if (arg >= argc || argv[arg][0] == '-') { 59 | std::printf("Error: Expected angle threshold after -a\n"); 60 | usage = true; 61 | --arg; 62 | } else { 63 | if (sscanf(argv[arg], "%f", &angleThreshold)) { 64 | automatic = true; 65 | } else { 66 | std::printf("Error interpreting [%s] as angle threshold\n", 67 | argv[arg]); 68 | usage = true; 69 | --arg; 70 | } 71 | } 72 | 73 | } else if (strcmp(argv[arg], "-s") == 0) { 74 | if (surface) { 75 | std::printf("Error: already specified surface output\n"); 76 | usage = true; 77 | failure = true; 78 | break; 79 | } 80 | ++arg; 81 | if (arg >= argc || argv[arg][0] == '-') { 82 | std::printf("Error: Expected OBJ filename after -s\n"); 83 | usage = true; 84 | --arg; 85 | } else { 86 | surface = true; 87 | surfaceFile = argv[arg]; 88 | } 89 | 90 | } else if (strcmp(argv[arg], "-i") == 0) { 91 | if (intermediate) { 92 | std::printf("Warning: Already specified intermediate " 93 | "outputs\n"); 94 | usage = true; 95 | } 96 | intermediate = true; 97 | 98 | } else if (strcmp(argv[arg], "-o") == 0) { 99 | if (optimize) { 100 | std::printf("Warning: Already specified optimization\n"); 101 | usage = true; 102 | } 103 | optimize = true; 104 | 105 | } else if (strcmp(argv[arg], "-u") == 0) { 106 | if (unsafe) { 107 | std::printf("Warning: Already specified unsafe option\n"); 108 | usage = true; 109 | } 110 | unsafe = true; 111 | 112 | } else { 113 | std::printf("Error: Invalid input argument: %s\n", argv[arg]); 114 | usage = true; 115 | failure = true; 116 | } 117 | } 118 | 119 | if (feature && automatic) { 120 | std::printf("Error: Feature file and automatic feature generation " 121 | "selected. Disabling automatic feature generation " 122 | "and using the given feature file.\n"); 123 | automatic = false; 124 | } 125 | 126 | if (usage) { 127 | std::printf("usage: quartet " 128 | "[-f ] [-a ] " 129 | "[-s ] [-i] [-o] \n" 130 | " -f -- Match the features in the given " 131 | " file\n" 132 | " -a -- Do automatic feature detection and matching on " 133 | "the input triangle mesh. " 134 | "Use the given .\n" 135 | " -s -- Output the surface of the tet mesh as an " 136 | "OBJ file\n" 137 | " -i -- Output intermediate tet meshes from each " 138 | "stage of the mesh generation algorithm.\n" 139 | " -o -- Enable mesh optimization.\n" 140 | " -u -- Enable unsafe feature-matching operations.\n"); 141 | } 142 | if (failure) { 143 | return 1; 144 | } 145 | 146 | 147 | // 148 | // Run quartet mesh generation 149 | // 150 | 151 | // Get a surface mesh 152 | std::vector surf_tri; 153 | std::vector surf_x; 154 | if (!read_objfile(surf_x, surf_tri, "%s", argv[1])) { 155 | std::printf("error reading [%s] as a .obj mesh\n", argv[1]); 156 | return 2; 157 | } 158 | if (surf_tri.size() < 4 || surf_x.size() < 4) { 159 | std::printf("Surface mesh is too small to have an interior.\n"); 160 | return 3; 161 | } 162 | 163 | // Get the grid spacing 164 | float dx=1; 165 | if (sscanf(argv[2], "%f", &dx) != 1) { 166 | std::printf("error interpreting [%s] as a float grid spacing\n", 167 | argv[2]); 168 | return 4; 169 | } 170 | 171 | // Find bounding box 172 | Vec3f xmin=surf_x[0], xmax=surf_x[0]; 173 | for (size_t i=1; i dihedralAngles; 266 | dihedralAngles.reserve(mesh.tSize()*6); 267 | for (size_t tIdx = 0; tIdx < mesh.tSize(); ++tIdx) 268 | { 269 | Tet currTet = mesh.getTet(tIdx); 270 | std::vector currAngles; 271 | currTet.dihedralAngles(currAngles); 272 | for (size_t i = 0; i < currAngles.size(); ++i) 273 | { 274 | dihedralAngles.push_back(currAngles[i] * 180.0 / M_PI); 275 | } 276 | } 277 | std::stringstream ss2; 278 | ss2 << filenameStr << "_hist.m"; 279 | std::ofstream out(ss2.str().c_str()); 280 | if (out.good()) 281 | { 282 | std::printf("Writing histogram to file: %s\n", ss2.str().c_str()); 283 | out << "histValues = [ "; 284 | for (size_t i = 0; i < dihedralAngles.size(); ++i) 285 | { 286 | out << dihedralAngles[i] << " "; 287 | } 288 | out << "];" << std::endl; 289 | } 290 | else 291 | { 292 | std::printf("Failed to write histogram file: %s\n", ss2.str().c_str()); 293 | } 294 | 295 | // Write out the exterior as a trimesh, if desired. 296 | if (surface) 297 | { 298 | std::vector surf_verts; 299 | std::vector surf_tris; 300 | mesh.getBoundary(surf_verts, surf_tris); 301 | std::printf("Writing surface mesh to file: %s\n", surfaceFile); 302 | result = write_objfile(surf_verts, surf_tris, surfaceFile); 303 | if (!result) 304 | { 305 | std::printf("ERROR: Failed to write surface mesh file.\n"); 306 | } 307 | } 308 | 309 | return 0; 310 | } 311 | 312 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_H 2 | #define UTIL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #ifndef M_PI 11 | const double M_PI = 3.1415926535897932384626433832795; 12 | #endif 13 | 14 | #ifdef WIN32 15 | #undef min 16 | #undef max 17 | #endif 18 | 19 | using std::min; 20 | using std::max; 21 | using std::swap; 22 | 23 | template 24 | inline T sqr(const T& x) 25 | { return x*x; } 26 | 27 | template 28 | inline T cube(const T& x) 29 | { return x*x*x; } 30 | 31 | template 32 | inline T min(T a1, T a2, T a3) 33 | { return min(a1, min(a2, a3)); } 34 | 35 | template 36 | inline T min(T a1, T a2, T a3, T a4) 37 | { return min(min(a1, a2), min(a3, a4)); } 38 | 39 | template 40 | inline T min(T a1, T a2, T a3, T a4, T a5) 41 | { return min(min(a1, a2), min(a3, a4), a5); } 42 | 43 | template 44 | inline T min(T a1, T a2, T a3, T a4, T a5, T a6) 45 | { return min(min(a1, a2), min(a3, a4), min(a5, a6)); } 46 | 47 | template 48 | inline T max(T a1, T a2, T a3) 49 | { return max(a1, max(a2, a3)); } 50 | 51 | template 52 | inline T max(T a1, T a2, T a3, T a4) 53 | { return max(max(a1, a2), max(a3, a4)); } 54 | 55 | template 56 | inline T max(T a1, T a2, T a3, T a4, T a5) 57 | { return max(max(a1, a2), max(a3, a4), a5); } 58 | 59 | template 60 | inline T max(T a1, T a2, T a3, T a4, T a5, T a6) 61 | { return max(max(a1, a2), max(a3, a4), max(a5, a6)); } 62 | 63 | template 64 | inline void minmax(T a1, T a2, T& amin, T& amax) 65 | { 66 | if(a1 76 | inline void minmax(T a1, T a2, T a3, T& amin, T& amax) 77 | { 78 | if(a1 101 | inline void minmax(T a1, T a2, T a3, T a4, T& amin, T& amax) 102 | { 103 | if(a1 123 | inline void minmax(T a1, T a2, T a3, T a4, T a5, T& amin, T& amax) 124 | { 125 | //@@@ the logic could be shortcircuited a lot! 126 | amin=min(a1,a2,a3,a4,a5); 127 | amax=max(a1,a2,a3,a4,a5); 128 | } 129 | 130 | template 131 | inline void minmax(T a1, T a2, T a3, T a4, T a5, T a6, T& amin, T& amax) 132 | { 133 | //@@@ the logic could be shortcircuited a lot! 134 | amin=min(a1,a2,a3,a4,a5,a6); 135 | amax=max(a1,a2,a3,a4,a5,a6); 136 | } 137 | 138 | template 139 | inline void update_minmax(T a1, T& amin, T& amax) 140 | { 141 | if(a1amax) amax=a1; 143 | } 144 | 145 | // swap so that a 147 | inline void sort(T &a, T &b) 148 | { 149 | if(a>b) swap(a,b); 150 | } 151 | 152 | // swap so that a 154 | inline void sort(T &a, T &b, T &c) 155 | { 156 | if(a>b) swap(a,b); 157 | if(a>c) swap(a,c); 158 | if(b>c) swap(b,c); 159 | } 160 | 161 | // swap so that a 163 | inline void sort(T &a, T &b, T &c, T &d) 164 | { 165 | if(a>b) swap(a,b); 166 | if(c>d) swap(c,d); 167 | if(a>c) swap(a,c); 168 | if(b>d) swap(b,d); 169 | if(b>c) swap(b,c); 170 | } 171 | 172 | template 173 | inline T clamp(T a, T lower, T upper) 174 | { 175 | if(aupper) return upper; 177 | else return a; 178 | } 179 | 180 | // only makes sense with T=float or double 181 | template 182 | inline T smooth_step(T r) 183 | { 184 | if(r<0) return 0; 185 | else if(r>1) return 1; 186 | return r*r*r*(10+r*(-15+r*6)); 187 | } 188 | 189 | // only makes sense with T=float or double 190 | template 191 | inline T smooth_step(T r, T r_lower, T r_upper, T value_lower, T value_upper) 192 | { return value_lower + smooth_step((r-r_lower)/(r_upper-r_lower)) * (value_upper-value_lower); } 193 | 194 | // only makes sense with T=float or double 195 | template 196 | inline T ramp(T r) 197 | { return smooth_step((r+1)/2)*2-1; } 198 | 199 | //#ifdef WIN32 200 | #ifdef _MSVC_VER 201 | inline int lround(double x) 202 | { 203 | if(x>0) 204 | return (x-floor(x)<0.5) ? (int)floor(x) : (int)ceil(x); 205 | else 206 | return (x-floor(x)<=0.5) ? (int)floor(x) : (int)ceil(x); 207 | } 208 | 209 | inline double remainder(double x, double y) 210 | { 211 | return x-std::floor(x/y+0.5)*y; 212 | } 213 | #endif 214 | 215 | inline unsigned int round_up_to_power_of_two(unsigned int n) 216 | { 217 | int exponent=0; 218 | --n; 219 | while(n){ 220 | ++exponent; 221 | n>>=1; 222 | } 223 | return 1<1){ 230 | ++exponent; 231 | n>>=1; 232 | } 233 | return 1<>16); 243 | i*=2654435769u; 244 | i^=(i>>16); 245 | i*=2654435769u; 246 | return i; 247 | } 248 | 249 | // the inverse of randhash 250 | inline unsigned int unhash(unsigned int h) 251 | { 252 | h*=340573321u; 253 | h^=(h>>16); 254 | h*=340573321u; 255 | h^=(h>>16); 256 | h*=340573321u; 257 | h^=0xA3C59AC3u; 258 | return h; 259 | } 260 | 261 | // returns repeatable stateless pseudo-random number in [0,1] 262 | inline double randhashd(unsigned int seed) 263 | { return randhash(seed)/(double)UINT_MAX; } 264 | inline float randhashf(unsigned int seed) 265 | { return randhash(seed)/(float)UINT_MAX; } 266 | 267 | // returns repeatable stateless pseudo-random number in [a,b] 268 | inline double randhashd(unsigned int seed, double a, double b) 269 | { return (b-a)*randhash(seed)/(double)UINT_MAX + a; } 270 | inline float randhashf(unsigned int seed, float a, float b) 271 | { return ( (b-a)*randhash(seed)/(float)UINT_MAX + a); } 272 | 273 | inline int intlog2(int x) 274 | { 275 | int exp=-1; 276 | while(x){ 277 | x>>=1; 278 | ++exp; 279 | } 280 | return exp; 281 | } 282 | 283 | template 284 | inline void get_barycentric(T x, int& i, T& f, int i_low, int i_high) 285 | { 286 | T s=std::floor(x); 287 | i=(int)s; 288 | if(ii_high-2){ 292 | i=i_high-2; 293 | f=1; 294 | }else 295 | f=(T)(x-s); 296 | } 297 | 298 | template 299 | inline S lerp(const S& value0, const S& value1, T f) 300 | { return (1-f)*value0 + f*value1; } 301 | 302 | template 303 | inline S bilerp(const S& v00, const S& v10, 304 | const S& v01, const S& v11, 305 | T fx, T fy) 306 | { 307 | return lerp(lerp(v00, v10, fx), 308 | lerp(v01, v11, fx), 309 | fy); 310 | } 311 | 312 | template 313 | inline S trilerp(const S& v000, const S& v100, 314 | const S& v010, const S& v110, 315 | const S& v001, const S& v101, 316 | const S& v011, const S& v111, 317 | T fx, T fy, T fz) 318 | { 319 | return lerp(bilerp(v000, v100, v010, v110, fx, fy), 320 | bilerp(v001, v101, v011, v111, fx, fy), 321 | fz); 322 | } 323 | 324 | template 325 | inline S quadlerp(const S& v0000, const S& v1000, 326 | const S& v0100, const S& v1100, 327 | const S& v0010, const S& v1010, 328 | const S& v0110, const S& v1110, 329 | const S& v0001, const S& v1001, 330 | const S& v0101, const S& v1101, 331 | const S& v0011, const S& v1011, 332 | const S& v0111, const S& v1111, 333 | T fx, T fy, T fz, T ft) 334 | { 335 | return lerp(trilerp(v0000, v1000, v0100, v1100, v0010, v1010, v0110, v1110, fx, fy, fz), 336 | trilerp(v0001, v1001, v0101, v1101, v0011, v1011, v0111, v1111, fx, fy, fz), 337 | ft); 338 | } 339 | 340 | // f should be between 0 and 1, with f=0.5 corresponding to balanced weighting between w0 and w2 341 | template 342 | inline void quadratic_bspline_weights(T f, T& w0, T& w1, T& w2) 343 | { 344 | w0=T(0.5)*sqr(f-1); 345 | w1=T(0.75)-sqr(f-T(0.5));; 346 | w2=T(0.5)*sqr(f); 347 | } 348 | 349 | // f should be between 0 and 1 350 | template 351 | inline void cubic_interp_weights(T f, T& wneg1, T& w0, T& w1, T& w2) 352 | { 353 | T f2(f*f), f3(f2*f); 354 | wneg1=-T(1./3)*f+T(1./2)*f2-T(1./6)*f3; 355 | w0=1-f2+T(1./2)*(f3-f); 356 | w1=f+T(1./2)*(f2-f3); 357 | w2=T(1./6)*(f3-f); 358 | } 359 | 360 | template 361 | inline S cubic_interp(const S& value_neg1, const S& value0, const S& value1, const S& value2, T f) 362 | { 363 | T wneg1, w0, w1, w2; 364 | cubic_interp_weights(f, wneg1, w0, w1, w2); 365 | return wneg1*value_neg1 + w0*value0 + w1*value1 + w2*value2; 366 | } 367 | 368 | template 369 | void zero(std::vector& v) 370 | { for(int i=(int)v.size()-1; i>=0; --i) v[i]=0; } 371 | 372 | template 373 | T abs_max(const std::vector& v) 374 | { 375 | T m=0; 376 | for(int i=(int)v.size()-1; i>=0; --i){ 377 | if(std::fabs(v[i])>m) 378 | m=std::fabs(v[i]); 379 | } 380 | return m; 381 | } 382 | 383 | template 384 | bool contains(const std::vector& a, T e) 385 | { 386 | for(unsigned int i=0; i 392 | void add_unique(std::vector& a, T e) 393 | { 394 | for(unsigned int i=0; i 400 | void insert(std::vector& a, unsigned int index, T e) 401 | { 402 | a.push_back(a.back()); 403 | for(unsigned int i=(unsigned int)a.size()-1; i>index; --i) 404 | a[i]=a[i-1]; 405 | a[index]=e; 406 | } 407 | 408 | template 409 | void erase(std::vector& a, unsigned int index) 410 | { 411 | for(unsigned int i=index; i 417 | void erase_swap(std::vector& a, unsigned int index) 418 | { 419 | for(unsigned int i=index; i 425 | void erase_unordered(std::vector& a, unsigned int index) 426 | { 427 | a[index]=a.back(); 428 | a.pop_back(); 429 | } 430 | 431 | template 432 | void erase_unordered_swap(std::vector& a, unsigned int index) 433 | { 434 | swap(a[index], a.back()); 435 | a.pop_back(); 436 | } 437 | 438 | template 439 | void find_and_erase_unordered(std::vector& a, const T& doomed_element) 440 | { 441 | for(unsigned int i=0; i 449 | void replace_once(std::vector& a, const T& old_element, const T& new_element) 450 | { 451 | for(unsigned int i=0; i 459 | void write_matlab(std::ostream& output, const std::vector& a, const char *variable_name, bool column_vector=true, int significant_digits=18) 460 | { 461 | output< 5 | #include 6 | #include 7 | #include "util.h" 8 | 9 | // Defines a thin wrapper around fixed size C-style arrays, using template 10 | // parameters, which is useful for dealing with vectors of different dimensions. 11 | // For example, float[3] is equivalent to Vec<3,float>. 12 | // Entries in the vector are accessed with the overloaded [] operator, so 13 | // for example if x is a Vec<3,float>, then the middle entry is x[1]. 14 | // For convenience, there are a number of typedefs for abbreviation: 15 | // Vec<3,float> -> Vec3f 16 | // Vec<2,int> -> Vec2i 17 | // and so on. 18 | // Arithmetic operators are appropriately overloaded, and functions are defined 19 | // for additional operations (such as dot-products, norms, cross-products, etc.) 20 | 21 | template 22 | struct Vec 23 | { 24 | T v[N]; 25 | 26 | Vec(void) 27 | {} 28 | 29 | template 30 | explicit Vec(S value_for_all) 31 | { for(unsigned int i=0; i 34 | explicit Vec(const S *source) 35 | { for(unsigned int i=0; i 38 | explicit Vec(const Vec& source) 39 | { for(unsigned int i=0; i 42 | Vec(S v0, S v1) 43 | { 44 | assert(N==2); 45 | v[0]=v0; v[1]=v1; 46 | } 47 | 48 | template 49 | Vec(S v0, S v1, S v2) 50 | { 51 | assert(N==3); 52 | v[0]=v0; v[1]=v1; v[2]=v2; 53 | } 54 | 55 | template 56 | Vec(S v0, S v1, S v2, S v3) 57 | { 58 | assert(N==4); 59 | v[0]=v0; v[1]=v1; v[2]=v2; v[3]=v3; 60 | } 61 | 62 | template 63 | Vec(S v0, S v1, S v2, S v3, S v4) 64 | { 65 | assert(N==5); 66 | v[0]=v0; v[1]=v1; v[2]=v2; v[3]=v3; v[4]=v4; 67 | } 68 | 69 | template 70 | Vec(S v0, S v1, S v2, S v3, S v4, S v5) 71 | { 72 | assert(N==6); 73 | v[0]=v0; v[1]=v1; v[2]=v2; v[3]=v3; v[4]=v4; v[5]=v5; 74 | } 75 | 76 | T &operator[](int index) 77 | { 78 | assert(0<=index && (unsigned int)index 95 | Vec operator+=(const Vec &w) 96 | { 97 | for(unsigned int i=0; i 102 | Vec operator+(const Vec &w) const 103 | { 104 | Vec sum(*this); 105 | sum+=w; 106 | return sum; 107 | } 108 | 109 | template 110 | Vec operator-=(const Vec &w) 111 | { 112 | for(unsigned int i=0; i operator-(void) const // unary minus 117 | { 118 | Vec negative; 119 | for(unsigned int i=0; i operator-(const Vec &w) const // (binary) subtraction 124 | { 125 | Vec diff(*this); 126 | diff-=w; 127 | return diff; 128 | } 129 | 130 | template 131 | Vec operator*=(S a) 132 | { 133 | for(unsigned int i=0; i 138 | Vec operator*(S a) const 139 | { 140 | Vec w(*this); 141 | w*=a; 142 | return w; 143 | } 144 | 145 | template 146 | Vec operator*=(const Vec &w) 147 | { 148 | for(unsigned int i=0; i operator*(const Vec &w) const 153 | { 154 | Vec componentwise_product; 155 | for(unsigned int i=0; i 160 | Vec operator/=(S a) 161 | { 162 | for(unsigned int i=0; i 167 | Vec operator/(S a) const 168 | { 169 | Vec w(*this); 170 | w/=a; 171 | return w; 172 | } 173 | }; 174 | 175 | typedef Vec<2,double> Vec2d; 176 | typedef Vec<2,float> Vec2f; 177 | typedef Vec<2,int> Vec2i; 178 | typedef Vec<2,unsigned int> Vec2ui; 179 | typedef Vec<2,short> Vec2s; 180 | typedef Vec<2,unsigned short> Vec2us; 181 | typedef Vec<2,char> Vec2c; 182 | typedef Vec<2,unsigned char> Vec2uc; 183 | 184 | typedef Vec<3,double> Vec3d; 185 | typedef Vec<3,float> Vec3f; 186 | typedef Vec<3,int> Vec3i; 187 | typedef Vec<3,unsigned int> Vec3ui; 188 | typedef Vec<3,short> Vec3s; 189 | typedef Vec<3,unsigned short> Vec3us; 190 | typedef Vec<3,char> Vec3c; 191 | typedef Vec<3,unsigned char> Vec3uc; 192 | 193 | typedef Vec<4,double> Vec4d; 194 | typedef Vec<4,float> Vec4f; 195 | typedef Vec<4,int> Vec4i; 196 | typedef Vec<4,unsigned int> Vec4ui; 197 | typedef Vec<4,short> Vec4s; 198 | typedef Vec<4,unsigned short> Vec4us; 199 | typedef Vec<4,char> Vec4c; 200 | typedef Vec<4,unsigned char> Vec4uc; 201 | 202 | typedef Vec<6,double> Vec6d; 203 | typedef Vec<6,float> Vec6f; 204 | typedef Vec<6,unsigned int> Vec6ui; 205 | typedef Vec<6,int> Vec6i; 206 | typedef Vec<6,short> Vec6s; 207 | typedef Vec<6,unsigned short> Vec6us; 208 | typedef Vec<6,char> Vec6c; 209 | typedef Vec<6,unsigned char> Vec6uc; 210 | 211 | 212 | template 213 | T mag2(const Vec &a) 214 | { 215 | T l=sqr(a.v[0]); 216 | for(unsigned int i=1; i 221 | T mag(const Vec &a) 222 | { return sqrt(mag2(a)); } 223 | 224 | template 225 | inline T dist2(const Vec &a, const Vec &b) 226 | { 227 | T d=sqr(a.v[0]-b.v[0]); 228 | for(unsigned int i=1; i 233 | inline T dist(const Vec &a, const Vec &b) 234 | { return std::sqrt(dist2(a,b)); } 235 | 236 | template 237 | inline void normalize(Vec &a) 238 | { a/=mag(a); } 239 | 240 | template 241 | inline Vec normalized(const Vec &a) 242 | { return a/mag(a); } 243 | 244 | template 245 | inline T infnorm(const Vec &a) 246 | { 247 | T d=std::fabs(a.v[0]); 248 | for(unsigned int i=1; i 253 | void zero(Vec &a) 254 | { 255 | for(unsigned int i=0; i 260 | std::ostream &operator<<(std::ostream &out, const Vec &v) 261 | { 262 | out< 269 | std::istream &operator>>(std::istream &in, Vec &v) 270 | { 271 | in>>v.v[0]; 272 | for(unsigned int i=1; i>v.v[i]; 274 | return in; 275 | } 276 | 277 | template 278 | inline bool operator==(const Vec &a, const Vec &b) 279 | { 280 | bool t = (a.v[0] == b.v[0]); 281 | unsigned int i=1; 282 | while(i 290 | inline bool operator!=(const Vec &a, const Vec &b) 291 | { 292 | bool t = (a.v[0] != b.v[0]); 293 | unsigned int i=1; 294 | while(i 303 | inline bool operator<(const Vec &a, const Vec &b) 304 | { 305 | for(unsigned int i=0; ib.v[i]) return false; 308 | } 309 | return false; 310 | } 311 | 312 | template 313 | inline Vec operator*(S a, const Vec &v) 314 | { 315 | Vec w(v); 316 | w*=a; 317 | return w; 318 | } 319 | 320 | template 321 | inline T min(const Vec &a) 322 | { 323 | T m=a.v[0]; 324 | for(unsigned int i=1; i 329 | inline Vec min_union(const Vec &a, const Vec &b) 330 | { 331 | Vec m; 332 | for(unsigned int i=0; i 337 | inline Vec max_union(const Vec &a, const Vec &b) 338 | { 339 | Vec m; 340 | for(unsigned int i=0; i b.v[i]) ? m.v[i]=a.v[i] : m.v[i]=b.v[i]; 341 | return m; 342 | } 343 | 344 | template 345 | inline T max(const Vec &a) 346 | { 347 | T m=a.v[0]; 348 | for(unsigned int i=1; im) m=a.v[i]; 349 | return m; 350 | } 351 | 352 | template 353 | inline T dot(const Vec &a, const Vec &b) 354 | { 355 | T d=a.v[0]*b.v[0]; 356 | for(unsigned int i=1; i 361 | inline Vec<2,T> rotate(const Vec<2,T>& a, float angle) 362 | { 363 | T c = cos(angle); 364 | T s = sin(angle); 365 | return Vec<2,T>(c*a[0] - s*a[1],s*a[0] + c*a[1]); // counter-clockwise 366 | } 367 | 368 | template 369 | inline Vec<2,T> perp(const Vec<2,T> &a) 370 | { return Vec<2,T>(-a.v[1], a.v[0]); } // counter-clockwise by 90 degrees 371 | 372 | template 373 | inline T cross(const Vec<2,T> &a, const Vec<2,T> &b) 374 | { return a.v[0]*b.v[1]-a.v[1]*b.v[0]; } 375 | 376 | template 377 | inline Vec<3,T> cross(const Vec<3,T> &a, const Vec<3,T> &b) 378 | { 379 | return Vec<3,T>(a.v[1]*b.v[2]-a.v[2]*b.v[1], 380 | a.v[2]*b.v[0]-a.v[0]*b.v[2], 381 | a.v[0]*b.v[1]-a.v[1]*b.v[0]); 382 | } 383 | 384 | template 385 | inline T triple(const Vec<3,T> &a, const Vec<3,T> &b, const Vec<3,T> &c) 386 | { 387 | return a.v[0]*(b.v[1]*c.v[2]-b.v[2]*c.v[1]) 388 | +a.v[1]*(b.v[2]*c.v[0]-b.v[0]*c.v[2]) 389 | +a.v[2]*(b.v[0]*c.v[1]-b.v[1]*c.v[0]); 390 | } 391 | 392 | template 393 | inline unsigned int hash(const Vec &a) 394 | { 395 | unsigned int h=a.v[0]; 396 | for(unsigned int i=1; i 402 | inline void assign(const Vec &a, T &a0, T &a1) 403 | { 404 | assert(N==2); 405 | a0=a.v[0]; a1=a.v[1]; 406 | } 407 | 408 | template 409 | inline void assign(const Vec &a, T &a0, T &a1, T &a2) 410 | { 411 | assert(N==3); 412 | a0=a.v[0]; a1=a.v[1]; a2=a.v[2]; 413 | } 414 | 415 | template 416 | inline void assign(const Vec &a, T &a0, T &a1, T &a2, T &a3) 417 | { 418 | assert(N==4); 419 | a0=a.v[0]; a1=a.v[1]; a2=a.v[2]; a3=a.v[3]; 420 | } 421 | 422 | template 423 | inline void assign(const Vec &a, T &a0, T &a1, T &a2, T &a3, T &a4, T &a5) 424 | { 425 | assert(N==6); 426 | a0=a.v[0]; a1=a.v[1]; a2=a.v[2]; a3=a.v[3]; a4=a.v[4]; a5=a.v[5]; 427 | } 428 | 429 | template 430 | inline Vec round(const Vec &a) 431 | { 432 | Vec rounded; 433 | for(unsigned int i=0; i 439 | inline Vec floor(const Vec &a) 440 | { 441 | Vec rounded; 442 | for(unsigned int i=0; i 448 | inline Vec ceil(const Vec &a) 449 | { 450 | Vec rounded; 451 | for(unsigned int i=0; i 457 | inline Vec fabs(const Vec &a) 458 | { 459 | Vec result; 460 | for(unsigned int i=0; i 466 | inline void minmax(const Vec &x0, const Vec &x1, Vec &xmin, Vec &xmax) 467 | { 468 | for(unsigned int i=0; i 473 | inline void minmax(const Vec &x0, const Vec &x1, const Vec &x2, Vec &xmin, Vec &xmax) 474 | { 475 | for(unsigned int i=0; i 480 | inline void minmax(const Vec &x0, const Vec &x1, const Vec &x2, const Vec &x3, 481 | Vec &xmin, Vec &xmax) 482 | { 483 | for(unsigned int i=0; i 488 | inline void minmax(const Vec &x0, const Vec &x1, const Vec &x2, const Vec &x3, const Vec &x4, 489 | Vec &xmin, Vec &xmax) 490 | { 491 | for(unsigned int i=0; i 496 | inline void minmax(const Vec &x0, const Vec &x1, const Vec &x2, const Vec &x3, const Vec &x4, 497 | const Vec &x5, Vec &xmin, Vec &xmax) 498 | { 499 | for(unsigned int i=0; i 504 | inline void update_minmax(const Vec &x, Vec &xmin, Vec &xmax) 505 | { 506 | for(unsigned int i=0; i 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "tet_quality.h" 10 | 11 | 12 | // Return a random number in the range -D to D 13 | float 14 | randf(float D) 15 | { 16 | return D*(2*static_cast(rand())/static_cast(RAND_MAX) - 1); 17 | } 18 | 19 | 20 | // Do local optimization of the position of the given vertex. 21 | float 22 | optimize_vertex(int vertex, 23 | TetMesh& mesh, 24 | const SDF &sdf, 25 | const FeatureSet& featureSet, 26 | bool isBoundary, 27 | int featureIndex) 28 | { 29 | static const int NUM_RAND_PERTURBATIONS = 16; 30 | std::vector testx; 31 | 32 | const float P=0.5*sdf.dx; // max perturbation in each coordinate 33 | 34 | if (featureIndex != -1) { 35 | // Optimize along the feature to which the vertex is constrained. 36 | // Generate random perturbation points along feature. 37 | // Do this by distributing points along the feature's tangent vector, 38 | // then projecting them all onto the feature. 39 | Feature feature; 40 | featureSet.getFeature(featureIndex, feature); 41 | Vec3f tangent = feature.getTangent(mesh.V(vertex)); 42 | normalize(tangent); 43 | for (int i = 0; i < NUM_RAND_PERTURBATIONS; ++i) { 44 | testx.push_back(feature.projectToFeature(mesh.V(vertex) + 45 | randf(P)*tangent)); 46 | } 47 | // and include original point and extreme points along feature. 48 | testx.push_back(feature.projectToFeature(mesh.V(vertex))); 49 | testx.push_back(feature.projectToFeature(mesh.V(vertex)-P*tangent)); 50 | testx.push_back(feature.projectToFeature(mesh.V(vertex)+P*tangent)); 51 | 52 | } else if (isBoundary) { 53 | // Generate random perturbation points on tangent plane, in 54 | // a square of side length dx, then projected onto isosurface 55 | Vec3f normal = sdf.normal(mesh.V(vertex)); 56 | if (mag2(normal) == 0.0) 57 | normal = Vec3f(0.0, 1.0, 0.0); // Default to y-axis if no normal. 58 | Vec3f u(1,0,0); 59 | if(std::fabs(normal[0])>.5) 60 | u = Vec3f(-normal[1], normal[0], 0.f); 61 | else 62 | u = Vec3f(0.f, -normal[2], normal[1]); 63 | u /= mag(u); 64 | Vec3f v = cross(normal, u); 65 | for (int i = 0; i < NUM_RAND_PERTURBATIONS; ++i) { 66 | Vec3f t = mesh.V(vertex) + randf(P)*u + randf(P)*v; 67 | testx.push_back(sdf.projectToIsosurface(t)); 68 | } 69 | // also original point and projected corners of unit square 70 | testx.push_back(sdf.projectToIsosurface(mesh.V(vertex))); 71 | testx.push_back(sdf.projectToIsosurface(mesh.V(vertex)-P*u-P*v)); 72 | testx.push_back(sdf.projectToIsosurface(mesh.V(vertex)+P*u-P*v)); 73 | testx.push_back(sdf.projectToIsosurface(mesh.V(vertex)-P*u+P*v)); 74 | testx.push_back(sdf.projectToIsosurface(mesh.V(vertex)+P*u+P*v)); 75 | 76 | } else { // interior vertex 77 | // Generate random perturbation points within cube 78 | for (int i = 0; i < NUM_RAND_PERTURBATIONS; ++i) { 79 | testx.push_back(mesh.V(vertex)+Vec3f(randf(P),randf(P),randf(P))); 80 | } 81 | // and include original point and corners of cube 82 | testx.push_back(mesh.V(vertex)); 83 | testx.push_back(mesh.V(vertex)+Vec3f(-P,-P,-P)); 84 | testx.push_back(mesh.V(vertex)+Vec3f(+P,-P,-P)); 85 | testx.push_back(mesh.V(vertex)+Vec3f(-P,+P,-P)); 86 | testx.push_back(mesh.V(vertex)+Vec3f(+P,+P,-P)); 87 | testx.push_back(mesh.V(vertex)+Vec3f(-P,-P,+P)); 88 | testx.push_back(mesh.V(vertex)+Vec3f(+P,-P,+P)); 89 | testx.push_back(mesh.V(vertex)+Vec3f(-P,+P,+P)); 90 | testx.push_back(mesh.V(vertex)+Vec3f(+P,+P,+P)); 91 | } 92 | 93 | // Find incident tetrahedra, and the indices that refer to the 94 | // current vertex within those tetrahedra. 95 | std::vector incidentTets; 96 | mesh.getIncidentTets(vertex, incidentTets); 97 | std::vector incidentVertexIndex(incidentTets.size(), -1); 98 | for (size_t i = 0; i < incidentTets.size(); ++i) { 99 | for (int v = 0; v < 4; ++v) { 100 | if (mesh.V(vertex) == incidentTets[i][v]) { 101 | incidentVertexIndex[i] = v; 102 | continue; 103 | } 104 | } 105 | assert(incidentVertexIndex[i] != -1); 106 | } 107 | 108 | 109 | // Find quality metrics for tetrahedra created from each perturbation 110 | // of the given vertex. 111 | // We're looking for the perturbation that gives the highest quality metric. 112 | float bestQuality = -FLT_MAX; 113 | int bestPerturb = -1; 114 | 115 | for (size_t p = 0; p < testx.size(); ++p) { 116 | // Create tetrahedra to represent the local neighbourhood if 117 | // the given vertex were perturbed by the current amount. 118 | Vec3f perturbedVert = testx[p]; 119 | 120 | // Compute quality of each tetrahedron and use the worst one 121 | // as representative of the current perturbation. 122 | float perturbQuality = FLT_MAX; 123 | for (size_t t = 0; t < incidentTets.size(); ++t) { 124 | 125 | // Perturb the current vertex in the tetrahedron. 126 | incidentTets[t][incidentVertexIndex[t]] = perturbedVert; 127 | 128 | float tetQuality = compute_tet_quality(incidentTets[t]); 129 | 130 | // Keep the lowest-quality tet's quality measure. 131 | if (tetQuality < perturbQuality) { 132 | perturbQuality = tetQuality; 133 | } 134 | } 135 | 136 | // Check whether the current perturbation is the best so far. 137 | if (perturbQuality > bestQuality) { 138 | bestQuality = perturbQuality; 139 | bestPerturb = p; 140 | } else if (perturbQuality == bestQuality && 141 | bestPerturb >= 0) { 142 | // To break a tie, take the smallest perturbation. 143 | if (dist2(mesh.V(vertex), testx[p]) < 144 | dist2(mesh.V(vertex), testx[bestPerturb])) { 145 | bestQuality = perturbQuality; 146 | bestPerturb = p; 147 | } 148 | } 149 | } 150 | 151 | // Pick the perturbation that yields the best quality (lowest value) 152 | // and apply it to the vertex. 153 | if (bestPerturb >= 0) { 154 | mesh.V(vertex) = testx[bestPerturb]; 155 | 156 | // Return the quality metric for the new vertex. 157 | return bestQuality; 158 | } 159 | 160 | // Just in case we failed to get a good perturbation, 161 | // compute quality of incident tets. 162 | float quality = FLT_MAX; 163 | for (size_t t = 0; t < incidentTets.size(); ++t) { 164 | 165 | // Move the vertex back to its original location in the tetrahedron. 166 | incidentTets[t][incidentVertexIndex[t]] = mesh.V(vertex); 167 | 168 | float tetQuality = compute_tet_quality(incidentTets[t]); 169 | 170 | // Keep the lowest-quality tet's quality measure. 171 | if (tetQuality < quality) { 172 | quality = tetQuality; 173 | } 174 | } 175 | return quality; 176 | } 177 | 178 | 179 | float 180 | optimize_tet_mesh(TetMesh& mesh, 181 | const SDF& sdf, 182 | const std::vector& boundary_verts, 183 | const FeatureSet& featureSet, 184 | const std::vector& feature_endpoints, 185 | const std::map& vertex_feature_map) 186 | { 187 | std::vector isBoundary(mesh.vSize(), false); 188 | std::vector isFeatureEndpoint(mesh.vSize(), false); 189 | std::vector vertexFeatures(mesh.vSize(), -1); 190 | 191 | for (size_t b = 0; b < boundary_verts.size(); ++b) { 192 | assert(boundary_verts[b] < (int)isBoundary.size()); 193 | isBoundary[boundary_verts[b]] = true; 194 | } 195 | for (size_t f = 0; f < feature_endpoints.size(); ++f) { 196 | assert(feature_endpoints[f] < (int)isFeatureEndpoint.size()); 197 | isFeatureEndpoint[feature_endpoints[f]] = true; 198 | } 199 | for (std::map::const_iterator vfItr = vertex_feature_map.begin(); 200 | vfItr != vertex_feature_map.end(); ++vfItr) { 201 | assert(vfItr->first < (int)vertexFeatures.size()); 202 | vertexFeatures[vfItr->first] = vfItr->second; 203 | } 204 | 205 | static const int MAX_ITERATIONS = 20; 206 | static const float MIN_IMPROVEMENT_RATIO = 1.0; 207 | 208 | // The "quality" of the tetrahedra will be represented by a 209 | // number that we want to maximize. 210 | // Therefore, a higher "quality" value => better tets 211 | float tetQuality = -1.0; 212 | 213 | for (int i = 0; i < MAX_ITERATIONS; ++i) { 214 | float currTetQuality = FLT_MAX; 215 | 216 | // First loop over interior vertices 217 | for (size_t v = 0; v < mesh.vSize(); ++v) { 218 | if (!isBoundary[v] && !isFeatureEndpoint[v] 219 | && vertexFeatures[v] == -1) { 220 | float quality = optimize_vertex(v, mesh, sdf, featureSet, 221 | false, -1); 222 | if (quality < currTetQuality) { 223 | currTetQuality = quality; 224 | } 225 | } 226 | } 227 | 228 | // Then loop over boundary vertices 229 | for (size_t v = 0; v < mesh.vSize(); ++v) { 230 | if (isBoundary[v] && !isFeatureEndpoint[v] 231 | && vertexFeatures[v] == -1) { 232 | float quality = optimize_vertex(v, mesh, sdf, featureSet, 233 | true, -1); 234 | if (quality < currTetQuality) { 235 | currTetQuality = quality; 236 | } 237 | } 238 | } 239 | 240 | // Then loop over feature vertices. 241 | for (size_t v = 0; v < mesh.vSize(); ++v) { 242 | if (vertexFeatures[v] != -1 && !isFeatureEndpoint[v]) { 243 | float quality = optimize_vertex(v, mesh, sdf, featureSet, 244 | false, vertexFeatures[v]); 245 | if (quality < currTetQuality) { 246 | currTetQuality = quality; 247 | } 248 | } 249 | } 250 | 251 | std::cout << "." << std::flush; 252 | 253 | // Terminate if quality did not improve significantly. 254 | if (currTetQuality <= tetQuality*MIN_IMPROVEMENT_RATIO) { 255 | std::cout << std::endl; 256 | return currTetQuality; 257 | } 258 | 259 | tetQuality = currTetQuality; 260 | } 261 | 262 | std::cout << std::endl; 263 | 264 | return tetQuality; 265 | } 266 | 267 | 268 | /* 269 | float 270 | optimize_tet_mesh(TetMesh& mesh, 271 | const SDF& sdf, 272 | const std::vector &boundary_verts, 273 | const std::vector &feature_verts, 274 | const std::map &feature_edge_verts, 275 | const std::map &vertex_feature_map) 276 | { 277 | // Do a breadth-first search through the tet mesh, starting at the 278 | // feature vertices and boundary vertices 279 | // (these are the ones that have been perturbed out of lattice locations). 280 | // At each tet vertex, try a bunch of new positions and pick the one 281 | // that optimizes our tet quality metric for the incident tets. 282 | 283 | std::queue bfsQueue; 284 | std::vector alreadyTraversed(mesh.v.size(), false); 285 | std::vector isBoundary(mesh.v.size(), false); 286 | std::vector isFeature(mesh.v.size(), false); 287 | std::vector isFeatureEdge(mesh.v.size(), false); 288 | 289 | // The "quality" of the tetrahedra will be represented by some 290 | // number that we want to minimize. 291 | // Therefore, a lower "quality" value => better tets 292 | float maxQualityMetric = 0.0f; 293 | 294 | for (std::vector::const_iterator fItr = feature_verts.begin(); 295 | fItr != feature_verts.end(); ++fItr) { 296 | isFeature[*fItr] = true; 297 | } 298 | for (std::map::const_iterator fItr = feature_edge_verts.begin(); 299 | fItr != feature_edge_verts.end(); ++fItr) { 300 | isFeatureEdge[fItr->first] = true; 301 | } 302 | for (std::vector::const_iterator bItr = boundary_verts.begin(); 303 | bItr != boundary_verts.end(); ++bItr) { 304 | isBoundary[*bItr] = true; 305 | } 306 | 307 | // Add feature vertices to queue, followed by boundary vertices. 308 | // This ensures that all boundary vertices are visited first by the BFS. 309 | if (!feature_verts.empty()) { 310 | for (std::vector::const_iterator fItr = feature_verts.begin(); 311 | fItr != feature_verts.end(); ++fItr) { 312 | if (!alreadyTraversed[*fItr]) { 313 | bfsQueue.push(*fItr); 314 | alreadyTraversed[*fItr] = true; 315 | } 316 | } 317 | } else { 318 | // If there are no feature vertices, start from boundary vertices. 319 | for (std::vector::const_iterator bItr = boundary_verts.begin(); 320 | bItr != boundary_verts.end(); ++bItr) { 321 | if (!alreadyTraversed[*bItr]) { 322 | bfsQueue.push(*bItr); 323 | alreadyTraversed[*bItr] = true; 324 | } 325 | } 326 | } 327 | 328 | // Breadth-First Search loop 329 | while (!bfsQueue.empty()) { 330 | int currVertex = bfsQueue.front(); 331 | bfsQueue.pop(); 332 | 333 | std::vector incidentTets; 334 | std::set neighbours; 335 | 336 | // Find the tets that contain the current vertex, 337 | // and its neighbouring vertices. 338 | // Shouldn't need to loop over all tets to get vertex+neighbours... 339 | for (size_t t = 0; t < mesh.t.size(); ++t) { 340 | if (mesh.t[t][0] == currVertex || mesh.t[t][1] == currVertex || 341 | mesh.t[t][2] == currVertex || mesh.t[t][3] == currVertex) { 342 | incidentTets.push_back(t); 343 | neighbours.insert(mesh.t[t][0]); 344 | neighbours.insert(mesh.t[t][1]); 345 | neighbours.insert(mesh.t[t][2]); 346 | neighbours.insert(mesh.t[t][3]); 347 | } 348 | } 349 | 350 | // Because we didn't guard against the current vertex being added 351 | // to the neighbours set, remove it from the set. 352 | neighbours.erase(currVertex); 353 | 354 | // Add neighbours to queue if they haven't been already. 355 | for (std::set::iterator nItr = neighbours.begin(); 356 | nItr != neighbours.end(); ++nItr) { 357 | if (!alreadyTraversed[*nItr]) { 358 | bfsQueue.push(*nItr); 359 | alreadyTraversed[*nItr] = true; 360 | } 361 | } 362 | 363 | // Don't move feature vertices. 364 | if (isFeature[currVertex]) { 365 | continue; 366 | } 367 | 368 | // Retrieve feature edge, if it exists. 369 | Vec3f edge(0.0f, 0.0f, 0.0f); 370 | std::map::const_iterator e = 371 | feature_edge_verts.find(currVertex); 372 | if (e != feature_edge_verts.end()) { 373 | edge = e->second; 374 | } 375 | 376 | // Use vertex_feature_map to know which verts were snapped to 377 | // which polyline features. 378 | 379 | // Do optimization of current vertex location. 380 | float quality = optimize_vertex(currVertex, mesh, sdf, 381 | incidentTets, 382 | isBoundary[currVertex], 383 | isFeatureEdge[currVertex], 384 | edge); 385 | 386 | // Update global quality metric. 387 | if (quality > maxQualityMetric) { 388 | maxQualityMetric = quality; 389 | } 390 | } 391 | 392 | return maxQualityMetric; 393 | } 394 | */ 395 | 396 | -------------------------------------------------------------------------------- /meshes/block.feat: -------------------------------------------------------------------------------- 1 | feat 112 2 | # Cube 3 | 2 4 | 10 -10 10 5 | -10 -10 10 6 | 2 7 | 10 -10 -10 8 | 10 -10 10 9 | 2 10 | -10 -10 -10 11 | 10 -10 -10 12 | 2 13 | -10 -10 10 14 | -10 -10 -10 15 | 2 16 | -10 10 -10 17 | -10 10 10 18 | 2 19 | 10 10 -10 20 | -10 10 -10 21 | 2 22 | 10 10 10 23 | 10 10 -10 24 | 2 25 | -10 10 10 26 | 10 10 10 27 | 2 28 | 10 -10 -10 29 | 10 10 -10 30 | 2 31 | 10 10 10 32 | 10 -10 10 33 | 2 34 | -10 10 10 35 | -10 -10 10 36 | 2 37 | -10 10 -10 38 | -10 -10 -10 39 | # Cylinder caps 40 | 17 41 | 0 7 -20 42 | 1.36563 6.8655 -20 43 | 2.67878 6.46716 -20 44 | 3.88899 5.82029 -20 45 | 4.94975 4.94975 -20 46 | 5.82029 3.88899 -20 47 | 6.46716 2.67879 -20 48 | 6.8655 1.36564 -20 49 | 7 4e-06 -20 50 | 6.8655 -1.36563 -20 51 | 6.46716 -2.67878 -20 52 | 5.82029 -3.88899 -20 53 | 4.94975 -4.94974 -20 54 | 3.88899 -5.82028 -20 55 | 2.67878 -6.46715 -20 56 | 1.36563 -6.86549 -20 57 | -2e-06 -7 -20 58 | 17 59 | -2e-06 -7 -20 60 | -1.36564 -6.86549 -20 61 | -2.67879 -6.46715 -20 62 | -3.88899 -5.82028 -20 63 | -4.94975 -4.94974 -20 64 | -5.82029 -3.88898 -20 65 | -6.46716 -2.67878 -20 66 | -6.8655 -1.36562 -20 67 | -7 1.1e-05 -20 68 | -6.8655 1.36564 -20 69 | -6.46715 2.6788 -20 70 | -5.82028 3.889 -20 71 | -4.94974 4.94976 -20 72 | -3.88898 5.8203 -20 73 | -2.67878 6.46716 -20 74 | -1.36562 6.8655 -20 75 | 0 7 -20 76 | 17 77 | -1.36568 6.86548 20 78 | -2.67883 6.46713 20 79 | -3.88903 5.82026 20 80 | -4.94978 4.94972 20 81 | -5.82031 3.88896 20 82 | -6.46717 2.67875 20 83 | -6.8655 1.3656 20 84 | -7 -3.3e-05 20 85 | -6.86549 -1.36566 20 86 | -6.46715 -2.67881 20 87 | -5.82028 -3.88901 20 88 | -4.94974 -4.94976 20 89 | -3.88898 -5.8203 20 90 | -2.67877 -6.46716 20 91 | -1.36562 -6.8655 20 92 | 7e-06 -7 20 93 | 1.36564 -6.8655 20 94 | 17 95 | 1.36564 -6.8655 20 96 | 2.67879 -6.46716 20 97 | 3.88899 -5.82029 20 98 | 4.94974 -4.94976 20 99 | 5.82028 -3.889 20 100 | 6.46715 -2.6788 20 101 | 6.86549 -1.36565 20 102 | 7 -2e-05 20 103 | 6.8655 1.36561 20 104 | 6.46716 2.67877 20 105 | 5.82029 3.88898 20 106 | 4.94976 4.94973 20 107 | 3.889 5.82028 20 108 | 2.6788 6.46715 20 109 | 1.36564 6.86549 20 110 | 1.1e-05 7 20 111 | -1.36568 6.86548 20 112 | # Cylinder-cube joins 113 | 44 114 | -7 0 -10 115 | -7 -2.7e-05 -10 116 | -6.93275 -0.68284 -10 117 | -6.87688 -1.25 -10 118 | -6.86549 -1.36566 -10 119 | -6.66632 -2.02223 -10 120 | -6.52139 -2.5 -10 121 | -6.46715 -2.67881 -10 122 | -6.25 -3.08507 -10 123 | -6.14371 -3.28391 -10 124 | -5.89458 -3.75 -10 125 | -5.82028 -3.88901 -10 126 | -5.38501 -4.41938 -10 127 | -5 -4.88852 -10 128 | -4.94974 -4.94976 -10 129 | -4.88852 -5 -10 130 | -3.88898 -5.8203 -10 131 | -3.75 -5.89459 -10 132 | -3.08506 -6.25 -10 133 | -2.67878 -6.46716 -10 134 | -2.5 -6.5214 -10 135 | -2.0222 -6.66633 -10 136 | -1.36563 -6.8655 -10 137 | -1.25 -6.87689 -10 138 | 5e-06 -7 -10 139 | 1.25 -6.87689 -10 140 | 1.36564 -6.8655 -10 141 | 2.5 -6.52139 -10 142 | 2.67879 -6.46716 -10 143 | 3.08506 -6.25 -10 144 | 3.88899 -5.82029 -10 145 | 4.88852 -5 -10 146 | 4.94974 -4.94975 -10 147 | 5 -4.88852 -10 148 | 5.82028 -3.889 -10 149 | 5.89458 -3.75 -10 150 | 6.25 -3.08506 -10 151 | 6.46715 -2.6788 -10 152 | 6.52139 -2.5 -10 153 | 6.8655 -1.36565 -10 154 | 6.87688 -1.25 -10 155 | 6.93275 -0.682834 -10 156 | 7 -1.7e-05 -10 157 | 7 0 -10 158 | 45 159 | 7 0 -10 160 | 6.87689 1.25 -10 161 | 6.8655 1.36562 -10 162 | 6.66633 2.02219 -10 163 | 6.52139 2.5 -10 164 | 6.46716 2.67877 -10 165 | 6.25 3.08505 -10 166 | 5.89458 3.75 -10 167 | 5.82029 3.88898 -10 168 | 5 4.88851 -10 169 | 4.94976 4.94974 -10 170 | 4.88851 5 -10 171 | 3.889 5.82028 -10 172 | 3.75 5.89458 -10 173 | 3.08505 6.25 -10 174 | 2.67879 6.46715 -10 175 | 2.5 6.52139 -10 176 | 1.36564 6.86549 -10 177 | 1.25 6.87688 -10 178 | 1e-05 7 -10 179 | 3e-06 7 -10 180 | -0.682826 6.93274 -10 181 | -1.25 6.87688 -10 182 | -1.36567 6.86549 -10 183 | -2.02224 6.66631 -10 184 | -2.5 6.52139 -10 185 | -2.67882 6.46714 -10 186 | -3.08505 6.25 -10 187 | -3.28392 6.1437 -10 188 | -3.88902 5.82026 -10 189 | -4.4194 5.38499 -10 190 | -4.88851 5 -10 191 | -4.94977 4.94972 -10 192 | -5 4.88852 -10 193 | -5.38504 4.41934 -10 194 | -5.8203 3.88896 -10 195 | -5.89458 3.75 -10 196 | -6.14374 3.28386 -10 197 | -6.25 3.08506 -10 198 | -6.46717 2.67875 -10 199 | -6.66634 2.02218 -10 200 | -6.8655 1.3656 -10 201 | -6.87689 1.25 -10 202 | -6.93275 0.682789 -10 203 | -7 0 -10 204 | 42 205 | 7 0 10 206 | 6.87688 -1.25 10 207 | 6.8655 -1.36563 10 208 | 6.66633 -2.0222 10 209 | 6.52139 -2.5 10 210 | 6.46716 -2.67878 10 211 | 5.89458 -3.75 10 212 | 5.82029 -3.88899 10 213 | 5 -4.88852 10 214 | 4.94975 -4.94975 10 215 | 4.88851 -5 10 216 | 3.88899 -5.82028 10 217 | 3.75 -5.89458 10 218 | 3.08505 -6.25 10 219 | 2.67878 -6.46715 10 220 | 2.5 -6.52139 10 221 | 1.36563 -6.86549 10 222 | 1.25 -6.87688 10 223 | -1e-06 -7 10 224 | -1.25 -6.87688 10 225 | -1.36563 -6.86549 10 226 | -2.02221 -6.66632 10 227 | -2.5 -6.52139 10 228 | -2.67879 -6.46715 10 229 | -3.08505 -6.25 10 230 | -3.75 -5.89458 10 231 | -3.88899 -5.82028 10 232 | -4.41937 -5.38502 10 233 | -4.88851 -5 10 234 | -4.94975 -4.94974 10 235 | -5 -4.88851 10 236 | -5.38502 -4.41937 10 237 | -5.82029 -3.88899 10 238 | -5.89458 -3.75 10 239 | -6.14372 -3.28389 10 240 | -6.25 -3.08505 10 241 | -6.46716 -2.67878 10 242 | -6.66633 -2.02221 10 243 | -6.8655 -1.36563 10 244 | -6.87688 -1.25 10 245 | -6.93275 -0.682814 10 246 | -7 5e-06 10 247 | 47 248 | -7 5e-06 10 249 | -6.93275 0.682818 10 250 | -6.87688 1.25 10 251 | -6.8655 1.36564 10 252 | -6.66633 2.02221 10 253 | -6.52139 2.5 10 254 | -6.46715 2.67879 10 255 | -6.25 3.08506 10 256 | -6.14372 3.28389 10 257 | -5.89458 3.75 10 258 | -5.82028 3.889 10 259 | -5.38502 4.41937 10 260 | -5 4.88852 10 261 | -4.94975 4.94975 10 262 | -4.88852 5 10 263 | -4.41937 5.38502 10 264 | -3.88899 5.82029 10 265 | -3.75 5.89458 10 266 | -3.28389 6.14372 10 267 | -3.08506 6.25 10 268 | -2.67878 6.46716 10 269 | -2.5 6.52139 10 270 | -2.02221 6.66633 10 271 | -1.36563 6.8655 10 272 | -1.25 6.87689 10 273 | -0.682814 6.93275 10 274 | -5e-06 7 10 275 | 1e-06 7 10 276 | 1.24999 6.87689 10 277 | 1.36563 6.8655 10 278 | 2.49999 6.5214 10 279 | 2.67879 6.46716 10 280 | 3.08506 6.25 10 281 | 3.28389 6.14372 10 282 | 3.74999 5.89459 10 283 | 3.88899 5.82029 10 284 | 4.88852 5 10 285 | 4.94975 4.94975 10 286 | 4.99999 4.88853 10 287 | 5.82029 3.88899 10 288 | 5.89458 3.75 10 289 | 6.24999 3.08507 10 290 | 6.46716 2.67879 10 291 | 6.52139 2.5 10 292 | 6.8655 1.36563 10 293 | 6.87688 1.25 10 294 | 7 0 10 295 | # Holes in cube 296 | 36 297 | -5.43429 -10 -2.5 298 | -5.54328 -10 -2.2961 299 | -5.86061 -10 -1.25 300 | -5.88471 -10 -1.17054 301 | -6 -10 -2e-06 302 | -6 -10 -1e-06 303 | -5.88471 -10 1.17054 304 | -5.86061 -10 1.25 305 | -5.54328 -10 2.2961 306 | -5.43429 -10 2.5 307 | -5 -10 3.31251 308 | -4.98882 -10 3.33342 309 | -4.64694 -10 3.75 310 | -4.24264 -10 4.24264 311 | -3.75 -10 4.64694 312 | -3.33342 -10 4.98882 313 | -3.31251 -10 5 314 | -2.5 -10 5.43429 315 | -2.2961 -10 5.54328 316 | -1.25 -10 5.86061 317 | -1.17054 -10 5.88471 318 | -1e-06 -10 6 319 | -0 -10 6 320 | 1.17054 -10 5.88471 321 | 1.25 -10 5.86061 322 | 2.2961 -10 5.54328 323 | 2.5 -10 5.43429 324 | 3.31251 -10 5 325 | 3.33342 -10 4.98882 326 | 3.75 -10 4.64694 327 | 4.24264 -10 4.24264 328 | 4.64694 -10 3.75 329 | 4.98882 -10 3.33342 330 | 5 -10 3.31251 331 | 5.43429 -10 2.5 332 | 5.54328 -10 2.2961 333 | 34 334 | 5.54328 -10 2.2961 335 | 5.86061 -10 1.25 336 | 5.88471 -10 1.17054 337 | 6 -10 2e-06 338 | 6 -10 -0 339 | 5.88471 -10 -1.17054 340 | 5.86061 -10 -1.25 341 | 5.54328 -10 -2.2961 342 | 5.43429 -10 -2.5 343 | 5 -10 -3.31251 344 | 4.98882 -10 -3.33342 345 | 4.64694 -10 -3.75 346 | 4.24264 -10 -4.24264 347 | 3.75 -10 -4.64694 348 | 3.33342 -10 -4.98882 349 | 3.3125 -10 -5 350 | 2.5 -10 -5.43429 351 | 2.2961 -10 -5.54328 352 | 1.25 -10 -5.86061 353 | 1.17054 -10 -5.88471 354 | 2e-06 -10 -6 355 | 1e-06 -10 -6 356 | -1.17054 -10 -5.88471 357 | -1.25 -10 -5.86061 358 | -2.2961 -10 -5.54328 359 | -2.5 -10 -5.43429 360 | -3.3125 -10 -5 361 | -3.33342 -10 -4.98882 362 | -3.75 -10 -4.64694 363 | -4.24264 -10 -4.24264 364 | -4.64694 -10 -3.75 365 | -4.98882 -10 -3.33342 366 | -5 -10 -3.31251 367 | -5.43429 -10 -2.5 368 | 36 369 | 2.49999 10 5.43429 370 | 2.2961 10 5.54328 371 | 1.25 10 5.86061 372 | 1.17054 10 5.88471 373 | 4e-06 10 6 374 | -4e-06 10 6 375 | -1.17054 10 5.88471 376 | -1.25 10 5.86061 377 | -2.29609 10 5.54328 378 | -2.5 10 5.43429 379 | -3.31251 10 5 380 | -3.33341 10 4.98882 381 | -3.75 10 4.64694 382 | -4.24263 10 4.24265 383 | -4.64694 10 3.75 384 | -4.98881 10 3.33343 385 | -5 10 3.3125 386 | -5.43429 10 2.5 387 | -5.54327 10 2.29611 388 | -5.86061 10 1.25 389 | -5.88471 10 1.17056 390 | -6 10 2e-05 391 | -6 10 -0 392 | -5.88472 10 -1.17052 393 | -5.86061 10 -1.25 394 | -5.54329 10 -2.29608 395 | -5.43429 10 -2.5 396 | -5 10 -3.31251 397 | -4.98883 10 -3.3334 398 | -4.64694 10 -3.75 399 | -4.24266 10 -4.24262 400 | -3.75 10 -4.64694 401 | -3.33345 10 -4.9888 402 | -3.3125 10 -5 403 | -2.5 10 -5.43429 404 | -2.29613 10 -5.54326 405 | 34 406 | -2.29613 10 -5.54326 407 | -1.25 10 -5.86061 408 | -1.17057 10 -5.88471 409 | 1e-06 10 -6 410 | 8e-06 10 -6 411 | 1.17055 10 -5.88471 412 | 1.25 10 -5.86061 413 | 2.29611 10 -5.54327 414 | 2.5 10 -5.43429 415 | 3.31251 10 -4.99999 416 | 3.33343 10 -4.98881 417 | 3.75 10 -4.64694 418 | 4.24265 10 -4.24263 419 | 4.64694 10 -3.74999 420 | 4.98882 10 -3.33341 421 | 5 10 -3.31251 422 | 5.43429 10 -2.49999 423 | 5.54328 10 -2.29609 424 | 5.86061 10 -1.24999 425 | 5.88471 10 -1.17053 426 | 6 10 4e-06 427 | 6 10 1.1e-05 428 | 5.88471 10 1.17055 429 | 5.86061 10 1.25 430 | 5.54327 10 2.29611 431 | 5.43429 10 2.5 432 | 5 10 3.31251 433 | 4.98881 10 3.33343 434 | 4.64694 10 3.75 435 | 4.24264 10 4.24264 436 | 3.74999 10 4.64694 437 | 3.33342 10 4.98882 438 | 3.3125 10 5 439 | 2.49999 10 5.43429 440 | 35 441 | 10 5.54326 2.29611 442 | 10 5.8606 1.25 443 | 10 5.8847 1.17056 444 | 10 5.99999 2e-05 445 | 10 5.99999 5e-06 446 | 10 5.88471 -1.17052 447 | 10 5.8606 -1.24999 448 | 10 5.54328 -2.29608 449 | 10 5.43429 -2.49999 450 | 10 5 -3.31249 451 | 10 4.98882 -3.3334 452 | 10 4.64694 -3.74999 453 | 10 4.24265 -4.24262 454 | 10 3.75 -4.64694 455 | 10 3.33344 -4.9888 456 | 10 3.3125 -4.99999 457 | 10 2.5 -5.43429 458 | 10 2.29612 -5.54326 459 | 10 1.25 -5.86061 460 | 10 1.17057 -5.88471 461 | 10 0 -6 462 | 10 -1.4e-05 -6 463 | 10 -1.17056 -5.88471 464 | 10 -1.25 -5.86061 465 | 10 -2.29611 -5.54327 466 | 10 -2.5 -5.4343 467 | 10 -3.31251 -5 468 | 10 -3.33344 -4.98881 469 | 10 -3.75 -4.64695 470 | 10 -4.24265 -4.24263 471 | 10 -4.64695 -3.75 472 | 10 -4.98883 -3.33341 473 | 10 -5 -3.31252 474 | 10 -5.4343 -2.5 475 | 10 -5.54329 -2.29609 476 | 33 477 | 10 -5.54329 -2.29609 478 | 10 -5.86061 -1.25 479 | 10 -5.88472 -1.17053 480 | 10 -6.00001 1e-06 481 | 10 -6.00001 1.1e-05 482 | 10 -5.88472 1.17055 483 | 10 -5.86062 1.25 484 | 10 -5.54328 2.29611 485 | 10 -5.4343 2.5 486 | 10 -5 3.31251 487 | 10 -4.98882 3.33343 488 | 10 -4.64695 3.75 489 | 10 -4.24264 4.24264 490 | 10 -3.75 4.64695 491 | 10 -3.33343 4.98882 492 | 10 -3.31251 5 493 | 10 -2.5 5.43429 494 | 10 -2.29611 5.54328 495 | 10 -1.25 5.86061 496 | 10 -1.17055 5.88471 497 | 10 -1.1e-05 6 498 | 10 0 6 499 | 10 1.17053 5.88471 500 | 10 1.25 5.86061 501 | 10 2.29609 5.54328 502 | 10 2.5 5.43429 503 | 10 3.3125 5 504 | 10 3.33341 4.98882 505 | 10 3.75 4.64693 506 | 10 4.24263 4.24265 507 | 10 4.64693 3.75 508 | 10 4.9888 3.33343 509 | 10 5.54326 2.29611 510 | 33 511 | -10 0 6 512 | -10 -1.17053 5.88471 513 | -10 -1.25 5.8606 514 | -10 -2.29609 5.54328 515 | -10 -2.5 5.43429 516 | -10 -3.3125 5 517 | -10 -3.33342 4.98882 518 | -10 -3.75 4.64693 519 | -10 -4.24263 4.24264 520 | -10 -4.64694 3.75 521 | -10 -4.98881 3.33342 522 | -10 -5 3.31249 523 | -10 -5.43429 2.5 524 | -10 -5.54327 2.2961 525 | -10 -5.8606 1.25 526 | -10 -5.88471 1.17054 527 | -10 -5.99999 2e-06 528 | -10 -5.99999 -3e-06 529 | -10 -5.88471 -1.17054 530 | -10 -5.8606 -1.25 531 | -10 -5.54327 -2.2961 532 | -10 -5.43428 -2.5 533 | -10 -5 -3.31249 534 | -10 -4.98881 -3.33342 535 | -10 -4.64693 -3.75 536 | -10 -4.24264 -4.24264 537 | -10 -3.75 -4.64693 538 | -10 -3.33342 -4.98882 539 | -10 -2.5 -5.43429 540 | -10 -2.2961 -5.54328 541 | -10 -1.25 -5.86061 542 | -10 -1.17054 -5.88471 543 | -10 0 -6 544 | 34 545 | -10 0 -6 546 | -10 5e-06 -6 547 | -10 1.17055 -5.88471 548 | -10 1.25 -5.86061 549 | -10 2.29611 -5.54328 550 | -10 2.5 -5.43429 551 | -10 3.31251 -5 552 | -10 3.33343 -4.98882 553 | -10 3.75 -4.64695 554 | -10 4.24265 -4.24264 555 | -10 4.64695 -3.75 556 | -10 4.98882 -3.33342 557 | -10 5 -3.31251 558 | -10 5.4343 -2.5 559 | -10 5.54328 -2.2961 560 | -10 5.86061 -1.25 561 | -10 5.88472 -1.17054 562 | -10 6.00001 -2e-06 563 | -10 6.00001 -1e-06 564 | -10 5.88472 1.17054 565 | -10 5.86062 1.25 566 | -10 5.54328 2.2961 567 | -10 4.98882 3.33342 568 | -10 4.64695 3.75 569 | -10 4.24265 4.24264 570 | -10 3.75 4.64695 571 | -10 3.33343 4.98882 572 | -10 3.31252 5 573 | -10 2.5 5.43429 574 | -10 2.29611 5.54328 575 | -10 1.25 5.86061 576 | -10 1.17055 5.88471 577 | -10 7e-06 6 578 | -10 0 6 579 | # Interior curves 580 | 29 581 | 0 0 6 582 | 0.9375 0.9375 5.90766 583 | 1.17054 1.17054 5.88471 584 | 1.875 1.875 5.67102 585 | 2.2961 2.2961 5.54328 586 | 2.8125 2.8125 5.26725 587 | 3.33342 3.33342 4.98882 588 | 3.75 3.75 4.64694 589 | 4.24264 4.24264 4.24264 590 | 4.6875 4.6875 3.70058 591 | 4.98882 4.98882 3.33342 592 | 5.54328 5.54328 2.2961 593 | 5.625 5.625 2.02671 594 | 5.88471 5.88471 1.17055 595 | 6 6 0 596 | 5.88471 5.88471 -1.17053 597 | 5.625 5.625 -2.02669 598 | 5.54328 5.54328 -2.2961 599 | 4.98882 4.98882 -3.33342 600 | 4.6875 4.6875 -3.70058 601 | 4.24264 4.24264 -4.24264 602 | 3.75 3.75 -4.64694 603 | 3.33342 3.33342 -4.98882 604 | 2.8125 2.8125 -5.26725 605 | 2.2961 2.2961 -5.54328 606 | 1.875 1.875 -5.67102 607 | 1.17054 1.17054 -5.88471 608 | 0.9375 0.9375 -5.90766 609 | 0 0 -6 610 | 29 611 | 0 0 6 612 | -0.9375 0.9375 5.90766 613 | -1.17054 1.17054 5.88471 614 | -1.875 1.875 5.67102 615 | -2.2961 2.2961 5.54328 616 | -2.8125 2.8125 5.26725 617 | -3.33342 3.33342 4.98882 618 | -3.75 3.75 4.64694 619 | -4.24264 4.24264 4.24264 620 | -4.6875 4.6875 3.70058 621 | -4.98882 4.98882 3.33342 622 | -5.54328 5.54328 2.2961 623 | -5.625 5.625 2.02671 624 | -5.88471 5.88471 1.17055 625 | -6 6 0 626 | -5.88471 5.88471 -1.17053 627 | -5.625 5.625 -2.02669 628 | -5.54328 5.54328 -2.2961 629 | -4.98882 4.98882 -3.33342 630 | -4.6875 4.6875 -3.70058 631 | -4.24264 4.24264 -4.24264 632 | -3.75 3.75 -4.64694 633 | -3.33342 3.33342 -4.98882 634 | -2.8125 2.8125 -5.26725 635 | -2.2961 2.2961 -5.54328 636 | -1.875 1.875 -5.67102 637 | -1.17054 1.17054 -5.88471 638 | -0.9375 0.9375 -5.90766 639 | 0 0 -6 640 | 29 641 | 0 0 6 642 | 0.9375 -0.9375 5.90766 643 | 1.17054 -1.17054 5.88471 644 | 1.875 -1.875 5.67102 645 | 2.2961 -2.2961 5.54328 646 | 2.8125 -2.8125 5.26725 647 | 3.33342 -3.33342 4.98882 648 | 3.75 -3.75 4.64694 649 | 4.24264 -4.24264 4.24264 650 | 4.6875 -4.6875 3.70058 651 | 4.98882 -4.98882 3.33342 652 | 5.54328 -5.54328 2.2961 653 | 5.625 -5.625 2.02671 654 | 5.88471 -5.88471 1.17055 655 | 6 -6 0 656 | 5.88471 -5.88471 -1.17053 657 | 5.625 -5.625 -2.02669 658 | 5.54328 -5.54328 -2.2961 659 | 4.98882 -4.98882 -3.33342 660 | 4.6875 -4.6875 -3.70058 661 | 4.24264 -4.24264 -4.24264 662 | 3.75 -3.75 -4.64694 663 | 3.33342 -3.33342 -4.98882 664 | 2.8125 -2.8125 -5.26725 665 | 2.2961 -2.2961 -5.54328 666 | 1.875 -1.875 -5.67102 667 | 1.17054 -1.17054 -5.88471 668 | 0.9375 -0.9375 -5.90766 669 | 0 0 -6 670 | 29 671 | 0 0 6 672 | -0.9375 -0.9375 5.90766 673 | -1.17054 -1.17054 5.88471 674 | -1.875 -1.875 5.67102 675 | -2.2961 -2.2961 5.54328 676 | -2.8125 -2.8125 5.26725 677 | -3.33342 -3.33342 4.98882 678 | -3.75 -3.75 4.64694 679 | -4.24264 -4.24264 4.24264 680 | -4.6875 -4.6875 3.70058 681 | -4.98882 -4.98882 3.33342 682 | -5.54328 -5.54328 2.2961 683 | -5.625 -5.625 2.02671 684 | -5.88471 -5.88471 1.17055 685 | -6 -6 0 686 | -5.88471 -5.88471 -1.17053 687 | -5.625 -5.625 -2.02669 688 | -5.54328 -5.54328 -2.2961 689 | -4.98882 -4.98882 -3.33342 690 | -4.6875 -4.6875 -3.70058 691 | -4.24264 -4.24264 -4.24264 692 | -3.75 -3.75 -4.64694 693 | -3.33342 -3.33342 -4.98882 694 | -2.8125 -2.8125 -5.26725 695 | -2.2961 -2.2961 -5.54328 696 | -1.875 -1.875 -5.67102 697 | -1.17054 -1.17054 -5.88471 698 | -0.9375 -0.9375 -5.90766 699 | 0 0 -6 700 | -------------------------------------------------------------------------------- /src/view_tet.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "gluvi.h" 6 | #include "vec.h" 7 | #include "feature.h" 8 | #include "read_obj.h" 9 | #include "tet_mesh.h" 10 | #include "tet_quality.h" 11 | 12 | std::vector x; 13 | std::vector tet; 14 | std::set boundary_face_set; 15 | 16 | std::vector tri; 17 | 18 | bool drawTriMesh; 19 | 20 | bool cutX = false, cutY = false, cutZ = false; 21 | float px, py, pz; 22 | 23 | int worst_n = 0; 24 | std::vector tet_quality; 25 | 26 | FeatureSet features; 27 | 28 | Vec3f centroid; 29 | 30 | // decides if a vertex is cut off by the cutting planes: 31 | // x-px = 0, y-py = 0, z-py = 0 32 | bool isCutOff(const Vec3f& v){ 33 | bool isCut = false; 34 | if(cutX){ 35 | if(v[0] > px) isCut = true; 36 | } 37 | if(cutY){ 38 | if(v[1] > py) isCut = true; 39 | } 40 | if(cutZ){ 41 | if(v[2] > pz) isCut = true; 42 | } 43 | return isCut; 44 | } 45 | 46 | // Decides if the first tet has worse quality than the second 47 | bool isWorse(const Vec4i& tet1, const Vec4i& tet2){ 48 | Tet t1, t2; 49 | t1.v[0] = x[tet1[0]]; 50 | t1.v[1] = x[tet1[1]]; 51 | t1.v[2] = x[tet1[2]]; 52 | t1.v[3] = x[tet1[3]]; 53 | t2.v[0] = x[tet2[0]]; 54 | t2.v[1] = x[tet2[1]]; 55 | t2.v[2] = x[tet2[2]]; 56 | t2.v[3] = x[tet2[3]]; 57 | return compute_tet_quality(t1) < compute_tet_quality(t2); 58 | 59 | } 60 | 61 | // Rearrange tets so that the worst N tets are at the front 62 | void sortTets(){ 63 | std::nth_element(tet.begin(), tet.begin() + worst_n -1, tet.end(), isWorse); 64 | } 65 | 66 | bool isBoundaryFace(int v1, int v2, int v3) 67 | { 68 | // Try all permutations. 69 | Vec3i perms[] = { 70 | Vec3i(v1, v2, v3), 71 | Vec3i(v2, v3, v1), 72 | Vec3i(v3, v1, v2), 73 | Vec3i(v1, v3, v2), 74 | Vec3i(v2, v1, v3), 75 | Vec3i(v3, v2, v1) 76 | }; 77 | 78 | for (int i = 0; i < 6; ++i) 79 | { 80 | if (boundary_face_set.find(perms[i]) != boundary_face_set.end()) 81 | { 82 | return true; 83 | } 84 | } 85 | 86 | return false; 87 | } 88 | 89 | 90 | void display(void) 91 | { 92 | glEnable(GL_LIGHTING); 93 | glEnable(GL_LIGHT0); 94 | glEnable(GL_COLOR_MATERIAL); 95 | glEnable(GL_DEPTH_TEST); 96 | 97 | glEnable(GL_POLYGON_OFFSET_FILL); 98 | glPolygonOffset(0.5, 0.5); 99 | 100 | glTranslatef(-centroid[0], -centroid[1], -centroid[2]); 101 | 102 | // Tet Faces 103 | if (!drawTriMesh) { 104 | glBegin(GL_TRIANGLES); 105 | 106 | // Draw worst n tets 107 | if(worst_n > 0){ 108 | for(int t=0; t< worst_n; ++t){ 109 | Vec3f p=x[tet[t][0]], 110 | q=x[tet[t][1]], 111 | r=x[tet[t][2]], 112 | s=x[tet[t][3]]; 113 | 114 | // if any vertex is cut off, don't draw tet 115 | if(isCutOff(p) || isCutOff(q) || isCutOff(r) || isCutOff(s)) 116 | continue; 117 | 118 | /* 119 | Vec3f c=(p+q+r+s)/4; 120 | p=0.8*p+0.2*c; 121 | q=0.8*q+0.2*c; 122 | r=0.8*r+0.2*c; 123 | s=0.8*s+0.2*c; 124 | //float h=0.25*(t+0.5)/tet.size(); 125 | float h = 0.25*(t+0.5)/(float)tet.size(); 126 | if (t%4 == 0) glColor3f(h+0.7,h,h); 127 | else if (t%4 == 1) glColor3f(h+0.2,h+0.5,h); 128 | else if (t%4 == 2) glColor3f(h,h+0.5,h+0.2); 129 | else glColor3f(h,h,h+0.7); 130 | */ 131 | glColor3f(1.0, 0.2, 0.0); 132 | glNormal3fv(normalized(cross((q-p),(r-p))).v); 133 | glVertex3fv(p.v); 134 | glVertex3fv(q.v); 135 | glVertex3fv(r.v); 136 | glNormal3fv(normalized(cross((s-p),(q-p))).v); 137 | glVertex3fv(p.v); 138 | glVertex3fv(s.v); 139 | glVertex3fv(q.v); 140 | glNormal3fv(normalized(cross((r-p),(s-p))).v); 141 | glVertex3fv(p.v); 142 | glVertex3fv(r.v); 143 | glVertex3fv(s.v); 144 | glNormal3fv(normalized(cross((r-q),(s-q))).v); 145 | glVertex3fv(q.v); 146 | glVertex3fv(r.v); 147 | glVertex3fv(s.v); 148 | } 149 | 150 | } else { 151 | // Draw all tets 152 | for(size_t t=0; t7) { 349 | usage = true; 350 | failure = true; 351 | } 352 | 353 | // Parse command line arguments 354 | for (int arg = 2; arg < argc && !failure; ++arg) 355 | { 356 | if (strcmp(argv[arg], "-s") == 0) { 357 | drawTriMesh = true; 358 | 359 | } else if (strcmp(argv[arg], "-f") == 0) { 360 | if (feature) { 361 | std::printf("ERROR: Feature file already specified."); 362 | usage = true; 363 | failure = true; 364 | break; 365 | } 366 | ++arg; 367 | if (arg >= argc) { 368 | std::printf("ERROR: No feature file provided after " 369 | "-f option\n"); 370 | usage = true; 371 | } else if (strcmp(argv[arg], "-p") == 0) { 372 | std::printf("ERROR: No feature file provided after " 373 | "-f option\n"); 374 | usage = true; 375 | } else { 376 | feature = true; 377 | featureFile = argv[arg]; 378 | } 379 | 380 | } else if(strcmp(argv[arg], "-p") == 0) { 381 | ++arg; 382 | if (arg >= argc) { 383 | std::printf("ERROR: No cutting plane specified after " 384 | "-p option\n"); 385 | usage = true; 386 | 387 | } else { 388 | 389 | char cutPlane; 390 | float cutVal; 391 | 392 | if (sscanf(argv[arg], "%c%f", &cutPlane, &cutVal) == 2) { 393 | 394 | if(cutPlane == 'x'){ 395 | cutX = true; 396 | px = cutVal; 397 | } else if(cutPlane == 'y'){ 398 | cutY = true; 399 | py = cutVal; 400 | } else if(cutPlane == 'z'){ 401 | cutZ = true; 402 | pz = cutVal; 403 | } else{ 404 | std::printf("ERROR: Wrong format in specifying cutting plane\n"); 405 | usage = true; 406 | } 407 | 408 | } else { 409 | std::printf("ERROR: Wrong format in specifying cutting plane\n"); 410 | usage = true; 411 | 412 | } 413 | } 414 | 415 | } else if(strcmp(argv[arg], "-w") == 0) { 416 | worst_n = atoi(argv[++arg]); 417 | if ( worst_n == 0) { 418 | std::printf("ERROR: A proper integer cannot be found after the '-w' option\n"); 419 | usage = true; 420 | } 421 | 422 | 423 | } else { // Invalid argument 424 | usage = true; 425 | failure = true; 426 | } 427 | } 428 | 429 | if (usage) { 430 | std::printf("Usage: view_tet [-s] [-f ] [-p (x|y|z)]* [-w num]\n"); 431 | } 432 | if (failure) { 433 | return 1; 434 | } 435 | 436 | Gluvi::init(argv[0], &argc, argv); 437 | Gluvi::Target3D cam; 438 | Gluvi::camera=&cam; 439 | 440 | 441 | if (!drawTriMesh) { 442 | // Read .tet file 443 | FILE *in=std::fopen(argv[1], "r"); 444 | if (!in) { 445 | std::printf("Error: could not read tet mesh file: %s\n", argv[1]); 446 | return 1; 447 | } 448 | 449 | if(std::fgetc(in)!='t') return 1; 450 | if(std::fgetc(in)!='e') return 1; 451 | if(std::fgetc(in)!='t') return 1; 452 | if(std::fgetc(in)!=' ') return 1; 453 | int nv=-1, nt=-1; 454 | std::fscanf(in, "%d %d", &nv, &nt); 455 | if(nv<=0 || nt<=0) return 2; 456 | x.resize(nv); 457 | tet.resize(nt); 458 | for(int i=0; i boundaryVerts; 467 | std::vector boundaryTris; 468 | tetMesh.getBoundary(boundaryVerts, boundaryTris); 469 | for (size_t i = 0; i < boundaryTris.size(); ++i) 470 | { 471 | boundary_face_set.insert(boundaryTris[i]); 472 | } 473 | 474 | 475 | if (!x.size()) { 476 | std::printf("Error reading tet mesh file %s: " 477 | "no vertex data found.", argv[1]); 478 | return 1; 479 | } 480 | 481 | // Reorder to have worst N tets in front 482 | if(worst_n > 0){ 483 | sortTets(); 484 | } 485 | 486 | } else { 487 | bool result = read_objfile(x, tri, argv[1]); 488 | if (!result) { 489 | std::printf("Error: could not read tri mesh file: %s\n", argv[1]); 490 | return 1; 491 | } 492 | if (!x.size()) { 493 | std::printf("Error reading tri mesh file %s: " 494 | "no vertex data found.", argv[1]); 495 | return 1; 496 | } 497 | } 498 | 499 | if (feature) { 500 | // Read feature file 501 | features.readFromFile(featureFile); 502 | } 503 | 504 | // Construct bounding box and use to find centroid. 505 | Vec3f bboxMin = x[0], bboxMax = x[0]; 506 | for (size_t i = 1; i < x.size(); ++i) { 507 | for (int j = 0; j < 3; ++j) { 508 | if (x[i][j] < bboxMin[j]) bboxMin[j] = x[i][j]; 509 | if (x[i][j] > bboxMax[j]) bboxMax[j] = x[i][j]; 510 | } 511 | } 512 | centroid = bboxMin + (bboxMax - bboxMin)*0.5; 513 | 514 | glClearColor(1.0, 1.0, 1.0, 1.0); 515 | 516 | glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1); 517 | Gluvi::userDisplayFunc=display; 518 | 519 | /* 520 | for(int t=0; t