diff --git a/src/mesh_fix/CMakeLists.txt b/src/mesh_fix/CMakeLists.txt index 073e912aaf..ecc43b415b 100644 --- a/src/mesh_fix/CMakeLists.txt +++ b/src/mesh_fix/CMakeLists.txt @@ -19,7 +19,7 @@ set(SOURCES src/Kernel/graph.cpp src/Kernel/point.cpp src/Kernel/jqsort.cpp - src/MeshFix/meshfix.cpp +# src/MeshFix/meshfix.cpp ) ################################################################################ diff --git a/src/mesh_fix/include/TMesh/tin.h b/src/mesh_fix/include/TMesh/tin.h index 2f55e3d024..e53814954b 100644 --- a/src/mesh_fix/include/TMesh/tin.h +++ b/src/mesh_fix/include/TMesh/tin.h @@ -28,777 +28,777 @@ * * ****************************************************************************/ -#ifndef _TIN_H -#define _TIN_H - -#include "tmesh.h" - -namespace T_MESH -{ - -//! Basic_TMesh - -//! This class represents a manifold and oriented triangle mesh. -//! Vertices, Edges and Triangles are stored in the Lists V, E and T -//! respectively. Methods boundaries(), handles() and shells() may -//! be used to retrieve the respective topological entities. -//! Navigation of the mesh is based on the topological relationships -//! stored in each Vertex, Edge and Triangle. -//! Some methods would require a global update only to maintain -//! consistent values of the protected fields n_boundaries, n_handles -//! and n_shells. On the other hand, the same methods would work in -//! constant time if these values would not need to be updated. -//! To keep complexity as low as possible, -//! we make use of the 'dirty bits' d_boundaries, d_handles and -//! d_shells to mark that the respective entities must be updated. -//! The access functions boundaries(), handles() and shells() -//! check the status of the respective dirty bit and do a global -//! update (i.e., eulerUpdate()) only if necessary. -//! The complexity of the methods is provided as a function of a -//! generic 'N', which is O(V.numels()) = O(E.numels()) = O(T.numels()). - -class Basic_TMesh -{ - protected: - - int n_boundaries; //!< Number of boundary loops - int n_handles; //!< Number of handles - int n_shells; //!< Number of connected components - - bool d_boundaries; //!< Dirty bit for n_boundaries - bool d_handles; //!< Dirty bit for n_handles - bool d_shells; //!< Dirty bit for n_shells - - public: - - List V; //!< Vertex set - List E; //!< Edge set - List T; //!< Triangle set - - void *info; //! Generic information attached to this mesh - - ///////////////////////////////////////////////////////////////////////////// - // - // Constructors/Destructor (Implemented in "MESH_STRUCTURE/io.cpp") - // - ///////////////////////////////////////////////////////////////////////////// - - //! Empty triangulation. Should be used only prior to a call to load(). - Basic_TMesh(); - - //! Pre-defined triangulation. Currently, only "triangle" and "tetrahedron" - //! are recognized. - Basic_TMesh(const char *); - void init(const char *); - - //! Clones an existing Trianglation. - Basic_TMesh(const Basic_TMesh *, const bool clone_info = false); - void init(const Basic_TMesh *, const bool clone_info = false); - - //! Clones an existing connected component. - - //! Creates a new Basic_TMesh out of a connected component of an existing - //! Basic_TMesh. 't' is a triangle of the connected component that must - //! be copied. If 'keep_ref' is TRUE, each element of the existing mesh - //! keeps a pointer to the corresponding new element in the 'info' field. - Basic_TMesh(const Triangle *t, const bool keep_ref = false); - void init(const Triangle *t, const bool keep_ref = false); +#ifndef _TIN_H +#define _TIN_H - //FIXED: - //! Destructor. Frees the memory allocated for all the mesh elements. - //! Warning! This method uses the freeNodes() method of the class List, - //! which is not guaranteed to work correctly on systems other than - //! Linux (see the documentation of List for details). - //! Assuming that the method works correctly, however, the calling - //! function is responsible of freeing the memory that was possibly - //! allocated for objects pointed to by the 'info' field of mesh - //! elements. Clearly, this must be done before calling the destructor. - ~Basic_TMesh(); - - //! Returns true only if object is a basic Basic_TMesh. All the reimplementations must return false. -// TMESH_VIRTUAL bool isBaseType() const { return true; } - - //! Get the number of boundary loops of the triangle mesh. O(1) or O(N). - int boundaries() { if (d_boundaries) eulerUpdate(); return n_boundaries; } - - //! Get the number of handles of the triangle mesh. O(1) or O(N). - int handles() { if (d_handles) eulerUpdate(); return n_handles; } - - //! Get the number of connected components of the triangle mesh. O(1) or O(N). - int shells() { if (d_shells) eulerUpdate(); return n_shells; } - - - ///////////////////////////////////////////////////////////////////////////// - // - // Input/Output methods (Implemented in "MESH_STRUCTURE/io.cpp") - // - ///////////////////////////////////////////////////////////////////////////// - - //! Initialize the triangle mesh from the file 'filename'. - - //! The file format is automatically deduced from the magic number - //! or the filename extension. If 'doupdate' is FALSE, the - //! global update for the topological entities is prevented. - //! Currently, the following file formats are supported: - //! Open Inventor (IV), VRML 1.0 and 2.0 (WRL), Object File Format (OFF), - //! IMATI Ver-Tri (VER, TRI), PLY, OBJ, STL. - //! A non-zero value is returned in case of error. Specifically, - //! IO_CANTOPEN means that the file couldn't be opened for reading. - //! IO_FORMAT means that the file format was not recognized by the loader. - //! IO_UNKNOWN represents all the other errors. - //! The calling function is responsible of verifying that the mesh is - //! empty before calling this method. - - int load(const char *filename, const bool update = 1); - int loadIV(const char *); //!< Loads IV - int loadVRML1(const char *); //!< Loads VRML 1.0 - int loadOFF(const char *); //!< Loads OFF - int loadEFF(const char *); //!< Loads EFF - int loadPLY(const char *); //!< Loads PLY - int loadVerTri(const char *); //!< Loads VER-TRI - int loadVRML2(const char *); //!< Loads VRML 2.0 - int loadOBJ(const char *); //!< Loads OBJ - int loadSTL(const char *); //!< Loads STL - - int cutAndStitch(); //!< Convert to manifold - Triangle * CreateIndexedTriangle(ExtVertex **, int, int, int); - TMESH_VIRTUAL Triangle * CreateTriangleFromVertices(ExtVertex *, ExtVertex *, ExtVertex *); - - //! This function approximates the vertex coordinates with the values - //! that can be represented in an ASCII file. - void coordBackApproximation(); - - protected: - void closeLoadingSession(FILE *, int, ExtVertex **, bool); - bool pinch(Edge *e, bool wcv); - //! If the 'e' is internal, creates a copy of the edge and associates e->t2 - //! to this copy. After this operation, 'e' and its copy are boundary edges - //! with the same vertices. If 'e' is already on boundary, nothing is done - //! and NULL is returned. Otherwise the newly created copy is returned. - TMESH_VIRTUAL Edge *duplicateEdge(Edge *e); - - public: - TMESH_VIRTUAL Vertex * newVertex(); - TMESH_VIRTUAL Vertex * newVertex(const coord &, const coord &, const coord &); - TMESH_VIRTUAL Vertex * newVertex(Point *); - TMESH_VIRTUAL Vertex * newVertex(Point &); - TMESH_VIRTUAL Vertex * newVertex(Vertex *); - TMESH_VIRTUAL Edge * newEdge(Vertex *, Vertex *); - TMESH_VIRTUAL Edge * newEdge(Edge *); - TMESH_VIRTUAL Triangle * newTriangle(); - TMESH_VIRTUAL Triangle * newTriangle(Edge *, Edge *, Edge *); - TMESH_VIRTUAL Basic_TMesh * newObject() const { return new Basic_TMesh(); } - TMESH_VIRTUAL Basic_TMesh * newObject(const Basic_TMesh *tm, const bool ci = false) const { return new Basic_TMesh(tm, ci); } - TMESH_VIRTUAL Basic_TMesh * newObject(const char *s) const { return new Basic_TMesh(s); } - - //! Save the triangle mesh to file 'filename'. - - //! The file format is deduced from the filename extension - //! (wrl = vrml 1.0), (iv = OpenInventor), (off = Object - //! file format), (ply = PLY format), (tri = IMATI Ver-Tri). - //! If 'back_approx' is set, vertex coordinates are approximated - //! to reflect the limited precision of floating point - //! representation in ASCII files. This should be used when - //! coherence is necessary between in-memory and saved data. - //! A non-zero return value is returned if errors occur. - - int save(const char *filename, bool back_approx = 0); - - int saveIV(const char *); //!< Saves IV - int saveOFF(const char *); //!< Saves OFF 1.0 - int saveEFF(const char *); //!< Saves EFF - int saveOBJ(const char *); //!< Saves OBJ - int saveSTL(const char *); //!< Saves STL - int savePLY(const char *, bool ascii = 1); //!< Saves PLY 1.0 (ascii or binary) - int saveVerTri(const char *); //!< Saves Ver-Tri - - //! Saves the triangle mesh to a VRML 1.0 file. - //! The value of 'mode' specifies whether to use additional - //! information attached to mesh elements in order to assign - //! them a proper color. - //! IO_CSAVE_OVERALL assigns a unique color for the entire mesh (default). - //! IO_CSAVE_PERFACE assigns a color to each triangle depending on the value - //! of its 'info' field. - //! IO_CSAVE_PERVERTEX assigns a color to each vertex depending on the value - //! of its 'info' field. - //! IO_CSAVE_PERFACE_INDEXED assigns one of five base colors to each triangle - //! depending on the value of its 'mask' field. - //! IO_CSAVE_PERVERTEX_INDEXED assigns one of five base colors to each vertex - //! depending on the value of its 'mask' field. - int saveVRML1(const char *, const int mode = 0); - - - //! Append another triangle mesh to the existing one. - - //! This method works exactly as the 'load()' method, except for the fact - //! that it does not assume that the mesh is empty. - int append(const char *filename, const bool doupdate = 1); - +#include "tmesh.h" - // Move all the elements of 't' to this mesh and delete 't' itself. - TMESH_VIRTUAL void moveMeshElements(Basic_TMesh *t, bool delInput = true); - - - ///////////////////////////////////////////////////////////////////////////// - // - // Primitive Construction (Implemented in "MESH_STRUCTURE/tin.cpp") - // - ///////////////////////////////////////////////////////////////////////////// - - //! Creates an Edge connecting two existing mesh vertices. - - //! Returns the newly created edge. If an edge connecting the two vertices - //! already exists in the mesh, then no new edge is created and the old one - //! is returned. - Edge *CreateEdge(Vertex *v1, Vertex *v2); - - - //! Creates an Edge connecting two existing mesh Extended vertices. - - //! Returns the newly created edge. If an edge connecting the two vertices - //! already exists in the mesh, then no new edge is created and the old one - //! is returned. - //! If 'check' is FALSE, the check for previously existing edges is skipped. - Edge *CreateEdge(ExtVertex *v1, ExtVertex *v2, const bool check = 1); - - - //! Creates a properly oriented Triangle bounded by three existing mesh edges. - - //! Returns the newly created Triangle. If e1, e2 and e3 - //! are not suitable for creating a properly oriented and - //! manifold triangle, the creation fails and NULL is returned. - TMESH_VIRTUAL Triangle * CreateTriangle(Edge *e1, Edge *e2, Edge *e3); - - - //! Creates an arbitrarily oriented Triangle bounded by three existing mesh edges. - - //! Returns the newly created Triangle. If either e1, e2 or e3 - //! has already two incident triangles, the creation fails and NULL is returned. - //! This method assumes that e1, e2 and e3 are incident to exactly three vertices. - TMESH_VIRTUAL Triangle * CreateUnorientedTriangle(Edge *, Edge *, Edge *); - - - //! Creates a newEdge 'e' and an oriented Triangle bounded by 'e', 'e1' and 'e2'. - - //! The newly created triangle is returned, unless 'e1' and 'e2' do not share a - //! vertex or they are not boundary edges. In this cases, NULL is returned. - TMESH_VIRTUAL Triangle *EulerEdgeTriangle(Edge *e1, Edge *e2); - - - //! Splits and edge at a given point and returns the newly created Vertex. - //! If the boolean parameter is set to true, the 'mask' fields of edges and - //! triangles are propagated to the new elements. - TMESH_VIRTUAL Vertex *splitEdge(Edge *, Point *, bool = 0); - - - //! Splits a triangle at a given point and returns the newly created Vertex. - //! If the boolean parameter is set to true, the 'mask' field of the - //! triangle is propagated to the new triangle. - TMESH_VIRTUAL Vertex *splitTriangle(Triangle *, Point *, bool = 0); - - //! Creates two new triangles connecting the boundary edges e1 and e2 - //! and returns their common edge. - //! If e1 and e2 share a vertex, then only one triangle is created and - //! e1 is returned. - //! Returns NULL if either e1 or e2 are not boundary edges. - TMESH_VIRTUAL Edge *bridgeBoundaries(Edge *e1, Edge *e2); - - ///////////////////////////////////////////////////////////////////////////// - // - // Primitive Destruction (Implemented in "MESH_STRUCTURE/tin.cpp") - // - ///////////////////////////////////////////////////////////////////////////// - - - //! Unlinks a triangle from the mesh. O(1). - - //! Resulting isolated vertices and edges are unlinked too. - //! If necessary, this method duplicates non-manifold vertices that can - //! occur due to the removal of the triangle. - //! The unlinked triangle, along with the other possible unlinked elements, - //! must be removed from the List T through removeUnlinkedElements(). - void unlinkTriangle(Triangle *); - - - //! Unlinks a triangle from the mesh. O(1). - - //! No check is performed on the resulting topology, which may be inconsistent. - //! The unlinked triangle, along with the other possible unlinked elements, - //! must be removed from the List T through removeUnlinkedElements(). - void unlinkTriangleNoManifold(Triangle *); - - - //! Removes a triangle from the mesh. O(N). - - //! This is equivalent to an unlinkTriangle(t) followed by a - //! removeUnlinkedElements(). - void removeTriangle(Triangle *t) { unlinkTriangle(t); removeUnlinkedElements(); } - - //! Removes all the unlinked triangles from List T. Returns the number of removed triangles. O(N). - int removeTriangles(); - - //! Removes all the unlinked edges from List E. Returns the number of removed edges. O(N). - int removeEdges(); - - //! Removes all the unlinked vertices from List V. Returns the number of removed vertices. O(N). - int removeVertices(); - - //! Removes all the unlinked elements from the lists. Returns the number of removed elements. O(N). - int removeUnlinkedElements() { return removeTriangles() + removeEdges() + removeVertices(); } - - //! Removes all the vertices that can be deleted without changing the geometric realization. O(N). - int removeRedundantVertices(); - - ///////////////////////////////////////////////////////////////////////////// - // - // Methods acting on selections (Implemented in "MESH_STRUCTURE/tin.cpp") - // - ///////////////////////////////////////////////////////////////////////////// - - //! Deselects all the triangles. O(N). - void deselectTriangles(); - - //! Removes all the selected triangles. O(N). - void removeSelectedTriangles(); - - //! Selects all the triangles having at least one boundary vertex. O(N). - //! Returns the number of selected triangles. - int selectBoundaryTriangles(); - - //! Enlarges the current selection of one triangle in width. O(N). - - //! Each triangle sharing at least one vertex with a currently selected - //! triangle becomes selected. - //! Returns the number of newly selected triangles. - int growSelection(); - - //! Shrinks the current selection of one triangle in width. O(N). - - //! Each triangle sharing at least one vertex with a currently unselected - //! triangle becomes unselected. - void shrinkSelection(); - - //! Inverts the selection status of all the triangles. O(N). - - //! If 't0' is not NULL, then only the connected component containing 't0' - //! is inverted. - void invertSelection(Triangle *t0 = NULL); - - //! If 't0' is selected, deselect everything but the selected triangles connected to 't0' - void reselectSelection(Triangle *t0); - - //! Creates a new Basic_TMesh out of an existing selection containing 't0'. O(output). - - //! If necessary, non-manifold vertices are properly duplicated. - //! If 'keep_ref' is set to TRUE, then elements of the original mesh point - //! (through their info field) to corresponding elements of the newly created copy. - - TMESH_VIRTUAL Basic_TMesh *createSubMeshFromSelection(Triangle *t0 = NULL, bool keep_ref = 0); - - //! Creates a new Basic_TMesh out of an existing triangle 't0'. O(output). - - TMESH_VIRTUAL Basic_TMesh *createSubMeshFromTriangle(Triangle *t0); - - //! Marks all the triangles within distance L from 'p' as selected. O(output). - - //! A triangle is considered to be within distance L from 'p' only if all - //! its three vertices are so. - //! Point 'p' is assumed to belong to triangle 't0', which is required to - //! limit the complexity to the size of the selected region. - //! Returns the number of selected triangles. - int selectSphericalRegion(Triangle *t0, const double L, const Point *p); - - - //! Marks all the triangles within distance L from 'p' as deselected. O(output). - - //! A triangle is considered to be within distance L from 'p' only if all - //! its three vertices are so. - //! Point 'p' is assumed to belong to triangle 't0', which is required to - //! limit the complexity to the size of the selected region. - //! Returns the number of deselected triangles. - int deselectSphericalRegion(Triangle *t0, const double L, const Point *p); - - - //! Deselects all the triangles farther than L from 'p'. O(N). - - //! A triangle is considered to be farther than L from 'p' if at least - //! one of its three vertices is so. - //! Point 'p' is assumed to belong to triangle 't0'. Passing 't0' avoids - //! the non robust and expensive computation of point-in-triangle. - void reselectSphericalRegion(Triangle *t0, const double L, const Point *p); - - //! Re-triangulates the currently selected region using a Delaunay-like approach. O(SlogS). - - //! A common plane is computed as the average of the planes of the triangles selected; - //! then, the vertices of the region are projected on the plane and edges are iteratively - //! swapped up to convergence (which is guaranteed on planar and simple domains). - //! Finally, the vertices are moved back to their original positions. This operation is - //! particularly useful to improve the quality of nearly flat regions. The selection must - //! be simple and its Gauss map must be entirely contained in a semi-sphere. - //! Returns TRUE on success, FALSE otherwise. - bool retriangulateSelectedRegion(); - - - //! TRUE iff the set of selected triangles in 'l' is simply connected. O(l->numels()). - bool isSelectionSimple(List *l); - - //! Unmarks all the elements but leaves the selection status of triangles as is. O(N). - void unmarkEverythingButSelections(); - - - //! Selects all the triangles of the connected component containing t0. O(N). - - //! If 'stop_on_sharp', expansion from 't0' brakes at tagged sharp edges. - //! Returns the number of selected triangles. - int selectConnectedComponent(Triangle *t0, bool stop_on_sharp = 0); - - - //! Deselects all the triangles of the connected component containing t0. O(N). - - //! If 'stop_on_sharp', expansion from 't0' brakes at tagged sharp edges. - //! Returns the number of deselected triangles. - int deselectConnectedComponent(Triangle *t0, bool stop_on_sharp = 0); - - - //! Append to the current mesh a copy of all the elements of 't'. - //! The newly created elements form a new selection. - TMESH_VIRTUAL void append(Basic_TMesh *t); - - //! This method removes one connected component from the mesh and creates - //! a separate new mesh out of it. The components to be removed is the one - //! containing the first triangle in the list T. - //! Possible selection flags are deleted by this method. - TMESH_VIRTUAL Basic_TMesh *split(); - - - ///////////////////////////////////////////////////////////////////////////// - // - // Region manipulation (Implemented in "MESH_STRUCTURE/tin.cpp") - // - ///////////////////////////////////////////////////////////////////////////// - - //! Make a list of triangles within distance L from 'p'. O(output). - - //! Starting from 't0', which is assumed to contain 'p', add a triangle at a - //! time to the list as long as all the vertices stay within distance L from 'p'. - List *getRegion(Triangle *t0, const double L, const Point *p); - - //! Removes triangles within distance L from 'p'. O(N). - - //! Starting from 't0', which is assumed to contain 'p', remove a triangle at a - //! time as long as all its vertices stay within distance L from 'p'. - void removeRegion(Triangle *t0, const double L, const Point *p); - - //! Get the vertex next to 'v' on the boundary of the region. O(1). - Vertex *nextVertexOnRegionBoundary(Vertex *v) const; - - //! Retrieve internal vertices of a region. O(l->numels()). - - //! This method returns a list containing an edge of the region's boundary - //! as its first element, and all the internal vertices as the remaining elements. - List *getRegionInternalVertices(List *l); - - //! Transform the vertices of the shell containing 't0' using the matrix m. O(S). - void transformShell(Triangle *t0, const Matrix4x4& m); - - //! Translate the mesh by a vector +namespace T_MESH +{ + +//! Basic_TMesh + +//! This class represents a manifold and oriented triangle mesh. +//! Vertices, Edges and Triangles are stored in the Lists V, E and T +//! respectively. Methods boundaries(), handles() and shells() may +//! be used to retrieve the respective topological entities. +//! Navigation of the mesh is based on the topological relationships +//! stored in each Vertex, Edge and Triangle. +//! Some methods would require a global update only to maintain +//! consistent values of the protected fields n_boundaries, n_handles +//! and n_shells. On the other hand, the same methods would work in +//! constant time if these values would not need to be updated. +//! To keep complexity as low as possible, +//! we make use of the 'dirty bits' d_boundaries, d_handles and +//! d_shells to mark that the respective entities must be updated. +//! The access functions boundaries(), handles() and shells() +//! check the status of the respective dirty bit and do a global +//! update (i.e., eulerUpdate()) only if necessary. +//! The complexity of the methods is provided as a function of a +//! generic 'N', which is O(V.numels()) = O(E.numels()) = O(T.numels()). + +class Basic_TMesh +{ + protected: + + int n_boundaries; //!< Number of boundary loops + int n_handles; //!< Number of handles + int n_shells; //!< Number of connected components + + bool d_boundaries; //!< Dirty bit for n_boundaries + bool d_handles; //!< Dirty bit for n_handles + bool d_shells; //!< Dirty bit for n_shells + + public: + + List V; //!< Vertex set + List E; //!< Edge set + List T; //!< Triangle set + + void *info; //! Generic information attached to this mesh + + ///////////////////////////////////////////////////////////////////////////// + // + // Constructors/Destructor (Implemented in "MESH_STRUCTURE/io.cpp") + // + ///////////////////////////////////////////////////////////////////////////// + + //! Empty triangulation. Should be used only prior to a call to load(). + Basic_TMesh(); + + //! Pre-defined triangulation. Currently, only "triangle" and "tetrahedron" + //! are recognized. + Basic_TMesh(const char *); + void init(const char *); + + //! Clones an existing Trianglation. + Basic_TMesh(const Basic_TMesh *, const bool clone_info = false); + void init(const Basic_TMesh *, const bool clone_info = false); + + //! Clones an existing connected component. + + //! Creates a new Basic_TMesh out of a connected component of an existing + //! Basic_TMesh. 't' is a triangle of the connected component that must + //! be copied. If 'keep_ref' is TRUE, each element of the existing mesh + //! keeps a pointer to the corresponding new element in the 'info' field. + Basic_TMesh(const Triangle *t, const bool keep_ref = false); + void init(const Triangle *t, const bool keep_ref = false); + + //FIXED: + //! Destructor. Frees the memory allocated for all the mesh elements. + //! Warning! This method uses the freeNodes() method of the class List, + //! which is not guaranteed to work correctly on systems other than + //! Linux (see the documentation of List for details). + //! Assuming that the method works correctly, however, the calling + //! function is responsible of freeing the memory that was possibly + //! allocated for objects pointed to by the 'info' field of mesh + //! elements. Clearly, this must be done before calling the destructor. + ~Basic_TMesh(); + + //! Returns true only if object is a basic Basic_TMesh. All the reimplementations must return false. +// TMESH_VIRTUAL bool isBaseType() const { return true; } + + //! Get the number of boundary loops of the triangle mesh. O(1) or O(N). + int boundaries() { if (d_boundaries) eulerUpdate(); return n_boundaries; } + + //! Get the number of handles of the triangle mesh. O(1) or O(N). + int handles() { if (d_handles) eulerUpdate(); return n_handles; } + + //! Get the number of connected components of the triangle mesh. O(1) or O(N). + int shells() { if (d_shells) eulerUpdate(); return n_shells; } + + + ///////////////////////////////////////////////////////////////////////////// + // + // Input/Output methods (Implemented in "MESH_STRUCTURE/io.cpp") + // + ///////////////////////////////////////////////////////////////////////////// + + //! Initialize the triangle mesh from the file 'filename'. + + //! The file format is automatically deduced from the magic number + //! or the filename extension. If 'doupdate' is FALSE, the + //! global update for the topological entities is prevented. + //! Currently, the following file formats are supported: + //! Open Inventor (IV), VRML 1.0 and 2.0 (WRL), Object File Format (OFF), + //! IMATI Ver-Tri (VER, TRI), PLY, OBJ, STL. + //! A non-zero value is returned in case of error. Specifically, + //! IO_CANTOPEN means that the file couldn't be opened for reading. + //! IO_FORMAT means that the file format was not recognized by the loader. + //! IO_UNKNOWN represents all the other errors. + //! The calling function is responsible of verifying that the mesh is + //! empty before calling this method. + + int load(const char *filename, const bool update = 1); + int loadIV(const char *); //!< Loads IV + int loadVRML1(const char *); //!< Loads VRML 1.0 + int loadOFF(const char *); //!< Loads OFF + int loadEFF(const char *); //!< Loads EFF + int loadPLY(const char *); //!< Loads PLY + int loadVerTri(const char *); //!< Loads VER-TRI + int loadVRML2(const char *); //!< Loads VRML 2.0 + int loadOBJ(const char *); //!< Loads OBJ + int loadSTL(const char *); //!< Loads STL + + int cutAndStitch(); //!< Convert to manifold + Triangle * CreateIndexedTriangle(ExtVertex **, int, int, int); + TMESH_VIRTUAL Triangle * CreateTriangleFromVertices(ExtVertex *, ExtVertex *, ExtVertex *); + + //! This function approximates the vertex coordinates with the values + //! that can be represented in an ASCII file. + void coordBackApproximation(); + + protected: + void closeLoadingSession(FILE *, int, ExtVertex **, bool); + bool pinch(Edge *e, bool wcv); + //! If the 'e' is internal, creates a copy of the edge and associates e->t2 + //! to this copy. After this operation, 'e' and its copy are boundary edges + //! with the same vertices. If 'e' is already on boundary, nothing is done + //! and NULL is returned. Otherwise the newly created copy is returned. + TMESH_VIRTUAL Edge *duplicateEdge(Edge *e); + + public: + TMESH_VIRTUAL Vertex * newVertex(); + TMESH_VIRTUAL Vertex * newVertex(const coord &, const coord &, const coord &); + TMESH_VIRTUAL Vertex * newVertex(Point *); + TMESH_VIRTUAL Vertex * newVertex(Point &); + TMESH_VIRTUAL Vertex * newVertex(Vertex *); + TMESH_VIRTUAL Edge * newEdge(Vertex *, Vertex *); + TMESH_VIRTUAL Edge * newEdge(Edge *); + TMESH_VIRTUAL Triangle * newTriangle(); + TMESH_VIRTUAL Triangle * newTriangle(Edge *, Edge *, Edge *); + TMESH_VIRTUAL Basic_TMesh * newObject() const { return new Basic_TMesh(); } + TMESH_VIRTUAL Basic_TMesh * newObject(const Basic_TMesh *tm, const bool ci = false) const { return new Basic_TMesh(tm, ci); } + TMESH_VIRTUAL Basic_TMesh * newObject(const char *s) const { return new Basic_TMesh(s); } + + //! Save the triangle mesh to file 'filename'. + + //! The file format is deduced from the filename extension + //! (wrl = vrml 1.0), (iv = OpenInventor), (off = Object + //! file format), (ply = PLY format), (tri = IMATI Ver-Tri). + //! If 'back_approx' is set, vertex coordinates are approximated + //! to reflect the limited precision of floating point + //! representation in ASCII files. This should be used when + //! coherence is necessary between in-memory and saved data. + //! A non-zero return value is returned if errors occur. + + int save(const char *filename, bool back_approx = 0); + + int saveIV(const char *); //!< Saves IV + int saveOFF(const char *); //!< Saves OFF 1.0 + int saveEFF(const char *); //!< Saves EFF + int saveOBJ(const char *); //!< Saves OBJ + int saveSTL(const char *); //!< Saves STL + int savePLY(const char *, bool ascii = 1); //!< Saves PLY 1.0 (ascii or binary) + int saveVerTri(const char *); //!< Saves Ver-Tri + + //! Saves the triangle mesh to a VRML 1.0 file. + //! The value of 'mode' specifies whether to use additional + //! information attached to mesh elements in order to assign + //! them a proper color. + //! IO_CSAVE_OVERALL assigns a unique color for the entire mesh (default). + //! IO_CSAVE_PERFACE assigns a color to each triangle depending on the value + //! of its 'info' field. + //! IO_CSAVE_PERVERTEX assigns a color to each vertex depending on the value + //! of its 'info' field. + //! IO_CSAVE_PERFACE_INDEXED assigns one of five base colors to each triangle + //! depending on the value of its 'mask' field. + //! IO_CSAVE_PERVERTEX_INDEXED assigns one of five base colors to each vertex + //! depending on the value of its 'mask' field. + int saveVRML1(const char *, const int mode = 0); + + + //! Append another triangle mesh to the existing one. + + //! This method works exactly as the 'load()' method, except for the fact + //! that it does not assume that the mesh is empty. + int append(const char *filename, const bool doupdate = 1); + + + // Move all the elements of 't' to this mesh and delete 't' itself. + TMESH_VIRTUAL void moveMeshElements(Basic_TMesh *t, bool delInput = true); + + + ///////////////////////////////////////////////////////////////////////////// + // + // Primitive Construction (Implemented in "MESH_STRUCTURE/tin.cpp") + // + ///////////////////////////////////////////////////////////////////////////// + + //! Creates an Edge connecting two existing mesh vertices. + + //! Returns the newly created edge. If an edge connecting the two vertices + //! already exists in the mesh, then no new edge is created and the old one + //! is returned. + Edge *CreateEdge(Vertex *v1, Vertex *v2); + + + //! Creates an Edge connecting two existing mesh Extended vertices. + + //! Returns the newly created edge. If an edge connecting the two vertices + //! already exists in the mesh, then no new edge is created and the old one + //! is returned. + //! If 'check' is FALSE, the check for previously existing edges is skipped. + Edge *CreateEdge(ExtVertex *v1, ExtVertex *v2, const bool check = 1); + + + //! Creates a properly oriented Triangle bounded by three existing mesh edges. + + //! Returns the newly created Triangle. If e1, e2 and e3 + //! are not suitable for creating a properly oriented and + //! manifold triangle, the creation fails and NULL is returned. + TMESH_VIRTUAL Triangle * CreateTriangle(Edge *e1, Edge *e2, Edge *e3); + + + //! Creates an arbitrarily oriented Triangle bounded by three existing mesh edges. + + //! Returns the newly created Triangle. If either e1, e2 or e3 + //! has already two incident triangles, the creation fails and NULL is returned. + //! This method assumes that e1, e2 and e3 are incident to exactly three vertices. + TMESH_VIRTUAL Triangle * CreateUnorientedTriangle(Edge *, Edge *, Edge *); + + + //! Creates a newEdge 'e' and an oriented Triangle bounded by 'e', 'e1' and 'e2'. + + //! The newly created triangle is returned, unless 'e1' and 'e2' do not share a + //! vertex or they are not boundary edges. In this cases, NULL is returned. + TMESH_VIRTUAL Triangle *EulerEdgeTriangle(Edge *e1, Edge *e2); + + + //! Splits and edge at a given point and returns the newly created Vertex. + //! If the boolean parameter is set to true, the 'mask' fields of edges and + //! triangles are propagated to the new elements. + TMESH_VIRTUAL Vertex *splitEdge(Edge *, Point *, bool = 0); + + + //! Splits a triangle at a given point and returns the newly created Vertex. + //! If the boolean parameter is set to true, the 'mask' field of the + //! triangle is propagated to the new triangle. + TMESH_VIRTUAL Vertex *splitTriangle(Triangle *, Point *, bool = 0); + + //! Creates two new triangles connecting the boundary edges e1 and e2 + //! and returns their common edge. + //! If e1 and e2 share a vertex, then only one triangle is created and + //! e1 is returned. + //! Returns NULL if either e1 or e2 are not boundary edges. + TMESH_VIRTUAL Edge *bridgeBoundaries(Edge *e1, Edge *e2); + + ///////////////////////////////////////////////////////////////////////////// + // + // Primitive Destruction (Implemented in "MESH_STRUCTURE/tin.cpp") + // + ///////////////////////////////////////////////////////////////////////////// + + + //! Unlinks a triangle from the mesh. O(1). + + //! Resulting isolated vertices and edges are unlinked too. + //! If necessary, this method duplicates non-manifold vertices that can + //! occur due to the removal of the triangle. + //! The unlinked triangle, along with the other possible unlinked elements, + //! must be removed from the List T through removeUnlinkedElements(). + void unlinkTriangle(Triangle *); + + + //! Unlinks a triangle from the mesh. O(1). + + //! No check is performed on the resulting topology, which may be inconsistent. + //! The unlinked triangle, along with the other possible unlinked elements, + //! must be removed from the List T through removeUnlinkedElements(). + void unlinkTriangleNoManifold(Triangle *); + + + //! Removes a triangle from the mesh. O(N). + + //! This is equivalent to an unlinkTriangle(t) followed by a + //! removeUnlinkedElements(). + void removeTriangle(Triangle *t) { unlinkTriangle(t); removeUnlinkedElements(); } + + //! Removes all the unlinked triangles from List T. Returns the number of removed triangles. O(N). + int removeTriangles(); + + //! Removes all the unlinked edges from List E. Returns the number of removed edges. O(N). + int removeEdges(); + + //! Removes all the unlinked vertices from List V. Returns the number of removed vertices. O(N). + int removeVertices(); + + //! Removes all the unlinked elements from the lists. Returns the number of removed elements. O(N). + int removeUnlinkedElements() { return removeTriangles() + removeEdges() + removeVertices(); } + + //! Removes all the vertices that can be deleted without changing the geometric realization. O(N). + int removeRedundantVertices(); + + ///////////////////////////////////////////////////////////////////////////// + // + // Methods acting on selections (Implemented in "MESH_STRUCTURE/tin.cpp") + // + ///////////////////////////////////////////////////////////////////////////// + + //! Deselects all the triangles. O(N). + void deselectTriangles(); + + //! Removes all the selected triangles. O(N). + void removeSelectedTriangles(); + + //! Selects all the triangles having at least one boundary vertex. O(N). + //! Returns the number of selected triangles. + int selectBoundaryTriangles(); + + //! Enlarges the current selection of one triangle in width. O(N). + + //! Each triangle sharing at least one vertex with a currently selected + //! triangle becomes selected. + //! Returns the number of newly selected triangles. + int growSelection(); + + //! Shrinks the current selection of one triangle in width. O(N). + + //! Each triangle sharing at least one vertex with a currently unselected + //! triangle becomes unselected. + void shrinkSelection(); + + //! Inverts the selection status of all the triangles. O(N). + + //! If 't0' is not NULL, then only the connected component containing 't0' + //! is inverted. + void invertSelection(Triangle *t0 = NULL); + + //! If 't0' is selected, deselect everything but the selected triangles connected to 't0' + void reselectSelection(Triangle *t0); + + //! Creates a new Basic_TMesh out of an existing selection containing 't0'. O(output). + + //! If necessary, non-manifold vertices are properly duplicated. + //! If 'keep_ref' is set to TRUE, then elements of the original mesh point + //! (through their info field) to corresponding elements of the newly created copy. + + TMESH_VIRTUAL Basic_TMesh *createSubMeshFromSelection(Triangle *t0 = NULL, bool keep_ref = 0); + + //! Creates a new Basic_TMesh out of an existing triangle 't0'. O(output). + + TMESH_VIRTUAL Basic_TMesh *createSubMeshFromTriangle(Triangle *t0); + + //! Marks all the triangles within distance L from 'p' as selected. O(output). + + //! A triangle is considered to be within distance L from 'p' only if all + //! its three vertices are so. + //! Point 'p' is assumed to belong to triangle 't0', which is required to + //! limit the complexity to the size of the selected region. + //! Returns the number of selected triangles. + int selectSphericalRegion(Triangle *t0, const double L, const Point *p); + + + //! Marks all the triangles within distance L from 'p' as deselected. O(output). + + //! A triangle is considered to be within distance L from 'p' only if all + //! its three vertices are so. + //! Point 'p' is assumed to belong to triangle 't0', which is required to + //! limit the complexity to the size of the selected region. + //! Returns the number of deselected triangles. + int deselectSphericalRegion(Triangle *t0, const double L, const Point *p); + + + //! Deselects all the triangles farther than L from 'p'. O(N). + + //! A triangle is considered to be farther than L from 'p' if at least + //! one of its three vertices is so. + //! Point 'p' is assumed to belong to triangle 't0'. Passing 't0' avoids + //! the non robust and expensive computation of point-in-triangle. + void reselectSphericalRegion(Triangle *t0, const double L, const Point *p); + + //! Re-triangulates the currently selected region using a Delaunay-like approach. O(SlogS). + + //! A common plane is computed as the average of the planes of the triangles selected; + //! then, the vertices of the region are projected on the plane and edges are iteratively + //! swapped up to convergence (which is guaranteed on planar and simple domains). + //! Finally, the vertices are moved back to their original positions. This operation is + //! particularly useful to improve the quality of nearly flat regions. The selection must + //! be simple and its Gauss map must be entirely contained in a semi-sphere. + //! Returns TRUE on success, FALSE otherwise. + bool retriangulateSelectedRegion(); + + + //! TRUE iff the set of selected triangles in 'l' is simply connected. O(l->numels()). + bool isSelectionSimple(List *l); + + //! Unmarks all the elements but leaves the selection status of triangles as is. O(N). + void unmarkEverythingButSelections(); + + + //! Selects all the triangles of the connected component containing t0. O(N). + + //! If 'stop_on_sharp', expansion from 't0' brakes at tagged sharp edges. + //! Returns the number of selected triangles. + int selectConnectedComponent(Triangle *t0, bool stop_on_sharp = 0); + + + //! Deselects all the triangles of the connected component containing t0. O(N). + + //! If 'stop_on_sharp', expansion from 't0' brakes at tagged sharp edges. + //! Returns the number of deselected triangles. + int deselectConnectedComponent(Triangle *t0, bool stop_on_sharp = 0); + + + //! Append to the current mesh a copy of all the elements of 't'. + //! The newly created elements form a new selection. + TMESH_VIRTUAL void append(Basic_TMesh *t); + + //! This method removes one connected component from the mesh and creates + //! a separate new mesh out of it. The components to be removed is the one + //! containing the first triangle in the list T. + //! Possible selection flags are deleted by this method. + TMESH_VIRTUAL Basic_TMesh *split(); + + + ///////////////////////////////////////////////////////////////////////////// + // + // Region manipulation (Implemented in "MESH_STRUCTURE/tin.cpp") + // + ///////////////////////////////////////////////////////////////////////////// + + //! Make a list of triangles within distance L from 'p'. O(output). + + //! Starting from 't0', which is assumed to contain 'p', add a triangle at a + //! time to the list as long as all the vertices stay within distance L from 'p'. + List *getRegion(Triangle *t0, const double L, const Point *p); + + //! Removes triangles within distance L from 'p'. O(N). + + //! Starting from 't0', which is assumed to contain 'p', remove a triangle at a + //! time as long as all its vertices stay within distance L from 'p'. + void removeRegion(Triangle *t0, const double L, const Point *p); + + //! Get the vertex next to 'v' on the boundary of the region. O(1). + Vertex *nextVertexOnRegionBoundary(Vertex *v) const; + + //! Retrieve internal vertices of a region. O(l->numels()). + + //! This method returns a list containing an edge of the region's boundary + //! as its first element, and all the internal vertices as the remaining elements. + List *getRegionInternalVertices(List *l); + + //! Transform the vertices of the shell containing 't0' using the matrix m. O(S). + void transformShell(Triangle *t0, const Matrix4x4& m); + + //! Translate the mesh by a vector void translate(const Point& t_vec); //! Return the center of mass of the mesh Point getCenter() const; - - //! Remove all the triangles belonging to the shell containing 't0'. O(N). - void removeShell(Triangle *t0); - - - ///////////////////////////////////////////////////////////////////////////// - // - // Global Operations (Implemented in "MESH_STRUCTURE/tin.cpp") - // - ///////////////////////////////////////////////////////////////////////////// - - //! Tag sharp edges based on threshold angle. O(N). - - //! Tag as sharp all the edges in which the normals of the two incident - //! triangles form an angle greater than 't'. - void sharpEdgeTagging(const double t); - - //! Unmark all the elements. O(N). - void unmarkEverything(); - - //! Bounding box longest edge. 'b' and 't' are set as the longest diagonal end-points. O(N). - coord getBoundingBox(Point& b, Point& t) const; - - //! Bounding box longest diagonal. O(N). - double bboxLongestDiagonal() { Point a, b; getBoundingBox(a, b); return a.distance(b); } - - //! Approximate bounding ball radius. O(N). - double getBoundingBallRadius() const; - - //! Total area of the mesh. O(N). - double area() const; - - //! Total volume of the mesh assuming that boundaries() = 0. O(N). - double volume() const; - - //! Scale the mesh to make it fit within a cube [0,0,0]-[s,s,s]. O(N). - void normalize(const coord s = 1.0); - - //! Scale the mesh to make it fit within a cube [0,0,0]-[s,s,s] and snap coordinates on grid points O(N). - void quantize(const int s = 65536); - - //! Transform the mesh geometry using the transformation matrix m. O(N). - void transform(const Matrix4x4& m); - - //! Randomly move vertices along their normals. O(N). - //! Displacement is bounded by 'p'% of the bounding ball radius. - void addNormalNoise(const double p); - - //! Iteratively swaps edges to minimize the Delaunay minimum angle. O(N). - - //! Edges tagged as sharp are constrained not to swap. - //! On generically curved manifolds this process is not guaranteed to converge. - //! This method returns TRUE if convergence is reached, FALSE otherwise. - bool iterativeEdgeSwaps(); - - //! True if the mesh properly contains 'p' (exact when using rationals). - //! The mesh is assumed to be a well-defined polyhedron (i.e. no boundary, - //! no degenerate triangles, no self-intersections) and to have a correct orientation: - //! result is undetermined otherwise. - bool isInnerPoint(Point& p) const; - - //! Performs one step of Loop subdivision. If 'midpoint' is set, only the connectivity - //! is subdivided, whereas the surface shape is kept unchanged. - void loopSubdivision(bool midpoint =false); - - ///////////////////////////////////////////////////////////////////////////// - // - // Surface topology manipulation (Implemented in "MESH_STRUCTURE/tin.cpp") - // - ///////////////////////////////////////////////////////////////////////////// - - //! Invert all the triangle and edge orientations. O(N). - void flipNormals(); - - //! Invert the orientation of triangles and edges belonging to the shell containing 't0'. O(S). - void flipNormals(Triangle *t0); - - //! Return the triangle with the maximum 'z' coordinate in the shell containing 't0'. O(N). - - //! Useful for orienting meshes bounding solids. - Triangle *topTriangle(Triangle *t0); - - //! Updates the values of n_boundaries, n_handles and n_shells. O(N). - - //! The relative dirty bits are set to zero. - void eulerUpdate(); - - //! Duplicates edges and vertices to make the mesh homeomorphic to a disk. O(N). - void openToDisk(); - - - ///////////////////////////////////////////////////////////////////////////// - // - // Topological fixes (Implemented in "MESH_STRUCTURE/checkAndRepair.cpp") - // - ///////////////////////////////////////////////////////////////////////////// - - //! Removes all connected components but the one having most triangles. - //! Returns the number of components removed. - int removeSmallestComponents(); - - //! Checks that triangles are consistently oriented and, if they are not, - //! invert some of them to achieve an overall consistency. If the mesh is - //! not orientable cut it. Returns: 0 if mesh was already oriented; 1 if - //! the mesh could be oriented without cuts; >1 if cuts were necessary. - int forceNormalConsistence(); - - //! Same as above, but acts on a single connected component and uses one - //! specific triangle from which the orientation is propagated. - int forceNormalConsistence(Triangle *); - - //! Detect singular vertices and duplicte them. Return number of singular - //! vertices being duplicated. - int duplicateNonManifoldVertices(); - - //! Remove redundant triangles (i.e. having the same vertices as others) - //! and return their number. - int removeDuplicatedTriangles(); - - //! Check the mesh connectivity. If everything is fine NULL is returned, - //! otherwise an error string is returned. - const char *checkConnectivity(); - - //! If called in rebuildConnectivity(bool) fix the connectivity between - //! geometric elements - bool fixConnectivity(); //!< AMF_ADD 1.1> - - //! Considers triangles as purely geometric entities and recomputes their - //! connectivity based on vertex correspondence. - //! Returns false if mesh was not an oriented manifold. - TMESH_VIRTUAL bool rebuildConnectivity(bool = true); //!< AMF_CHANGE 1.1> - - //! Looks for topologically different edges having the same geometry - //! (i.e. coincident vertices) and unify them. Return their number. - int mergeCoincidentEdges(); - - - ///////////////////////////////////////////////////////////////////////////// - // - // Geometrical fixes (Implemented in "MESH_STRUCTURE/checkAndRepair.cpp") - // - ///////////////////////////////////////////////////////////////////////////// - - //! Looks for zero-area triangles and resolve them by splits and collapses. - //! If, for any reason, some degeneracies cannot be removed - //! a warning is issued and the degenerate triangles that could not - //! be resolved are selected. - //! The absolute value of the integer returned is the number of - //! collapses performed; the return value is negative if some - //! degenerate triangles could not be resolved. - int removeDegenerateTriangles(); - - //! Calls 'removeDegenerateTriangles()' and, if some degeneracies remain, - //! removes them and fills the resulting holes. Then tries again and, if - //! some degeneracies still remain, removes them and their neighbors and - //! fills the resulting holes, and so on, until the neighborhood growth - //! reaches max-iters. If even in this case some degeneracies remain, - //! returns false, otherwise returns true. - bool strongDegeneracyRemoval(int max_iters); - - //! Removes all the intersecting triangles and patches the resulting holes. - //! If the patches still produce intersections, iterates again on a larger - //! neighborhood. Tries up to max_iters times before giving up. Returns - //! true only if all the intersections could be removed. - bool strongIntersectionRemoval(int max_iters); - - //! Iteratively call strongDegeneracyRemoval and strongIntersectionRemoval - //! to produce an eventually clean mesh without degeneracies and intersections. - //! The two aforementioned methods are called up to max_iter times and - //! each of them is called using 'inner_loops' as a parameter. - //! Returns true only if the mesh could be completely cleaned. + + //! Remove all the triangles belonging to the shell containing 't0'. O(N). + void removeShell(Triangle *t0); + + + ///////////////////////////////////////////////////////////////////////////// + // + // Global Operations (Implemented in "MESH_STRUCTURE/tin.cpp") + // + ///////////////////////////////////////////////////////////////////////////// + + //! Tag sharp edges based on threshold angle. O(N). + + //! Tag as sharp all the edges in which the normals of the two incident + //! triangles form an angle greater than 't'. + void sharpEdgeTagging(const double t); + + //! Unmark all the elements. O(N). + void unmarkEverything(); + + //! Bounding box longest edge. 'b' and 't' are set as the longest diagonal end-points. O(N). + coord getBoundingBox(Point& b, Point& t) const; + + //! Bounding box longest diagonal. O(N). + double bboxLongestDiagonal() { Point a, b; getBoundingBox(a, b); return a.distance(b); } + + //! Approximate bounding ball radius. O(N). + double getBoundingBallRadius() const; + + //! Total area of the mesh. O(N). + double area() const; + + //! Total volume of the mesh assuming that boundaries() = 0. O(N). + double volume() const; + + //! Scale the mesh to make it fit within a cube [0,0,0]-[s,s,s]. O(N). + void normalize(const coord s = 1.0); + + //! Scale the mesh to make it fit within a cube [0,0,0]-[s,s,s] and snap coordinates on grid points O(N). + void quantize(const int s = 65536); + + //! Transform the mesh geometry using the transformation matrix m. O(N). + void transform(const Matrix4x4& m); + + //! Randomly move vertices along their normals. O(N). + //! Displacement is bounded by 'p'% of the bounding ball radius. + void addNormalNoise(const double p); + + //! Iteratively swaps edges to minimize the Delaunay minimum angle. O(N). + + //! Edges tagged as sharp are constrained not to swap. + //! On generically curved manifolds this process is not guaranteed to converge. + //! This method returns TRUE if convergence is reached, FALSE otherwise. + bool iterativeEdgeSwaps(); + + //! True if the mesh properly contains 'p' (exact when using rationals). + //! The mesh is assumed to be a well-defined polyhedron (i.e. no boundary, + //! no degenerate triangles, no self-intersections) and to have a correct orientation: + //! result is undetermined otherwise. + bool isInnerPoint(Point& p) const; + + //! Performs one step of Loop subdivision. If 'midpoint' is set, only the connectivity + //! is subdivided, whereas the surface shape is kept unchanged. + void loopSubdivision(bool midpoint =false); + + ///////////////////////////////////////////////////////////////////////////// + // + // Surface topology manipulation (Implemented in "MESH_STRUCTURE/tin.cpp") + // + ///////////////////////////////////////////////////////////////////////////// + + //! Invert all the triangle and edge orientations. O(N). + void flipNormals(); + + //! Invert the orientation of triangles and edges belonging to the shell containing 't0'. O(S). + void flipNormals(Triangle *t0); + + //! Return the triangle with the maximum 'z' coordinate in the shell containing 't0'. O(N). + + //! Useful for orienting meshes bounding solids. + Triangle *topTriangle(Triangle *t0); + + //! Updates the values of n_boundaries, n_handles and n_shells. O(N). + + //! The relative dirty bits are set to zero. + void eulerUpdate(); + + //! Duplicates edges and vertices to make the mesh homeomorphic to a disk. O(N). + void openToDisk(); + + + ///////////////////////////////////////////////////////////////////////////// + // + // Topological fixes (Implemented in "MESH_STRUCTURE/checkAndRepair.cpp") + // + ///////////////////////////////////////////////////////////////////////////// + + //! Removes all connected components but the one having most triangles. + //! Returns the number of components removed. + int removeSmallestComponents(); + + //! Checks that triangles are consistently oriented and, if they are not, + //! invert some of them to achieve an overall consistency. If the mesh is + //! not orientable cut it. Returns: 0 if mesh was already oriented; 1 if + //! the mesh could be oriented without cuts; >1 if cuts were necessary. + int forceNormalConsistence(); + + //! Same as above, but acts on a single connected component and uses one + //! specific triangle from which the orientation is propagated. + int forceNormalConsistence(Triangle *); + + //! Detect singular vertices and duplicte them. Return number of singular + //! vertices being duplicated. + int duplicateNonManifoldVertices(); + + //! Remove redundant triangles (i.e. having the same vertices as others) + //! and return their number. + int removeDuplicatedTriangles(); + + //! Check the mesh connectivity. If everything is fine NULL is returned, + //! otherwise an error string is returned. + const char *checkConnectivity(); + + //! If called in rebuildConnectivity(bool) fix the connectivity between + //! geometric elements + bool fixConnectivity(); //!< AMF_ADD 1.1> + + //! Considers triangles as purely geometric entities and recomputes their + //! connectivity based on vertex correspondence. + //! Returns false if mesh was not an oriented manifold. + TMESH_VIRTUAL bool rebuildConnectivity(bool = true); //!< AMF_CHANGE 1.1> + + //! Looks for topologically different edges having the same geometry + //! (i.e. coincident vertices) and unify them. Return their number. + int mergeCoincidentEdges(); + + + ///////////////////////////////////////////////////////////////////////////// + // + // Geometrical fixes (Implemented in "MESH_STRUCTURE/checkAndRepair.cpp") + // + ///////////////////////////////////////////////////////////////////////////// + + //! Looks for zero-area triangles and resolve them by splits and collapses. + //! If, for any reason, some degeneracies cannot be removed + //! a warning is issued and the degenerate triangles that could not + //! be resolved are selected. + //! The absolute value of the integer returned is the number of + //! collapses performed; the return value is negative if some + //! degenerate triangles could not be resolved. + int removeDegenerateTriangles(); + + //! Calls 'removeDegenerateTriangles()' and, if some degeneracies remain, + //! removes them and fills the resulting holes. Then tries again and, if + //! some degeneracies still remain, removes them and their neighbors and + //! fills the resulting holes, and so on, until the neighborhood growth + //! reaches max-iters. If even in this case some degeneracies remain, + //! returns false, otherwise returns true. + bool strongDegeneracyRemoval(int max_iters); + + //! Removes all the intersecting triangles and patches the resulting holes. + //! If the patches still produce intersections, iterates again on a larger + //! neighborhood. Tries up to max_iters times before giving up. Returns + //! true only if all the intersections could be removed. + bool strongIntersectionRemoval(int max_iters); + + //! Iteratively call strongDegeneracyRemoval and strongIntersectionRemoval + //! to produce an eventually clean mesh without degeneracies and intersections. + //! The two aforementioned methods are called up to max_iter times and + //! each of them is called using 'inner_loops' as a parameter. + //! Returns true only if the mesh could be completely cleaned. bool meshclean(int max_iters = 10, int inner_loops = 3); - - //! Removes overlapping triangles and return their number. - int removeOverlappingTriangles(); - - //! Checks the mesh for degeneracies, concident vertices and overlaps. - //! If such a flaw is found returns its closest vertex. - Vertex *checkGeometry(); - - //! Selects all the triangles that unproperly intersect other parts of - //! the mesh and return their number. The parameter 'tris_per_cell' - //! determines the depth of the recursive space subdivision used to keep - //! the complexity under a resonable threchold. The default value is safe - //! in most cases. - //! if 'justproper' is true, coincident edges and vertices are not regarded - //! as intersections even if they are not common subsimplexes. - int selectIntersectingTriangles(UINT16 tris_per_cell = 50, bool justproper = false); - - - //! This is as coordBackApproximation() but it also checks for - //! intersections and, if any, it tries different approximations. - //! Returns true if no intersections remain. + + //! Removes overlapping triangles and return their number. + int removeOverlappingTriangles(); + + //! Checks the mesh for degeneracies, concident vertices and overlaps. + //! If such a flaw is found returns its closest vertex. + Vertex *checkGeometry(); + + //! Selects all the triangles that unproperly intersect other parts of + //! the mesh and return their number. The parameter 'tris_per_cell' + //! determines the depth of the recursive space subdivision used to keep + //! the complexity under a resonable threchold. The default value is safe + //! in most cases. + //! if 'justproper' is true, coincident edges and vertices are not regarded + //! as intersections even if they are not common subsimplexes. + int selectIntersectingTriangles(UINT16 tris_per_cell = 50, bool justproper = false); + + + //! This is as coordBackApproximation() but it also checks for + //! intersections and, if any, it tries different approximations. + //! Returns true if no intersections remain. bool safeCoordBackApproximation(); - - //! Removes all the connected components whose area is less than 'epsilon'. - //! Returns the number of components removed. - int removeSmallestComponents(double epsilon); - - - ///////////////////////////////////////////////////////////////////////////// - // - // Hole triangulation (Implemented in "MESH_STRUCTURE/holeFilling.cpp") - // - ///////////////////////////////////////////////////////////////////////////// - - //! Computes the barycenter of the boundary loop and connects it with - //! all the edges of the loop. Returns the number of triangles - //! created. - TMESH_VIRTUAL int StarTriangulateHole(Edge *); - - //! Creates a triangulation whose projection on the plane with normal 'nor' - //! is Delaunay. Returns the number of triangles created. - int TriangulateHole(Edge *, Point *nor); - - //! Creates a triangulation of the hole based on heuristics. - //! Returns the number of triangles created. - int TriangulateHole(Edge *); - - //! Creates a triangulation of the hole based on the assumption that it is flat. - //! Returns the number of triangles created. - // int TriangulateFlatHole(Edge *); - - //! Creates a triangulation and inserts the additional points in the List. - //! Returns the number of triangles created. - TMESH_VIRTUAL int TriangulateHole(Edge *, List *); - - //! Hole filling algorithm. Performs a triangulation based on heuristics and, - //! if 'refine' is set to true, adds inner vertices to reproduce the sampling - //! density of the surroundings. - void FillHole(Edge *, bool refine = true); - - //! Fills all the holes having at least 'nbe' boundary edges. If 'refine' - //! is true, adds inner vertices to reproduce the sampling density - //! of the surroundings. Returns number of holes patched. - //! If 'nbe' is 0 (default), all the holes are patched. - int fillSmallBoundaries(int nbe = 0, bool refine = true); - - //! Takes a selected region and inserts inner vertices to reproduce the - //! sampling density of the surroundings. If 't0' is not NULL, only the - //! selected region containing 't0' is refined. Returns the number of - //! vertices inserted. - TMESH_VIRTUAL int refineSelectedHolePatches(Triangle *t0 =NULL); - - //! Retriangulates the vertex neghborhood based on heuristics. - int retriangulateVT(Vertex *); - - //! Joins the two boundary vertices gv and gw through an edge. A pair of triangles is - //! added to properly change the topology of the mesh. - Edge *joinBoundaryLoops(Vertex *, Vertex *, bool = 0, bool = 1); - - // Debug and work-in-progress - - void printReport(); - - protected: - Vertex *watsonInsert(Point *, List *, int); - }; - -#define FOREACHTRIANGLE(Tt, n) for (n = T.head(), Tt = (n)?((Triangle *)n->data):NULL; n != NULL; n=n->next(), Tt = (n)?((Triangle *)n->data):NULL) -#define FOREACHEDGE(Tt, n) for (n = E.head(), Tt = (n)?((Edge *)n->data):NULL; n != NULL; n=n->next(), Tt = (n)?((Edge *)n->data):NULL) -#define FOREACHVERTEX(Tt, n) for (n = V.head(), Tt = (n)?((Vertex *)n->data):NULL; n != NULL; n=n->next(), Tt = (n)?((Vertex *)n->data):NULL) - -#define MARK_VISIT(a) ((a)->mask |= ((unsigned char)1)) -#define IS_VISITED(a) ((a)->mask & ((unsigned char)1)) -#define UNMARK_VISIT(a) ((a)->mask &= (~((unsigned char)1))) - -#define MARK_VISIT2(a) ((a)->mask |= ((unsigned char)2)) -#define IS_VISITED2(a) ((a)->mask & ((unsigned char)2)) -#define UNMARK_VISIT2(a) ((a)->mask &= (~((unsigned char)2))) - -#define MARK_BIT(a,b) ((a)->mask |= ((unsigned char)(1<mask & ((unsigned char)(1<mask &= (~((unsigned char)(1<data):NULL; n != NULL; n=n->next(), Tt = (n)?((Triangle *)n->data):NULL) +#define FOREACHEDGE(Tt, n) for (n = E.head(), Tt = (n)?((Edge *)n->data):NULL; n != NULL; n=n->next(), Tt = (n)?((Edge *)n->data):NULL) +#define FOREACHVERTEX(Tt, n) for (n = V.head(), Tt = (n)?((Vertex *)n->data):NULL; n != NULL; n=n->next(), Tt = (n)?((Vertex *)n->data):NULL) + +#define MARK_VISIT(a) ((a)->mask |= ((unsigned char)1)) +#define IS_VISITED(a) ((a)->mask & ((unsigned char)1)) +#define UNMARK_VISIT(a) ((a)->mask &= (~((unsigned char)1))) + +#define MARK_VISIT2(a) ((a)->mask |= ((unsigned char)2)) +#define IS_VISITED2(a) ((a)->mask & ((unsigned char)2)) +#define UNMARK_VISIT2(a) ((a)->mask &= (~((unsigned char)2))) + +#define MARK_BIT(a,b) ((a)->mask |= ((unsigned char)(1<mask & ((unsigned char)(1<mask &= (~((unsigned char)(1< parts = its_split(mv->mesh().its); + unsigned percent_per_part = 95 / parts.size(); for (size_t part_idx = 0; part_idx < parts.size(); ++part_idx) { + unsigned progress_part_base = part_idx * percent_per_part; detail::Basic_TMesh_Adapter tin { }; - on_progress(L("Loading source model"), 0); + on_progress(L("Loading source model"), progress_part_base); if (canceled) throw RepairCanceledException(); tin.load_indexed_triangle_set(parts[part_idx]); tin.boundaries(); - on_progress(L("Join closest components"), 10); + on_progress(L("Join closest components"), unsigned(progress_part_base + 0.1 * percent_per_part)); if (canceled) throw RepairCanceledException(); joinClosestComponents(&tin); tin.deselectTriangles(); tin.boundaries(); // Keep only the largest component (i.e. with most triangles) - on_progress(L("Remove smallest components"), 20); + on_progress(L("Remove smallest components"), unsigned(progress_part_base + 0.2 * percent_per_part)); if (canceled) throw RepairCanceledException(); tin.removeSmallestComponents(); // Fill holes - on_progress(L("Check holes"), 30); + on_progress(L("Check holes"), unsigned(progress_part_base + 0.3 * percent_per_part)); if (canceled) throw RepairCanceledException(); if (tin.boundaries()) { - on_progress(L("Patch small holes"), 40); + on_progress(L("Patch small holes"), unsigned(progress_part_base + 0.4 * percent_per_part)); if (canceled) throw RepairCanceledException(); tin.fillSmallBoundaries(0, true); } - on_progress(L("Geometry check"), 50); + on_progress(L("Geometry check"), unsigned(progress_part_base + 0.5 * percent_per_part)); if (canceled) throw RepairCanceledException(); // Run geometry correction if (!tin.boundaries()) { int iteration = 0; - on_progress(L("Iterative geometry correction"), 55); + on_progress(L("Start iterative correction"), unsigned(progress_part_base + 0.55 * percent_per_part)); tin.deselectTriangles(); tin.invertSelection(); bool fixed = false; while (iteration < 10 && !fixed) { //default constants taken from TMesh library fixed = tin.meshclean_single_iteration(3, canceled); - on_progress(L("Fixing geometry"), std::min(95, 60 + iteration * 8)); // majority of objects should finish in 4 iterations + on_progress(L("Fixing geometry"), progress_part_base + percent_per_part * std::min(0.9, 0.6 + iteration*0.08)); // majority of objects should finish in 4 iterations if (canceled) throw RepairCanceledException(); iteration++;