@@ -13,8 +13,8 @@ using namespace Ra::Core;
1313using namespace Ra ::Core::Utils;
1414using namespace Ra ::Core::Geometry;
1515
16- bool isSameMesh ( Ra::Core::Geometry::TriangleMesh& meshOne,
17- Ra::Core::Geometry::TriangleMesh& meshTwo ) {
16+ bool isSameMesh ( const Ra::Core::Geometry::TriangleMesh& meshOne,
17+ const Ra::Core::Geometry::TriangleMesh& meshTwo ) {
1818
1919 bool result = true ;
2020 int i = 0 ;
@@ -196,13 +196,13 @@ TEST_CASE( "Core/Geometry/TopologicalMesh", "[Core][Core/Geometry][TopologicalMe
196196 using Ra::Core::Geometry::TopologicalMesh;
197197 using Ra::Core::Geometry::TriangleMesh;
198198
199- using Catmull =
200- OpenMesh::Subdivider::Uniform::CatmullClarkT<Ra::Core::Geometry::TopologicalMesh>;
201- using Loop = OpenMesh::Subdivider::Uniform::LoopT<Ra::Core::Geometry::TopologicalMesh>;
199+ // using Catmull =
200+ // OpenMesh::Subdivider::Uniform::CatmullClarkT<Ra::Core::Geometry::TopologicalMesh>;
201+ // using Loop = OpenMesh::Subdivider::Uniform::LoopT<Ra::Core::Geometry::TopologicalMesh>;
202202
203- using Decimater = OpenMesh::Decimater::DecimaterT<Ra::Core::Geometry::TopologicalMesh>;
204- using HModQuadric =
205- OpenMesh::Decimater::ModQuadricT<Ra::Core::Geometry::TopologicalMesh>::Handle;
203+ // using Decimater = OpenMesh::Decimater::DecimaterT<Ra::Core::Geometry::TopologicalMesh>;
204+ // using HModQuadric =
205+ // OpenMesh::Decimater::ModQuadricT<Ra::Core::Geometry::TopologicalMesh>::Handle;
206206
207207 TriangleMesh newMesh;
208208 TriangleMesh newMesh2;
@@ -384,3 +384,118 @@ TEST_CASE( "Core/Geometry/TopologicalMesh/EdgeSplit", "[Core][Core/Geometry][Top
384384 // collapse
385385 // check float attrib value
386386}
387+
388+ TEST_CASE ( " Core/Geometry/TopologicalMesh/Manifold" , " [Core][Core/Geometry][TopologicalMesh]" ) {
389+
390+ struct MyNonManifoldCommand {
391+ inline MyNonManifoldCommand ( int target ) : targetNonManifoldFaces( target ) {}
392+ inline void initialize ( const TriangleMesh& /* triMesh*/ ) {}
393+ inline void process ( const std::vector<TopologicalMesh::VertexHandle>& /* face_vhandles*/ ) {
394+ LOG ( logINFO ) << " Non Manifold face found" ;
395+ nonManifoldFaces++;
396+ }
397+ inline void postProcess ( TopologicalMesh& /* tm*/ ) {
398+ // Todo : For each non manifold face, remove the vertices that are not part of a face of
399+ // the topomesh For the test, this will reduce the mesh_2 to mesh1
400+ REQUIRE ( nonManifoldFaces == targetNonManifoldFaces );
401+ LOG ( logINFO ) << " Process non-manifold faces" ;
402+ }
403+
404+ int nonManifoldFaces {0 };
405+ const int targetNonManifoldFaces;
406+ };
407+
408+ VectorArray<Vector3> vertices = {
409+ {0_ra, 0_ra, 0_ra}, {0_ra, 1_ra, 0_ra}, {1_ra, 1_ra, 0_ra}, {1_ra, 0_ra, 0_ra}};
410+ VectorArray<Vector3> normals {
411+ {0_ra, 0_ra, 1_ra}, {0_ra, 0_ra, 1_ra}, {0_ra, 0_ra, 1_ra}, {0_ra, 0_ra, 1_ra}};
412+ VectorArray<Vector3ui> indices {{0 , 2 , 1 }, {0 , 3 , 2 }};
413+
414+ VectorArray<Vector3> vertices_2 = {{0_ra, 0_ra, 0_ra},
415+ {0_ra, 1_ra, 0_ra},
416+ {1_ra, 1_ra, 0_ra},
417+ {1_ra, 0_ra, 0_ra},
418+ {1_ra, 0_ra, 1_ra}};
419+ VectorArray<Vector3> normals_2 {
420+ {0_ra, 0_ra, 1_ra},
421+ {0_ra, 0_ra, 1_ra},
422+ {0_ra, 0_ra, 1_ra},
423+ {0_ra, 0_ra, 1_ra},
424+ {0_ra, -1_ra, 0_ra},
425+ };
426+
427+ VectorArray<Vector3ui> indices_2 {{0 , 2 , 1 }, {0 , 3 , 2 }, {0 , 2 , 4 }};
428+
429+ auto buildMesh = []( const VectorArray<Vector3>& v,
430+ const VectorArray<Vector3>& n,
431+ const VectorArray<Vector3ui>& i ) {
432+ TriangleMesh m;
433+ m.setVertices ( v );
434+ m.setNormals ( n );
435+ auto & idx = m.getIndicesWithLock ();
436+ std::copy ( i.begin (), i.end (), std::back_inserter ( idx ) );
437+ m.indicesUnlock ();
438+
439+ LOG ( logINFO ) << " Built a mesh with " << m.vertices ().size () << " vertices, "
440+ << m.normals ().size () << " normals and " << m.getIndices ().size ()
441+ << " indices." ;
442+
443+ return m;
444+ };
445+
446+ auto testConverter =
447+ []( const TriangleMesh& mesh, const TriangleMesh& mesh2, MyNonManifoldCommand command ) {
448+ // test with functor
449+ LOG ( logINFO ) << " Converter with custom command" ;
450+ TopologicalMesh topo2 {mesh2, command};
451+ auto mesh3 = topo2.toTriangleMesh ();
452+ REQUIRE ( isSameMesh ( mesh, mesh3 ) );
453+
454+ // test without functor
455+ LOG ( logINFO ) << " Converter without custom command" ;
456+ TopologicalMesh topo3 {mesh2};
457+ auto mesh4 = topo3.toTriangleMesh ();
458+ REQUIRE ( isSameMesh ( mesh, mesh4 ) );
459+ return mesh4;
460+ };
461+
462+ using Vector5 = Eigen::Matrix<Scalar, 5 , 1 >;
463+ VectorArray<Vector5> attrib_array {
464+ {0_ra, 0_ra, 0_ra, 0_ra, 1_ra},
465+ {0_ra, 0_ra, 0_ra, 0_ra, 1_ra},
466+ {0_ra, 0_ra, 0_ra, 0_ra, 1_ra},
467+ {0_ra, -1_ra, 0_ra, 0_ra, 0_ra},
468+ };
469+
470+ // well formed mesh
471+ auto mesh = buildMesh ( vertices, normals, indices );
472+
473+ // edge shared by three faces
474+ LOG ( logINFO ) << " Test with edge shared by three faces" ;
475+ auto mesh2 = buildMesh ( vertices_2, normals_2, indices_2 );
476+ testConverter ( mesh, mesh2, MyNonManifoldCommand ( 1 ) ); // we should find 1 non-manifold face
477+
478+ // test with unsupported attribute type
479+ LOG ( logINFO ) << " Test with unsupported attribute (all faces are manifold)" ;
480+ auto mesh3 {mesh}, mesh4 {mesh};
481+ auto handle = mesh3.addAttrib <Vector5>( " vector5_attrib" );
482+ auto & attrib = mesh3.getAttrib ( handle );
483+ auto & buf = attrib.getDataWithLock ();
484+ buf = attrib_array;
485+ attrib.unlock ();
486+
487+ REQUIRE ( mesh4.vertexAttribs ().hasSameAttribs ( mesh.vertexAttribs () ) );
488+ REQUIRE ( mesh.vertexAttribs ().hasSameAttribs ( mesh4.vertexAttribs () ) );
489+ REQUIRE ( !mesh4.vertexAttribs ().hasSameAttribs ( mesh3.vertexAttribs () ) );
490+ REQUIRE ( !mesh3.vertexAttribs ().hasSameAttribs ( mesh4.vertexAttribs () ) );
491+ mesh4 = testConverter (
492+ mesh, mesh3, MyNonManifoldCommand ( 0 ) ); // we should find 0 non-manifold face
493+ REQUIRE ( mesh4.vertexAttribs ().hasSameAttribs ( mesh.vertexAttribs () ) );
494+ REQUIRE ( mesh.vertexAttribs ().hasSameAttribs ( mesh4.vertexAttribs () ) );
495+ REQUIRE ( !mesh4.vertexAttribs ().hasSameAttribs ( mesh3.vertexAttribs () ) );
496+ REQUIRE ( !mesh3.vertexAttribs ().hasSameAttribs ( mesh4.vertexAttribs () ) );
497+
498+ // TODO : build a functor that add the faces as independant faces in the topomesh and
499+ // define a manifold mesh that is similar to the result of processing of this non manifold.
500+ //
501+ }
0 commit comments