DGtal 1.3.0
Loading...
Searching...
No Matches
Functions
testSurfaceMesh.cpp File Reference
#include <iostream>
#include <sstream>
#include <algorithm>
#include "DGtal/base/Common.h"
#include "ConfigTest.h"
#include "DGtalCatch.h"
#include "DGtal/helpers/StdDefs.h"
#include "DGtal/kernel/PointVector.h"
#include "DGtal/graph/CUndirectedSimpleGraph.h"
#include "DGtal/graph/BreadthFirstVisitor.h"
#include "DGtal/shapes/SurfaceMesh.h"
#include "DGtal/shapes/SurfaceMeshHelper.h"
#include "DGtal/io/readers/SurfaceMeshReader.h"
#include "DGtal/io/writers/SurfaceMeshWriter.h"

Go to the source code of this file.

Functions

SurfaceMesh< PointVector< 3, double >, PointVector< 3, double > > makeBox ()
 
SurfaceMesh< PointVector< 3, double >, PointVector< 3, double > > makeNonManifoldBoundary ()
 
 SCENARIO ("SurfaceMesh< RealPoint3 > concept check tests", "[surfmesh][concepts]")
 
 SCENARIO ("SurfaceMesh< RealPoint3 > build tests", "[surfmesh][build]")
 
 SCENARIO ("SurfaceMesh< RealPoint3 > mesh helper tests", "[surfmesh][helper]")
 
 SCENARIO ("SurfaceMesh< RealPoint3 > reader/writer tests", "[surfmesh][io]")
 
 SCENARIO ("SurfaceMesh< RealPoint3 > boundary tests", "[surfmesh][boundary]")
 

Detailed Description

This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Author
Jacques-Olivier Lachaud (jacqu.nosp@m.es-o.nosp@m.livie.nosp@m.r.la.nosp@m.chaud.nosp@m.@uni.nosp@m.v-sav.nosp@m.oie..nosp@m.fr ) Laboratory of Mathematics (CNRS, UMR 5127), University of Savoie, France
Date
2020/07/12

Functions for testing class SurfaceMesh.

This file is part of the DGtal library.

Definition in file testSurfaceMesh.cpp.

Function Documentation

◆ makeBox()

SurfaceMesh< PointVector< 3, double >, PointVector< 3, double > > makeBox ( )

Definition at line 57 of file testSurfaceMesh.cpp.

58{
61 typedef SurfaceMesh< RealPoint, RealVector > PolygonMesh;
62 typedef PolygonMesh::Vertices Vertices;
63 std::vector< RealPoint > positions;
64 std::vector< Vertices > faces;
65 positions.push_back( RealPoint( 0, 0, 0 ) );
66 positions.push_back( RealPoint( 1, 0, 0 ) );
67 positions.push_back( RealPoint( 0, 1, 0 ) );
68 positions.push_back( RealPoint( 1, 1, 0 ) );
69 positions.push_back( RealPoint( 0, 0, 1 ) );
70 positions.push_back( RealPoint( 1, 0, 1 ) );
71 positions.push_back( RealPoint( 0, 1, 1 ) );
72 positions.push_back( RealPoint( 1, 1, 1 ) );
73 positions.push_back( RealPoint( 1, 0, 2 ) );
74 positions.push_back( RealPoint( 0, 0, 2 ) );
75 faces.push_back( { 1, 0, 2, 3 } );
76 faces.push_back( { 0, 1, 5, 4 } );
77 faces.push_back( { 1, 3, 7, 5 } );
78 faces.push_back( { 3, 2, 6, 7 } );
79 faces.push_back( { 2, 0, 4, 6 } );
80 faces.push_back( { 4, 5, 8, 9 } );
81 return PolygonMesh( positions.cbegin(), positions.cend(),
82 faces.cbegin(), faces.cend() );
83}
Aim: Implements basic operations that will be used in Point and Vector classes.
Definition: PointVector.h:593
SMesh::Vertices Vertices
Aim: Represents an embedded mesh as faces and a list of vertices. Vertices may be shared among faces ...
Definition: SurfaceMesh.h:92
Z2i::RealPoint RealPoint

Referenced by SCENARIO().

◆ makeNonManifoldBoundary()

SurfaceMesh< PointVector< 3, double >, PointVector< 3, double > > makeNonManifoldBoundary ( )

Definition at line 87 of file testSurfaceMesh.cpp.

88{
91 typedef SurfaceMesh< RealPoint, RealVector > PolygonMesh;
92 typedef PolygonMesh::Vertices Vertices;
93 std::vector< RealPoint > positions;
94 std::vector< Vertices > faces;
95 positions.push_back( RealPoint( 0, 0, 1 ) );
96 positions.push_back( RealPoint( 0, -1, 0 ) );
97 positions.push_back( RealPoint( 1, 0, 0 ) );
98 positions.push_back( RealPoint( 0, 1, 0 ) );
99 positions.push_back( RealPoint( 0, 0, 0 ) );
100 faces.push_back( { 0, 4, 1 } );
101 faces.push_back( { 0, 4, 2 } );
102 faces.push_back( { 0, 4, 3 } );
103 return PolygonMesh( positions.cbegin(), positions.cend(),
104 faces.cbegin(), faces.cend() );
105}

Referenced by SCENARIO().

◆ SCENARIO() [1/5]

SCENARIO ( "SurfaceMesh< RealPoint3 > boundary tests"  ,
""  [surfmesh][boundary] 
)

Definition at line 338 of file testSurfaceMesh.cpp.

339{
342 typedef SurfaceMesh< RealPoint, RealVector > PolygonMesh;
343 auto polymesh = makeNonManifoldBoundary();
344 auto polymesh2 = makeBox();
345 WHEN( "Checking the topolopgy of the mesh boundary" ) {
346 auto chains = polymesh2.computeManifoldBoundaryChains();
347 THEN( "The box as a manifold boundary" ) {
348 CAPTURE(chains);
349 REQUIRE( polymesh2.isBoundariesManifold() == true);
350 REQUIRE( polymesh2.isBoundariesManifold(false) == true);
351 REQUIRE( chains.size() == 1);
352 REQUIRE( chains[0].size() == 6);
353 }
354 THEN( "The extra mesh does not have a manifold boundary" ) {
355 REQUIRE( polymesh.isBoundariesManifold() == false);
356 }
357 }
358}
CAPTURE(thicknessHV)
REQUIRE(domain.isInside(aPoint))
SurfaceMesh< PointVector< 3, double >, PointVector< 3, double > > makeBox()
SurfaceMesh< PointVector< 3, double >, PointVector< 3, double > > makeNonManifoldBoundary()

References CAPTURE(), makeBox(), makeNonManifoldBoundary(), and REQUIRE().

◆ SCENARIO() [2/5]

SCENARIO ( "SurfaceMesh< RealPoint3 > build tests"  ,
""  [surfmesh][build] 
)

Definition at line 115 of file testSurfaceMesh.cpp.

116{
119 typedef SurfaceMesh< RealPoint, RealVector > PolygonMesh;
120 typedef PolygonMesh::Vertices Vertices;
121 typedef PolygonMesh::Face Face;
122 typedef PolygonMesh::Edge Edge;
123 typedef PolygonMesh::Vertex Vertex;
124 GIVEN( "A box with an open side" ) {
125 PolygonMesh polymesh = makeBox();
126 THEN( "The mesh has 10 vertices, v0 has 3 neighbors, v1 has 3 neighbors, etc" ) {
127 REQUIRE( polymesh.size() == 10 );
128 REQUIRE( polymesh.degree( 0 ) == 3 );
129 REQUIRE( polymesh.degree( 1 ) == 3 );
130 REQUIRE( polymesh.degree( 2 ) == 3 );
131 REQUIRE( polymesh.degree( 3 ) == 3 );
132 REQUIRE( polymesh.degree( 4 ) == 4 );
133 REQUIRE( polymesh.degree( 5 ) == 4 );
134 REQUIRE( polymesh.degree( 6 ) == 3 );
135 REQUIRE( polymesh.degree( 7 ) == 3 );
136 REQUIRE( polymesh.degree( 8 ) == 2 );
137 REQUIRE( polymesh.degree( 9 ) == 2 );
138 }
139 THEN( "Euler number is 1 as is the Euler number of a disk." )
140 {
141 REQUIRE( polymesh.nbVertices() == 10 );
142 REQUIRE( polymesh.nbEdges() == 15 );
143 REQUIRE( polymesh.nbFaces() == 6 );
144 REQUIRE( polymesh.Euler() == 1 );
145 }
146 THEN( "Checking distances." )
147 {
148 REQUIRE( polymesh.distance(0,0) == Approx(0.0) );
149 REQUIRE( polymesh.distance(0,7) == Approx(std::sqrt(3)));
150 }
151 THEN( "Breadth-first visiting the mesh from vertex 0, visit {0}, then {1,2,4}, then {3,5,6,9}, then {7,8}." )
152 {
153 BreadthFirstVisitor< PolygonMesh > visitor( polymesh, 0 );
154 std::vector<unsigned long> vertices;
155 std::vector<unsigned long> distances;
156 while ( ! visitor.finished() )
157 {
158 vertices.push_back( visitor.current().first );
159 distances.push_back( visitor.current().second );
160 visitor.expand();
161 }
162 REQUIRE( vertices.size() == 10 );
163 REQUIRE( distances.size() == 10 );
164 int expected_vertices[] = { 0, 1, 2, 4, 3, 5, 6, 9, 7, 8 };
165 int expected_distance[] = { 0, 1, 1, 1, 2, 2, 2, 2, 3, 3 };
166 auto itb = vertices.begin();
167 std::sort( itb+1, itb+4 );
168 std::sort( itb+4, itb+8 );
169 std::sort( itb+8, itb+10 );
170 bool vertices_ok
171 = std::equal( vertices.begin(), vertices.end(), expected_vertices );
172 REQUIRE( vertices_ok );
173 bool distances_ok
174 = std::equal( distances.begin(), distances.end(), expected_distance );
175 REQUIRE( distances_ok );
176 }
177 THEN( "The mesh has 6 boundary edges and 9 manifold inner consistent edges, the boundary is a 1d manifold" ) {
178 auto mani_bdry = polymesh.computeManifoldBoundaryEdges();
179 auto mani_inner = polymesh.computeManifoldInnerEdges();
180 auto mani_inner_c = polymesh.computeManifoldInnerConsistentEdges();
181 auto mani_inner_u = polymesh.computeManifoldInnerUnconsistentEdges();
182 auto non_mani = polymesh.computeNonManifoldEdges();
183 CAPTURE( polymesh );
184 REQUIRE( mani_bdry.size() == 6 );
185 REQUIRE( mani_inner.size() == 9 );
186 REQUIRE( mani_inner_c.size() == 9 );
187 REQUIRE( mani_inner_u.size() == 0 );
188 REQUIRE( non_mani.size() == 0 );
189 }
190 THEN( "The face along (1,3) is a quadrangle (1,3,7,5)" ) {
191 Edge e13 = polymesh.makeEdge( 1, 3 );
192 auto lfs = polymesh.edgeLeftFaces( e13 );
193 Vertices T = polymesh.incidentVertices( lfs[ 0 ] );
194 REQUIRE( T.size() == 4 );
195 std::sort( T.begin(), T.end() );
196 REQUIRE( T[ 0 ] == 1 );
197 REQUIRE( T[ 1 ] == 3 );
198 REQUIRE( T[ 2 ] == 5 );
199 REQUIRE( T[ 3 ] == 7 );
200 }
201 THEN( "The face along (3,1) is a quadrangle (3,1,0,2)" ) {
202 Edge e13 = polymesh.makeEdge( 1, 3 );
203 auto rfs = polymesh.edgeRightFaces( e13 );
204 Vertices T = polymesh.incidentVertices( rfs[ 0 ] );
205 REQUIRE( T.size() == 4 );
206 std::sort( T.begin(), T.end() );
207 REQUIRE( T[ 0 ] == 0 );
208 REQUIRE( T[ 1 ] == 1 );
209 REQUIRE( T[ 2 ] == 2 );
210 REQUIRE( T[ 3 ] == 3 );
211 }
212 THEN( "The lower part of the mesh has the barycenter (0.5, 0.5, 0.5) " ) {
213 auto positions = polymesh.positions();
214 RealPoint b;
215 for ( Vertex v = 0; v < 8; ++v )
216 b += positions[ v ];
217 b /= 8;
218 REQUIRE( b[ 0 ] == 0.5 );
219 REQUIRE( b[ 1 ] == 0.5 );
220 REQUIRE( b[ 2 ] == 0.5 );
221 }
222 THEN( "We can iterate over the vertices" ) {
223 auto positions = polymesh.positions();
224 RealPoint exp_positions[] = { { 0,0,0 }, { 1,0,0 }, { 0,1,0 }, { 1,1,0 },
225 { 0,0,1 }, { 1,0,1 }, { 0,1,1 }, { 1,1,1 },
226 { 1,0,2 }, { 0,0,2 } };
227 for ( auto it = polymesh.begin(), itE = polymesh.end(); it != itE; ++it ) {
228 REQUIRE( positions[ *it ] == exp_positions[ *it ] );
229 }
230 }
231 }
232}
Aim: This class is useful to perform a breadth-first exploration of a graph given a starting point or...
std::pair< typename graph_traits< DGtal::DigitalSurface< TDigitalSurfaceContainer > >::vertex_iterator, typename graph_traits< DGtal::DigitalSurface< TDigitalSurfaceContainer > >::vertex_iterator > vertices(const DGtal::DigitalSurface< TDigitalSurfaceContainer > &digSurf)
GIVEN("A cubical complex with random 3-cells")
HalfEdgeDataStructure::Edge Edge
TriMesh::Face Face
TriMesh::Vertex Vertex

References CAPTURE(), DGtal::BreadthFirstVisitor< TGraph, TMarkSet >::current(), DGtal::BreadthFirstVisitor< TGraph, TMarkSet >::expand(), DGtal::BreadthFirstVisitor< TGraph, TMarkSet >::finished(), GIVEN(), makeBox(), and REQUIRE().

◆ SCENARIO() [3/5]

SCENARIO ( "SurfaceMesh< RealPoint3 > concept check tests"  ,
""  [surfmesh][concepts] 
)

Definition at line 107 of file testSurfaceMesh.cpp.

108{
111 typedef SurfaceMesh< RealPoint, RealVector > PolygonMesh;
112 BOOST_CONCEPT_ASSERT(( concepts::CUndirectedSimpleGraph< PolygonMesh > ));
113}
Aim: Represents the concept of local graph: each vertex has neighboring vertices, but we do not neces...

◆ SCENARIO() [4/5]

SCENARIO ( "SurfaceMesh< RealPoint3 > mesh helper tests"  ,
""  [surfmesh][helper] 
)

Definition at line 235 of file testSurfaceMesh.cpp.

236{
239 typedef SurfaceMesh< RealPoint, RealVector > PolygonMesh;
240 typedef SurfaceMeshHelper< RealPoint, RealVector > PolygonMeshHelper;
241 typedef PolygonMeshHelper::NormalsType NormalsType;
242 GIVEN( "A sphere of radius 10" ) {
243 auto polymesh = PolygonMeshHelper::makeSphere( 3.0, RealPoint::zero,
244 10, 10, NormalsType::NO_NORMALS );
245 THEN( "The mesh has Euler characteristic 2" ) {
246 REQUIRE( polymesh.Euler() == 2 );
247 }
248 THEN( "It is a consistent manifold without boundary" ) {
249 auto mani_bdry = polymesh.computeManifoldBoundaryEdges();
250 auto mani_inner = polymesh.computeManifoldInnerEdges();
251 auto mani_inner_c = polymesh.computeManifoldInnerConsistentEdges();
252 auto mani_inner_u = polymesh.computeManifoldInnerUnconsistentEdges();
253 auto non_mani = polymesh.computeNonManifoldEdges();
254 CAPTURE( polymesh );
255 REQUIRE( mani_bdry.size() == 0 );
256 REQUIRE( mani_inner.size() == mani_inner_c.size() );
257 REQUIRE( mani_inner_u.size() == 0 );
258 REQUIRE( non_mani.size() == 0 );
259 }
260 }
261 GIVEN( "A torus with radii 3 and 1" ) {
262 auto polymesh = PolygonMeshHelper::makeTorus( 3.0, 1.0, RealPoint::zero,
263 10, 10, 0, NormalsType::NO_NORMALS );
264 THEN( "The mesh has Euler characteristic 0" ) {
265 REQUIRE( polymesh.Euler() == 0 );
266 }
267 THEN( "It is a consistent manifold without boundary" ) {
268 auto mani_bdry = polymesh.computeManifoldBoundaryEdges();
269 auto mani_inner = polymesh.computeManifoldInnerEdges();
270 auto mani_inner_c = polymesh.computeManifoldInnerConsistentEdges();
271 auto mani_inner_u = polymesh.computeManifoldInnerUnconsistentEdges();
272 auto non_mani = polymesh.computeNonManifoldEdges();
273 CAPTURE( polymesh );
274 REQUIRE( mani_bdry.size() == 0 );
275 REQUIRE( mani_inner.size() == mani_inner_c.size() );
276 REQUIRE( mani_inner_u.size() == 0 );
277 REQUIRE( non_mani.size() == 0 );
278 }
279 }
280 GIVEN( "A lantern with radii 3" ) {
281 auto polymesh = PolygonMeshHelper::makeLantern( 3.0, 3.0, RealPoint::zero,
282 10, 10, NormalsType::NO_NORMALS );
283 THEN( "The mesh has Euler characteristic 0" ) {
284 REQUIRE( polymesh.Euler() == 0 );
285 }
286 THEN( "It is a consistent manifold with boundary" ) {
287 auto mani_bdry = polymesh.computeManifoldBoundaryEdges();
288 auto mani_inner = polymesh.computeManifoldInnerEdges();
289 auto mani_inner_c = polymesh.computeManifoldInnerConsistentEdges();
290 auto mani_inner_u = polymesh.computeManifoldInnerUnconsistentEdges();
291 auto non_mani = polymesh.computeNonManifoldEdges();
292 CAPTURE( polymesh );
293 REQUIRE( mani_bdry.size() == 20 );
294 REQUIRE( mani_inner.size() == mani_inner_c.size() );
295 REQUIRE( mani_inner_u.size() == 0 );
296 REQUIRE( non_mani.size() == 0 );
297 }
298 }
299}
static Self zero
Static const for zero PointVector.
Definition: PointVector.h:1595
Aim: An helper class for building classical meshes.

References CAPTURE(), GIVEN(), REQUIRE(), and DGtal::PointVector< dim, TEuclideanRing, TContainer >::zero.

◆ SCENARIO() [5/5]

SCENARIO ( "SurfaceMesh< RealPoint3 > reader/writer tests"  ,
""  [surfmesh][io] 
)

Definition at line 301 of file testSurfaceMesh.cpp.

302{
305 typedef SurfaceMesh< RealPoint, RealVector > PolygonMesh;
306 typedef SurfaceMeshHelper< RealPoint, RealVector > PolygonMeshHelper;
307 typedef SurfaceMeshReader< RealPoint, RealVector > PolygonMeshReader;
308 typedef SurfaceMeshWriter< RealPoint, RealVector > PolygonMeshWriter;
309 typedef PolygonMeshHelper::NormalsType NormalsType;
310 auto polymesh = PolygonMeshHelper::makeSphere( 3.0, RealPoint::zero,
311 10, 10, NormalsType::VERTEX_NORMALS );
312 WHEN( "Writing the mesh as an OBJ file and reading into another mesh" ) {
313 PolygonMesh readmesh;
314 std::ostringstream output;
315 bool okw = PolygonMeshWriter::writeOBJ( output, polymesh );
316 std::string file = output.str();
317 std::istringstream input( file );
318 bool okr = PolygonMeshReader::readOBJ ( input, readmesh );
319 THEN( "The read mesh is the same as the original one" ) {
320 CAPTURE( file );
321 CAPTURE( polymesh );
322 CAPTURE( readmesh );
323 REQUIRE( okw );
324 REQUIRE( okr );
325 REQUIRE( polymesh.Euler() == readmesh.Euler() );
326 REQUIRE( polymesh.nbVertices() == readmesh.nbVertices() );
327 REQUIRE( polymesh.nbEdges() == readmesh.nbEdges() );
328 REQUIRE( polymesh.nbFaces() == readmesh.nbFaces() );
329 REQUIRE( polymesh.neighborVertices( 0 ).size()
330 == readmesh.neighborVertices( 0 ).size() );
331 REQUIRE( polymesh.neighborVertices( 20 ).size()
332 == readmesh.neighborVertices( 20 ).size() );
333 REQUIRE( polymesh.vertexNormals().size() == readmesh.vertexNormals().size() );
334 }
335 }
336}
Aim: An helper class for reading mesh files (Wavefront OBJ at this point) and creating a SurfaceMesh.
Aim: An helper class for writing mesh file formats (Waverfront OBJ at this point) and creating a Surf...

References CAPTURE(), REQUIRE(), and DGtal::PointVector< dim, TEuclideanRing, TContainer >::zero.