DGtal  1.3.beta
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"
Include dependency graph for testSurfaceMesh.cpp:

Go to the source code of this file.

Functions

SurfaceMesh< PointVector< 3, double >, PointVector< 3, double > > makeBox ()
 
 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]")
 

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 }

Referenced by SCENARIO().

◆ SCENARIO() [1/4]

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

Definition at line 93 of file testSurfaceMesh.cpp.

94 {
97  typedef SurfaceMesh< RealPoint, RealVector > PolygonMesh;
98  typedef PolygonMesh::Vertices Vertices;
99  typedef PolygonMesh::Face Face;
100  typedef PolygonMesh::Edge Edge;
101  typedef PolygonMesh::Vertex Vertex;
102  GIVEN( "A box with an open side" ) {
103  PolygonMesh polymesh = makeBox();
104  THEN( "The mesh has 10 vertices, v0 has 3 neighbors, v1 has 3 neighbors, etc" ) {
105  REQUIRE( polymesh.size() == 10 );
106  REQUIRE( polymesh.degree( 0 ) == 3 );
107  REQUIRE( polymesh.degree( 1 ) == 3 );
108  REQUIRE( polymesh.degree( 2 ) == 3 );
109  REQUIRE( polymesh.degree( 3 ) == 3 );
110  REQUIRE( polymesh.degree( 4 ) == 4 );
111  REQUIRE( polymesh.degree( 5 ) == 4 );
112  REQUIRE( polymesh.degree( 6 ) == 3 );
113  REQUIRE( polymesh.degree( 7 ) == 3 );
114  REQUIRE( polymesh.degree( 8 ) == 2 );
115  REQUIRE( polymesh.degree( 9 ) == 2 );
116  }
117  THEN( "Euler number is 1 as is the Euler number of a disk." )
118  {
119  REQUIRE( polymesh.nbVertices() == 10 );
120  REQUIRE( polymesh.nbEdges() == 15 );
121  REQUIRE( polymesh.nbFaces() == 6 );
122  REQUIRE( polymesh.Euler() == 1 );
123  }
124  THEN( "Breadth-first visiting the mesh from vertex 0, visit {0}, then {1,2,4}, then {3,5,6,9}, then {7,8}." )
125  {
126  BreadthFirstVisitor< PolygonMesh > visitor( polymesh, 0 );
127  std::vector<unsigned long> vertices;
128  std::vector<unsigned long> distances;
129  while ( ! visitor.finished() )
130  {
131  vertices.push_back( visitor.current().first );
132  distances.push_back( visitor.current().second );
133  visitor.expand();
134  }
135  REQUIRE( vertices.size() == 10 );
136  REQUIRE( distances.size() == 10 );
137  int expected_vertices[] = { 0, 1, 2, 4, 3, 5, 6, 9, 7, 8 };
138  int expected_distance[] = { 0, 1, 1, 1, 2, 2, 2, 2, 3, 3 };
139  auto itb = vertices.begin();
140  std::sort( itb+1, itb+4 );
141  std::sort( itb+4, itb+8 );
142  std::sort( itb+8, itb+10 );
143  bool vertices_ok
144  = std::equal( vertices.begin(), vertices.end(), expected_vertices );
145  REQUIRE( vertices_ok );
146  bool distances_ok
147  = std::equal( distances.begin(), distances.end(), expected_distance );
148  REQUIRE( distances_ok );
149  }
150  THEN( "The mesh has 6 boundary edges and 9 manifold inner consistent edges" ) {
151  auto mani_bdry = polymesh.computeManifoldBoundaryEdges();
152  auto mani_inner = polymesh.computeManifoldInnerEdges();
153  auto mani_inner_c = polymesh.computeManifoldInnerConsistentEdges();
154  auto mani_inner_u = polymesh.computeManifoldInnerUnconsistentEdges();
155  auto non_mani = polymesh.computeNonManifoldEdges();
156  CAPTURE( polymesh );
157  REQUIRE( mani_bdry.size() == 6 );
158  REQUIRE( mani_inner.size() == 9 );
159  REQUIRE( mani_inner_c.size() == 9 );
160  REQUIRE( mani_inner_u.size() == 0 );
161  REQUIRE( non_mani.size() == 0 );
162  }
163  THEN( "The face along (1,3) is a quadrangle (1,3,7,5)" ) {
164  Edge e13 = polymesh.makeEdge( 1, 3 );
165  auto lfs = polymesh.edgeLeftFaces( e13 );
166  Vertices T = polymesh.incidentVertices( lfs[ 0 ] );
167  REQUIRE( T.size() == 4 );
168  std::sort( T.begin(), T.end() );
169  REQUIRE( T[ 0 ] == 1 );
170  REQUIRE( T[ 1 ] == 3 );
171  REQUIRE( T[ 2 ] == 5 );
172  REQUIRE( T[ 3 ] == 7 );
173  }
174  THEN( "The face along (3,1) is a quadrangle (3,1,0,2)" ) {
175  Edge e13 = polymesh.makeEdge( 1, 3 );
176  auto rfs = polymesh.edgeRightFaces( e13 );
177  Vertices T = polymesh.incidentVertices( rfs[ 0 ] );
178  REQUIRE( T.size() == 4 );
179  std::sort( T.begin(), T.end() );
180  REQUIRE( T[ 0 ] == 0 );
181  REQUIRE( T[ 1 ] == 1 );
182  REQUIRE( T[ 2 ] == 2 );
183  REQUIRE( T[ 3 ] == 3 );
184  }
185  THEN( "The lower part of the mesh has the barycenter (0.5, 0.5, 0.5) " ) {
186  auto positions = polymesh.positions();
187  RealPoint b;
188  for ( Vertex v = 0; v < 8; ++v )
189  b += positions[ v ];
190  b /= 8;
191  REQUIRE( b[ 0 ] == 0.5 );
192  REQUIRE( b[ 1 ] == 0.5 );
193  REQUIRE( b[ 2 ] == 0.5 );
194  }
195  THEN( "We can iterate over the vertices" ) {
196  auto positions = polymesh.positions();
197  RealPoint exp_positions[] = { { 0,0,0 }, { 1,0,0 }, { 0,1,0 }, { 1,1,0 },
198  { 0,0,1 }, { 1,0,1 }, { 0,1,1 }, { 1,1,1 },
199  { 1,0,2 }, { 0,0,2 } };
200  for ( auto it = polymesh.begin(), itE = polymesh.end(); it != itE; ++it ) {
201  REQUIRE( positions[ *it ] == exp_positions[ *it ] );
202  }
203  }
204  }
205 }

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

◆ SCENARIO() [2/4]

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

Definition at line 85 of file testSurfaceMesh.cpp.

86 {
89  typedef SurfaceMesh< RealPoint, RealVector > PolygonMesh;
90  BOOST_CONCEPT_ASSERT(( concepts::CUndirectedSimpleGraph< PolygonMesh > ));
91 }

◆ SCENARIO() [3/4]

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

Definition at line 208 of file testSurfaceMesh.cpp.

209 {
212  typedef SurfaceMesh< RealPoint, RealVector > PolygonMesh;
213  typedef SurfaceMeshHelper< RealPoint, RealVector > PolygonMeshHelper;
214  typedef PolygonMeshHelper::NormalsType NormalsType;
215  GIVEN( "A sphere of radius 10" ) {
216  auto polymesh = PolygonMeshHelper::makeSphere( 3.0, RealPoint::zero,
217  10, 10, NormalsType::NO_NORMALS );
218  THEN( "The mesh has Euler characteristic 2" ) {
219  REQUIRE( polymesh.Euler() == 2 );
220  }
221  THEN( "It is a consistent manifold without boundary" ) {
222  auto mani_bdry = polymesh.computeManifoldBoundaryEdges();
223  auto mani_inner = polymesh.computeManifoldInnerEdges();
224  auto mani_inner_c = polymesh.computeManifoldInnerConsistentEdges();
225  auto mani_inner_u = polymesh.computeManifoldInnerUnconsistentEdges();
226  auto non_mani = polymesh.computeNonManifoldEdges();
227  CAPTURE( polymesh );
228  REQUIRE( mani_bdry.size() == 0 );
229  REQUIRE( mani_inner.size() == mani_inner_c.size() );
230  REQUIRE( mani_inner_u.size() == 0 );
231  REQUIRE( non_mani.size() == 0 );
232  }
233  }
234  GIVEN( "A torus with radii 3 and 1" ) {
235  auto polymesh = PolygonMeshHelper::makeTorus( 3.0, 1.0, RealPoint::zero,
236  10, 10, 0, NormalsType::NO_NORMALS );
237  THEN( "The mesh has Euler characteristic 0" ) {
238  REQUIRE( polymesh.Euler() == 0 );
239  }
240  THEN( "It is a consistent manifold without boundary" ) {
241  auto mani_bdry = polymesh.computeManifoldBoundaryEdges();
242  auto mani_inner = polymesh.computeManifoldInnerEdges();
243  auto mani_inner_c = polymesh.computeManifoldInnerConsistentEdges();
244  auto mani_inner_u = polymesh.computeManifoldInnerUnconsistentEdges();
245  auto non_mani = polymesh.computeNonManifoldEdges();
246  CAPTURE( polymesh );
247  REQUIRE( mani_bdry.size() == 0 );
248  REQUIRE( mani_inner.size() == mani_inner_c.size() );
249  REQUIRE( mani_inner_u.size() == 0 );
250  REQUIRE( non_mani.size() == 0 );
251  }
252  }
253  GIVEN( "A lantern with radii 3" ) {
254  auto polymesh = PolygonMeshHelper::makeLantern( 3.0, 3.0, RealPoint::zero,
255  10, 10, NormalsType::NO_NORMALS );
256  THEN( "The mesh has Euler characteristic 0" ) {
257  REQUIRE( polymesh.Euler() == 0 );
258  }
259  THEN( "It is a consistent manifold with boundary" ) {
260  auto mani_bdry = polymesh.computeManifoldBoundaryEdges();
261  auto mani_inner = polymesh.computeManifoldInnerEdges();
262  auto mani_inner_c = polymesh.computeManifoldInnerConsistentEdges();
263  auto mani_inner_u = polymesh.computeManifoldInnerUnconsistentEdges();
264  auto non_mani = polymesh.computeNonManifoldEdges();
265  CAPTURE( polymesh );
266  REQUIRE( mani_bdry.size() == 20 );
267  REQUIRE( mani_inner.size() == mani_inner_c.size() );
268  REQUIRE( mani_inner_u.size() == 0 );
269  REQUIRE( non_mani.size() == 0 );
270  }
271  }
272 }

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

◆ SCENARIO() [4/4]

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

Definition at line 274 of file testSurfaceMesh.cpp.

275 {
278  typedef SurfaceMesh< RealPoint, RealVector > PolygonMesh;
279  typedef SurfaceMeshHelper< RealPoint, RealVector > PolygonMeshHelper;
280  typedef SurfaceMeshReader< RealPoint, RealVector > PolygonMeshReader;
281  typedef SurfaceMeshWriter< RealPoint, RealVector > PolygonMeshWriter;
282  typedef PolygonMeshHelper::NormalsType NormalsType;
283  auto polymesh = PolygonMeshHelper::makeSphere( 3.0, RealPoint::zero,
284  10, 10, NormalsType::VERTEX_NORMALS );
285  WHEN( "Writing the mesh as an OBJ file and reading into another mesh" ) {
286  PolygonMesh readmesh;
287  std::ostringstream output;
288  bool okw = PolygonMeshWriter::writeOBJ( output, polymesh );
289  std::string file = output.str();
290  std::istringstream input( file );
291  bool okr = PolygonMeshReader::readOBJ ( input, readmesh );
292  THEN( "The read mesh is the same as the original one" ) {
293  CAPTURE( file );
294  CAPTURE( polymesh );
295  CAPTURE( readmesh );
296  REQUIRE( okw );
297  REQUIRE( okr );
298  REQUIRE( polymesh.Euler() == readmesh.Euler() );
299  REQUIRE( polymesh.nbVertices() == readmesh.nbVertices() );
300  REQUIRE( polymesh.nbEdges() == readmesh.nbEdges() );
301  REQUIRE( polymesh.nbFaces() == readmesh.nbFaces() );
302  REQUIRE( polymesh.neighborVertices( 0 ).size()
303  == readmesh.neighborVertices( 0 ).size() );
304  REQUIRE( polymesh.neighborVertices( 20 ).size()
305  == readmesh.neighborVertices( 20 ).size() );
306  REQUIRE( polymesh.vertexNormals().size() == readmesh.vertexNormals().size() );
307  }
308  }
309 }

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

DGtal::PointVector::zero
static Self zero
Static const for zero PointVector.
Definition: PointVector.h:1595
Face
TriMesh::Face Face
Definition: testTriangulatedSurface.cpp:56
DGtal::SurfaceMeshHelper
Aim: An helper class for building classical meshes.
Definition: SurfaceMeshHelper.h:63
RealVector
Z3i::RealVector RealVector
Definition: sphereCotangentLaplaceOperator.cpp:71
DGtal::BreadthFirstVisitor
Aim: This class is useful to perform a breadth-first exploration of a graph given a starting point or...
Definition: BreadthFirstVisitor.h:94
DGtal::SurfaceMesh
Aim: Represents an embedded mesh as faces and a list of vertices. Vertices may be shared among faces ...
Definition: SurfaceMesh.h:91
DGtal::SurfaceMeshWriter
Aim: An helper class for writing mesh file formats (Waverfront OBJ at this point) and creating a Surf...
Definition: SurfaceMeshWriter.h:64
REQUIRE
REQUIRE(domain.isInside(aPoint))
Vertex
TriMesh::Vertex Vertex
Definition: testTriangulatedSurface.cpp:57
boost::vertices
std::pair< typename graph_traits< DGtal::DigitalSurface< TDigitalSurfaceContainer > >::vertex_iterator, typename graph_traits< DGtal::DigitalSurface< TDigitalSurfaceContainer > >::vertex_iterator > vertices(const DGtal::DigitalSurface< TDigitalSurfaceContainer > &digSurf)
DGtal::HalfEdgeDataStructure::Edge
Definition: HalfEdgeDataStructure.h:126
CAPTURE
CAPTURE(thicknessHV)
DGtal::SurfaceMeshReader
Aim: An helper class for reading mesh files (Wavefront OBJ at this point) and creating a SurfaceMesh.
Definition: SurfaceMeshReader.h:63
DGtal::concepts::CUndirectedSimpleGraph
Aim: Represents the concept of local graph: each vertex has neighboring vertices, but we do not neces...
Definition: CUndirectedSimpleGraph.h:102
Edge
HalfEdgeDataStructure::Edge Edge
Definition: testHalfEdgeDataStructure.cpp:47
DGtal::PointVector< 3, double >
GIVEN
GIVEN("A cubical complex with random 3-cells")
Definition: testCubicalComplex.cpp:70
RealPoint
Z2i::RealPoint RealPoint
Definition: testAstroid2D.cpp:46
makeBox
SurfaceMesh< PointVector< 3, double >, PointVector< 3, double > > makeBox()
Definition: testSurfaceMesh.cpp:57