diff --git a/geometry/include/pcl/geometry/mesh_conversion.h b/geometry/include/pcl/geometry/mesh_conversion.h index dbefdbaa32b..0f16330e43f 100644 --- a/geometry/include/pcl/geometry/mesh_conversion.h +++ b/geometry/include/pcl/geometry/mesh_conversion.h @@ -48,45 +48,86 @@ namespace pcl { namespace geometry { - /** \brief Conversions for the half-edge mesh. + /** \brief Convert a half-edge mesh to a face-vertex mesh. + * \param[in] half_edge_mesh The input mesh. + * \param[out] face_vertex_mesh The output mesh. * \author Martin Saelzle * \ingroup geometry */ - class MeshConversion + template void + toFaceVertexMesh (const HalfEdgeMeshT& half_edge_mesh, pcl::PolygonMesh& face_vertex_mesh) { - public: + typedef HalfEdgeMeshT HalfEdgeMesh; + typedef typename HalfEdgeMesh::VertexAroundFaceCirculator VAFC; + typedef typename HalfEdgeMesh::FaceIndex FaceIndex; - /** \brief Constructor. */ - MeshConversion () {} + pcl::Vertices polygon; + pcl::toROSMsg (half_edge_mesh.getVertexDataCloud (), face_vertex_mesh.cloud); - /** \brief Convert from a half-edge mesh to a face-vertex mesh. - * \param[in] half_edge_mesh The input mesh. - * \param[out] face_vertex_mesh The output mesh. - */ - template void - toFaceVertexMesh (const HalfEdgeMeshT& half_edge_mesh, pcl::PolygonMesh& face_vertex_mesh) + face_vertex_mesh.polygons.reserve (half_edge_mesh.sizeFaces ()); + for (size_t i=0; i int + toHalfEdgeMesh (const pcl::PolygonMesh& face_vertex_mesh, HalfEdgeMeshT& half_edge_mesh) + { + typedef HalfEdgeMeshT HalfEdgeMesh; + typedef typename HalfEdgeMesh::VertexDataCloud VertexDataCloud; + typedef typename HalfEdgeMesh::VertexIndex VertexIndex; + typedef typename HalfEdgeMesh::VertexIndices VertexIndices; + + BOOST_STATIC_ASSERT (HalfEdgeMesh::HasVertexData::value); // Output mesh must have data associated with the vertices! + + VertexDataCloud vertices; + pcl::fromROSMsg (face_vertex_mesh.cloud, vertices); + + half_edge_mesh.reserveVertices (vertices.size ()); + half_edge_mesh.reserveEdges (3 * face_vertex_mesh.polygons.size ()); + half_edge_mesh.reserveFaces ( face_vertex_mesh.polygons.size ()); - face_vertex_mesh.polygons.reserve (half_edge_mesh.sizeFaces ()); - for (size_t i=0; i +#include +#include #include #include #include @@ -49,128 +51,296 @@ //////////////////////////////////////////////////////////////////////////////// -TEST (TestMeshConversion, HalfEdgeMeshToFaceVertexMesh) +template +class TestMeshConversion : public ::testing::Test { - typedef pcl::geometry::DefaultMeshTraits MeshTraits; - typedef pcl::geometry::PolygonMesh Mesh; + protected: - // 2 - 1 7 - 6 17 - 16 // - // \ / | | / \ // - // 0 8 - 5 - 11 12 15 // - // / \ | | \ / // - // 3 - 4 9 - 10 13 - 14 // + typedef MeshTraitsT MeshTraits; - Mesh half_edge_mesh; - Mesh::VertexIndices vi, hexagon; - pcl::PointXYZRGBNormal pt; - pcl::PointCloud expected_cloud; - for (unsigned int i=0; i<18; ++i) - { - pt.x = static_cast (10 * i); - pt.y = static_cast (20 * i); - pt.z = static_cast (30 * i); + // 2 - 1 7 - 6 17 - 16 // + // \ / | | / \ // + // 0 8 - 5 - 11 12 15 // + // / \ | | \ / // + // 3 - 4 9 - 10 13 - 14 // + void + SetUp () + { + // Vertices + pcl::PointXYZRGBNormal pt; + for (unsigned int i=0; i<18; ++i) + { + pt.x = static_cast (10 * i); + pt.y = static_cast (20 * i); + pt.z = static_cast (30 * i); - pt.normal_x = static_cast (100 * i); - pt.normal_y = static_cast (200 * i); - pt.normal_z = static_cast (300 * i); + pt.normal_x = static_cast (100 * i); + pt.normal_y = static_cast (200 * i); + pt.normal_z = static_cast (300 * i); - pt.r = static_cast (1 * i); - pt.g = static_cast (2 * i); - pt.b = static_cast (3 * i); + pt.r = static_cast (1 * i); + pt.g = static_cast (2 * i); + pt.b = static_cast (3 * i); - expected_cloud.push_back (pt); - vi.push_back (half_edge_mesh.addVertex (pt)); - } + vertices_.push_back (pt); + } + + // Faces + std::vector face; + + face.push_back (0); + face.push_back (1); + face.push_back (2); + manifold_faces_.push_back (face); + non_manifold_faces_.push_back (face); + + face.clear (); + face.push_back (0); + face.push_back (3); + face.push_back (4); + non_manifold_faces_.push_back (face); + + face.clear (); + face.push_back (5); + face.push_back (6); + face.push_back (7); + face.push_back (8); + manifold_faces_.push_back (face); + non_manifold_faces_.push_back (face); + + face.clear (); + face.push_back ( 5); + face.push_back ( 9); + face.push_back (10); + face.push_back (11); + non_manifold_faces_.push_back (face); + + face.clear (); + face.push_back (12); + face.push_back (13); + face.push_back (14); + face.push_back (15); + face.push_back (16); + face.push_back (17); + manifold_faces_.push_back (face); + non_manifold_faces_.push_back (face); + } + + pcl::PointCloud vertices_; + std::vector > non_manifold_faces_; + std::vector > manifold_faces_; + + public: + EIGEN_MAKE_ALIGNED_OPERATOR_NEW +}; + +template +struct MeshTraits +{ + typedef pcl::PointXYZRGBNormal VertexData; + typedef pcl::geometry::NoData HalfEdgeData; + typedef pcl::geometry::NoData EdgeData; + typedef pcl::geometry::NoData FaceData; + typedef boost::integral_constant IsManifold; +}; + +typedef MeshTraits ManifoldMeshTraits; +typedef MeshTraits NonManifoldMeshTraits; + +typedef testing::Types MeshTraitsTypes; + +TYPED_TEST_CASE (TestMeshConversion, MeshTraitsTypes); + +//////////////////////////////////////////////////////////////////////////////// + +TYPED_TEST (TestMeshConversion, HalfEdgeMeshToFaceVertexMesh) +{ + typedef typename TestFixture::MeshTraits Traits; + typedef pcl::geometry::PolygonMesh Mesh; + typedef typename Mesh::VertexIndex VertexIndex; + typedef typename Mesh::VertexIndices VertexIndices; + + const std::vector > faces = + Mesh::IsManifold::value ? TestFixture::manifold_faces_ : + TestFixture::non_manifold_faces_; - ASSERT_TRUE (half_edge_mesh.addFace (vi [0], vi [1], vi [ 2]).isValid ()); - ASSERT_TRUE (half_edge_mesh.addFace (vi [0], vi [3], vi [ 4]).isValid ()); + // Generate the mesh + Mesh half_edge_mesh; + VertexIndices vi; + + for (size_t i=0; i (faces [i][j]))); + } - hexagon.push_back (vi [12]); - hexagon.push_back (vi [13]); - hexagon.push_back (vi [14]); - hexagon.push_back (vi [15]); - hexagon.push_back (vi [16]); - hexagon.push_back (vi [17]); - ASSERT_TRUE (half_edge_mesh.addFace (hexagon).isValid ()); + ASSERT_TRUE (half_edge_mesh.addFace (vi).isValid ()) << "Face number " << i; + } - // The conversion - pcl::geometry::MeshConversion conv; + // Convert pcl::PolygonMesh face_vertex_mesh; - conv.toFaceVertexMesh (half_edge_mesh, face_vertex_mesh); + pcl::geometry::toFaceVertexMesh (half_edge_mesh, face_vertex_mesh); // Check if the cloud got copied correctly. pcl::PointCloud converted_cloud; pcl::fromROSMsg (face_vertex_mesh.cloud, converted_cloud); - ASSERT_EQ (expected_cloud.size (), converted_cloud.size ()); - for (unsigned int i=0; i actual, expected; - - actual = face_vertex_mesh.polygons [0].vertices; - expected.push_back (0); - expected.push_back (1); - expected.push_back (2); - EXPECT_TRUE (isCircularPermutation (expected, actual)); - - actual = face_vertex_mesh.polygons [1].vertices; - expected.clear (); - expected.push_back (0); - expected.push_back (3); - expected.push_back (4); - EXPECT_TRUE (isCircularPermutation (expected, actual)); - - actual = face_vertex_mesh.polygons [2].vertices; - expected.clear (); - expected.push_back (5); - expected.push_back (6); - expected.push_back (7); - expected.push_back (8); - EXPECT_TRUE (isCircularPermutation (expected, actual)); - - actual = face_vertex_mesh.polygons [3].vertices; - expected.clear (); - expected.push_back ( 5); - expected.push_back ( 9); - expected.push_back (10); - expected.push_back (11); - EXPECT_TRUE (isCircularPermutation (expected, actual)); - - actual = face_vertex_mesh.polygons [4].vertices; - expected.clear (); - expected.push_back (12); - expected.push_back (13); - expected.push_back (14); - expected.push_back (15); - expected.push_back (16); - expected.push_back (17); - EXPECT_TRUE (isCircularPermutation (expected, actual)); + ASSERT_EQ (faces.size (), face_vertex_mesh.polygons.size ()); + for (size_t i=0; i Mesh; + typedef typename Mesh::FaceIndex FaceIndex; + typedef typename Mesh::VertexAroundFaceCirculator VAFC; + + // Generate the mesh + pcl::PolygonMesh face_vertex_mesh; + pcl::toROSMsg (TestFixture::vertices_, face_vertex_mesh.cloud); + pcl::Vertices face; + for (size_t i=0; i > expected_faces = + Mesh::IsManifold::value ? TestFixture::manifold_faces_ : + TestFixture::non_manifold_faces_; + + ASSERT_EQ (expected_faces.size (), half_edge_mesh.sizeFaces ()); + + std::vector converted_face; + for (size_t i=0; i (circ.getTargetIndex ().get ())); + } while (++circ != circ_end); + + EXPECT_TRUE (isCircularPermutation (expected_faces [i], converted_face)) << "Face number " << i; + } +} + +//////////////////////////////////////////////////////////////////////////////// + +// This test should not compile (mesh has no vertex data). + +//TEST (TestFaceVertexMeshToHalfEdgeMesh, NoVertexData) +//{ +// typedef pcl::geometry::DefaultMeshTraits <> MeshTraits; +// typedef pcl::geometry::PolygonMesh Mesh; + +// Mesh half_edge_mesh; +// pcl::PolygonMesh face_vertex_mesh; + +// pcl::geometry::toHalfEdgeMesh (face_vertex_mesh, half_edge_mesh); +//} + +//////////////////////////////////////////////////////////////////////////////// + +TYPED_TEST (TestMeshConversion, NonConvertibleCases) +{ + typedef typename TestFixture::MeshTraits Traits; + typedef pcl::geometry::TriangleMesh TriangleMesh; + typedef pcl::geometry::QuadMesh QuadMesh; + typedef pcl::geometry::PolygonMesh PolygonMesh; + + // Generate the mesh + pcl::PolygonMesh face_vertex_mesh; + pcl::toROSMsg (TestFixture::vertices_, face_vertex_mesh.cloud); + pcl::Vertices face; + for (size_t i=0; i