Commit 0c555299 authored by Jan Möbius's avatar Jan Möbius

Merge branch 'Eigen' into 'master'

OpenMesh Eigen support

Closes #59

See merge request !207
parents ae8a4989 87a4f6f1
Pipeline #8273 passed with stages
in 100 minutes and 15 seconds
......@@ -19,6 +19,7 @@
<li>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)</li>
<li>PolyConnectivity: Fixed PolyConnectivity is_collapse_ok, missing some configurations (Thanks to Simon Flöry for the patch)</li>
<li>Connectivity type is now set at compile time</li>
<li>Added header to interface with Eigen3 vectors as the basic type (Documentation on how to use it is also integrated)</li>
</ul>
<b>IO</b>
......
......@@ -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
......
......@@ -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 <OpenMesh/Core/Geometry/EigenVectorT.hh>
\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<EigenTraits>;
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
*/
......
# - 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)
/* ========================================================================= *
* *
* 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 <Eigen/Core>
#include <Eigen/Dense>
#include <Eigen/Geometry>
namespace OpenMesh {
template <typename _Scalar, int _Rows, int _Cols, int _Options>
struct vector_traits<Eigen::Matrix<_Scalar, _Rows, _Cols, _Options>> {
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>
typename Derived::Scalar dot(const MatrixBase<Derived> &x,
const MatrixBase<Derived> &y) {
return x.dot(y);
}
template <typename Derived>
typename MatrixBase< Derived >::PlainObject cross(const MatrixBase<Derived> &x, const MatrixBase<Derived> &y) {
return x.cross(y);
}
template <typename Derived>
typename Derived::Scalar norm(const MatrixBase<Derived> &x) {
return x.norm();
}
template <typename Derived>
typename Derived::Scalar sqrnorm(const MatrixBase<Derived> &x) {
return x.dot(x);
}
template <typename Derived>
MatrixBase<Derived> normalize(MatrixBase<Derived> &x) {
x /= x.norm();
return x;
}
template <typename Derived>
MatrixBase<Derived> &vectorize(MatrixBase<Derived> &x,
typename Derived::Scalar const &val) {
x.fill(val);
return x;
}
} // namespace Eigen
......@@ -154,7 +154,15 @@ typename PolyMeshT<Kernel>::Normal
PolyMeshT<Kernel>::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 <class Kernel>
typename PolyMeshT<Kernel>::Normal
PolyMeshT<Kernel>::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;
}
//-----------------------------------------------------------------------------
......
......@@ -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")
......
#ifdef ENABLE_EIGEN3_TEST
#include <gtest/gtest.h>
#include <Unittests/unittests_common.hh>
#include <iostream>
#include <OpenMesh/Core/Mesh/Traits.hh>
#include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh>
#include <OpenMesh/Core/Geometry/EigenVectorT.hh>
struct EigenTraits : OpenMesh::DefaultTraits {
using Point = Eigen::Vector3d;
using Normal = Eigen::Vector3d;
using TexCoord2D = Eigen::Vector2d;
};
using EigenTriMesh = OpenMesh::TriMesh_ArrayKernelT<EigenTraits>;
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<EigenTriMesh::VertexHandle> 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<EigenTriMesh::VertexHandle> 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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment