├── .gitignore ├── Acknowledgments.txt ├── README ├── VS_files ├── SimplexMesh.sln ├── SimplexMesh.vcxproj └── Test.vcxproj ├── headers ├── IncidenceMatrix.h ├── SimplexHandles.h ├── SimplexIterators.h ├── SimplexProperty.h └── SimplicialComplex.h ├── src ├── IncidenceMatrix.cpp ├── SimplexIterators.cpp └── SimplicialComplex.cpp └── test └── test.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | *.sdf 2 | *.user 3 | *.suo 4 | *.filters 5 | *.obj 6 | *.lib 7 | *.log 8 | *.tlog 9 | *.manifest 10 | *.lastbuildstate 11 | *.pdb 12 | *.opensdf 13 | *.ipch 14 | *.exe 15 | -------------------------------------------------------------------------------- /Acknowledgments.txt: -------------------------------------------------------------------------------- 1 | This code was initially developed primarily by Christopher Batty during a postdoc under Eitan Grinspun in Columbia University's Computer Graphics Group (C2G2), supported by a Banting Postdoctoral Fellowship. The C2G2 lab itself is supported in part by the Sloan Foundation, the NSF (grants CMMI-11-29917, IIS-11-17257, IIS-10-48948, IIS-09-16129, CCF-06-43268), and generous gifts from Adobe, Autodesk, Intel, mental images, NVIDIA, Side Effects Software, and The Walt Disney Company. 2 | 3 | Thanks are also due to other members of the lab for helpful discussions and suggestions, including Andres Uribe, Fang Da, Danny Kaufman, and Breannan Smith. 4 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | The goal is to construct a general non-manifold, non-regular simplicial mesh data structure and library for representing simplicial complexes up to 3D (verts, edges, faces, tets) and associated data stored on the simplices. 2 | (This essentially means a mesh that mixes and matches regions of solo vertices, segments, triangles and tetrahedra, rather than requiring restrictions, e.g. only triangles, embedded in 3D, with no non-manifold edges.) 3 | Priority is given to simplicity and usability over speed, hence the choice (for now at least) of using a representation based loosely on sparse incidence matrices. Our goal is to facilitate research in non-manifold mesh processing and simulation. 4 | 5 | Because of the potential complexity of such meshes, the intention is for mesh operations to rely on a core set of "atomic" operations of the form addSimplex/deleteSimplex, and write all other operations in terms of these ones. Only common operations that would be significantly accelerated by being integrated into the core data structure should be implemented within SimplicialComplex itself, while all others should be relegated to external utility functions/classes. 6 | 7 | The underlying data structure is analogous to that discussed in "Building Your Own DEC at Home" by Sharif Elcott and Peter Schroeder, again emphasizing simplicity. Ultimately, it should be possible to swap this out for a more efficient (but complex) one, if desired. E.g. the IA* data structure from the SMI paper "IA*: An Adjacency-Based Representation for Non-Manifold Simplicial Shapes in Arbitrary Dimensions" by Canino, de Floriani, and Weiss. 8 | 9 | Likewise, since the code is intended for research purposes, we should err on the side of safety when we can, by spending extra effort to make handles (and possibly some iterators) robust in the face of mesh editing. This has not been done yet. 10 | 11 | We aim for a similar degree of ease of use (and relatively comparable API) as the recent Surface_mesh library for triangle meshes, by Daniel Sieger and Mario Botsch described in the IMR paper "Design, Implementation, and Evaluation of the Surface mesh Data Structure". 12 | -------------------------------------------------------------------------------- /VS_files/SimplexMesh.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual Studio 2010 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SimplexMesh", "SimplexMesh.vcxproj", "{B5F86F74-CB6D-424B-99DC-3A34EC8B374F}" 5 | EndProject 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test", "Test.vcxproj", "{4DF8B7AC-56CE-4F35-88C6-FC0ACC739EE0}" 7 | ProjectSection(ProjectDependencies) = postProject 8 | {B5F86F74-CB6D-424B-99DC-3A34EC8B374F} = {B5F86F74-CB6D-424B-99DC-3A34EC8B374F} 9 | EndProjectSection 10 | EndProject 11 | Global 12 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 13 | Debug|Win32 = Debug|Win32 14 | Release|Win32 = Release|Win32 15 | EndGlobalSection 16 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 17 | {B5F86F74-CB6D-424B-99DC-3A34EC8B374F}.Debug|Win32.ActiveCfg = Debug|Win32 18 | {B5F86F74-CB6D-424B-99DC-3A34EC8B374F}.Debug|Win32.Build.0 = Debug|Win32 19 | {B5F86F74-CB6D-424B-99DC-3A34EC8B374F}.Release|Win32.ActiveCfg = Release|Win32 20 | {B5F86F74-CB6D-424B-99DC-3A34EC8B374F}.Release|Win32.Build.0 = Release|Win32 21 | {4DF8B7AC-56CE-4F35-88C6-FC0ACC739EE0}.Debug|Win32.ActiveCfg = Debug|Win32 22 | {4DF8B7AC-56CE-4F35-88C6-FC0ACC739EE0}.Debug|Win32.Build.0 = Debug|Win32 23 | {4DF8B7AC-56CE-4F35-88C6-FC0ACC739EE0}.Release|Win32.ActiveCfg = Release|Win32 24 | {4DF8B7AC-56CE-4F35-88C6-FC0ACC739EE0}.Release|Win32.Build.0 = Release|Win32 25 | EndGlobalSection 26 | GlobalSection(SolutionProperties) = preSolution 27 | HideSolutionNode = FALSE 28 | EndGlobalSection 29 | EndGlobal 30 | -------------------------------------------------------------------------------- /VS_files/SimplexMesh.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | {B5F86F74-CB6D-424B-99DC-3A34EC8B374F} 15 | SimplexMesh 16 | 17 | 18 | 19 | StaticLibrary 20 | true 21 | MultiByte 22 | v110 23 | 24 | 25 | StaticLibrary 26 | false 27 | true 28 | MultiByte 29 | v110 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | Level3 45 | Disabled 46 | C:\Users\Christopher Batty\Documents\Research\GitHub\SimplexMesh\headers;%(AdditionalIncludeDirectories) 47 | 48 | 49 | true 50 | 51 | 52 | 53 | 54 | Level3 55 | MaxSpeed 56 | true 57 | true 58 | ..\headers;%(AdditionalIncludeDirectories) 59 | 60 | 61 | true 62 | true 63 | true 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /VS_files/Test.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | 15 | 16 | 17 | {4DF8B7AC-56CE-4F35-88C6-FC0ACC739EE0} 18 | Win32Proj 19 | Test 20 | 21 | 22 | 23 | Application 24 | true 25 | Unicode 26 | v110 27 | 28 | 29 | Application 30 | false 31 | true 32 | Unicode 33 | v110 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | true 47 | 48 | 49 | false 50 | 51 | 52 | 53 | 54 | 55 | Level3 56 | Disabled 57 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 58 | 59 | 60 | Console 61 | true 62 | 63 | 64 | 65 | 66 | Level3 67 | 68 | 69 | MaxSpeed 70 | true 71 | true 72 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 73 | ..\headers;%(AdditionalIncludeDirectories) 74 | 75 | 76 | Console 77 | true 78 | true 79 | true 80 | ..\VS_files\Release;%(AdditionalLibraryDirectories) 81 | SimplexMesh.lib;%(AdditionalDependencies) 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /headers/IncidenceMatrix.h: -------------------------------------------------------------------------------- 1 | #ifndef INCIDENCEMATRIX_H 2 | #define INCIDENCEMATRIX_H 3 | 4 | #include 5 | #include 6 | 7 | namespace SimplexMesh { 8 | 9 | //A simple std::vector-based sparse compressed row incidence matrix 10 | //to store the topology of our simplex mesh structure. 11 | //It needs to be resize-able in order to add/delete simplices. 12 | class IncidenceMatrix { 13 | 14 | private: 15 | 16 | //Matrix dimensions 17 | unsigned int n_rows, n_cols; 18 | 19 | //For each row, a list of all column indices, in oriented order where appropriate. 20 | //The sign indicates whether the value is intended to be: +1 or -1. 21 | //NOTE: We shift all column indices up by 1, so the zero'th column is enabled to have a sign! 22 | std::vector< std::vector > m_indices; 23 | 24 | public: 25 | IncidenceMatrix(); 26 | IncidenceMatrix(unsigned int rows, unsigned int cols); 27 | 28 | //Matrix dimensions 29 | unsigned int getNumRows() const {return n_rows;} 30 | unsigned int getNumCols() const {return n_cols;} 31 | void addRows(unsigned int rows); 32 | void addCols(unsigned int cols); 33 | 34 | //Regular accessors 35 | void set(unsigned int i, unsigned int j, int new_val); 36 | int get(unsigned int i, unsigned int j) const; 37 | bool exists(unsigned int i, unsigned int j) const; 38 | void remove(unsigned int i, unsigned int j); 39 | void zeroRow(unsigned int i); 40 | void zeroAll(); 41 | 42 | void cycleRow(unsigned int index); //permute the row by shifting them all over by 1 43 | 44 | //Constant-time access within the row, by row-index rather than column number 45 | unsigned int getNumEntriesInRow(unsigned int row) const; 46 | unsigned int getColByIndex(unsigned int i, unsigned int index_in_row) const; 47 | int getValueByIndex(unsigned int i, unsigned int index_in_row) const; 48 | void setByIndex(unsigned int i, unsigned int index_in_row, unsigned int col, int val); 49 | 50 | //Debugging 51 | void printMatrix() const; 52 | }; 53 | 54 | } // namespace SimplexMesh 55 | 56 | 57 | #endif //INCIDENCEMATRIX_H -------------------------------------------------------------------------------- /headers/SimplexHandles.h: -------------------------------------------------------------------------------- 1 | #ifndef SIMPLEXHANDLES_H 2 | #define SIMPLEXHANDLES_H 3 | 4 | namespace SimplexMesh { 5 | 6 | 7 | /** Handle for referring to a vertex */ 8 | class VertexHandle 9 | { 10 | 11 | private: 12 | int m_idx; 13 | explicit VertexHandle(int idx) : m_idx(idx) {} 14 | int idx() const { return m_idx; } 15 | 16 | public: 17 | 18 | explicit VertexHandle() : m_idx(-1) {} 19 | 20 | friend class VertexIterator; 21 | friend class VertexVertexIterator; 22 | friend class VertexEdgeIterator; 23 | friend class EdgeVertexIterator; 24 | friend class VertexFaceIterator; 25 | 26 | friend class VertexIterator; 27 | friend class VertexEdgeIterator;friend class EdgeVertexIterator; 28 | 29 | friend class SimplicialComplex; 30 | 31 | template friend class VertexProperty; 32 | 33 | bool operator== (const VertexHandle& rhs) const { return m_idx == rhs.m_idx; } 34 | bool operator!= (const VertexHandle& rhs) const { return m_idx != rhs.m_idx; } 35 | bool operator< (const VertexHandle& rhs) const { return m_idx < rhs.m_idx; } 36 | 37 | bool isValid() const { return m_idx >= 0; } 38 | static VertexHandle invalid() { return VertexHandle(-1); } 39 | }; 40 | 41 | /** Handle for referring to an edge */ 42 | class EdgeHandle 43 | { 44 | 45 | private: 46 | int m_idx; 47 | explicit EdgeHandle(int idx) : m_idx(idx) {} 48 | int idx() const { return m_idx; } 49 | 50 | public: 51 | 52 | explicit EdgeHandle() : m_idx(-1) {} 53 | 54 | friend class EdgeIterator; 55 | friend class VertexEdgeIterator; 56 | friend class EdgeVertexIterator; 57 | friend class FaceEdgeIterator; 58 | friend class EdgeFaceIterator; 59 | friend class FaceVertexIterator; 60 | 61 | friend class EdgeIterator; 62 | friend class VertexEdgeIterator; friend class EdgeVertexIterator; 63 | friend class EdgeFaceIterator; friend class FaceEdgeIterator; 64 | 65 | friend class SimplicialComplex; 66 | 67 | template friend class EdgeProperty; 68 | 69 | bool operator== (const EdgeHandle& rhs) const { return m_idx == rhs.m_idx; } 70 | bool operator!= (const EdgeHandle& rhs) const { return m_idx != rhs.m_idx; } 71 | bool operator< (const EdgeHandle& rhs) const { return m_idx < rhs.m_idx; } 72 | 73 | bool isValid() const { return m_idx >= 0; } 74 | 75 | static EdgeHandle invalid() { return EdgeHandle(-1); } 76 | }; 77 | 78 | /** Handle for referring to a face */ 79 | class FaceHandle 80 | { 81 | private: 82 | int m_idx; 83 | explicit FaceHandle(int idx) : m_idx(idx) {} 84 | int idx() const { return m_idx; } 85 | 86 | public: 87 | 88 | explicit FaceHandle() : m_idx(-1) {} 89 | 90 | friend class FaceIterator; 91 | friend class FaceEdgeIterator; 92 | friend class EdgeFaceIterator; 93 | friend class FaceVertexIterator; 94 | friend class VertexFaceIterator; 95 | friend class SimplicialComplex; 96 | 97 | friend class FaceIterator; 98 | friend class FaceEdgeIterator; 99 | friend class EdgeFaceIterator; 100 | friend class FaceTetIterator; 101 | friend class TetFaceIterator; 102 | 103 | template friend class FaceProperty; 104 | 105 | bool operator== (const FaceHandle& rhs) const { return m_idx == rhs.m_idx; } 106 | bool operator!= (const FaceHandle& rhs) const { return m_idx != rhs.m_idx; } 107 | bool operator< (const FaceHandle& rhs) const { return m_idx < rhs.m_idx; } 108 | 109 | bool isValid() const { return m_idx >= 0; } 110 | 111 | static FaceHandle invalid() { return FaceHandle(-1); } 112 | }; 113 | 114 | /** Handle for referring to a tet */ 115 | class TetHandle 116 | { 117 | 118 | private: 119 | int m_idx; 120 | explicit TetHandle(int idx) : m_idx(idx) {} 121 | int idx() const { return m_idx; } 122 | 123 | public: 124 | 125 | explicit TetHandle() : m_idx(-1) {} 126 | 127 | friend class TetIterator; 128 | friend class SimplicialComplex; 129 | 130 | friend class TetIterator; 131 | friend class FaceTetIterator; friend class TetFaceIterator; 132 | 133 | template friend class TetProperty; 134 | 135 | bool operator== (const TetHandle& rhs) const { return m_idx == rhs.m_idx; } 136 | bool operator!= (const TetHandle& rhs) const { return m_idx != rhs.m_idx; } 137 | bool operator< (const TetHandle& rhs) const { return m_idx < rhs.m_idx; } 138 | 139 | bool isValid() const { return m_idx >= 0; } 140 | 141 | static TetHandle invalid() { return TetHandle(-1); } 142 | }; 143 | 144 | 145 | 146 | } // namespace SimplexMesh 147 | 148 | #endif // SIMPLEXHANDLES_H 149 | -------------------------------------------------------------------------------- /headers/SimplexIterators.h: -------------------------------------------------------------------------------- 1 | #ifndef SIMPLEXITERATORS_H 2 | #define SIMPLEXITERATORS_H 3 | 4 | #include "SimplicialComplex.h" 5 | #include "SimplexHandles.h" 6 | 7 | #include 8 | 9 | namespace SimplexMesh 10 | { 11 | 12 | ////////////////////////////////////////////////////////////////////////// 13 | //Basic iterators 14 | 15 | class VertexIterator { 16 | public: 17 | VertexIterator(const SimplicialComplex& obj); 18 | void advance(); 19 | bool done() const; 20 | VertexHandle current() const; 21 | 22 | private: 23 | int m_idx; 24 | const SimplicialComplex& m_obj; 25 | 26 | }; 27 | 28 | class EdgeIterator { 29 | public: 30 | EdgeIterator(const SimplicialComplex& obj); 31 | void advance(); 32 | bool done() const; 33 | EdgeHandle current() const; 34 | 35 | private: 36 | int m_idx; 37 | const SimplicialComplex& m_obj; 38 | 39 | }; 40 | 41 | 42 | class FaceIterator { 43 | public: 44 | FaceIterator(const SimplicialComplex& obj); 45 | void advance(); 46 | bool done() const; 47 | FaceHandle current() const; 48 | 49 | private: 50 | int m_idx; 51 | const SimplicialComplex& m_obj; 52 | 53 | }; 54 | 55 | class TetIterator { 56 | public: 57 | TetIterator(const SimplicialComplex& obj); 58 | void advance(); 59 | bool done() const; 60 | TetHandle current() const; 61 | 62 | private: 63 | int m_idx; 64 | const SimplicialComplex& m_obj; 65 | 66 | }; 67 | 68 | 69 | 70 | ////////////////////////////////////////////////////////////////////////// 71 | //Adjacency iterators 72 | 73 | class VertexEdgeIterator { 74 | public: 75 | VertexEdgeIterator(const SimplicialComplex& obj, const VertexHandle& vh); 76 | void advance(); 77 | bool done() const; 78 | EdgeHandle current() const; 79 | 80 | private: 81 | int m_idx; 82 | VertexHandle m_vh; 83 | const SimplicialComplex& m_obj; 84 | 85 | }; 86 | 87 | class EdgeVertexIterator { 88 | public: 89 | EdgeVertexIterator(const SimplicialComplex& obj, const EdgeHandle& eh, bool ordered = false); 90 | void advance(); 91 | bool done() const; 92 | VertexHandle current() const; 93 | 94 | private: 95 | bool m_ordered; 96 | int m_idx; 97 | EdgeHandle m_eh; 98 | const SimplicialComplex& m_obj; 99 | 100 | }; 101 | 102 | class VertexVertexIterator { 103 | public: 104 | VertexVertexIterator(const SimplicialComplex& obj, const VertexHandle& vh); 105 | void advance(); 106 | bool done() const; 107 | VertexHandle current() const; 108 | 109 | private: 110 | int m_idx; 111 | VertexHandle m_vh; 112 | const SimplicialComplex& m_obj; 113 | 114 | }; 115 | 116 | class EdgeFaceIterator { 117 | public: 118 | EdgeFaceIterator(const SimplicialComplex& obj, const EdgeHandle& eh); 119 | void advance(); 120 | bool done() const; 121 | FaceHandle current() const; 122 | 123 | private: 124 | int m_idx; 125 | EdgeHandle m_eh; 126 | const SimplicialComplex& m_obj; 127 | 128 | }; 129 | 130 | class FaceEdgeIterator { 131 | public: 132 | FaceEdgeIterator(const SimplicialComplex& obj, const FaceHandle& eh, bool ordered = false); 133 | void advance(); 134 | bool done() const; 135 | EdgeHandle current() const; 136 | 137 | private: 138 | bool m_ordered; 139 | int m_idx; 140 | EdgeHandle m_cur; 141 | FaceHandle m_fh; 142 | const SimplicialComplex& m_obj; 143 | 144 | }; 145 | 146 | 147 | class FaceTetIterator { 148 | public: 149 | FaceTetIterator(const SimplicialComplex& obj, const FaceHandle& eh); 150 | void advance(); 151 | bool done() const; 152 | TetHandle current() const; 153 | 154 | private: 155 | int m_idx; 156 | FaceHandle m_fh; 157 | const SimplicialComplex& m_obj; 158 | 159 | }; 160 | 161 | class TetFaceIterator { 162 | public: 163 | TetFaceIterator(const SimplicialComplex& obj, const TetHandle& eh); 164 | void advance(); 165 | bool done() const; 166 | FaceHandle current() const; 167 | 168 | private: 169 | int m_idx; 170 | TetHandle m_th; 171 | const SimplicialComplex& m_obj; 172 | 173 | }; 174 | 175 | ////////////////////////////////////////////////////////////////////////// 176 | //Trickier adjacency iterators 177 | 178 | class VertexFaceIterator { 179 | public: 180 | VertexFaceIterator(const SimplicialComplex& obj, const VertexHandle& vh); 181 | void advance(); 182 | bool done() const; 183 | FaceHandle current() const; 184 | 185 | private: 186 | 187 | const SimplicialComplex& m_obj; 188 | std::set m_faces; 189 | std::set::iterator m_fiter; 190 | 191 | }; 192 | 193 | class FaceVertexIterator { 194 | public: 195 | FaceVertexIterator(const SimplicialComplex& obj, const FaceHandle& fh, bool ordered = false); 196 | void advance(); 197 | bool done() const; 198 | VertexHandle current() const; 199 | 200 | private: 201 | FaceHandle m_fh; 202 | FaceEdgeIterator m_feit; 203 | const SimplicialComplex& m_obj; 204 | 205 | }; 206 | 207 | class VertexTetIterator { 208 | public: 209 | VertexTetIterator(const SimplicialComplex& obj, const VertexHandle& vh); 210 | void advance(); 211 | bool done() const; 212 | TetHandle current() const; 213 | 214 | private: 215 | 216 | const SimplicialComplex& m_obj; 217 | std::set m_tets; 218 | std::set::iterator m_titer; 219 | 220 | }; 221 | 222 | class TetVertexIterator { 223 | public: 224 | TetVertexIterator(const SimplicialComplex& obj, const TetHandle& th); 225 | void advance(); 226 | bool done() const; 227 | VertexHandle current() const; 228 | 229 | private: 230 | const SimplicialComplex& m_obj; 231 | std::set m_verts; 232 | std::set::iterator m_viter; 233 | 234 | }; 235 | 236 | 237 | class EdgeTetIterator { 238 | public: 239 | EdgeTetIterator (const SimplicialComplex& obj, const EdgeHandle& eh); 240 | void advance(); 241 | bool done() const; 242 | TetHandle current() const; 243 | 244 | private: 245 | 246 | const SimplicialComplex& m_obj; 247 | std::set m_tets; 248 | std::set::iterator m_titer; 249 | 250 | }; 251 | 252 | class TetEdgeIterator { 253 | public: 254 | TetEdgeIterator(const SimplicialComplex& obj, const TetHandle& th); 255 | void advance(); 256 | bool done() const; 257 | EdgeHandle current() const; 258 | 259 | private: 260 | const SimplicialComplex& m_obj; 261 | std::set m_edges; 262 | std::set::iterator m_eiter; 263 | 264 | }; 265 | 266 | 267 | } 268 | 269 | #endif -------------------------------------------------------------------------------- /headers/SimplexProperty.h: -------------------------------------------------------------------------------- 1 | #ifndef SIMPLEXPROPERTY_H 2 | #define SIMPLEXPROPERTY_H 3 | 4 | #include "SimplicialComplex.h" 5 | 6 | namespace SimplexMesh { 7 | 8 | //Base class, so TopologicalObject can store pointers to TopObjProperties of different types in a single list. 9 | class SimplexPropertyBase { 10 | 11 | public: 12 | 13 | SimplexPropertyBase(SimplicialComplex& obj) : m_obj(obj) {} 14 | virtual ~SimplexPropertyBase() {} 15 | 16 | protected: 17 | 18 | //Only the TopologicalObject that owns this object is allowed to manipulate its size, 19 | //in order to match the number of the given simplex type. (eg. #of edge properties should equal #of edges) 20 | virtual size_t size() const = 0; 21 | virtual void resize(size_t n) = 0; 22 | 23 | //The simplex mesh this property is associated with. 24 | SimplicialComplex& m_obj; 25 | 26 | friend class SimplicialComplex; 27 | }; 28 | 29 | 30 | //The templated object property that stores the data. This is a base class that should not be used, since it doesn't get registered with 31 | //a particular simplex type (edge, face, etc.), and cannot be resized. 32 | template 33 | class SimplexProperty : public SimplexPropertyBase { 34 | 35 | public: 36 | SimplexProperty(SimplicialComplex& obj, size_t n) : SimplexPropertyBase(obj), m_data(n) {} 37 | 38 | virtual ~SimplexProperty() {} 39 | 40 | void assign(const T& data_value) { for(unsigned int i = 0; i < m_data.size(); ++i) m_data[i] = data_value; } 41 | 42 | protected: 43 | 44 | size_t size() const { return m_data.size(); } 45 | void resize(size_t n) { m_data.resize(n); } 46 | 47 | std::vector m_data; 48 | 49 | }; 50 | 51 | //The properties associated to simplices of different dimensions. They are instantiated with a pointer to the the TopologicalObject that 52 | //"owns" them, and registered with that object, so they can be automatically resized to match the mesh when necessary. 53 | template 54 | class VertexProperty : public SimplexProperty { 55 | 56 | public: 57 | 58 | VertexProperty(SimplicialComplex& obj) : SimplexProperty(obj,obj.numVertexSlots()) { 59 | m_obj.registerVertexProperty(this); 60 | } 61 | 62 | explicit VertexProperty(const VertexProperty& prop) : SimplexProperty(prop.m_obj,prop.m_obj->numVertexSlots()) { 63 | this->m_obj.registerVertexProperty(this); 64 | this->m_data = prop.m_data; 65 | } 66 | 67 | ~VertexProperty() { 68 | this->m_obj.removeVertexProperty(this); 69 | } 70 | 71 | VertexProperty& operator=(const VertexProperty& other) { 72 | 73 | if(this != &other) { 74 | this->m_obj.removeVertexProperty(this); 75 | 76 | this->m_obj = other.m_obj; 77 | this->m_data = other.m_data; 78 | 79 | this->m_obj.registerVertexProperty(this); 80 | } 81 | 82 | return *this; 83 | } 84 | 85 | T& operator[] (const VertexHandle& h) { 86 | assert(h.idx() >= 0 && h.idx() < (int)m_data.size()); 87 | return m_data[h.idx()]; 88 | } 89 | 90 | T const& operator[] (const VertexHandle& h) const { 91 | assert(h.idx() >= 0 && h.idx() < (int)m_data.size()); 92 | return m_data[h.idx()]; 93 | } 94 | 95 | }; 96 | 97 | 98 | template 99 | class EdgeProperty : public SimplexProperty { 100 | public: 101 | 102 | EdgeProperty(SimplicialComplex& obj) : SimplexProperty(obj,obj.numEdgeSlots()) { 103 | m_obj.registerEdgeProperty(this); 104 | } 105 | 106 | explicit EdgeProperty(const EdgeProperty& prop) : SimplexProperty(prop.m_obj,prop.m_obj.numEdgeSlots()) { 107 | m_obj.registerEdgeProperty(this); 108 | m_data = prop.m_data; 109 | } 110 | 111 | ~EdgeProperty() { 112 | this->m_obj.removeEdgeProperty(this); 113 | } 114 | 115 | EdgeProperty& operator=(const EdgeProperty& other) { 116 | 117 | if(this != &other) { 118 | m_obj.removeEdgeProperty(this); 119 | 120 | this->m_obj = other.m_obj; 121 | this->m_data = other.m_data; 122 | 123 | m_obj.registerEdgeProperty(this); 124 | } 125 | 126 | return *this; 127 | } 128 | 129 | 130 | T& operator[] (const EdgeHandle& h) { 131 | assert(h.idx() >= 0 && h.idx() < (int)m_data.size()); 132 | return m_data[h.idx()]; 133 | } 134 | 135 | T const& operator[] (const EdgeHandle& h) const { 136 | assert(h.idx() >= 0 && h.idx() < (int)m_data.size()); 137 | return m_data[h.idx()]; 138 | } 139 | }; 140 | 141 | template 142 | class FaceProperty : public SimplexProperty { 143 | public: 144 | FaceProperty(SimplicialComplex& obj) : SimplexProperty(obj,obj.numFaceSlots()) { 145 | m_obj.registerFaceProperty(this); 146 | } 147 | 148 | explicit FaceProperty(const FaceProperty& prop) : SimplexProperty(prop.m_obj,prop.m_obj.numFaceSlots()) { 149 | m_obj.registerFaceProperty(this); 150 | m_data = prop.m_data; 151 | } 152 | 153 | ~FaceProperty() { 154 | m_obj.removeFaceProperty(this); 155 | } 156 | 157 | FaceProperty& operator=(const FaceProperty& other) { 158 | 159 | if(this != &other) { 160 | m_obj.removeFaceProperty(this); 161 | 162 | this->m_obj = other.m_obj; 163 | this->m_data = other.m_data; 164 | 165 | m_obj.registerFaceProperty(this); 166 | } 167 | 168 | return *this; 169 | } 170 | 171 | T& operator[] (const FaceHandle& h) { 172 | assert(h.idx() >= 0 && h.idx() < (int)m_data.size()); 173 | return m_data[h.idx()]; 174 | } 175 | 176 | T const& operator[] (const FaceHandle& h) const { 177 | assert(h.idx() >= 0 && h.idx() < (int)m_data.size()); 178 | return m_data[h.idx()]; 179 | } 180 | 181 | }; 182 | 183 | 184 | template 185 | class TetProperty : public SimplexProperty { 186 | public: 187 | TetProperty(SimplicialComplex& obj) : SimplexProperty(obj, obj.numTetSlots()) { 188 | m_obj.registerTetProperty(this); 189 | } 190 | 191 | explicit TetProperty(const TetProperty& prop) : SimplexProperty(prop.m_obj,prop.m_obj.numTetSlots()) { 192 | m_obj->registerTetProperty(this); 193 | m_data = prop.m_data; 194 | } 195 | 196 | ~TetProperty() { 197 | m_obj.removeTetProperty(this); 198 | } 199 | 200 | TetProperty& operator=(const TetProperty& other) { 201 | 202 | if(this != &other) { 203 | m_obj.removeTetProperty(this); 204 | 205 | this->m_obj = other.m_obj; 206 | this->m_data = other.m_data; 207 | 208 | m_obj.registerTetProperty(this); 209 | } 210 | 211 | return *this; 212 | } 213 | 214 | T& operator[] (const TetHandle& h) { 215 | assert(h.idx() >= 0 && h.idx() < (int)m_data.size()); 216 | return m_data[h.idx()]; 217 | } 218 | 219 | T const& operator[] (const TetHandle& h) const { 220 | assert(h.idx() >= 0 && h.idx() < (int)m_data.size()); 221 | return m_data[h.idx()]; 222 | } 223 | 224 | }; 225 | 226 | } 227 | 228 | #endif -------------------------------------------------------------------------------- /headers/SimplicialComplex.h: -------------------------------------------------------------------------------- 1 | #ifndef SIMPLICIALCOMPLEX_H 2 | #define SIMPLICIALCOMPLEX_H 3 | 4 | #include 5 | 6 | #include "SimplexHandles.h" 7 | #include "IncidenceMatrix.h" 8 | 9 | namespace SimplexMesh { 10 | 11 | class SimplexPropertyBase; 12 | 13 | // An object that represents a collection of vertices, edges, faces and tets 14 | // with associated connectivity information. 15 | class SimplicialComplex 16 | { 17 | public: 18 | 19 | SimplicialComplex(); 20 | 21 | //Whether to perform potentially expensive safety checks (duplicates, validity) when constructing the mesh. 22 | void setSafeMode(bool safe) { m_safetyChecks = safe; } 23 | 24 | int numVerts() const; 25 | int numEdges() const; 26 | int numFaces() const; 27 | int numTets() const; 28 | 29 | //Addition: note that the resulting orientation is (in most cases) dependent on the order of parameters 30 | VertexHandle addVertex(); 31 | EdgeHandle addEdge(const VertexHandle& v0, const VertexHandle& v1); //ordered from v0 to v1 32 | FaceHandle addFace(const EdgeHandle& e0, const EdgeHandle& e1, const EdgeHandle& e2); //in order of given edges 33 | TetHandle addTet(const FaceHandle& f0, const FaceHandle& f1, //choose in/out orientation based on face0, possibly flipped 34 | const FaceHandle& f2, const FaceHandle& f3, 35 | bool flip_face0 = false); 36 | 37 | //auxiliary addition functions for convenience. These will necessarily be slower. 38 | FaceHandle addFace(const VertexHandle& v0, const VertexHandle& v1, const VertexHandle& v2); 39 | TetHandle addTet(const VertexHandle& v0, const VertexHandle& v1, const VertexHandle& v2, const VertexHandle& v3); 40 | 41 | //deletion: recurse=true will recursively delete its composing sub-simplices if they are not used in any other simplices 42 | bool deleteVertex(const VertexHandle& vertex); 43 | bool deleteEdge(const EdgeHandle& edge, bool recurse); 44 | bool deleteFace(const FaceHandle& face, bool recurse); 45 | bool deleteTet(const TetHandle& tet, bool recurse); 46 | 47 | //Existence: check if simplices still exist 48 | bool vertexExists(const VertexHandle& vertex) const; 49 | bool edgeExists(const EdgeHandle& edge) const; 50 | bool faceExists(const FaceHandle& face) const; 51 | bool tetExists(const TetHandle& tet) const; 52 | 53 | //Exploit fixed ordering to provide fast/easy access to sub-elements of a simplex 54 | VertexHandle getVertex(const EdgeHandle& eh, int index) const; 55 | EdgeHandle getEdge(const FaceHandle& fh, int index) const; 56 | FaceHandle getFace(const TetHandle& th, int index) const; //Is there an important/inherent ordering here? should we unique-ify by sorting? 57 | 58 | //Get functions - grab a simplex by its constitutive simplices - slower! 59 | EdgeHandle getEdge(const VertexHandle& v0, const VertexHandle& v1) const; 60 | FaceHandle getFace(const EdgeHandle& e0, const EdgeHandle& e1, const EdgeHandle& e2) const; 61 | TetHandle getTet(const FaceHandle& f0, const FaceHandle& f1, const FaceHandle& f2, const FaceHandle& f3) const; 62 | 63 | //determine relative orientation of simplices (+1 or -1) 64 | int getRelativeOrientation(const TetHandle& th, const FaceHandle& fh) const; 65 | int getRelativeOrientation(const FaceHandle& fh, const EdgeHandle& eh) const; 66 | int getRelativeOrientation(const EdgeHandle& eh, const VertexHandle& vh) const; 67 | 68 | //incidence counts for simplices of neighbouring dimensions 69 | int vertexIncidentEdgeCount(const VertexHandle& v) const { return m_VE.getNumEntriesInRow(v.idx()); } 70 | int edgeIncidentFaceCount(const EdgeHandle& e) const { return m_EF.getNumEntriesInRow(e.idx()); } 71 | int faceIncidentTetCount(const FaceHandle& f) const { return m_FT.getNumEntriesInRow(f.idx()); } 72 | 73 | //manifoldness tests - complicated... 74 | bool isManifold(const VertexHandle& v) const; 75 | bool isManifold(const EdgeHandle& e) const; 76 | bool isManifold(const FaceHandle& f) const; 77 | 78 | //boundary tests 79 | bool isOnBoundary(const VertexHandle& v) const; 80 | bool isOnBoundary(const EdgeHandle& e) const; 81 | bool isOnBoundary(const FaceHandle& f) const; 82 | 83 | //incidence tests 84 | bool isIncident(const VertexHandle& vh, const EdgeHandle& eh) const; 85 | bool isIncident(const EdgeHandle& eh, const FaceHandle& fh) const; 86 | bool isIncident(const FaceHandle& fh, const TetHandle& th) const; 87 | 88 | //Pairwise relationships / traversal 89 | //---------------------------------- 90 | //Edge/Vert 91 | VertexHandle fromVertex(const EdgeHandle& eh) const; 92 | VertexHandle toVertex(const EdgeHandle& eh) const; 93 | 94 | //Face/Edge - assumes consistent orientation and 2D 95 | FaceHandle frontFace(const EdgeHandle& fh) const; 96 | FaceHandle backFace(const EdgeHandle& fh) const; 97 | 98 | //Tet/Face - assumes consistent orientation 99 | TetHandle frontTet(const FaceHandle& fh) const; 100 | TetHandle backTet(const FaceHandle& fh) const; 101 | 102 | //Simple global traversal functions (terser than iterators, safer/slower when editing meshes) 103 | //------------------------------------ 104 | //Vertices 105 | VertexHandle nextVertex(const VertexHandle& curVertex) const; 106 | VertexHandle prevVertex(const VertexHandle& curVertex) const; 107 | 108 | //Edges 109 | EdgeHandle nextEdge(const EdgeHandle& curEdge) const; 110 | EdgeHandle prevEdge(const EdgeHandle& curEdge) const; 111 | 112 | //Faces 113 | FaceHandle nextFace(const FaceHandle& curFace) const; 114 | FaceHandle prevFace(const FaceHandle& curFace) const; 115 | 116 | //Tets 117 | TetHandle nextTet(const TetHandle& curTet) const; 118 | TetHandle prevTet(const TetHandle& curTet) const; 119 | 120 | //Simple local traversal functions (stateless, terser than iterators, safer & slower. Useful e.g. when editing meshes) 121 | //--------------------------------- 122 | //Vertex traversal 123 | VertexHandle nextVertex(const EdgeHandle&edge, const VertexHandle& curVertex) const; 124 | VertexHandle prevVertex(const EdgeHandle&edge, const VertexHandle& curVertex) const; 125 | VertexHandle nextVertex(const FaceHandle&face, const VertexHandle& curVertex) const; 126 | VertexHandle prevVertex(const FaceHandle&face, const VertexHandle& curVertex) const; 127 | 128 | //Edge traversal 129 | EdgeHandle nextEdge(const VertexHandle& vert, const EdgeHandle& curEdge) const; 130 | EdgeHandle prevEdge(const VertexHandle& vert, const EdgeHandle& curEdge) const; 131 | EdgeHandle nextEdge(const FaceHandle& face, const EdgeHandle& curEdge) const; 132 | EdgeHandle prevEdge(const FaceHandle& face, const EdgeHandle& curEdge) const; 133 | 134 | //Face traversal 135 | FaceHandle nextFace(const TetHandle& tet, const FaceHandle& curFace) const; 136 | FaceHandle prevFace(const TetHandle& tet, const FaceHandle& curFace) const; 137 | FaceHandle nextFace(const EdgeHandle& edge, const FaceHandle& curFace) const; 138 | FaceHandle prevFace(const EdgeHandle& edge, const FaceHandle& curFace) const; 139 | 140 | //Tet traversal 141 | TetHandle nextTet(const FaceHandle& face, const TetHandle& curTet) const; 142 | TetHandle prevTet(const FaceHandle& face, const TetHandle& curTet) const; 143 | 144 | //Common connectivity editing operations 145 | //--------------------------------- 146 | 147 | VertexHandle collapseEdge(const EdgeHandle& eh, const VertexHandle& vertToRemove); 148 | 149 | //Split an edge and insert a new vertex in between, subdividing all the faces sharing the edge. 150 | //Should be non-manifold friendly. Orientation is maintained. 151 | VertexHandle splitEdge(const EdgeHandle& h, std::vector& newFaces); 152 | 153 | //Takes an edge with two adjacent faces comprising a quad, and replaces the edge with the other diagonal of the quad. 154 | //Not defined in the non-manifold edge case. Orientation maintained if original face orientations matched. 155 | EdgeHandle flipEdge(const EdgeHandle& h); 156 | 157 | //-------------------------------- 158 | private: 159 | 160 | 161 | 162 | //Friendship relations 163 | ////////////////////////////////////////////////////////////////////////// 164 | 165 | //Basic iterators 166 | friend class VertexIterator; friend class EdgeIterator; 167 | friend class FaceIterator; friend class TetIterator; 168 | 169 | //Neighbour iterators 170 | friend class VertexEdgeIterator; friend class EdgeVertexIterator; friend class VertexVertexIterator; 171 | friend class EdgeFaceIterator; friend class FaceEdgeIterator; 172 | friend class FaceTetIterator; friend class TetFaceIterator; 173 | 174 | //allow properties to access object internals (for registering/unregistering themselves) 175 | template friend class VertexProperty; 176 | template friend class EdgeProperty; 177 | template friend class FaceProperty; 178 | template friend class TetProperty; 179 | 180 | //Internal functions 181 | ////////////////////////////////////////////////////////////////////////// 182 | 183 | EdgeHandle getSharedEdge(const FaceHandle& f0, const FaceHandle& f1) const; 184 | FaceHandle getSharedFace(const TetHandle &t0, const TetHandle& t1) const; 185 | 186 | //Functions for registering/unregistering properties associated to simplex elements 187 | void registerVertexProperty(SimplexPropertyBase* prop); 188 | void removeVertexProperty(SimplexPropertyBase* prop); 189 | 190 | void registerEdgeProperty(SimplexPropertyBase* prop); 191 | void removeEdgeProperty(SimplexPropertyBase* prop); 192 | 193 | void registerFaceProperty(SimplexPropertyBase* prop); 194 | void removeFaceProperty(SimplexPropertyBase* prop); 195 | 196 | void registerTetProperty(SimplexPropertyBase* prop); 197 | void removeTetProperty(SimplexPropertyBase* prop); 198 | 199 | //The number of spaces currently allocated for each simplex type. (Note this is different 200 | //from the number of active simplices of each type.) 201 | unsigned int numVertexSlots() const {return m_V.size();} 202 | unsigned int numEdgeSlots() const {return m_EV.getNumRows();} 203 | unsigned int numFaceSlots() const {return m_FE.getNumRows();} 204 | unsigned int numTetSlots() const {return m_TF.getNumRows();} 205 | 206 | //Core Data 207 | ////////////////////////////////////////////////////////////////////////// 208 | 209 | //Simplex counts 210 | int m_nVerts, m_nEdges, m_nFaces, m_nTets; 211 | 212 | //Fundamental mesh data (incidence matrix format) 213 | IncidenceMatrix m_TF; ///< tet-to-face relations 214 | IncidenceMatrix m_FE; ///< face-to-edge relations 215 | IncidenceMatrix m_EV; ///< edge-to-vert relations 216 | std::vector m_V; ///< vertex existence, to support isolated vertices 217 | 218 | //Transposes, needed for efficient deletion/traversal/etc 219 | IncidenceMatrix m_FT; ///< face-to-tet relations 220 | IncidenceMatrix m_EF; ///< edge-to-face relations 221 | IncidenceMatrix m_VE; ///< vert-to-edge relations 222 | 223 | //Pools of empty rows/columns in the above matrices, to efficiently add 224 | //data in previously deleted slots. 225 | std::vector m_deadVerts, m_deadEdges, m_deadFaces, m_deadTets; 226 | 227 | //Lists of simplex properties. These are just pointers so that memory can be managed by the SimplexMesh for adding/deleting. 228 | //But the data "lives" wherever it has been created. 229 | std::vector m_vertProperties; 230 | std::vector m_edgeProperties; 231 | std::vector m_faceProperties; 232 | std::vector m_tetProperties; 233 | 234 | //Option flags 235 | bool m_safetyChecks; 236 | 237 | }; 238 | 239 | } // namespace SimplexMesh 240 | 241 | #include "SimplexProperty.h" 242 | #include "SimplexIterators.h" 243 | 244 | #endif // SIMPLICIALCOMPLEX_H 245 | -------------------------------------------------------------------------------- /src/IncidenceMatrix.cpp: -------------------------------------------------------------------------------- 1 | #include "IncidenceMatrix.h" 2 | 3 | namespace SimplexMesh { 4 | 5 | int signum(int val) { 6 | return (val >= 0 ? 1 : -1); 7 | 8 | } 9 | 10 | IncidenceMatrix::IncidenceMatrix() : 11 | n_rows(0), n_cols(0), m_indices(0) 12 | { 13 | } 14 | 15 | IncidenceMatrix::IncidenceMatrix(unsigned int rows, unsigned int cols) : 16 | n_rows(rows), n_cols(cols), 17 | m_indices(rows, std::vector()) 18 | { 19 | } 20 | 21 | unsigned int IncidenceMatrix::getNumEntriesInRow(unsigned int row) const { 22 | assert(row < n_rows); 23 | return m_indices[row].size(); 24 | } 25 | 26 | int IncidenceMatrix::getValueByIndex(unsigned int i, unsigned int index_in_row) const { 27 | assert(i < n_rows); 28 | assert(index_in_row < m_indices[i].size()); 29 | 30 | return signum(m_indices[i][index_in_row]); 31 | } 32 | 33 | unsigned int IncidenceMatrix::getColByIndex(unsigned int i, unsigned int index_in_row) const { 34 | assert(i < n_rows); 35 | assert(index_in_row < m_indices[i].size()); 36 | 37 | return abs(m_indices[i][index_in_row]) - 1; 38 | } 39 | 40 | void IncidenceMatrix::cycleRow(unsigned int i) { 41 | int t = m_indices[i][0]; 42 | int row_len = m_indices[i].size(); 43 | for(int j = 0; j < row_len-1; ++j) 44 | m_indices[i][j] = m_indices[i][j+1]; 45 | m_indices[i][row_len-1] = t; 46 | } 47 | 48 | void IncidenceMatrix::setByIndex(unsigned int i, unsigned int index_in_row, unsigned int col, int value) { 49 | assert(value == 1 || value == -1); 50 | if(index_in_row >= m_indices[i].size()) m_indices[i].resize(index_in_row+1); 51 | 52 | m_indices[i][index_in_row] = (col+1)*value; 53 | } 54 | 55 | void IncidenceMatrix::set(unsigned int i, unsigned int j, int new_val) { 56 | assert(i < n_rows && j < n_cols); 57 | if(new_val == 0) { 58 | remove(i,j); 59 | return; 60 | } 61 | 62 | assert(new_val == 1 || new_val == -1); 63 | 64 | int colShift = j+1; 65 | bool found = false; 66 | for(unsigned int cur = 0; cur < m_indices[i].size(); ++cur) { 67 | if(abs(m_indices[i][cur]) == (int)colShift ) { 68 | m_indices[i][cur] = signum(new_val)*colShift; 69 | found = true; 70 | break; 71 | } 72 | } 73 | if(!found) 74 | m_indices[i].push_back(signum(new_val)*colShift); 75 | 76 | } 77 | 78 | int IncidenceMatrix::get(unsigned int i, unsigned int j) const { 79 | assert(i < n_rows && j < n_cols); 80 | 81 | int colShift = j+1; 82 | for(unsigned int k=0; k 0?'+':'-', abs(m_indices[row][i])-1); 135 | printf("\n"); 136 | } 137 | } 138 | 139 | void IncidenceMatrix::zeroAll() 140 | { 141 | for(unsigned int i = 0; i < getNumRows(); ++i) 142 | zeroRow(i); 143 | } 144 | 145 | } -------------------------------------------------------------------------------- /src/SimplexIterators.cpp: -------------------------------------------------------------------------------- 1 | #include "SimplexIterators.h" 2 | 3 | namespace SimplexMesh { 4 | 5 | //VertexIterator 6 | 7 | VertexIterator::VertexIterator(const SimplicialComplex& obj): m_obj(obj), m_idx(-1) { 8 | advance(); 9 | } 10 | 11 | void VertexIterator::advance() { 12 | do { 13 | m_idx++; 14 | } while(m_idx < (int)m_obj.numVertexSlots() && !m_obj.m_V[m_idx]); 15 | } 16 | 17 | bool VertexIterator::done() const { 18 | return m_idx >= (int)m_obj.numVertexSlots(); 19 | } 20 | 21 | VertexHandle VertexIterator::current() const { 22 | return VertexHandle(m_idx); 23 | } 24 | 25 | //EdgeIterator 26 | 27 | EdgeIterator::EdgeIterator(const SimplicialComplex& obj): m_obj(obj), m_idx(-1) { 28 | advance(); 29 | } 30 | 31 | void EdgeIterator::advance() { 32 | do { 33 | m_idx++; 34 | } while(m_idx < (int)m_obj.numEdgeSlots() && m_obj.m_EV.getNumEntriesInRow(m_idx) == 0); 35 | } 36 | 37 | bool EdgeIterator::done() const { 38 | return m_idx >= (int)m_obj.numEdgeSlots(); 39 | } 40 | 41 | EdgeHandle EdgeIterator::current() const { 42 | return EdgeHandle(m_idx); 43 | } 44 | 45 | //FaceIterator 46 | 47 | FaceIterator::FaceIterator(const SimplicialComplex& obj): m_obj(obj), m_idx(-1) { 48 | advance(); 49 | } 50 | 51 | void FaceIterator::advance() { 52 | do { 53 | m_idx++; 54 | } while(m_idx < (int)m_obj.numFaceSlots() && m_obj.m_FE.getNumEntriesInRow(m_idx) == 0); 55 | } 56 | 57 | bool FaceIterator::done() const { 58 | return m_idx >= (int)m_obj.numFaceSlots(); 59 | } 60 | 61 | FaceHandle FaceIterator::current() const { 62 | return FaceHandle(m_idx); 63 | } 64 | 65 | 66 | //TetIterator 67 | 68 | TetIterator::TetIterator(const SimplicialComplex& obj): m_obj(obj), m_idx(-1) { 69 | advance(); 70 | } 71 | 72 | void TetIterator::advance() { 73 | do { 74 | m_idx++; 75 | } while(m_idx < (int)m_obj.numTetSlots() && m_obj.m_TF.getNumEntriesInRow(m_idx) == 0); 76 | } 77 | 78 | bool TetIterator::done() const { 79 | return m_idx >= (int)m_obj.numTetSlots(); 80 | } 81 | 82 | TetHandle TetIterator::current() const { 83 | return TetHandle(m_idx); 84 | } 85 | 86 | 87 | ////////////////////////////////////////////////////////////////////////// 88 | //Adjacency iterators 89 | 90 | 91 | //VertexEdgeIterator 92 | VertexEdgeIterator::VertexEdgeIterator(const SimplicialComplex& obj, const VertexHandle& vh): m_obj(obj), m_idx(0), m_vh(vh) { 93 | } 94 | 95 | void VertexEdgeIterator::advance() { 96 | m_idx++; 97 | } 98 | 99 | bool VertexEdgeIterator::done() const { 100 | return m_idx >= (int)m_obj.vertexIncidentEdgeCount(m_vh); 101 | } 102 | 103 | EdgeHandle VertexEdgeIterator::current() const { 104 | return m_idx >= (int)m_obj.vertexIncidentEdgeCount(m_vh) ? 105 | EdgeHandle::invalid() : EdgeHandle(m_obj.m_VE.getColByIndex(m_vh.idx(), m_idx)); 106 | } 107 | 108 | //EdgeVertexIterator 109 | EdgeVertexIterator::EdgeVertexIterator(const SimplicialComplex& obj, const EdgeHandle& eh, bool ordered): m_obj(obj), m_idx(0), m_eh(eh), m_ordered(ordered) { 110 | } 111 | 112 | void EdgeVertexIterator::advance() { 113 | m_idx++; 114 | } 115 | 116 | bool EdgeVertexIterator::done() const { 117 | return m_idx >= 2; 118 | } 119 | 120 | VertexHandle EdgeVertexIterator::current() const { 121 | if(m_ordered) { 122 | if(m_idx == 0) 123 | return m_obj.fromVertex(m_eh); 124 | else if(m_idx == 1) 125 | return m_obj.toVertex(m_eh); 126 | else return VertexHandle::invalid(); 127 | } 128 | else { 129 | return m_idx >= 2 ? 130 | VertexHandle::invalid() : VertexHandle(m_obj.m_EV.getColByIndex(m_eh.idx(), m_idx)); 131 | } 132 | } 133 | 134 | 135 | //VertexVertexIterator 136 | VertexVertexIterator::VertexVertexIterator(const SimplicialComplex& obj, const VertexHandle& vh): m_obj(obj), m_idx(0), m_vh(vh) { 137 | } 138 | 139 | void VertexVertexIterator::advance() { 140 | m_idx++; 141 | } 142 | 143 | bool VertexVertexIterator::done() const { 144 | return m_idx >= (int)m_obj.vertexIncidentEdgeCount(m_vh); 145 | } 146 | 147 | VertexHandle VertexVertexIterator::current() const { 148 | if(m_idx >= (int)m_obj.vertexIncidentEdgeCount(m_vh)) 149 | return VertexHandle::invalid(); 150 | 151 | unsigned int edgeID = m_obj.m_VE.getColByIndex(m_vh.idx(), m_idx); 152 | unsigned int vertID = m_obj.m_EV.getColByIndex(edgeID, 0); 153 | if(vertID == m_vh.idx()) 154 | vertID = m_obj.m_EV.getColByIndex(edgeID, 1); 155 | return VertexHandle(vertID); 156 | } 157 | 158 | //EdgeFaceIterator 159 | EdgeFaceIterator::EdgeFaceIterator(const SimplicialComplex& obj, const EdgeHandle& eh): m_obj(obj), m_idx(0), m_eh(eh) { 160 | } 161 | 162 | void EdgeFaceIterator::advance() { 163 | m_idx++; 164 | } 165 | 166 | bool EdgeFaceIterator::done() const { 167 | return m_idx >= (int)m_obj.edgeIncidentFaceCount(m_eh); 168 | } 169 | 170 | FaceHandle EdgeFaceIterator::current() const { 171 | return m_idx >= (int)m_obj.edgeIncidentFaceCount(m_eh) ? 172 | FaceHandle::invalid() : FaceHandle(m_obj.m_EF.getColByIndex(m_eh.idx(), m_idx)); 173 | } 174 | 175 | //FaceEdgeIterator 176 | FaceEdgeIterator::FaceEdgeIterator(const SimplicialComplex& obj, const FaceHandle& fh, bool ordered): m_obj(obj), m_idx(0), m_fh(fh), m_ordered(ordered) { 177 | 178 | if(m_ordered) { //start at an arbitrary edge 179 | m_cur = EdgeHandle(m_obj.m_FE.getColByIndex(m_fh.idx(), m_idx)); 180 | } 181 | 182 | } 183 | 184 | void FaceEdgeIterator::advance() { 185 | m_idx++; 186 | if(m_ordered) 187 | m_cur = m_obj.nextEdge(m_fh, m_cur); 188 | } 189 | 190 | bool FaceEdgeIterator::done() const { 191 | return m_idx >= 3; 192 | } 193 | 194 | EdgeHandle FaceEdgeIterator::current() const { 195 | if(m_ordered) { 196 | return m_idx >= 3? EdgeHandle::invalid() : m_cur; 197 | } 198 | else { 199 | return m_idx >= 3 ? EdgeHandle::invalid() : EdgeHandle(m_obj.m_FE.getColByIndex(m_fh.idx(), m_idx)); 200 | } 201 | } 202 | 203 | 204 | //FaceTetIterator 205 | FaceTetIterator::FaceTetIterator(const SimplicialComplex& obj, const FaceHandle& fh): m_obj(obj), m_idx(0), m_fh(fh) { 206 | } 207 | 208 | void FaceTetIterator::advance() { 209 | m_idx++; 210 | } 211 | 212 | bool FaceTetIterator::done() const { 213 | return m_idx >= (int)m_obj.m_FT.getNumEntriesInRow(m_fh.idx()); 214 | } 215 | 216 | TetHandle FaceTetIterator::current() const { 217 | return m_idx >= (int)m_obj.m_FT.getNumEntriesInRow(m_fh.idx()) ? 218 | TetHandle::invalid(): 219 | TetHandle(m_obj.m_FT.getColByIndex(m_fh.idx(), m_idx)); 220 | } 221 | 222 | //TetFaceIterator 223 | TetFaceIterator::TetFaceIterator(const SimplicialComplex& obj, const TetHandle& th): m_obj(obj), m_idx(0), m_th(th) { 224 | } 225 | 226 | void TetFaceIterator::advance() { 227 | m_idx++; 228 | } 229 | 230 | bool TetFaceIterator::done() const { 231 | return m_idx >= 4; 232 | } 233 | 234 | FaceHandle TetFaceIterator::current() const { 235 | return m_idx >= 4 ? 236 | FaceHandle::invalid(): 237 | FaceHandle(m_obj.m_TF.getColByIndex(m_th.idx(), m_idx)); 238 | } 239 | 240 | //VertexFaceIterator 241 | VertexFaceIterator::VertexFaceIterator(const SimplicialComplex& obj, const VertexHandle& vh) : m_obj(obj) { 242 | 243 | //build the set of (unique) faces 244 | for(VertexEdgeIterator veit(m_obj, vh); !veit.done(); veit.advance()) 245 | for(EdgeFaceIterator efit(m_obj, veit.current()); !efit.done(); efit.advance()) 246 | m_faces.insert(efit.current()); 247 | 248 | //...and set up an iterator 249 | m_fiter = m_faces.begin(); 250 | } 251 | 252 | 253 | void VertexFaceIterator::advance() { 254 | if(!done()) 255 | ++m_fiter; 256 | } 257 | 258 | bool VertexFaceIterator::done() const { 259 | return m_fiter == m_faces.end(); 260 | } 261 | 262 | FaceHandle VertexFaceIterator::current() const { 263 | if(!done()) 264 | return *m_fiter; 265 | else 266 | return FaceHandle::invalid(); 267 | } 268 | 269 | 270 | //FaceVertexIterator 271 | FaceVertexIterator::FaceVertexIterator(const SimplicialComplex& obj, const FaceHandle& fh, bool ordered) : 272 | m_obj(obj), m_feit(obj, fh, ordered), m_fh(fh) 273 | { 274 | 275 | } 276 | 277 | void FaceVertexIterator::advance() { 278 | m_feit.advance(); 279 | } 280 | 281 | bool FaceVertexIterator::done() const { 282 | return m_feit.done(); 283 | } 284 | 285 | VertexHandle FaceVertexIterator::current() const { 286 | EdgeHandle curEdge = m_feit.current(); 287 | int direction = m_obj.getRelativeOrientation(m_fh, curEdge); 288 | return direction > 0? m_obj.fromVertex(curEdge) : m_obj.toVertex(curEdge); 289 | } 290 | 291 | //VertexTetIterator 292 | VertexTetIterator::VertexTetIterator(const SimplicialComplex& obj, const VertexHandle& vh) : m_obj(obj) { 293 | 294 | //build the set of (unique) tets 295 | for(VertexEdgeIterator veit(m_obj, vh); !veit.done(); veit.advance()) 296 | for(EdgeFaceIterator efit(m_obj, veit.current()); !efit.done(); efit.advance()) 297 | for(FaceTetIterator ftit(m_obj, efit.current()); !ftit.done(); ftit.advance()) 298 | m_tets.insert(ftit.current()); 299 | 300 | //...and set up an iterator 301 | m_titer = m_tets.begin(); 302 | } 303 | 304 | 305 | void VertexTetIterator::advance() { 306 | if(!done()) 307 | ++m_titer; 308 | } 309 | 310 | bool VertexTetIterator::done() const { 311 | return m_titer == m_tets.end(); 312 | } 313 | 314 | TetHandle VertexTetIterator::current() const { 315 | if(!done()) 316 | return *m_titer; 317 | else 318 | return TetHandle::invalid(); 319 | } 320 | 321 | 322 | //TetVertexIterator 323 | TetVertexIterator::TetVertexIterator(const SimplicialComplex& obj, const TetHandle& th) : 324 | m_obj(obj) 325 | { 326 | //build the set of (unique) vertices 327 | for(TetFaceIterator tfit(m_obj, th); !tfit.done(); tfit.advance()) 328 | for(FaceEdgeIterator feit(m_obj, tfit.current(), false); !feit.done(); feit.advance()) 329 | for(EdgeVertexIterator evit(m_obj, feit.current(), false); !evit.done(); evit.advance()) 330 | m_verts.insert(evit.current()); 331 | 332 | //...and set up an iterator 333 | m_viter = m_verts.begin(); 334 | 335 | } 336 | 337 | void TetVertexIterator::advance() { 338 | if(!done()) 339 | ++m_viter; 340 | } 341 | 342 | bool TetVertexIterator::done() const { 343 | return m_viter == m_verts.end(); 344 | } 345 | 346 | VertexHandle TetVertexIterator::current() const { 347 | if(!done()) 348 | return *m_viter; 349 | else 350 | return VertexHandle::invalid(); 351 | } 352 | 353 | 354 | //EdgeTetIterator 355 | EdgeTetIterator::EdgeTetIterator(const SimplicialComplex& obj, const EdgeHandle& eh) : m_obj(obj) { 356 | 357 | //build the set of (unique) tets 358 | for(EdgeFaceIterator efit(m_obj, eh); !efit.done(); efit.advance()) 359 | for(FaceTetIterator ftit(m_obj, efit.current()); !ftit.done(); ftit.advance()) 360 | m_tets.insert(ftit.current()); 361 | 362 | //...and set up an iterator 363 | m_titer = m_tets.begin(); 364 | } 365 | 366 | 367 | void EdgeTetIterator::advance() { 368 | if(!done()) 369 | ++m_titer; 370 | } 371 | 372 | bool EdgeTetIterator::done() const { 373 | return m_titer == m_tets.end(); 374 | } 375 | 376 | TetHandle EdgeTetIterator::current() const { 377 | if(!done()) 378 | return *m_titer; 379 | else 380 | return TetHandle::invalid(); 381 | } 382 | 383 | 384 | //TetEdgeIterator 385 | TetEdgeIterator::TetEdgeIterator(const SimplicialComplex& obj, const TetHandle& th) : 386 | m_obj(obj) 387 | { 388 | //build the set of (unique) vertices 389 | for(TetFaceIterator tfit(m_obj, th); !tfit.done(); tfit.advance()) 390 | for(FaceEdgeIterator feit(m_obj, tfit.current(), false); !feit.done(); feit.advance()) 391 | m_edges.insert(feit.current()); 392 | 393 | //...and set up an iterator 394 | m_eiter = m_edges.begin(); 395 | 396 | } 397 | 398 | void TetEdgeIterator::advance() { 399 | if(!done()) 400 | ++m_eiter; 401 | } 402 | 403 | bool TetEdgeIterator::done() const { 404 | return m_eiter == m_edges.end(); 405 | } 406 | 407 | EdgeHandle TetEdgeIterator::current() const { 408 | if(!done()) 409 | return *m_eiter; 410 | else 411 | return EdgeHandle::invalid(); 412 | } 413 | 414 | } -------------------------------------------------------------------------------- /src/SimplicialComplex.cpp: -------------------------------------------------------------------------------- 1 | #include "SimplicialComplex.h" 2 | #include "SimplexProperty.h" 3 | #include "SimplexIterators.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace SimplexMesh { 12 | 13 | int SimplicialComplex::numVerts() const { return m_nVerts;} 14 | 15 | int SimplicialComplex::numEdges() const { return m_nEdges;} 16 | 17 | int SimplicialComplex::numFaces() const { return m_nFaces;} 18 | 19 | int SimplicialComplex::numTets() const { return m_nTets;} 20 | 21 | SimplicialComplex::SimplicialComplex() 22 | { 23 | 24 | m_nVerts = 0; 25 | m_nEdges = 0; 26 | m_nFaces = 0; 27 | m_nTets = 0; 28 | 29 | } 30 | 31 | 32 | bool SimplicialComplex::vertexExists(const VertexHandle& vertex) const { 33 | if(vertex.idx() < 0 || vertex.idx() >= (int)m_V.size()) 34 | return false; 35 | else 36 | return m_V[vertex.idx()]; 37 | } 38 | 39 | bool SimplicialComplex::edgeExists(const EdgeHandle& edge) const { 40 | if(edge.idx() < 0 || edge.idx() >= (int)m_EV.getNumRows()) 41 | return false; 42 | else 43 | return m_EV.getNumEntriesInRow(edge.idx()) > 0; 44 | 45 | } 46 | 47 | bool SimplicialComplex::faceExists(const FaceHandle& face) const { 48 | if(face.idx() < 0 || face.idx() >= (int)m_FE.getNumRows()) 49 | return false; 50 | else 51 | return m_FE.getNumEntriesInRow(face.idx()) > 0; 52 | } 53 | 54 | bool SimplicialComplex::tetExists(const TetHandle& tet) const { 55 | if(tet.idx() < 0 || tet.idx() >= (int)m_TF.getNumRows()) 56 | return false; 57 | else 58 | return m_TF.getNumEntriesInRow(tet.idx()) > 0; 59 | } 60 | 61 | 62 | int SimplicialComplex::getRelativeOrientation(const TetHandle& th, const FaceHandle& fh) const { 63 | if(th.idx() < 0 || th.idx() >= (int)m_TF.getNumRows() || fh.idx() < 0 || fh.idx() >= (int)m_TF.getNumCols()) 64 | return 0; 65 | return m_TF.get(th.idx(), fh.idx()); 66 | } 67 | 68 | int SimplicialComplex::getRelativeOrientation(const FaceHandle& fh, const EdgeHandle& eh) const { 69 | if(fh.idx() < 0 || fh.idx() >= (int)m_FE.getNumRows() || eh.idx() < 0 || eh.idx() >= (int)m_FE.getNumCols()) 70 | return 0; 71 | return m_FE.get(fh.idx(), eh.idx()); 72 | } 73 | 74 | int SimplicialComplex::getRelativeOrientation(const EdgeHandle& eh, const VertexHandle& vh) const { 75 | if(eh.idx() < 0 || eh.idx() >= (int)m_EV.getNumRows() || vh.idx() < 0 || vh.idx() >= (int)m_EV.getNumCols()) 76 | return 0; 77 | return m_EV.get(eh.idx(), vh.idx()); 78 | } 79 | 80 | 81 | bool SimplicialComplex::isIncident(const VertexHandle& vh, const EdgeHandle& eh) const { 82 | return m_EV.get(eh.idx(), vh.idx()) != 0; 83 | } 84 | 85 | bool SimplicialComplex::isIncident(const EdgeHandle& eh, const FaceHandle& fh) const { 86 | return m_FE.get(fh.idx(), eh.idx()) != 0; 87 | } 88 | 89 | bool SimplicialComplex::isIncident(const FaceHandle& fh, const TetHandle& th) const { 90 | return m_TF.get(th.idx(), fh.idx()) != 0; 91 | } 92 | 93 | 94 | VertexHandle SimplicialComplex::addVertex() 95 | { 96 | 97 | int new_index; 98 | if(m_deadVerts.size() == 0) { 99 | //create new vertex in incidence matrices 100 | m_EV.addCols(1); 101 | m_VE.addRows(1); 102 | 103 | //add the vertex to the data/property array 104 | m_V.push_back(true); 105 | for(unsigned int i = 0; i < m_vertProperties.size(); ++i) m_vertProperties[i]->resize(m_V.size()); 106 | 107 | new_index = m_V.size()-1; 108 | } 109 | else { 110 | new_index = m_deadVerts.back(); 111 | m_deadVerts.pop_back(); 112 | 113 | assert(!m_V[new_index]); 114 | 115 | m_V[new_index] = true; 116 | } 117 | 118 | m_nVerts += 1; 119 | 120 | return VertexHandle(new_index); 121 | } 122 | 123 | 124 | EdgeHandle SimplicialComplex::addEdge(const VertexHandle& v0, const VertexHandle& v1) 125 | { 126 | 127 | //Cheap safety checks 128 | if(!vertexExists(v0) || !vertexExists(v1)) 129 | return EdgeHandle::invalid(); 130 | if(v0.idx() == v1.idx()) 131 | return EdgeHandle::invalid(); 132 | 133 | 134 | if(m_safetyChecks) { 135 | 136 | //full duplication: check if any edge joining these vertices already exists 137 | for(unsigned int i = 0; i < m_VE.getNumEntriesInRow(v0.idx()); ++i) { 138 | int edgeID = m_VE.getColByIndex(v0.idx(), i); 139 | for(unsigned int j = 0; j < m_EV.getNumEntriesInRow(edgeID); ++j){ 140 | int vertID = m_EV.getColByIndex(edgeID, j); 141 | if(vertID == v1.idx()) //this creates a duplicate edge 142 | return EdgeHandle::invalid(); 143 | } 144 | } 145 | } 146 | 147 | 148 | //get the next free edge index, or add space 149 | int new_index; 150 | if(m_deadEdges.size() == 0) { 151 | //make room for the new edge 152 | m_FE.addCols(1); 153 | m_EF.addRows(1); 154 | 155 | m_EV.addRows(1); 156 | m_VE.addCols(1); 157 | 158 | new_index = m_EV.getNumRows()-1; 159 | 160 | //add property slots for the new edge 161 | for(unsigned int i = 0; i < m_edgeProperties.size(); ++i) m_edgeProperties[i]->resize(m_EV.getNumRows()); 162 | 163 | assert(m_EV.getNumRows() == m_VE.getNumCols()); 164 | assert(m_EV.getNumCols() == m_VE.getNumRows()); 165 | assert(m_FE.getNumRows() == m_EF.getNumCols()); 166 | assert(m_FE.getNumCols() == m_EF.getNumRows()); 167 | } 168 | else { 169 | //grab the first dead edge off the pile 170 | new_index = m_deadEdges.back(); 171 | m_deadEdges.pop_back(); 172 | } 173 | 174 | //build new edge connectivity 175 | //choose indices explicitly, to indicate ordering 176 | m_EV.setByIndex(new_index, 0, v0.idx(), -1); 177 | m_EV.setByIndex(new_index, 1, v1.idx(), +1); 178 | 179 | m_VE.set(v0.idx(), new_index, -1); 180 | m_VE.set(v1.idx(), new_index, 1); 181 | 182 | //adjust edge count 183 | m_nEdges += 1; 184 | 185 | return EdgeHandle(new_index); 186 | } 187 | 188 | FaceHandle SimplicialComplex::addFace(const EdgeHandle& e0, 189 | const EdgeHandle& e1, 190 | const EdgeHandle& e2) 191 | { 192 | 193 | //cheap safety checks 194 | if(!edgeExists(e0) || !edgeExists(e1) || !edgeExists(e2)) //make sure all edges exists 195 | return FaceHandle::invalid(); 196 | 197 | if(e0 == e1 || e1 == e2 || e0 == e2) //prevent degenerate faces 198 | return FaceHandle::invalid(); 199 | 200 | 201 | if(m_safetyChecks) { 202 | 203 | //check if a duplicate face already exists (including partial matches and reversed orientations) 204 | for(unsigned int i = 0; i < m_EF.getNumEntriesInRow(e0.idx()); ++i) { 205 | int faceInd = m_EF.getColByIndex(e0.idx(), i); 206 | for(unsigned int j = 0; j < m_FE.getNumEntriesInRow(faceInd); ++j) { 207 | int edgeInd = m_FE.getColByIndex(faceInd, j); 208 | if(edgeInd == e1.idx() || edgeInd == e2.idx()) 209 | return FaceHandle::invalid(); 210 | } 211 | } 212 | for(unsigned int i = 0; i < m_EF.getNumEntriesInRow(e1.idx()); ++i) { 213 | int faceInd = m_EF.getColByIndex(e1.idx(), i); 214 | for(unsigned int j = 0; j < m_FE.getNumEntriesInRow(faceInd); ++j) { 215 | int edgeInd = m_FE.getColByIndex(faceInd, j); 216 | if(edgeInd == e0.idx() || edgeInd == e2.idx()) 217 | return FaceHandle::invalid(); 218 | } 219 | } 220 | for(unsigned int i = 0; i < m_EF.getNumEntriesInRow(e2.idx()); ++i) { 221 | int faceInd = m_EF.getColByIndex(e2.idx(), i); 222 | for(unsigned int j = 0; j < m_FE.getNumEntriesInRow(faceInd); ++j) { 223 | int edgeInd = m_FE.getColByIndex(faceInd, j); 224 | if(edgeInd == e0.idx() || edgeInd == e1.idx()) 225 | return FaceHandle::invalid(); 226 | } 227 | } 228 | 229 | //check that the composing edges actually share the same 3 vertices, and use them twice each 230 | std::map vertList; 231 | vertList[fromVertex(e0).idx()]++; 232 | vertList[toVertex(e0).idx()]++; 233 | vertList[fromVertex(e1).idx()]++; 234 | vertList[toVertex(e1).idx()]++; 235 | vertList[fromVertex(e2).idx()]++; 236 | vertList[toVertex(e2).idx()]++; 237 | if(vertList.size() != 3) 238 | return FaceHandle::invalid(); 239 | for(std::map::iterator iter = vertList.begin(); iter != vertList.end(); ++iter) { 240 | if(iter->second != 2) { 241 | return FaceHandle::invalid(); 242 | } 243 | } 244 | } 245 | 246 | //get the next free face or add one 247 | int new_index; 248 | if(m_deadFaces.size() == 0) { 249 | //create space for new face 250 | m_TF.addCols(1); 251 | m_FT.addRows(1); 252 | 253 | m_FE.addRows(1); 254 | m_EF.addCols(1); 255 | 256 | new_index = m_FE.getNumRows()-1; 257 | 258 | //allocate space for properties associated to the edge 259 | for(unsigned int i = 0; i < m_faceProperties.size(); ++i) m_faceProperties[i]->resize(m_FE.getNumRows()); 260 | 261 | assert(m_FE.getNumRows() == m_EF.getNumCols()); 262 | assert(m_FE.getNumCols() == m_EF.getNumRows()); 263 | assert(m_FT.getNumRows() == m_TF.getNumCols()); 264 | assert(m_FT.getNumCols() == m_TF.getNumRows()); 265 | } 266 | else { 267 | //grab the next empty face off the pile 268 | new_index = m_deadFaces.back(); 269 | m_deadFaces.pop_back(); 270 | } 271 | 272 | //Signs are chosen to follow the ordering of edges provided as input, 273 | //so we flip to ensure edge vertices connect properly. 274 | 275 | //If the head of the first edge doesn't match either of the second edge's vertices, we must flip it, 276 | //since we want it oriented towards the second edge. 277 | bool flip0 = (toVertex(e0) != fromVertex(e1) && toVertex(e0) != toVertex(e1)); 278 | 279 | //Now determine the shared vertex between edges 0 and 1. 280 | //Then flip edge1 if the shared_vertex is at the head; it should be at the tail. 281 | VertexHandle shared_vert0_hnd = flip0? fromVertex(e0) : toVertex(e0); 282 | bool flip1 = (shared_vert0_hnd != fromVertex(e1)); 283 | 284 | //Determine shared vertex between edge 1 and 2. 285 | //Then flip edge2 if the shared_vertex is at the head. 286 | VertexHandle shared_vert1_hnd = flip1 ? fromVertex(e1) : toVertex(e1); 287 | bool flip2 = (shared_vert1_hnd != fromVertex(e2)); 288 | 289 | //build face connectivity 290 | //get a unique ordering by arbitrarily choosing smallest index to go first 291 | int smallest = std::min(std::min(e0.idx(), e1.idx()), e2.idx()); 292 | 293 | //add'em in requested ordering 294 | m_FE.setByIndex(new_index, 0, e0.idx(), flip0?-1:1); 295 | m_FE.setByIndex(new_index, 1, e1.idx(), flip1?-1:1); 296 | m_FE.setByIndex(new_index, 2, e2.idx(), flip2?-1:1); 297 | 298 | //cycle to get the smallest one first, for consistency 299 | while(m_FE.getColByIndex((unsigned int)new_index, (unsigned int)0) != (unsigned int)smallest) 300 | m_FE.cycleRow(new_index); 301 | 302 | //build the other one the usual way 303 | m_EF.set(e0.idx(), new_index, flip0?-1:1); 304 | m_EF.set(e1.idx(), new_index, flip1?-1:1); 305 | m_EF.set(e2.idx(), new_index, flip2?-1:1); 306 | 307 | m_nFaces += 1; 308 | 309 | return FaceHandle(new_index); 310 | } 311 | 312 | TetHandle SimplicialComplex::addTet(const FaceHandle& f0, 313 | const FaceHandle& f1, 314 | const FaceHandle& f2, 315 | const FaceHandle& f3, bool flip_face0) 316 | { 317 | 318 | //ensure all the faces actually exist 319 | if(!faceExists(f0) || !faceExists(f1) || !faceExists(f2) || !faceExists(f3)) 320 | return TetHandle::invalid(); 321 | 322 | //prevent degenerate tets 323 | if(f0 == f1 || f0 == f2 || f0 == f3 || f1 == f2 || f1 == f3 || f2 == f3) 324 | return TetHandle::invalid(); 325 | 326 | if(m_safetyChecks) { 327 | 328 | //check for tets that share 2 (or possibly more) of the same faces, since this isn't really valid when embedded in 3D. 329 | //[technically perhaps sharing 3 faces should be the no-no] 330 | FaceHandle faceList[4] = {f0, f1, f2, f3}; 331 | for(int i = 0; i < 4; ++i) { 332 | FaceHandle curF = faceList[i]; 333 | for(unsigned int j = 0; j < m_FT.getNumEntriesInRow(curF.idx()); ++j) { 334 | unsigned tetId = m_FT.getColByIndex(curF.idx(), j); 335 | for(unsigned int k = 0; k < m_TF.getNumEntriesInRow(tetId); ++k) { 336 | int otherF = m_TF.getColByIndex(tetId, k); 337 | if(otherF == f0.idx() || otherF == f1.idx() || otherF == f2.idx() || otherF == f3.idx()) 338 | return TetHandle::invalid(); 339 | } 340 | } 341 | } 342 | 343 | 344 | //Check that these faces share the same 6 edges, twice each 345 | FaceHandle faces[4] = {f0, f1, f2, f3}; 346 | std::map edgeList; 347 | for(int i = 0; i < 4; ++i) { 348 | for(unsigned int j = 0; j < m_FE.getNumEntriesInRow(faces[i].idx()); ++j) { 349 | int edgeInd = m_FE.getColByIndex(faces[i].idx(), j); 350 | edgeList[edgeInd]++; 351 | } 352 | } 353 | 354 | if(edgeList.size() != 6) 355 | return TetHandle::invalid(); 356 | for(std::map::iterator iter = edgeList.begin(); iter != edgeList.end(); ++iter) { 357 | if(iter->second != 2) { 358 | return TetHandle::invalid(); 359 | } 360 | } 361 | } 362 | 363 | //get the next free tet or add one 364 | int new_index; 365 | if(m_deadTets.size() == 0) { 366 | //create new tet 367 | m_TF.addRows(1); 368 | m_FT.addCols(1); 369 | 370 | new_index = m_TF.getNumRows()-1; 371 | 372 | //allocate space for the properties 373 | for(unsigned int i = 0; i < m_tetProperties.size(); ++i) m_tetProperties[i]->resize(m_TF.getNumRows()); 374 | 375 | assert(m_FT.getNumRows() == m_TF.getNumCols()); 376 | assert(m_FT.getNumCols() == m_TF.getNumRows()); 377 | } 378 | else { 379 | //grab the next unused tet 380 | new_index = m_deadTets.back(); 381 | m_deadTets.pop_back(); 382 | } 383 | 384 | //Need to figure out signs to be consistent with the choice of the first face 385 | 386 | //Determine the shared edge between two adjacent faces. Flip the 2nd so its direction is opposed (signs differ) after the flips. 387 | EdgeHandle shared_edge0 = getSharedEdge(f0, f1); 388 | bool flip1 = flip_face0 ? m_FE.get(f0.idx(), shared_edge0.idx()) == m_FE.get(f1.idx(), shared_edge0.idx()) : 389 | m_FE.get(f0.idx(), shared_edge0.idx()) != m_FE.get(f1.idx(), shared_edge0.idx()); 390 | 391 | EdgeHandle shared_edge1 = getSharedEdge(f0, f2); 392 | bool flip2 = flip_face0 ? m_FE.get(f0.idx(), shared_edge1.idx()) == m_FE.get(f2.idx(), shared_edge1.idx()) : 393 | m_FE.get(f0.idx(), shared_edge1.idx()) != m_FE.get(f2.idx(), shared_edge1.idx()); 394 | 395 | EdgeHandle shared_edge2 = getSharedEdge(f0, f3); 396 | bool flip3 = flip_face0 ? m_FE.get(f0.idx(), shared_edge2.idx()) == m_FE.get(f3.idx(), shared_edge2.idx()) : 397 | m_FE.get(f0.idx(), shared_edge2.idx()) != m_FE.get(f3.idx(), shared_edge1.idx()); 398 | 399 | //build tet connectivity 400 | m_TF.set(new_index, f0.idx(), flip_face0?1:-1); 401 | m_TF.set(new_index, f1.idx(), flip1?1:-1); 402 | m_TF.set(new_index, f2.idx(), flip2?1:-1); 403 | m_TF.set(new_index, f3.idx(), flip3?1:-1); 404 | 405 | m_FT.set(f0.idx(), new_index, flip_face0?1:-1); 406 | m_FT.set(f1.idx(), new_index, flip1?1:-1); 407 | m_FT.set(f2.idx(), new_index, flip2?1:-1); 408 | m_FT.set(f3.idx(), new_index, flip3?1:-1); 409 | 410 | m_nTets += 1; 411 | 412 | //invalidate the relevant cached neighbour data 413 | 414 | return TetHandle(new_index); 415 | } 416 | 417 | FaceHandle SimplicialComplex::addFace(const VertexHandle& v0, const VertexHandle& v1, const VertexHandle& v2) 418 | { 419 | //This alternate method for adding a face requires 420 | //searching for the edges containing the desired vertices, and creating them if they don't exist. 421 | //Likely to be slower, albeit more convenient. 422 | //ensure the vertices exist, and aren't duplicated 423 | if(!vertexExists(v0) || !vertexExists(v1) || !vertexExists(v2)) 424 | return FaceHandle::invalid(); 425 | 426 | //Careful about making the ordering match the desired input ordering of the vertices... 427 | 428 | //Find the edges we need, or create them. 429 | EdgeHandle e01 = getEdge(v0,v1); 430 | if(!e01.isValid()) e01 = addEdge(v0,v1); 431 | 432 | EdgeHandle e02 = getEdge(v2,v0); 433 | if(!e02.isValid()) e02 = addEdge(v2,v0); 434 | 435 | EdgeHandle e12 = getEdge(v1,v2); 436 | if(!e12.isValid()) e12 = addEdge(v1,v2); 437 | 438 | return addFace(e01, e12, e02); 439 | } 440 | 441 | TetHandle SimplicialComplex::addTet(const VertexHandle& v0, const VertexHandle& v1, const VertexHandle& v2, const VertexHandle& v3) { 442 | if(!v0.isValid() || !v1.isValid() || !v2.isValid() || !v3.isValid()) 443 | return TetHandle::invalid(); 444 | 445 | EdgeHandle e0 = getEdge(v0,v1); 446 | if(!e0.isValid()) e0 = addEdge(v0,v1); 447 | EdgeHandle e1 = getEdge(v0,v2); 448 | if(!e1.isValid()) e1 = addEdge(v0,v2); 449 | EdgeHandle e2 = getEdge(v0,v3); 450 | if(!e2.isValid()) e2 = addEdge(v0,v3); 451 | EdgeHandle e3 = getEdge(v1,v2); 452 | if(!e3.isValid()) e3 = addEdge(v1,v2); 453 | EdgeHandle e4 = getEdge(v1,v3); 454 | if(!e4.isValid()) e4 = addEdge(v1,v3); 455 | EdgeHandle e5 = getEdge(v2,v3); 456 | if(!e5.isValid()) e5 = addEdge(v2,v3); 457 | 458 | FaceHandle f0 = getFace(e0,e2,e4); 459 | if(!f0.isValid()) f0 = addFace(e0,e2,e4); 460 | FaceHandle f1 = getFace(e3,e4,e5); 461 | if(!f1.isValid()) f1 = addFace(e3,e4,e5); 462 | FaceHandle f2 = getFace(e0,e1,e3); 463 | if(!f2.isValid()) f2 = addFace(e0,e1,e3); 464 | FaceHandle f3 = getFace(e1,e2,e5); 465 | if(!f3.isValid()) f3 = addFace(e1,e2,e5); 466 | 467 | return addTet(f0,f1,f2,f3); 468 | } 469 | 470 | 471 | 472 | bool SimplicialComplex::deleteVertex(const VertexHandle& vertex) 473 | { 474 | if(!vertexExists(vertex)) 475 | return false; 476 | 477 | //test for safety - don't perform the delete if the simplex is not orphaned, or we get inconsistency. 478 | if(m_VE.getNumEntriesInRow(vertex.idx()) != 0) 479 | return false; 480 | 481 | //set the vertex to inactive 482 | m_V[vertex.idx()] = false; 483 | m_deadVerts.push_back(vertex.idx()); 484 | 485 | //adjust the vertex count 486 | m_nVerts -= 1; 487 | 488 | return true; 489 | } 490 | 491 | bool SimplicialComplex::deleteEdge(const EdgeHandle& edge, bool recurse) 492 | { 493 | if(!edgeExists(edge)) 494 | return false; 495 | 496 | //test for safety - don't perform delete if not orphaned, or it creates inconsistency. 497 | if(m_EF.getNumEntriesInRow(edge.idx()) != 0) 498 | return false; 499 | 500 | //determine the corresponding vertices 501 | for(unsigned int i = 0; i < m_EV.getNumEntriesInRow(edge.idx()); ++i) { 502 | 503 | //delete the edge entry in the transpose 504 | int col = m_EV.getColByIndex(edge.idx(),i); 505 | m_VE.remove(col, edge.idx()); 506 | 507 | //delete the composing vertices if desired 508 | if(recurse) 509 | deleteVertex(VertexHandle(col)); 510 | } 511 | 512 | //...and delete the row 513 | m_EV.zeroRow(edge.idx()); 514 | m_deadEdges.push_back(edge.idx()); 515 | 516 | //adjust the edge count 517 | m_nEdges -= 1; 518 | 519 | return true; 520 | } 521 | 522 | bool SimplicialComplex::deleteFace(const FaceHandle& face, bool recurse) 523 | { 524 | if(!faceExists(face)) 525 | return false; 526 | 527 | //test for safety - don't perform delete if not orphaned, or we'll have inconsistencies. 528 | if(m_FT.getNumEntriesInRow(face.idx()) != 0) 529 | return false; 530 | 531 | //determine the corresponding edges 532 | for(unsigned int i = 0; i < m_FE.getNumEntriesInRow(face.idx()); ++i) { 533 | 534 | //remove face entry from the transpose 535 | int col = m_FE.getColByIndex(face.idx(), i); 536 | m_EF.remove(col, face.idx()); 537 | 538 | //delete the composing edges 539 | if(recurse) 540 | deleteEdge(EdgeHandle(col), recurse); 541 | } 542 | 543 | //...and delete the row 544 | m_FE.zeroRow(face.idx()); 545 | m_deadFaces.push_back(face.idx()); 546 | 547 | //adjust the face count 548 | m_nFaces -= 1; 549 | 550 | return true; 551 | } 552 | 553 | bool SimplicialComplex::deleteTet(const TetHandle& tet, bool recurse) { 554 | if(!tetExists(tet)) 555 | return false; 556 | 557 | //There are no higher dimensional simplices in 3D, so deleting the tet cannot introduce inconsistencies 558 | //as it can in the lower cases. 559 | 560 | //determine the corresponding faces 561 | for(unsigned int i = 0; i < m_TF.getNumEntriesInRow(tet.idx()); ++i) { 562 | 563 | //clear the tet entries in the transpose 564 | int col = m_TF.getColByIndex(tet.idx(), i); 565 | m_FT.remove(col, tet.idx()); 566 | 567 | //delete composing faces if desired 568 | if(recurse) 569 | deleteFace(FaceHandle(col), recurse); 570 | } 571 | 572 | //...and delete the row 573 | m_TF.zeroRow(tet.idx()); 574 | m_deadTets.push_back(tet.idx()); 575 | 576 | //adjust the tet count 577 | m_nTets -= 1; 578 | 579 | //invalidate cached relationships 580 | 581 | return true; 582 | 583 | } 584 | 585 | 586 | 587 | VertexHandle SimplicialComplex::getVertex(const EdgeHandle& edge, int index) const { 588 | assert(edgeExists(edge)); 589 | assert(index >= 0 && index <= 1); 590 | return VertexHandle(m_EV.getColByIndex(edge.idx(), index)); 591 | } 592 | 593 | EdgeHandle SimplicialComplex::getEdge(const FaceHandle& face, int index) const { 594 | assert(faceExists(face)); 595 | assert(index >= 0 && index <= 2); 596 | return EdgeHandle(m_FE.getColByIndex(face.idx(), index)); 597 | } 598 | 599 | FaceHandle SimplicialComplex::getFace(const TetHandle& tet, int index) const { 600 | assert(tetExists(tet)); 601 | assert(index >= 0 && index <= 3); 602 | return FaceHandle(m_TF.getColByIndex(tet.idx(), index)); 603 | } 604 | 605 | EdgeHandle SimplicialComplex::getEdge(const VertexHandle& v0, const VertexHandle& v1) const { 606 | //returns the appropriate edge if it exists, ignoring orientation 607 | if(!vertexExists(v0) || !vertexExists(v1)) 608 | return EdgeHandle::invalid(); 609 | 610 | for(VertexEdgeIterator veit(*this, v0); !veit.done(); veit.advance()) { 611 | EdgeHandle curEdge = veit.current(); 612 | if(fromVertex(curEdge) == v1 || toVertex(curEdge) == v1) 613 | return curEdge; 614 | } 615 | 616 | return EdgeHandle::invalid(); 617 | } 618 | 619 | FaceHandle SimplicialComplex::getFace(const EdgeHandle& e0, const EdgeHandle& e1, const EdgeHandle& e2) const { 620 | //returns the appropriate face if it exists, ignoring orientation 621 | if(!edgeExists(e0) || !edgeExists(e1) || !edgeExists(e2)) 622 | return FaceHandle::invalid(); 623 | 624 | for(EdgeFaceIterator efit(*this, e0); !efit.done(); efit.advance()) { 625 | FaceHandle curFace = efit.current(); 626 | bool foundE1 = false, foundE2 = false; 627 | for(FaceEdgeIterator feit(*this, curFace, false); !feit.done(); feit.advance()) { 628 | EdgeHandle curEdge = feit.current(); 629 | if(curEdge == e1) foundE1 = true; 630 | if(curEdge == e2) foundE2 = true; 631 | } 632 | if(foundE1 && foundE2) 633 | return curFace; 634 | } 635 | 636 | return FaceHandle::invalid(); 637 | } 638 | 639 | TetHandle SimplicialComplex::getTet(const FaceHandle& f0, const FaceHandle& f1, const FaceHandle& f2, const FaceHandle& f3) const { 640 | //returns the appropriate tet if it exists, ignoring orientation 641 | if(!faceExists(f0) || !faceExists(f1) || !faceExists(f2) || !faceExists(f3)) 642 | return TetHandle::invalid(); 643 | 644 | for(FaceTetIterator ftit(*this, f0); !ftit.done(); ftit.advance()) { 645 | TetHandle curTet = ftit.current(); 646 | bool foundf1 = false, foundf2 = false, foundf3 = false; 647 | for(TetFaceIterator tfit(*this, curTet); !tfit.done(); tfit.advance()) { 648 | FaceHandle curFace = tfit.current(); 649 | if(curFace == f1) foundf1 = true; 650 | if(curFace == f2) foundf2 = true; 651 | if(curFace == f3) foundf3 = true; 652 | } 653 | if(foundf1 && foundf2 && foundf3) 654 | return curTet; 655 | } 656 | 657 | return TetHandle::invalid(); 658 | } 659 | 660 | 661 | EdgeHandle SimplicialComplex::getSharedEdge(const FaceHandle& f0, const FaceHandle& f1) const { 662 | assert(faceExists(f0) && faceExists(f1)); 663 | assert(m_FE.getNumEntriesInRow(f0.idx()) == 3); 664 | assert(m_FE.getNumEntriesInRow(f1.idx()) == 3); 665 | 666 | //Iterate over all pairs, stop when we hit the match. 667 | for(unsigned int ind0 = 0; ind0 < 3; ++ind0) for(unsigned ind1 = 0; ind1 < 3; ++ind1) { 668 | int col0 = m_FE.getColByIndex(f0.idx(), ind0); 669 | int col1 = m_FE.getColByIndex(f1.idx(), ind1); 670 | if(col0 == col1) 671 | return EdgeHandle(col0); 672 | } 673 | 674 | return EdgeHandle(-1); 675 | } 676 | 677 | 678 | VertexHandle SimplicialComplex::fromVertex(const EdgeHandle& eh) const 679 | { 680 | assert(eh.isValid()); 681 | assert(m_EV.getNumEntriesInRow(eh.idx()) == 2); 682 | 683 | //1st vertex is the from vertex, by design 684 | return VertexHandle(m_EV.getColByIndex(eh.idx(), 0)); 685 | } 686 | 687 | VertexHandle SimplicialComplex::toVertex(const EdgeHandle& eh) const 688 | { 689 | assert(eh.isValid()); 690 | assert(m_EV.getNumEntriesInRow(eh.idx()) == 2); 691 | 692 | //2nd vertex is the to vertex, by design 693 | return VertexHandle(m_EV.getColByIndex(eh.idx(), 1)); 694 | } 695 | 696 | FaceHandle SimplicialComplex::frontFace(const EdgeHandle& eh) const { 697 | assert(eh.isValid()); 698 | assert(m_EF.getNumEntriesInRow(eh.idx()) <= 2); 699 | 700 | int edgeIdx = eh.idx(); 701 | 702 | if(m_EF.getNumCols() == 0) 703 | return FaceHandle::invalid(); 704 | else if(m_EF.getNumCols() == 1) 705 | return (m_EF.getValueByIndex(edgeIdx, 0) == 1 ? FaceHandle(m_EF.getColByIndex(edgeIdx, 0)) : FaceHandle::invalid()); 706 | else 707 | return (m_EF.getValueByIndex(edgeIdx, 0) == 1 ? FaceHandle(m_EF.getColByIndex(edgeIdx, 0)) : FaceHandle(m_EF.getColByIndex(edgeIdx, 1))); 708 | } 709 | 710 | FaceHandle SimplicialComplex::backFace(const EdgeHandle& eh) const { 711 | assert(eh.isValid()); 712 | assert(m_EF.getNumEntriesInRow(eh.idx()) <= 2); 713 | 714 | int edgeIdx = eh.idx(); 715 | if(m_EF.getNumCols() == 0) 716 | return FaceHandle::invalid(); 717 | else if(m_EF.getNumCols() == 1) 718 | return (m_EF.getValueByIndex(edgeIdx, 0) == -1 ? FaceHandle(m_EF.getColByIndex(edgeIdx, 0)) : FaceHandle::invalid()); 719 | else 720 | return (m_EF.getValueByIndex(edgeIdx, 0) == -1 ? FaceHandle(m_EF.getColByIndex(edgeIdx, 0)) : FaceHandle(m_EF.getColByIndex(edgeIdx, 1))); 721 | } 722 | 723 | TetHandle SimplicialComplex::frontTet(const FaceHandle& fh) const { 724 | assert(fh.isValid()); 725 | assert(m_FT.getNumEntriesInRow(fh.idx()) <= 2); 726 | 727 | int faceIdx = fh.idx(); 728 | if(m_FT.getNumCols() == 0) 729 | return TetHandle::invalid(); 730 | else if(m_FT.getNumCols() == 1) 731 | return (m_FT.getValueByIndex(faceIdx, 0) == 1 ? TetHandle(m_FT.getColByIndex(faceIdx, 0)) : TetHandle::invalid()); 732 | else 733 | return (m_FT.getValueByIndex(faceIdx, 0) == 1 ? TetHandle(m_FT.getColByIndex(faceIdx, 0)) : TetHandle(m_FT.getColByIndex(faceIdx, 1))); 734 | } 735 | 736 | TetHandle SimplicialComplex::backTet(const FaceHandle& fh) const { 737 | assert(fh.isValid()); 738 | assert(m_FT.getNumEntriesInRow(fh.idx()) <= 2); 739 | 740 | int faceIdx = fh.idx(); 741 | if(m_FT.getNumCols() == 0) 742 | return TetHandle::invalid(); 743 | else if(m_FT.getNumCols() == 1) 744 | return (m_FT.getValueByIndex(faceIdx, 0) == -1 ? TetHandle(m_FT.getColByIndex(faceIdx, 0)) : TetHandle::invalid()); 745 | else 746 | return (m_FT.getValueByIndex(faceIdx, 0) == -1 ? TetHandle(m_FT.getColByIndex(faceIdx, 0)) : TetHandle(m_FT.getColByIndex(faceIdx, 1))); 747 | } 748 | 749 | 750 | VertexHandle SimplicialComplex::nextVertex(const VertexHandle& curVertex) const { 751 | assert(vertexExists(curVertex)); 752 | 753 | int idx = curVertex.idx(); 754 | do { 755 | ++idx; 756 | idx %= numVertexSlots(); 757 | } while(!m_V[idx]); 758 | 759 | return VertexHandle(idx); 760 | } 761 | 762 | VertexHandle SimplicialComplex::prevVertex(const VertexHandle& curVertex) const { 763 | assert(vertexExists(curVertex)); 764 | 765 | int idx = curVertex.idx(); 766 | do { 767 | idx+=numVertexSlots()-1; 768 | idx %= numVertexSlots(); 769 | } while(!m_V[idx]); 770 | 771 | return VertexHandle(idx); 772 | } 773 | 774 | EdgeHandle SimplicialComplex::nextEdge(const EdgeHandle& curEdge) const { 775 | assert(edgeExists(curEdge)); 776 | 777 | int idx = curEdge.idx(); 778 | do { 779 | ++idx; 780 | idx %= numEdgeSlots(); 781 | }while (m_EV.getNumEntriesInRow(idx) == 0); 782 | 783 | return EdgeHandle(idx); 784 | } 785 | 786 | EdgeHandle SimplicialComplex::prevEdge(const EdgeHandle& curEdge) const { 787 | assert(edgeExists(curEdge)); 788 | 789 | int idx = curEdge.idx(); 790 | do { 791 | idx += numEdgeSlots()-1; 792 | idx %= numEdgeSlots(); 793 | }while (m_EV.getNumEntriesInRow(idx) == 0); 794 | 795 | return EdgeHandle(idx); 796 | } 797 | 798 | FaceHandle SimplicialComplex::nextFace(const FaceHandle& curFace) const { 799 | assert(faceExists(curFace)); 800 | 801 | int idx = curFace.idx(); 802 | do { 803 | ++idx; 804 | idx %= numFaceSlots(); 805 | }while (m_FE.getNumEntriesInRow(idx) == 0); 806 | 807 | return FaceHandle(idx); 808 | } 809 | 810 | FaceHandle SimplicialComplex::prevFace(const FaceHandle& curFace) const { 811 | assert(faceExists(curFace)); 812 | 813 | int idx = curFace.idx(); 814 | do { 815 | idx += numFaceSlots()-1; 816 | idx %= numFaceSlots(); 817 | } while (m_FE.getNumEntriesInRow(idx) == 0); 818 | 819 | return FaceHandle(idx); 820 | } 821 | 822 | TetHandle SimplicialComplex::nextTet(const TetHandle& curTet) const { 823 | assert(tetExists(curTet)); 824 | 825 | int idx = curTet.idx(); 826 | do { 827 | ++idx; 828 | idx %= numTetSlots(); 829 | }while (m_TF.getNumEntriesInRow(idx) == 0); 830 | 831 | return TetHandle(idx); 832 | } 833 | 834 | TetHandle SimplicialComplex::prevTet(const TetHandle& curTet) const { 835 | assert(tetExists(curTet)); 836 | 837 | int idx = curTet.idx(); 838 | do { 839 | idx += numTetSlots()-1; 840 | idx %= numTetSlots(); 841 | }while (m_TF.getNumEntriesInRow(idx) == 0); 842 | 843 | return TetHandle(idx); 844 | } 845 | 846 | //--------------------------------------- 847 | 848 | VertexHandle SimplicialComplex::nextVertex(const EdgeHandle& edge, const VertexHandle& curVert) const { 849 | assert(edgeExists(edge)); 850 | assert(vertexExists(curVert)); 851 | assert(m_EV.getNumEntriesInRow(edge.idx()) == 3); 852 | assert(isIncident(curVert, edge)); 853 | 854 | //just return whichever vert is the opposite 855 | int edgeIdx = edge.idx(); 856 | int col0 = m_EV.getColByIndex(edgeIdx, 0); 857 | int vertIdx = (col0 == curVert.idx() ? m_EV.getColByIndex(edgeIdx, 1) : col0); 858 | 859 | return VertexHandle(vertIdx); 860 | } 861 | 862 | VertexHandle SimplicialComplex::prevVertex(const EdgeHandle& edge, const VertexHandle& curVert) const { 863 | return nextVertex(edge, curVert); 864 | } 865 | 866 | 867 | EdgeHandle SimplicialComplex::nextEdge(const FaceHandle& face, const EdgeHandle& curEdge) const { 868 | assert(faceExists(face)); 869 | assert(edgeExists(curEdge)); 870 | assert(m_FE.getNumEntriesInRow(face.idx()) == 3); 871 | assert(isIncident(curEdge, face)); 872 | 873 | //take advantage of known fixed ordering of indices 874 | int faceIdx = face.idx(); 875 | int colIdx = curEdge.idx(); 876 | int col0 = m_FE.getColByIndex(faceIdx, 0); 877 | int col1 = m_FE.getColByIndex(faceIdx, 1); 878 | if(col0 == colIdx) 879 | return EdgeHandle(col1); 880 | else if(col1 == colIdx) 881 | return EdgeHandle(m_FE.getColByIndex(faceIdx, 2)); 882 | else 883 | return EdgeHandle(col0); 884 | } 885 | 886 | EdgeHandle SimplicialComplex::prevEdge(const FaceHandle& face, const EdgeHandle& curEdge) const { 887 | assert(faceExists(face)); 888 | assert(edgeExists(curEdge)); 889 | assert(m_FE.getNumEntriesInRow(face.idx()) == 3); 890 | 891 | //take advantage of known fixed ordering of indices 892 | int faceIdx = face.idx(); 893 | int colIdx = curEdge.idx(); 894 | int col0 = m_FE.getColByIndex(faceIdx, 0); 895 | int col2 = m_FE.getColByIndex(faceIdx, 2); 896 | if(col0 == colIdx) 897 | return EdgeHandle(col2); 898 | else if(col2 == colIdx) 899 | return EdgeHandle(col0); 900 | else 901 | return EdgeHandle(m_FE.getColByIndex(faceIdx, 1)); 902 | } 903 | 904 | FaceHandle SimplicialComplex::nextFace(const TetHandle& tet, const FaceHandle& curFace) const { 905 | assert(tetExists(tet)); 906 | assert(faceExists(curFace)); 907 | assert(m_TF.getNumEntriesInRow(tet.idx()) == 4); 908 | assert(isIncident(curFace, tet)); 909 | 910 | int tetIdx = tet.idx(); 911 | 912 | //find the current face 913 | int colIdx = 0; 914 | for(; colIdx < 4; ++colIdx) { 915 | if(curFace.idx() == (int)m_TF.getColByIndex(tetIdx, colIdx)) { 916 | break; 917 | } 918 | } 919 | int nextIdx = (colIdx+1)%4; 920 | return FaceHandle(m_TF.getColByIndex(tetIdx, nextIdx)); 921 | } 922 | 923 | FaceHandle SimplicialComplex::prevFace(const TetHandle& tet, const FaceHandle& curFace) const { 924 | assert(tetExists(tet)); 925 | assert(faceExists(curFace)); 926 | assert(m_TF.getNumEntriesInRow(tet.idx()) == 4); 927 | assert(isIncident(curFace, tet)); 928 | 929 | int tetIdx = tet.idx(); 930 | 931 | //find the current face 932 | int colIdx = 0; 933 | for(; colIdx < 4; ++colIdx) { 934 | if(curFace.idx() == (int)m_TF.getColByIndex(tetIdx, colIdx)) { 935 | break; 936 | } 937 | } 938 | int nextIdx = (colIdx+3)%4; 939 | return FaceHandle(m_TF.getColByIndex(tetIdx, nextIdx)); 940 | } 941 | 942 | //-------------------------------- 943 | 944 | 945 | VertexHandle SimplicialComplex::collapseEdge(const EdgeHandle& eh, const VertexHandle& vertToRemove) { 946 | 947 | VertexHandle fromV = fromVertex(eh); 948 | VertexHandle toV = toVertex(eh); 949 | assert(fromV == vertToRemove || toV == vertToRemove); //make sure the vertex selected for deletion is actually used by the edge 950 | 951 | //grab the vertex that we are keeping 952 | int vertToKeep = fromV == vertToRemove? toV.idx() : fromV.idx(); 953 | 954 | //determine adjacent faces to the collapsing edge 955 | int faceCount = m_EF.getNumEntriesInRow(eh.idx()); 956 | 957 | //look at faces on left and right of collapsing edge 958 | std::vector facesToDelete(faceCount); 959 | for(int f = 0; f < faceCount; ++f) { 960 | int face_idx = m_EF.getColByIndex(eh.idx(), f); 961 | 962 | //visit the face's other edges 963 | std::set neighbourEdges; 964 | for(unsigned int i = 0; i < m_FE.getNumEntriesInRow(face_idx); ++i) { 965 | int edge_idx = m_FE.getColByIndex(face_idx, i); 966 | if(edge_idx != eh.idx()) { 967 | 968 | //walk over the other faces that this edge belongs to, checking for a shared edge 969 | for(unsigned int j = 0; j < m_EF.getNumEntriesInRow(edge_idx); ++j) { 970 | int curFace = m_EF.getColByIndex(edge_idx, j); 971 | 972 | //if we see a shared edge, then the collapsing edge merges two faces 973 | //and that's unacceptable. 974 | if(curFace != face_idx) { 975 | for(unsigned int k = 0; k < m_FE.getNumEntriesInRow(curFace); ++k) { 976 | int curEdge = m_FE.getColByIndex(curFace, k); 977 | 978 | if(neighbourEdges.find(curEdge) != neighbourEdges.end()) { 979 | return VertexHandle(-1); //there's a shared face/edge, don't collapse 980 | } 981 | else { 982 | neighbourEdges.insert(curEdge); 983 | } 984 | } 985 | } 986 | } 987 | } 988 | } 989 | 990 | facesToDelete[f] = face_idx; 991 | } 992 | 993 | 994 | //delete the faces and then edge, leaving a hole to be stitched 995 | for(unsigned int i = 0; i < facesToDelete.size(); ++i) { 996 | bool success = deleteFace(FaceHandle(facesToDelete[i]), false); 997 | assert(success); 998 | } 999 | bool success = deleteEdge(eh, false); 1000 | assert(success); 1001 | 1002 | //determine all existing edges using the vertex being eliminated 1003 | std::vector< std::pair > edgeIndices; 1004 | for(unsigned int e = 0; e < m_VE.getNumEntriesInRow(vertToRemove.idx()); ++e) { 1005 | unsigned int edgeInd = m_VE.getColByIndex(vertToRemove.idx(), e); 1006 | int sign = m_VE.getValueByIndex(vertToRemove.idx(), e); 1007 | edgeIndices.push_back(std::make_pair(edgeInd,sign)); 1008 | } 1009 | 1010 | //relabel all the edges' to-be-deleted endpoints to the vertex being kept. 1011 | //doing it "in place" like this rather than using safer atomic add/deletes 1012 | //ensures that the original data on the modified edges gets maintained. 1013 | for(unsigned int i = 0; i < edgeIndices.size(); ++i) { 1014 | unsigned int edgeInd = edgeIndices[i].first; 1015 | int vertSign = edgeIndices[i].second; 1016 | 1017 | m_VE.remove(vertToRemove.idx(), edgeInd); 1018 | m_EV.remove(edgeInd, vertToRemove.idx()); 1019 | 1020 | m_VE.set(vertToKeep, edgeInd, vertSign); 1021 | m_EV.set(edgeInd, vertToKeep, vertSign); 1022 | } 1023 | 1024 | 1025 | //now we have some edges that are duplicates, possibly pointing in opposite directions 1026 | //identify duplicate edges for deletion. 1027 | std::vector< std::pair > duplicateEdges; //list of (edge,edge) pairs that are duplicates 1028 | std::map vertEdgeMap;//for each vertex in the set of neighbours, the first edge we hit that uses it. 1029 | for(unsigned int e = 0; e < m_VE.getNumEntriesInRow(vertToKeep); ++e) { 1030 | int edgeInd = m_VE.getColByIndex(vertToKeep,e); 1031 | int fromV = fromVertex(EdgeHandle(edgeInd)).idx(); 1032 | int toV = toVertex(EdgeHandle(edgeInd)).idx(); 1033 | int otherVert = fromV == vertToKeep? toV : fromV; 1034 | 1035 | //check if an edge with this end vertex has been seen yet 1036 | //if not, add to the set; if so, log it as a duplicate 1037 | std::map::iterator iter = vertEdgeMap.find(otherVert); 1038 | if(iter != vertEdgeMap.end()) 1039 | duplicateEdges.push_back(std::make_pair(edgeInd,(*iter).second)); 1040 | else 1041 | vertEdgeMap[otherVert] = edgeInd; 1042 | } 1043 | 1044 | 1045 | //now replace the duplicate edges with their partner everywhere they're used (relabelling again) 1046 | for(unsigned int i = 0; i < duplicateEdges.size(); ++i) { 1047 | EdgeHandle e0(duplicateEdges[i].first); 1048 | EdgeHandle e1(duplicateEdges[i].second); 1049 | 1050 | //if the edges pointed in opposite directions, their directions in faces need to be swapped during the relabelling 1051 | int flipSign = fromVertex(e0) != fromVertex(e1) ? -1 : 1; 1052 | 1053 | //let's choose to remove e1 arbitrarily. 1054 | 1055 | //collect all faces that use this edge 1056 | std::vector< std::pair > faceIndices; 1057 | for(unsigned int f = 0; f < m_EF.getNumEntriesInRow(e1.idx()); ++f) { 1058 | unsigned int faceInd = m_EF.getColByIndex(e1.idx(), f); 1059 | int sign = m_EF.getValueByIndex(e1.idx(), f); 1060 | faceIndices.push_back(std::make_pair(faceInd,sign)); 1061 | } 1062 | 1063 | //relabel all the faces' to-be-deleted edge to the other duplicate edge being kept. 1064 | //doing it "in place" like this rather than using safer atomic add/deletes 1065 | //ensures that the original data on the retained edges gets maintained. 1066 | for(unsigned int i = 0; i < faceIndices.size(); ++i) { 1067 | unsigned int faceInd = faceIndices[i].first; 1068 | int edgeSign = faceIndices[i].second; 1069 | 1070 | int newSign = flipSign*edgeSign; 1071 | m_EF.remove(e1.idx(), faceInd); 1072 | m_FE.remove(faceInd, e1.idx()); 1073 | 1074 | m_EF.set(e0.idx(), faceInd, newSign); 1075 | m_FE.set(faceInd, e0.idx(), newSign); 1076 | } 1077 | 1078 | //finally, delete the orphaned edge 1079 | bool success = deleteEdge(e1, false); 1080 | assert(success); 1081 | } 1082 | 1083 | success = deleteVertex(vertToRemove); 1084 | assert(success); 1085 | 1086 | return VertexHandle(vertToKeep); 1087 | } 1088 | 1089 | //Two silly utility functions 1090 | VertexHandle getSharedVertexFromEdgePair(const SimplicialComplex& obj, const EdgeHandle& e0, const EdgeHandle& e1) { 1091 | VertexHandle v0 = obj.fromVertex(e0), v1 = obj.toVertex(e0), 1092 | v2 = obj.fromVertex(e1), v3 = obj.toVertex(e1); 1093 | 1094 | if(v0 == v2 || v0 == v3) return v0; 1095 | else if(v1 == v2 || v1 == v3) return v1; 1096 | else return VertexHandle::invalid(); 1097 | } 1098 | 1099 | EdgeHandle getEdgeFromVertexPair( const SimplicialComplex& obj, const VertexHandle& v0, const VertexHandle& v1 ) { 1100 | for(VertexEdgeIterator ve_it(obj, v0); !ve_it.done(); ve_it.advance()) { 1101 | EdgeHandle eh = ve_it.current(); 1102 | if(obj.fromVertex(eh) == v1 || obj.toVertex(eh) == v1) 1103 | return eh; 1104 | } 1105 | return EdgeHandle::invalid(); 1106 | } 1107 | 1108 | VertexHandle SimplicialComplex::splitEdge(const EdgeHandle& splitEdge, std::vector& newFaces) { 1109 | 1110 | EdgeFaceIterator ef_iter(*this, splitEdge); 1111 | 1112 | newFaces.clear(); 1113 | 1114 | //get the edge's vertices 1115 | VertexHandle from_vh, to_vh; 1116 | from_vh = fromVertex(splitEdge); 1117 | to_vh = toVertex(splitEdge); 1118 | 1119 | //add a new midpoint vertex 1120 | VertexHandle newVert = addVertex(); 1121 | 1122 | //add the two new edges that are part of the original split edge 1123 | EdgeHandle e_0 = addEdge(from_vh, newVert); 1124 | EdgeHandle e_1 = addEdge(to_vh, newVert); 1125 | 1126 | //now iterate over the existing faces, splitting them in two appropriately 1127 | std::vector facesToDelete; 1128 | EdgeFaceIterator ef_it(*this, splitEdge); 1129 | for(;!ef_it.done(); ef_it.advance()) { 1130 | FaceHandle fh = ef_it.current(); 1131 | 1132 | //store this for deletion later. 1133 | facesToDelete.push_back(fh); 1134 | 1135 | //find the other vertex in the first face 1136 | FaceVertexIterator fv_it(*this, fh); 1137 | while((fv_it.current() == from_vh) || (fv_it.current() == to_vh)) fv_it.advance(); 1138 | VertexHandle other_vh = fv_it.current(); 1139 | 1140 | //create the new edge that splits this face 1141 | EdgeHandle e_faceSplit = addEdge(other_vh, newVert); 1142 | 1143 | //build the two new faces 1144 | FaceEdgeIterator fe_it(*this, fh); 1145 | for(;!fe_it.done(); fe_it.advance()) { 1146 | EdgeHandle cur = fe_it.current(); 1147 | 1148 | //for each edge that isn't the splitEdge... 1149 | if(cur == splitEdge) continue; 1150 | 1151 | //accumulate a list of the new edges for the associated new face. 1152 | //it is done in this manner to ensure new orientation is consistent with the old 1153 | std::vector edgeList; 1154 | 1155 | //determine which half of the splitEdge to use for this new face 1156 | EdgeHandle halfEdge = fromVertex(cur) == from_vh || toVertex(cur) == from_vh? e_0 : e_1; 1157 | 1158 | FaceEdgeIterator fe_it2(*this, fh); 1159 | for(;!fe_it2.done(); fe_it2.advance()) { 1160 | EdgeHandle cur2 = fe_it2.current(); 1161 | if(cur2 == cur) edgeList.push_back(cur); //the current edge 1162 | else if(cur2 == splitEdge) edgeList.push_back(halfEdge); //half of the original split edge 1163 | else edgeList.push_back(e_faceSplit); //the new edge that splits the face 1164 | } 1165 | FaceHandle newFace = addFace(edgeList[0], edgeList[1], edgeList[2]); 1166 | newFaces.push_back(newFace); 1167 | } 1168 | 1169 | } 1170 | 1171 | //Delete the previous faces and edge. Done as a post-process so as not to mess up the iterators. 1172 | for(unsigned int i = 0; i < facesToDelete.size(); ++i) 1173 | deleteFace(facesToDelete[i], false); 1174 | deleteEdge(splitEdge, false); 1175 | 1176 | //Return a handle to the vertex we created 1177 | return newVert; 1178 | } 1179 | 1180 | EdgeHandle SimplicialComplex::flipEdge(const EdgeHandle& eh) { 1181 | assert(edgeExists(eh)); 1182 | 1183 | VertexHandle from_vh, to_vh; 1184 | from_vh = fromVertex(eh); 1185 | to_vh = toVertex(eh); 1186 | 1187 | assert(from_vh != to_vh); 1188 | 1189 | //Just use the first two faces we hit. 1190 | EdgeFaceIterator ef_it(*this, eh); 1191 | if(ef_it.done()) return EdgeHandle::invalid(); //if there is no face at all, can't flip 1192 | const FaceHandle fh = ef_it.current(); 1193 | ef_it.advance(); 1194 | if(ef_it.done()) return EdgeHandle::invalid(); //there is no 2nd face, we also can't flip 1195 | const FaceHandle fh2 = ef_it.current(); 1196 | ef_it.advance(); 1197 | assert(fh != fh2); //we should never hit the same face 1198 | assert(ef_it.done()); //this edge should have no more faces. A non-manifold edge flip doesn't make sense. 1199 | 1200 | //find the 3rd vertex in the first face 1201 | FaceVertexIterator fv_it(*this, fh); 1202 | while((fv_it.current() == from_vh) || (fv_it.current() == to_vh)) fv_it.advance(); 1203 | VertexHandle f1_vh = fv_it.current(); 1204 | 1205 | //find the 3rd vertex in the second face 1206 | FaceVertexIterator fv_it2(*this, fh2); 1207 | while((fv_it2.current() == from_vh) || (fv_it2.current() == to_vh)) fv_it2.advance(); 1208 | VertexHandle f2_vh = fv_it2.current(); 1209 | 1210 | assert(f1_vh != f2_vh); 1211 | 1212 | assert(from_vh != f1_vh); 1213 | assert(from_vh != f2_vh); 1214 | assert(to_vh != f1_vh); 1215 | assert(to_vh != f2_vh); 1216 | 1217 | //check for an edge already matching this description... and don't do the flip. 1218 | EdgeHandle edge = getEdgeFromVertexPair(*this, f1_vh, f2_vh); 1219 | if(edge.isValid()) return EdgeHandle::invalid(); 1220 | 1221 | EdgeHandle newEdge = addEdge(f1_vh, f2_vh); 1222 | 1223 | //grab all the current edges in proper order, starting from the shared face 1224 | EdgeHandle e0 = nextEdge(fh, eh); 1225 | EdgeHandle e1 = nextEdge(fh, e0); 1226 | 1227 | EdgeHandle e2 = nextEdge(fh2, eh); 1228 | EdgeHandle e3 = nextEdge(fh2, e2); 1229 | 1230 | assert(e0 != e1); 1231 | assert(e0 != e2); 1232 | assert(e0 != e3); 1233 | assert(e0 != eh); 1234 | assert(e0 != newEdge); 1235 | 1236 | assert(e1 != e2); 1237 | assert(e1 != e3); 1238 | assert(e1 != eh); 1239 | assert(e1 != newEdge); 1240 | 1241 | assert(e2 != e3); 1242 | assert(e2 != eh); 1243 | assert(e2 != newEdge); 1244 | 1245 | assert(e3 != eh); 1246 | assert(e3 != newEdge); 1247 | 1248 | assert(eh != newEdge); 1249 | 1250 | //flip the edges of the second face so it matches the first face 1251 | //(if the original faces didn't sync, it doesn't matter, since there's no way to flip and maintain consistency) 1252 | VertexHandle sharedV = getSharedVertexFromEdgePair(*this, e1, e2); 1253 | if(!sharedV.isValid()) 1254 | std::swap(e2,e3); 1255 | 1256 | //add in the new faces 1257 | addFace(e1, e2, newEdge); 1258 | addFace(e3, e0, newEdge); 1259 | 1260 | //Delete the old patch 1261 | bool success = deleteFace(fh, false); 1262 | assert(success); 1263 | success = deleteFace(fh2, false); 1264 | assert(success); 1265 | success = deleteEdge(eh, false); 1266 | assert(success); 1267 | 1268 | return newEdge; 1269 | } 1270 | 1271 | 1272 | 1273 | void SimplicialComplex::registerVertexProperty(SimplexPropertyBase* prop) { 1274 | m_vertProperties.push_back(prop); 1275 | prop->resize(numVertexSlots()); 1276 | } 1277 | void SimplicialComplex::removeVertexProperty(SimplexPropertyBase* prop) { 1278 | std::vector::iterator it = std::find(m_vertProperties.begin(), m_vertProperties.end(), prop); 1279 | if(it != m_vertProperties.end()) 1280 | m_vertProperties.erase(it); 1281 | } 1282 | 1283 | 1284 | void SimplicialComplex::registerEdgeProperty(SimplexPropertyBase* prop) { 1285 | m_edgeProperties.push_back(prop); 1286 | prop->resize(numEdgeSlots()); 1287 | } 1288 | void SimplicialComplex::removeEdgeProperty(SimplexPropertyBase* prop) { 1289 | std::vector::iterator it = std::find(m_edgeProperties.begin(), m_edgeProperties.end(), prop); 1290 | if(it != m_edgeProperties.end()) 1291 | m_edgeProperties.erase(it); 1292 | } 1293 | 1294 | 1295 | void SimplicialComplex::registerFaceProperty(SimplexPropertyBase* prop) { 1296 | m_faceProperties.push_back(prop); 1297 | prop->resize(numFaceSlots()); 1298 | } 1299 | void SimplicialComplex::removeFaceProperty(SimplexPropertyBase* prop) { 1300 | std::vector::iterator it = std::find(m_faceProperties.begin(), m_faceProperties.end(), prop); 1301 | if(it != m_faceProperties.end()) 1302 | m_faceProperties.erase(it); 1303 | } 1304 | 1305 | 1306 | void SimplicialComplex::registerTetProperty(SimplexPropertyBase* prop) { 1307 | m_tetProperties.push_back(prop); 1308 | prop->resize(numTetSlots()); 1309 | } 1310 | void SimplicialComplex::removeTetProperty(SimplexPropertyBase* prop) { 1311 | std::vector::iterator it = std::find(m_tetProperties.begin(), m_tetProperties.end(), prop); 1312 | if(it != m_tetProperties.end()) 1313 | m_tetProperties.erase(it); 1314 | } 1315 | 1316 | 1317 | 1318 | bool SimplicialComplex::isOnBoundary(const FaceHandle& fh) const { 1319 | int numTets = faceIncidentTetCount(fh); 1320 | 1321 | //in 3D, it is boundary if it has only one tet 1322 | //in 2D is is never on the boundary (is this true?) 1323 | //in 1D it cannot exist 1324 | return numTets == 1; 1325 | } 1326 | 1327 | bool SimplicialComplex::isOnBoundary(const EdgeHandle& eh) const { 1328 | 1329 | //Ignoring mixed dimensional points here... 1330 | 1331 | //it's boundary if at least one of the tets is a boundary tet 1332 | bool partOfAnyTets = false; 1333 | for(EdgeFaceIterator efit(*this, eh); !efit.done(); efit.advance()) { 1334 | FaceHandle fh = efit.current(); 1335 | int tets = faceIncidentTetCount(fh); 1336 | if(tets > 0) 1337 | partOfAnyTets = true; 1338 | if(tets == 1) 1339 | return true; //it's 1340 | } 1341 | 1342 | if(partOfAnyTets) //all the tets are connected, there's no boundary. 1343 | return false; 1344 | 1345 | //if it's part of a face, it's on the boundary if it has only 1 face 1346 | int numFaces = edgeIncidentFaceCount(eh); 1347 | if(numFaces > 0) 1348 | return numFaces == 1; 1349 | else //if it's just an edge, it's never part of the boundary 1350 | return false; 1351 | 1352 | } 1353 | 1354 | bool SimplicialComplex::isOnBoundary(const VertexHandle& vh) const { 1355 | 1356 | //ignore mixed dimension points! 1357 | 1358 | //3D 1359 | //if it's part of a tet, it's on the boundary if there are any faces without 2 tets 1360 | bool partOfAnyTets = false; 1361 | for(VertexEdgeIterator veit(*this, vh); !veit.done(); veit.advance()) { 1362 | EdgeHandle eh = veit.current(); 1363 | for(EdgeFaceIterator efit(*this, eh); !efit.done(); efit.advance()) { 1364 | FaceHandle fh = efit.current(); 1365 | int tets = faceIncidentTetCount(fh); 1366 | if(tets > 0) 1367 | partOfAnyTets = true; 1368 | if(tets == 1) 1369 | return true; 1370 | } 1371 | } 1372 | 1373 | if(partOfAnyTets) 1374 | return false; 1375 | 1376 | 1377 | //2D 1378 | //if it's part of a face, it's on the boundary if, the faces don't form a closed loop 1379 | bool partOfAnyFace = false; 1380 | for(VertexEdgeIterator veit(*this, vh); !veit.done(); veit.advance()) { 1381 | EdgeHandle eh = veit.current(); 1382 | int faces = edgeIncidentFaceCount(eh); 1383 | if(faces > 0) 1384 | partOfAnyFace = true; 1385 | if(faces == 1) 1386 | return true; 1387 | } 1388 | 1389 | if(partOfAnyFace) 1390 | return false; 1391 | 1392 | //1D 1393 | int numEdges = vertexIncidentEdgeCount(vh); 1394 | if(numEdges > 0) //it's on the boundary if there's only 1 outgoing edge 1395 | return numEdges == 1; 1396 | else //otherwise it's an isolated vertex, which we define as not being on the boundary 1397 | return false; 1398 | } 1399 | 1400 | 1401 | bool SimplicialComplex::isManifold(const FaceHandle& fh) const { 1402 | //in a 3D scenario, it's manifold if it belongs to one or two tets 1403 | //in a 2D scenario, it is guaranteed to be manifold 1404 | int numTets = faceIncidentTetCount(fh); 1405 | return numTets < 3; 1406 | } 1407 | 1408 | bool SimplicialComplex::isManifold(const EdgeHandle& eh) const { 1409 | 1410 | //check if we're part of any tetrahedra, looking for stray faces or non-manifold faces 1411 | bool partOfAnyTets = false; 1412 | bool freeFace = false; 1413 | 1414 | FaceHandle boundaryFace; 1415 | std::set faceSet; 1416 | 1417 | for(EdgeFaceIterator efit(*this, eh); !efit.done(); efit.advance()) { 1418 | FaceHandle fh = efit.current(); 1419 | faceSet.insert(fh); 1420 | 1421 | int tets = faceIncidentTetCount(fh); 1422 | if(tets == 0) { 1423 | freeFace = true; 1424 | } 1425 | else if(tets == 1 || tets == 2) { 1426 | partOfAnyTets = true; 1427 | if(tets == 1) 1428 | boundaryFace = fh; //save the boundary face for later 1429 | } 1430 | else if (tets > 2) { //an edge is non-manifold if it has a non-manifold face 1431 | return false; 1432 | } 1433 | } 1434 | 1435 | if(partOfAnyTets) { 1436 | if(freeFace) { 1437 | return false; 1438 | } 1439 | else { 1440 | //if we have only tets, then the test is whether we can walk around the edge, via tet-face connections 1441 | //and in doing so visit all the faces. Because of the check above, guaranteed no non-manifold faces, 1442 | //so there are definitely no branches in our walk. 1443 | 1444 | std::set unvisitedSet = faceSet; 1445 | 1446 | //start from one boundary if it exists, otherwise start anywhere, since it should be a closed loop 1447 | FaceHandle startFace = boundaryFace.isValid() ? boundaryFace : *unvisitedSet.begin(); 1448 | unvisitedSet.erase(startFace); 1449 | 1450 | //try to walk around the edge, via tets, either to the other boundary or back to the start again, 1451 | //and see if we visit all the faces. if not, we know there are (at least) two disconnected components. 1452 | FaceHandle prevFace = startFace; 1453 | 1454 | TetHandle prevTet = TetHandle::invalid(); 1455 | do { 1456 | 1457 | //Get the next tet connected to this face. 1458 | FaceTetIterator ftit(*this, prevFace); 1459 | while(ftit.current() == prevTet && !ftit.done()) 1460 | ftit.advance(); 1461 | if(ftit.done()) break; //ran out of tets, dead end 1462 | 1463 | //find the next face of the tet that is part of the faceSet (and is not the preceding one) 1464 | TetHandle curTet = ftit.current(); 1465 | TetFaceIterator tfit(*this, curTet); 1466 | while(!tfit.done() && (tfit.current() == prevFace || faceSet.find(tfit.current()) == faceSet.end())) 1467 | tfit.advance(); 1468 | 1469 | assert(!tfit.done()); 1470 | 1471 | //walk to the next face, and mark this one visited 1472 | prevFace = tfit.current(); 1473 | prevTet = curTet; 1474 | unvisitedSet.erase(prevFace); 1475 | 1476 | } while(prevFace != startFace && unvisitedSet.size() > 0); 1477 | 1478 | //if our cycle around the edges's faces left no face unvisited, then we're manifold 1479 | return unvisitedSet.size() == 0; 1480 | } 1481 | } 1482 | else { 1483 | //dimension 2/1/0 1484 | int numFaces = edgeIncidentFaceCount(eh); 1485 | return numFaces < 3; 1486 | } 1487 | 1488 | } 1489 | 1490 | bool SimplicialComplex::isManifold(const VertexHandle& vh) const { 1491 | 1492 | //dimension 3 1493 | //the surrounding boundary tets form a set that is fully connected via faces 1494 | //and there are no stray faces or edges 1495 | bool partOfAnyTets = false; 1496 | bool freeFace = false; 1497 | bool freeEdge = false; 1498 | std::set boundaryFaces; 1499 | for(VertexEdgeIterator veit(*this, vh); !veit.done(); veit.advance()) { 1500 | EdgeHandle eh = veit.current(); 1501 | 1502 | //check for solo edges 1503 | if(edgeIncidentFaceCount(eh) == 0) { 1504 | freeEdge = true; 1505 | } 1506 | 1507 | for(EdgeFaceIterator efit(*this, eh); !efit.done(); efit.advance()) { //this will visit some faces multiple times, no biggie. 1508 | FaceHandle fh = efit.current(); 1509 | int tets = faceIncidentTetCount(fh); 1510 | if(tets > 0) { //we have encountered at least one tet, so we know we're in 3D mode. 1511 | partOfAnyTets = true; 1512 | } 1513 | if(tets == 0) { //check for solo faces 1514 | freeFace = true; 1515 | } 1516 | } 1517 | } 1518 | 1519 | if(partOfAnyTets) { 1520 | if(freeFace || freeEdge) { 1521 | return false; 1522 | } 1523 | else { 1524 | //test if all faces touching the vertex can be reached by starting at one faces and only walking across tets 1525 | 1526 | std::set boundaryFaces; 1527 | 1528 | //collect all the faces, and boundaryfaces. 1529 | std::set faceSet; 1530 | for(VertexEdgeIterator veit(*this, vh); !veit.done(); veit.advance()) { 1531 | EdgeHandle eh = veit.current(); 1532 | for(EdgeFaceIterator efit(*this, eh); !efit.done(); efit.advance()) { 1533 | FaceHandle fh = efit.current(); 1534 | faceSet.insert(fh); 1535 | if(faceIncidentTetCount(fh) == 1) { 1536 | boundaryFaces.insert(fh); 1537 | } 1538 | } 1539 | } 1540 | 1541 | std::set unvisitedFaces = faceSet; 1542 | //now do an exhaustive search across faces to see if we can reach them all. 1543 | std::queue facesToVisit; 1544 | facesToVisit.push(*(faceSet.begin())); //push the first tet, whatever it may be 1545 | while(facesToVisit.size() > 0) { 1546 | FaceHandle curFace = facesToVisit.front(); 1547 | facesToVisit.pop(); 1548 | if(unvisitedFaces.find(curFace) == unvisitedFaces.end()) continue; //already dealt with this one, so skip it 1549 | unvisitedFaces.erase(curFace); 1550 | 1551 | //look at all neighbouring faces for unvisited ones 1552 | for(FaceTetIterator ftit(*this, curFace); !ftit.done(); ftit.advance()) { 1553 | TetHandle curTet = ftit.current(); 1554 | for(TetFaceIterator tfit(*this, curTet); !tfit.done(); tfit.advance()) { 1555 | FaceHandle nbrFace = tfit.current(); 1556 | if(faceSet.find(nbrFace) == faceSet.end() || nbrFace == curFace) continue; //skip irrelevant faces 1557 | 1558 | //if we found an unvisited face in the ring, push it 1559 | if(unvisitedFaces.find(nbrFace) != unvisitedFaces.end()) 1560 | facesToVisit.push(nbrFace); 1561 | } 1562 | } 1563 | } 1564 | bool allFacesReachable = (unvisitedFaces.size() == 0); 1565 | 1566 | //Also need to check for the case where there are tets all the way around, EXCEPT 1567 | //for two spots that ony meet at a vertex, (or a single edge?). right? 1568 | 1569 | 1570 | std::set unvisitedSet = boundaryFaces; 1571 | if(boundaryFaces.size() > 0) { 1572 | //try to walk around the boundary, via faces/edges, back to the start again. We know it has to be a loop. 1573 | //see if we visit all the edges 1574 | EdgeHandle prevEdge_ = EdgeHandle::invalid(); 1575 | FaceHandle startFace = *boundaryFaces.begin(); 1576 | FaceHandle prevFace = startFace; 1577 | do { 1578 | //get the next edge that is connected to our relevant vertex 1579 | FaceEdgeIterator feit(*this, prevFace, false); 1580 | while((feit.current() == prevEdge_ || !isIncident(vh, feit.current())) && !feit.done() ) 1581 | feit.advance(); 1582 | 1583 | EdgeHandle curEdge = feit.current(); 1584 | 1585 | //count how many boundary faces this edge is connected to. if more than 2, we're in non-manifold scenario 1586 | int boundaryFaceCount = 0; 1587 | for(EdgeFaceIterator efit(*this, curEdge); !efit.done(); efit.advance()) { 1588 | if(boundaryFaces.find(efit.current()) != boundaryFaces.end()) 1589 | ++boundaryFaceCount; 1590 | } 1591 | 1592 | if(boundaryFaceCount > 2) //this is non-manifold connection between two tets, via an edge 1593 | return false; 1594 | assert(boundaryFaceCount == 2); 1595 | 1596 | //find the next face that is part of the faceSet, but is not the preceding one 1597 | EdgeFaceIterator efit(*this, curEdge); 1598 | while(!efit.done() && (efit.current() == prevFace || boundaryFaces.find(efit.current()) == boundaryFaces.end())) 1599 | efit.advance(); 1600 | 1601 | assert(!feit.done()); 1602 | 1603 | //move to next edge 1604 | prevFace = efit.current(); 1605 | prevEdge_ = curEdge; 1606 | unvisitedSet.erase(prevFace); 1607 | } while(prevFace != startFace && unvisitedSet.size() > 0); 1608 | } 1609 | //if our linear cycle around the vertex's boundary faces left no boundary face unvisited, then we're manifold 1610 | bool boundaryConnected = (unvisitedSet.size() == 0); 1611 | 1612 | return allFacesReachable && boundaryConnected; //if we successfully walked over all the faces via tets, then it has to be manifold 1613 | 1614 | } 1615 | } 1616 | 1617 | //dimension 2 1618 | //need to check if all adjacent faces form a *single* closed loop (or half-loop) 1619 | bool partOfAnyFaces = false; 1620 | std::set edgeSet; 1621 | EdgeHandle boundaryEdge; 1622 | for(VertexEdgeIterator veit(*this, vh); !veit.done(); veit.advance()) { 1623 | EdgeHandle eh = veit.current(); 1624 | edgeSet.insert(eh); 1625 | 1626 | int faces = edgeIncidentFaceCount(eh); 1627 | 1628 | if(faces > 0) //we know we're in 2D 1629 | partOfAnyFaces = true; 1630 | 1631 | if(faces == 1) //we have found one end to the potential series of faces around the vertex 1632 | boundaryEdge = eh; 1633 | 1634 | if(faces == 3) //if there is a non-manifold edge, then we know the vertex is non-manifold too. 1635 | return false; 1636 | 1637 | } 1638 | 1639 | if(partOfAnyFaces && freeEdge) { //edge is part of at least one face, so do the dimension 2 case 1640 | return false; 1641 | } 1642 | else if(partOfAnyFaces) { //test if we have a single closed loop 1643 | 1644 | std::set unvisitedSet = edgeSet; 1645 | 1646 | EdgeHandle startEdge = boundaryEdge.isValid() ? boundaryEdge : *unvisitedSet.begin(); 1647 | unvisitedSet.erase(startEdge); 1648 | 1649 | //try to walk around the vertex, via faces, either to a dead end or back to the start again, and see if we visit all the edges 1650 | EdgeHandle prevEdge_ = startEdge; 1651 | FaceHandle prevFace = FaceHandle::invalid(); 1652 | do { 1653 | EdgeFaceIterator efit(*this, prevEdge_); 1654 | while(efit.current() == prevFace && !efit.done()) 1655 | efit.advance(); 1656 | if(efit.done()) break; //ran out of faces, dead end 1657 | 1658 | FaceHandle curFace = efit.current(); //find the next edge that is part of the edgeSet, but not the preceding one 1659 | FaceEdgeIterator feit(*this, curFace, false); 1660 | while(!feit.done() && (feit.current() == prevEdge_ || edgeSet.find(feit.current()) == edgeSet.end())) 1661 | feit.advance(); 1662 | 1663 | assert(!feit.done()); 1664 | prevEdge_ = feit.current(); //move to next edge 1665 | prevFace = curFace; 1666 | 1667 | unvisitedSet.erase(prevEdge_); 1668 | 1669 | } while(prevEdge_ != startEdge && unvisitedSet.size() > 0); 1670 | 1671 | //if our cycle around the vertex's edges left no edge unvisited, then we're manifold 1672 | return unvisitedSet.size() == 0; 1673 | } 1674 | 1675 | //dimension 1/0 1676 | int numEdges = vertexIncidentEdgeCount(vh); 1677 | return numEdges < 3; 1678 | } 1679 | 1680 | 1681 | 1682 | 1683 | } //namespace SimplexMesh -------------------------------------------------------------------------------- /test/test.cpp: -------------------------------------------------------------------------------- 1 | #include "SimplicialComplex.h" 2 | 3 | #include 4 | 5 | using namespace SimplexMesh; 6 | 7 | bool test_constructTetAndIterateSimplices(); 8 | bool test_constructSimplicesFromVerts(); 9 | bool test_edgeDuplication(); 10 | bool test_faceDuplication(); 11 | bool test_faceCreationValid(); 12 | bool test_tetDuplication(); 13 | bool test_tetCreationValid(); 14 | bool test_vertexVertexIterator(); 15 | 16 | typedef bool (*test_func)(); 17 | 18 | const int test_count = 8; 19 | test_func tests[] = {test_constructSimplicesFromVerts, 20 | test_constructTetAndIterateSimplices, 21 | test_edgeDuplication, 22 | test_faceDuplication, 23 | test_faceCreationValid, 24 | test_tetDuplication, 25 | test_tetCreationValid, 26 | test_vertexVertexIterator}; 27 | 28 | 29 | void main() { 30 | int success_count = 0; 31 | for(int i = 0; i < test_count; ++i) { 32 | if( (*tests[i])() ) 33 | ++success_count; 34 | } 35 | 36 | std::cout << "Tests passed: " << success_count << " of " << test_count << std::endl; 37 | 38 | } 39 | 40 | bool test_constructTetAndIterateSimplices() { 41 | 42 | //construct one tetrahedron, from the ground up. 43 | //Iterate through its elements. 44 | int failures = 0; 45 | 46 | SimplicialComplex mesh; 47 | 48 | VertexHandle v0 = mesh.addVertex(); 49 | VertexHandle v1 = mesh.addVertex(); 50 | VertexHandle v2 = mesh.addVertex(); 51 | VertexHandle v3 = mesh.addVertex(); 52 | 53 | EdgeHandle e0 = mesh.addEdge(v0, v1); 54 | EdgeHandle e1 = mesh.addEdge(v0, v2); 55 | EdgeHandle e2 = mesh.addEdge(v0, v3); 56 | EdgeHandle e3 = mesh.addEdge(v1, v2); 57 | EdgeHandle e4 = mesh.addEdge(v1, v3); 58 | EdgeHandle e5 = mesh.addEdge(v2, v3); 59 | 60 | FaceHandle f0 = mesh.addFace(e0, e1, e3); 61 | FaceHandle f1 = mesh.addFace(e3, e4, e5); 62 | FaceHandle f2 = mesh.addFace(e0, e2, e4); 63 | FaceHandle f3 = mesh.addFace(e1, e2, e5); 64 | 65 | TetHandle t0 = mesh.addTet(f0,f1,f2,f3); 66 | 67 | //Assign data. 68 | VertexProperty vertexID(mesh); 69 | vertexID[v0] = 0; 70 | vertexID[v1] = 1; 71 | vertexID[v2] = 2; 72 | vertexID[v3] = 3; 73 | 74 | EdgeProperty edgeID(mesh); 75 | edgeID[e0] = 0; 76 | edgeID[e1] = 1; 77 | edgeID[e2] = 2; 78 | edgeID[e3] = 3; 79 | edgeID[e4] = 4; 80 | edgeID[e5] = 5; 81 | 82 | FaceProperty faceID(mesh); 83 | faceID[f0] = 0; 84 | faceID[f1] = 1; 85 | faceID[f2] = 2; 86 | faceID[f3] = 3; 87 | 88 | TetProperty tetID(mesh); 89 | tetID[t0] = 0; 90 | 91 | int i = 0; 92 | for(VertexIterator it(mesh); !it.done(); it.advance()) { 93 | VertexHandle cur = it.current(); 94 | if(vertexID[cur] != i) ++failures; 95 | ++i; 96 | } 97 | 98 | i = 0; 99 | for(EdgeIterator it(mesh); !it.done(); it.advance()) { 100 | EdgeHandle cur = it.current(); 101 | if(edgeID[cur] != i) ++failures; 102 | ++i; 103 | } 104 | 105 | i = 0; 106 | for(FaceIterator it(mesh); !it.done(); it.advance()) { 107 | FaceHandle cur = it.current(); 108 | if(faceID[cur] != i) ++failures; 109 | ++i; 110 | } 111 | 112 | i = 0; 113 | for(TetIterator it(mesh); !it.done(); it.advance()) { 114 | TetHandle cur = it.current(); 115 | if(tetID[cur] != i) ++failures; 116 | ++i; 117 | } 118 | 119 | return failures == 0; 120 | } 121 | 122 | bool test_constructSimplicesFromVerts() { 123 | 124 | //Try adding faces directly from vertices 125 | SimplicialComplex mesh; 126 | 127 | VertexHandle v0 = mesh.addVertex(); 128 | VertexHandle v1 = mesh.addVertex(); 129 | VertexHandle v2 = mesh.addVertex(); 130 | VertexHandle v3 = mesh.addVertex(); 131 | 132 | mesh.addFace(v0, v1, v2); 133 | mesh.addFace(v0, v1, v3); 134 | mesh.addFace(v0, v2, v3); 135 | mesh.addFace(v1, v2, v3); 136 | 137 | //Try adding a tet directly from vertices 138 | SimplicialComplex mesh2; 139 | 140 | v0 = mesh2.addVertex(); 141 | v1 = mesh2.addVertex(); 142 | v2 = mesh2.addVertex(); 143 | v3 = mesh2.addVertex(); 144 | 145 | TetHandle th = mesh.addTet(v0, v1, v2, v3); 146 | 147 | return true; 148 | } 149 | 150 | bool test_edgeDuplication() { 151 | SimplicialComplex mesh; 152 | 153 | mesh.setSafeMode(true); 154 | 155 | VertexHandle v0 = mesh.addVertex(); 156 | VertexHandle v1 = mesh.addVertex(); 157 | VertexHandle v2 = mesh.addVertex(); 158 | 159 | EdgeHandle edge0 = mesh.addEdge(v0, v1); 160 | EdgeHandle edge1 = mesh.addEdge(v0, v1); 161 | 162 | return edge0.isValid() && !edge1.isValid(); 163 | } 164 | 165 | bool test_faceDuplication() { 166 | SimplicialComplex mesh; 167 | 168 | mesh.setSafeMode(true); 169 | 170 | VertexHandle v0 = mesh.addVertex(); 171 | VertexHandle v1 = mesh.addVertex(); 172 | VertexHandle v2 = mesh.addVertex(); 173 | 174 | FaceHandle face0 = mesh.addFace(v0, v1, v2); 175 | FaceHandle face1 = mesh.addFace(v0, v1, v2); 176 | FaceHandle face2 = mesh.addFace(v0, v2, v1); 177 | 178 | return face0.isValid() && !face1.isValid() && !face1.isValid(); 179 | } 180 | 181 | 182 | bool test_faceCreationValid() { 183 | SimplicialComplex mesh; 184 | 185 | mesh.setSafeMode(true); 186 | 187 | VertexHandle v0 = mesh.addVertex(); 188 | VertexHandle v1 = mesh.addVertex(); 189 | VertexHandle v2 = mesh.addVertex(); 190 | VertexHandle v3 = mesh.addVertex(); 191 | 192 | EdgeHandle e0 = mesh.addEdge(v0, v1); 193 | EdgeHandle e1 = mesh.addEdge(v1, v2); 194 | EdgeHandle e2 = mesh.addEdge(v0, v2); 195 | EdgeHandle e3 = mesh.addEdge(v0, v3); 196 | 197 | FaceHandle face0 = mesh.addFace(e0, e1, e2); //good face 198 | FaceHandle face1 = mesh.addFace(e0, e1, e3); //bad face 199 | 200 | return face0.isValid() && !face1.isValid(); 201 | } 202 | 203 | bool test_tetDuplication() { 204 | SimplicialComplex mesh; 205 | 206 | mesh.setSafeMode(true); 207 | 208 | VertexHandle v0 = mesh.addVertex(); 209 | VertexHandle v1 = mesh.addVertex(); 210 | VertexHandle v2 = mesh.addVertex(); 211 | VertexHandle v3 = mesh.addVertex(); 212 | 213 | TetHandle tet0 = mesh.addTet(v0, v1, v2, v3); 214 | TetHandle tet1 = mesh.addTet(v0, v1, v2, v3); 215 | TetHandle tet2 = mesh.addTet(v0, v1, v3, v2); 216 | 217 | return tet0.isValid() && !tet1.isValid() && !tet2.isValid(); 218 | } 219 | 220 | bool test_tetCreationValid() { 221 | SimplicialComplex mesh; 222 | 223 | mesh.setSafeMode(true); 224 | 225 | VertexHandle v0 = mesh.addVertex(); 226 | VertexHandle v1 = mesh.addVertex(); 227 | VertexHandle v2 = mesh.addVertex(); 228 | VertexHandle v3 = mesh.addVertex(); 229 | VertexHandle v4 = mesh.addVertex(); 230 | 231 | FaceHandle f0 = mesh.addFace(v0,v1,v2); 232 | FaceHandle f1 = mesh.addFace(v0,v2,v3); 233 | FaceHandle f2 = mesh.addFace(v0,v1,v3); 234 | FaceHandle f3 = mesh.addFace(v1,v2,v3); 235 | FaceHandle f4 = mesh.addFace(v0, v2, v4); 236 | 237 | TetHandle t0 = mesh.addTet(f0,f1,f2,f3); 238 | TetHandle t1 = mesh.addTet(f0,f1,f2,f4); 239 | TetHandle t2 = mesh.addTet(f0,f1,f3,f4); 240 | 241 | return t0.isValid() && !t1.isValid() && !t2.isValid(); 242 | } 243 | 244 | bool test_vertexVertexIterator() { 245 | SimplicialComplex mesh; 246 | 247 | mesh.setSafeMode(true); 248 | 249 | VertexHandle v0 = mesh.addVertex(); 250 | VertexHandle v1 = mesh.addVertex(); 251 | VertexHandle v2 = mesh.addVertex(); 252 | VertexHandle v3 = mesh.addVertex(); 253 | VertexHandle v4 = mesh.addVertex(); 254 | 255 | FaceHandle f0 = mesh.addFace(v0,v1,v2); 256 | FaceHandle f1 = mesh.addFace(v0,v2,v3); 257 | FaceHandle f2 = mesh.addFace(v0,v1,v3); 258 | FaceHandle f3 = mesh.addFace(v1,v2,v3); 259 | FaceHandle f4 = mesh.addFace(v0,v2,v4); 260 | 261 | int count = 0; 262 | for(VertexVertexIterator vit(mesh, v0); !vit.done(); vit.advance()) { 263 | VertexHandle vh = vit.current(); 264 | ++count; 265 | } 266 | if(count != 4) 267 | return false; 268 | 269 | count = 0; 270 | for(VertexVertexIterator vit(mesh, v4); !vit.done(); vit.advance()) { 271 | VertexHandle vh = vit.current(); 272 | ++count; 273 | } 274 | if(count != 2) 275 | return false; 276 | 277 | count = 0; 278 | for(VertexVertexIterator vit(mesh, v3); !vit.done(); vit.advance()) { 279 | VertexHandle vh = vit.current(); 280 | ++count; 281 | } 282 | if(count != 3) 283 | return false; 284 | 285 | return true; 286 | } --------------------------------------------------------------------------------