diff --git a/Doc/changelog.docu b/Doc/changelog.docu
index 00fbc7f5..b497a8b6 100644
--- a/Doc/changelog.docu
+++ b/Doc/changelog.docu
@@ -19,6 +19,7 @@
TriConnectivity: Added two functions split_edge and split_edge_copy to mask the PolyConnectivity functions of the same name (Prevents creation of valence 2 vertices on trimeshes)
PolyConnectivity: Fixed PolyConnectivity is_collapse_ok, missing some configurations (Thanks to Simon Flöry for the patch)
Connectivity type is now set at compile time
+Added header to interface with Eigen3 vectors as the basic type (Documentation on how to use it is also integrated)
IO
diff --git a/Doc/mainpage.docu b/Doc/mainpage.docu
index 9ce3e3dd..47ddfa3f 100644
--- a/Doc/mainpage.docu
+++ b/Doc/mainpage.docu
@@ -109,6 +109,7 @@ repeatedly replacing each vertex' position by the center of gravity
\li \subpage mesh_operations
\li \subpage mesh_hierarchy
\li \subpage mesh_type
+\li \subpage mesh_eigen
\page additional_information Additional Information on OpenMesh
diff --git a/Doc/mesh.docu b/Doc/mesh.docu
index 488e06da..a506f300 100644
--- a/Doc/mesh.docu
+++ b/Doc/mesh.docu
@@ -555,6 +555,45 @@ curvature, i.e. vertex color.
That's it.
+//-----------------------------------------------------------------------------
+
+/** \page mesh_eigen Specifying an OpenMesh using Eigen3 vectors
+
+This section will show how to build your own custom mesh type using
+Eigen3 vectors for points, normals or other entities.
+
+First of all you need to include the Eigen header shipped with OpenMesh:
+\code
+#include
+\endcode
+
+This header contains the external functions and vector traits used by
+OpenMesh.
+
+Afterwards you can specify your mesh:
+
+\code
+struct EigenTraits : OpenMesh::DefaultTraits {
+ using Point = Eigen::Vector3d;
+ using Normal = Eigen::Vector3d;
+
+ using TexCoord2D = Eigen::Vector2d;
+};
+
+using EigenTriMesh = OpenMesh::TriMesh_ArrayKernelT;
+
+EigenTriMesh mesh;
+
+\endcode
+
+Now you can use mesh as any other OpenMesh while using Eigen vectors
+as the underlying data type.
+
+\note OpenMesh uses stl vectors for storing its data. This might lead to errors
+ regarding memory alignment with sse instructions:
+ http://eigen.tuxfamily.org/dox/group__TopicStlContainers.html
+ You might need to define -DEIGEN_DONT_VECTORIZE
+
*/
diff --git a/cmake/FindEIGEN3.cmake b/cmake/FindEIGEN3.cmake
new file mode 100644
index 00000000..fa8fc4f7
--- /dev/null
+++ b/cmake/FindEIGEN3.cmake
@@ -0,0 +1,70 @@
+# - Try to find EIGEN3
+# Once done this will define
+# EIGEN3_FOUND - System has EIGEN3
+# EIGEN3_INCLUDE_DIRS - The EIGEN3 include directories
+
+if (EIGEN3_INCLUDE_DIR)
+ # in cache already
+ set(EIGEN3_FOUND TRUE)
+ set(EIGEN3_INCLUDE_DIRS "${EIGEN3_INCLUDE_DIR}" )
+else (EIGEN3_INCLUDE_DIR)
+
+# Check if the base path is set
+if ( NOT CMAKE_WINDOWS_LIBS_DIR )
+ # This is the base directory for windows library search used in the finders we shipp.
+ set(CMAKE_WINDOWS_LIBS_DIR "c:/libs" CACHE STRING "Default Library search dir on windows." )
+endif()
+
+if ( CMAKE_GENERATOR MATCHES "^Visual Studio 11.*Win64" )
+ SET(VS_SEARCH_PATH "${CMAKE_WINDOWS_LIBS_DIR}/vs2012/x64/")
+elseif ( CMAKE_GENERATOR MATCHES "^Visual Studio 11.*" )
+ SET(VS_SEARCH_PATH "${CMAKE_WINDOWS_LIBS_DIR}/vs2012/x32/")
+elseif ( CMAKE_GENERATOR MATCHES "^Visual Studio 12.*Win64" )
+ SET(VS_SEARCH_PATH "${CMAKE_WINDOWS_LIBS_DIR}/vs2013/x64/")
+elseif ( CMAKE_GENERATOR MATCHES "^Visual Studio 12.*" )
+ SET(VS_SEARCH_PATH "${CMAKE_WINDOWS_LIBS_DIR}/vs2013/x32/")
+elseif ( CMAKE_GENERATOR MATCHES "^Visual Studio 14.*Win64" )
+ SET(VS_SEARCH_PATH "${CMAKE_WINDOWS_LIBS_DIR}/vs2015/x64/")
+elseif ( CMAKE_GENERATOR MATCHES "^Visual Studio 14.*" )
+ SET(VS_SEARCH_PATH "${CMAKE_WINDOWS_LIBS_DIR}/vs2015/x32/")
+elseif ( CMAKE_GENERATOR MATCHES "^Visual Studio 15.*Win64" )
+ SET(VS_SEARCH_PATH "${CMAKE_WINDOWS_LIBS_DIR}/vs2017/x64/")
+elseif ( CMAKE_GENERATOR MATCHES "^Visual Studio 15.*" )
+ SET(VS_SEARCH_PATH "${CMAKE_WINDOWS_LIBS_DIR}/vs2017/x32/")
+endif()
+
+
+find_path( EIGEN3_INCLUDE_DIR
+ NAMES Eigen/Dense
+ PATHS $ENV{EIGEN_DIR}
+ /usr/include/eigen3
+ /usr/local/include
+ /usr/local/include/eigen3/
+ /opt/local/include/eigen3/
+ "${CMAKE_WINDOWS_LIBS_DIR}/general/Eigen-3.3.4"
+ "${CMAKE_WINDOWS_LIBS_DIR}/general/Eigen-3.2.8"
+ "${CMAKE_WINDOWS_LIBS_DIR}/general/Eigen-3.2.6"
+ "${CMAKE_WINDOWS_LIBS_DIR}/Eigen-3.2.6"
+ "${CMAKE_WINDOWS_LIBS_DIR}/Eigen-3.2.6/include"
+ "${CMAKE_WINDOWS_LIBS_DIR}/Eigen-3.2.1"
+ "${CMAKE_WINDOWS_LIBS_DIR}/Eigen-3.2.1/include"
+ "${CMAKE_WINDOWS_LIBS_DIR}/Eigen-3.2/include"
+ "${CMAKE_WINDOWS_LIBS_DIR}/eigen3/include"
+ "${CMAKE_WINDOWS_LIBS_DIR}/eigen/include"
+ ${PROJECT_SOURCE_DIR}/MacOS/Libs/eigen3/include
+ ../../External/include
+ ${module_file_path}/../../../External/include
+ )
+
+set(EIGEN3_INCLUDE_DIRS "${EIGEN3_INCLUDE_DIR}" )
+
+
+include(FindPackageHandleStandardArgs)
+# handle the QUIETLY and REQUIRED arguments and set LIBCPLEX_FOUND to TRUE
+# if all listed variables are TRUE
+find_package_handle_standard_args(EIGEN3 DEFAULT_MSG
+ EIGEN3_INCLUDE_DIR)
+
+mark_as_advanced(EIGEN3_INCLUDE_DIR)
+
+endif(EIGEN3_INCLUDE_DIR)
diff --git a/src/OpenMesh/Core/Geometry/EigenVectorT.hh b/src/OpenMesh/Core/Geometry/EigenVectorT.hh
new file mode 100644
index 00000000..fb7c3be3
--- /dev/null
+++ b/src/OpenMesh/Core/Geometry/EigenVectorT.hh
@@ -0,0 +1,104 @@
+/* ========================================================================= *
+ * *
+ * OpenMesh *
+ * Copyright (c) 2001-2015, RWTH-Aachen University *
+ * Department of Computer Graphics and Multimedia *
+ * All rights reserved. *
+ * www.openmesh.org *
+ * *
+ *---------------------------------------------------------------------------*
+ * This file is part of OpenMesh. *
+ *---------------------------------------------------------------------------*
+ * *
+ * Redistribution and use in source and binary forms, with or without *
+ * modification, are permitted provided that the following conditions *
+ * are met: *
+ * *
+ * 1. Redistributions of source code must retain the above copyright notice, *
+ * this list of conditions and the following disclaimer. *
+ * *
+ * 2. Redistributions in binary form must reproduce the above copyright *
+ * notice, this list of conditions and the following disclaimer in the *
+ * documentation and/or other materials provided with the distribution. *
+ * *
+ * 3. Neither the name of the copyright holder nor the names of its *
+ * contributors may be used to endorse or promote products derived from *
+ * this software without specific prior written permission. *
+ * *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
+ * *
+ * ========================================================================= */
+
+/** This file contains all code required to use Eigen3 vectors as Mesh
+ * vectors
+ */
+#pragma once
+
+#include
+#include
+#include
+
+
+namespace OpenMesh {
+ template
+ struct vector_traits> {
+ static_assert(_Rows != Eigen::Dynamic && _Cols != Eigen::Dynamic,
+ "Should not use dynamic vectors.");
+ static_assert(_Rows == 1 || _Cols == 1, "Should not use matrices.");
+
+ using vector_type = Eigen::Matrix<_Scalar, _Rows, _Cols, _Options>;
+ using value_type = _Scalar;
+ static const size_t size_ = _Rows * _Cols;
+ static size_t size() { return size_; }
+};
+
+} // namespace OpenMesh
+
+namespace Eigen {
+
+ template
+ typename Derived::Scalar dot(const MatrixBase &x,
+ const MatrixBase &y) {
+ return x.dot(y);
+ }
+
+ template
+ typename MatrixBase< Derived >::PlainObject cross(const MatrixBase &x, const MatrixBase &y) {
+ return x.cross(y);
+ }
+
+ template
+ typename Derived::Scalar norm(const MatrixBase &x) {
+ return x.norm();
+ }
+
+ template
+ typename Derived::Scalar sqrnorm(const MatrixBase &x) {
+ return x.dot(x);
+ }
+
+ template
+ MatrixBase normalize(MatrixBase &x) {
+ x /= x.norm();
+ return x;
+ }
+
+ template
+ MatrixBase &vectorize(MatrixBase &x,
+ typename Derived::Scalar const &val) {
+ x.fill(val);
+ return x;
+ }
+
+} // namespace Eigen
+
diff --git a/src/OpenMesh/Core/Mesh/PolyMeshT.cc b/src/OpenMesh/Core/Mesh/PolyMeshT.cc
index 689a2c20..78e6b004 100644
--- a/src/OpenMesh/Core/Mesh/PolyMeshT.cc
+++ b/src/OpenMesh/Core/Mesh/PolyMeshT.cc
@@ -154,7 +154,15 @@ typename PolyMeshT::Normal
PolyMeshT::calc_face_normal_impl(FaceHandle, PointIsNot3DTag) const
{
// Dummy fallback implementation
- return Normal(typename Normal::value_type(0));
+ // Returns just an initialized all 0 normal
+ // This function is only used if we don't hate a matching implementation
+ // for normal computation with the current vector type defined in the mesh traits
+
+ assert(false);
+
+ Normal normal;
+ vectorize(normal,0);
+ return normal;
}
//-----------------------------------------------------------------------------
@@ -212,7 +220,17 @@ template
typename PolyMeshT::Normal
PolyMeshT::calc_face_normal_impl(const Point&, const Point&, const Point&, PointIsNot3DTag) const
{
- return Normal(typename Normal::value_type(0));
+
+ // Dummy fallback implementation
+ // Returns just an initialized all 0 normal
+ // This function is only used if we don't hate a matching implementation
+ // for normal computation with the current vector type defined in the mesh traits
+
+ assert(false);
+
+ Normal normal;
+ vectorize(normal,0);
+ return normal;
}
//-----------------------------------------------------------------------------
diff --git a/src/Unittests/CMakeLists.txt b/src/Unittests/CMakeLists.txt
index f73e4ddd..7173ddde 100644
--- a/src/Unittests/CMakeLists.txt
+++ b/src/Unittests/CMakeLists.txt
@@ -17,25 +17,35 @@ if ( OPENMESH_BUILD_UNIT_TESTS )
enable_testing()
+ find_package(EIGEN3)
+
# Set correct include paths so that the compiler can find the headers
- include_directories(${GTEST_INCLUDE_DIRS})
+ include_directories(${GTEST_INCLUDE_DIRS} )
+
+
# set additional link directories
link_directories(${GTEST_LIBRARY_DIR} )
+
+ if (EIGEN3_FOUND)
+ add_definitions( -DENABLE_EIGEN3_TEST )
+ include_directories(${EIGEN3_INCLUDE_DIR})
+ endif()
+
if ( CMAKE_GENERATOR MATCHES "^Visual Studio 11.*" )
add_definitions( /D _VARIADIC_MAX=10 )
endif()
# Create new target named unittests_hexmeshing
FILE(GLOB UNITTEST_SRC *.cc)
- # Create unittest executable
- acg_add_executable(unittests ${UNITTEST_SRC})
- acg_add_executable(unittests_customvec ${UNITTEST_SRC})
- target_compile_definitions(unittests_customvec PRIVATE TEST_CUSTOM_TRAITS)
+ # Create unittest executable
+ acg_add_executable(unittests ${UNITTEST_SRC})
+ acg_add_executable(unittests_customvec ${UNITTEST_SRC})
+ target_compile_definitions(unittests_customvec PRIVATE TEST_CUSTOM_TRAITS)
- # For the unittest we don't want the install rpath as set by acg_add_executable
- set_target_properties ( unittests PROPERTIES BUILD_WITH_INSTALL_RPATH 0 )
- set_target_properties ( unittests_customvec PROPERTIES BUILD_WITH_INSTALL_RPATH 0 )
+ # For the unittest we don't want the install rpath as set by acg_add_executable
+ set_target_properties ( unittests PROPERTIES BUILD_WITH_INSTALL_RPATH 0 )
+ set_target_properties ( unittests_customvec PROPERTIES BUILD_WITH_INSTALL_RPATH 0 )
# Set output directory to ${BINARY_DIR}/Unittests
set (OUTPUT_DIR "${CMAKE_BINARY_DIR}/Unittests")
diff --git a/src/Unittests/unittests_eigen3_type.cc b/src/Unittests/unittests_eigen3_type.cc
new file mode 100644
index 00000000..6d263220
--- /dev/null
+++ b/src/Unittests/unittests_eigen3_type.cc
@@ -0,0 +1,237 @@
+
+#ifdef ENABLE_EIGEN3_TEST
+
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+
+struct EigenTraits : OpenMesh::DefaultTraits {
+ using Point = Eigen::Vector3d;
+ using Normal = Eigen::Vector3d;
+
+ using TexCoord2D = Eigen::Vector2d;
+};
+
+using EigenTriMesh = OpenMesh::TriMesh_ArrayKernelT;
+
+namespace {
+
+
+class OpenMeshEigenTest : public testing::Test {
+
+ protected:
+
+ // This function is called before each test is run
+ virtual void SetUp() {
+
+ // Do some initial stuff with the member data here...
+ }
+
+ // This function is called after all tests are through
+ virtual void TearDown() {
+
+
+ }
+
+ EigenTriMesh mesh_;
+};
+
+TEST_F(OpenMeshEigenTest, Test_external_vectorize) {
+
+
+ EigenTriMesh::Normal normal;
+
+ vectorize(normal,2);
+
+ EXPECT_EQ(normal[0],2.0f ) << "Wrong x value";
+ EXPECT_EQ(normal[1],2.0f ) << "Wrong y value";
+ EXPECT_EQ(normal[2],2.0f ) << "Wrong z value";
+
+
+}
+
+TEST_F(OpenMeshEigenTest, Test_external_norm) {
+
+
+ EigenTriMesh::Normal normal(1,0,0);
+
+ EigenTriMesh::Scalar result = norm(normal);
+
+ EXPECT_EQ(result,1.0f ) << "Wrong norm";
+
+ normal = EigenTriMesh::Normal(2,0,0);
+
+ result = norm(normal);
+
+ EXPECT_EQ(result,2.0f ) << "Wrong norm";
+}
+
+TEST_F(OpenMeshEigenTest, Test_external_sqrnorm) {
+
+
+ EigenTriMesh::Normal normal(1,0,0);
+
+ EigenTriMesh::Scalar result = sqrnorm(normal);
+
+ EXPECT_EQ(result,1.0f ) << "Wrong norm";
+
+ normal = EigenTriMesh::Normal(2,0,0);
+
+ result = sqrnorm(normal);
+
+ EXPECT_EQ(result,4.0f ) << "Wrong norm";
+}
+
+TEST_F(OpenMeshEigenTest, Test_external_normalize) {
+
+
+ EigenTriMesh::Normal normal(2,0,0);
+
+ normalize(normal);
+
+ EXPECT_EQ(norm(normal),1.0f ) << "Wrong norm after normalization";
+
+ normal = EigenTriMesh::Normal(2,6,9);
+
+ normalize(normal);
+
+ EXPECT_EQ(norm(normal),1.0f ) << "Wrong norm after normalization";
+
+}
+
+TEST_F(OpenMeshEigenTest, Test_external_cross_Product) {
+
+
+ EigenTriMesh::Normal normal1(1,0,0);
+ EigenTriMesh::Normal normal2(1,1,0);
+
+ EigenTriMesh::Normal result = cross(normal1,normal2);
+
+ EXPECT_EQ(result[0],0.0f ) << "Wrong result x direction";
+ EXPECT_EQ(result[1],0.0f ) << "Wrong result y direction";
+ EXPECT_EQ(result[2],1.0f ) << "Wrong result z direction";
+}
+
+TEST_F(OpenMeshEigenTest, Test_external_dot_Product) {
+
+
+ EigenTriMesh::Normal normal1(1,0,0);
+ EigenTriMesh::Normal normal2(1,1,0);
+ EigenTriMesh::Normal normal3(1,1,1);
+ EigenTriMesh::Normal normal4(2,4,6);
+
+ EigenTriMesh::Scalar result = dot(normal1,normal2);
+ EigenTriMesh::Scalar result1 = dot(normal3,normal4);
+
+ EXPECT_EQ(result,1.0f ) << "Wrong dot product";
+ EXPECT_EQ(result1,12.0f ) << "Wrong dot product";
+
+}
+
+
+TEST_F(OpenMeshEigenTest, Test_Basic_setup) {
+
+ // Add some vertices
+ EigenTriMesh::VertexHandle vhandle[4];
+
+ vhandle[0] = mesh_.add_vertex(EigenTriMesh::Point(0, 0, 0));
+ vhandle[1] = mesh_.add_vertex(EigenTriMesh::Point(0, 1, 0));
+ vhandle[2] = mesh_.add_vertex(EigenTriMesh::Point(1, 1, 0));
+ vhandle[3] = mesh_.add_vertex(EigenTriMesh::Point(1, 0, 0));
+
+ // Add two faces
+ std::vector face_vhandles;
+
+ face_vhandles.push_back(vhandle[2]);
+ face_vhandles.push_back(vhandle[1]);
+ face_vhandles.push_back(vhandle[0]);
+
+ mesh_.add_face(face_vhandles);
+
+ face_vhandles.clear();
+
+ face_vhandles.push_back(vhandle[2]);
+ face_vhandles.push_back(vhandle[0]);
+ face_vhandles.push_back(vhandle[3]);
+ mesh_.add_face(face_vhandles);
+
+
+ EXPECT_EQ(mesh_.n_faces(),2) << "Wrong number of faces";
+
+}
+
+TEST_F(OpenMeshEigenTest, test_normal_computation) {
+
+ // Add some vertices
+ EigenTriMesh::VertexHandle vhandle[4];
+
+ mesh_.request_vertex_normals();
+ mesh_.request_face_normals();
+
+ vhandle[0] = mesh_.add_vertex(EigenTriMesh::Point(0, 0, 0));
+ vhandle[1] = mesh_.add_vertex(EigenTriMesh::Point(0, 1, 0));
+ vhandle[2] = mesh_.add_vertex(EigenTriMesh::Point(1, 1, 0));
+ vhandle[3] = mesh_.add_vertex(EigenTriMesh::Point(1, 0, 0));
+
+ // Add two faces
+ std::vector face_vhandles;
+
+ face_vhandles.push_back(vhandle[2]);
+ face_vhandles.push_back(vhandle[1]);
+ face_vhandles.push_back(vhandle[0]);
+
+ EigenTriMesh::FaceHandle face1 = mesh_.add_face(face_vhandles);
+
+ face_vhandles.clear();
+
+ face_vhandles.push_back(vhandle[2]);
+ face_vhandles.push_back(vhandle[0]);
+ face_vhandles.push_back(vhandle[3]);
+ EigenTriMesh::FaceHandle face2 = mesh_.add_face(face_vhandles);
+
+ mesh_.update_face_normals();
+
+
+ EXPECT_EQ(mesh_.n_faces(),2) << "Wrong number of faces";
+
+ EigenTriMesh::Normal normal = mesh_.normal(face1);
+
+ EXPECT_EQ(normal[0],0.0f ) << "Wrong normal x direction";
+ EXPECT_EQ(normal[1],0.0f ) << "Wrong normal y direction";
+ EXPECT_EQ(normal[2],1.0f ) << "Wrong normal z direction";
+
+ normal = mesh_.normal(face2);
+
+ EXPECT_EQ(normal[0],0.0f ) << "Wrong normal x direction";
+ EXPECT_EQ(normal[1],0.0f ) << "Wrong normal y direction";
+ EXPECT_EQ(normal[2],1.0f ) << "Wrong normal z direction";
+
+}
+
+/* Just load a simple mesh file in obj format and count whether
+* the right number of entities has been loaded. Afterwards recompute
+* normals
+*/
+TEST_F(OpenMeshEigenTest, LoadSimpleOFFFile) {
+
+ mesh_.clear();
+
+ bool ok = OpenMesh::IO::read_mesh(mesh_, "cube1.off");
+
+ EXPECT_TRUE(ok);
+
+ EXPECT_EQ(7526u , mesh_.n_vertices()) << "The number of loaded vertices is not correct!";
+ EXPECT_EQ(22572u, mesh_.n_edges()) << "The number of loaded edges is not correct!";
+ EXPECT_EQ(15048u, mesh_.n_faces()) << "The number of loaded faces is not correct!";
+
+ mesh_.update_normals();
+}
+
+}
+
+#endif