From 8a19cd851e54b4c26549d8ab1f1983a7f1db16a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20M=C3=B6bius?= Date: Tue, 16 Sep 2014 11:15:13 +0000 Subject: [PATCH] Python source git-svn-id: http://www.openmesh.org/svnrepo/OpenMesh/trunk@1171 fdac6126-5c0c-442c-9429-916003d36597 --- src/Python/Bindings.cc | 148 +++ src/Python/Bindings.hh | 48 + src/Python/CMakeLists.txt | 202 ++++ src/Python/Circulator.hh | 98 ++ src/Python/Example/CMakeLists.txt | 27 + src/Python/Example/Example.cc | 22 + src/Python/InputOutput.hh | 115 +++ src/Python/Iterator.hh | 121 +++ src/Python/Mesh.hh | 931 ++++++++++++++++++ src/Python/PropertyManager.hh | 143 +++ src/Python/Unittests/test_add_face.py | 333 +++++++ src/Python/Unittests/test_boundary.py | 86 ++ src/Python/Unittests/test_delete_face.py | 530 ++++++++++ src/Python/Unittests/test_property.py | 289 ++++++ src/Python/Unittests/test_python.py | 71 ++ src/Python/Unittests/test_read_write_obj.py | 229 +++++ src/Python/Unittests/test_read_write_off.py | 163 +++ src/Python/Unittests/test_read_write_om.py | 201 ++++ src/Python/Unittests/test_read_write_ply.py | 342 +++++++ src/Python/Unittests/test_read_write_stl.py | 75 ++ src/Python/Unittests/test_split_copy.py | 87 ++ ...tor_current_halfedge_handle_replacement.py | 269 +++++ .../test_trimesh_circulator_face_edge.py | 50 + .../test_trimesh_circulator_face_face.py | 156 +++ .../test_trimesh_circulator_face_halfedge.py | 50 + .../test_trimesh_circulator_face_vertex.py | 48 + .../test_trimesh_circulator_halfedge_loop.py | 131 +++ .../test_trimesh_circulator_vertex_edge.py | 55 ++ .../test_trimesh_circulator_vertex_face.py | 93 ++ ...est_trimesh_circulator_vertex_ihalfedge.py | 80 ++ ...est_trimesh_circulator_vertex_ohalfedge.py | 80 ++ .../test_trimesh_circulator_vertex_vertex.py | 56 ++ src/Python/Unittests/test_trimesh_collapse.py | 516 ++++++++++ .../test_trimesh_garbage_collection.py | 168 ++++ .../Unittests/test_trimesh_iterators.py | 396 ++++++++ .../test_trimesh_normal_calculations.py | 103 ++ src/Python/Unittests/test_trimesh_others.py | 129 +++ src/Python/Unittests/test_vector_type.py | 30 + src/Python/Vector.hh | 173 ++++ 39 files changed, 6844 insertions(+) create mode 100644 src/Python/Bindings.cc create mode 100644 src/Python/Bindings.hh create mode 100644 src/Python/CMakeLists.txt create mode 100644 src/Python/Circulator.hh create mode 100644 src/Python/Example/CMakeLists.txt create mode 100644 src/Python/Example/Example.cc create mode 100644 src/Python/InputOutput.hh create mode 100644 src/Python/Iterator.hh create mode 100644 src/Python/Mesh.hh create mode 100644 src/Python/PropertyManager.hh create mode 100644 src/Python/Unittests/test_add_face.py create mode 100644 src/Python/Unittests/test_boundary.py create mode 100644 src/Python/Unittests/test_delete_face.py create mode 100755 src/Python/Unittests/test_property.py create mode 100644 src/Python/Unittests/test_python.py create mode 100644 src/Python/Unittests/test_read_write_obj.py create mode 100644 src/Python/Unittests/test_read_write_off.py create mode 100644 src/Python/Unittests/test_read_write_om.py create mode 100644 src/Python/Unittests/test_read_write_ply.py create mode 100644 src/Python/Unittests/test_read_write_stl.py create mode 100644 src/Python/Unittests/test_split_copy.py create mode 100644 src/Python/Unittests/test_trimesh_circulator_current_halfedge_handle_replacement.py create mode 100644 src/Python/Unittests/test_trimesh_circulator_face_edge.py create mode 100644 src/Python/Unittests/test_trimesh_circulator_face_face.py create mode 100644 src/Python/Unittests/test_trimesh_circulator_face_halfedge.py create mode 100644 src/Python/Unittests/test_trimesh_circulator_face_vertex.py create mode 100644 src/Python/Unittests/test_trimesh_circulator_halfedge_loop.py create mode 100644 src/Python/Unittests/test_trimesh_circulator_vertex_edge.py create mode 100644 src/Python/Unittests/test_trimesh_circulator_vertex_face.py create mode 100644 src/Python/Unittests/test_trimesh_circulator_vertex_ihalfedge.py create mode 100644 src/Python/Unittests/test_trimesh_circulator_vertex_ohalfedge.py create mode 100644 src/Python/Unittests/test_trimesh_circulator_vertex_vertex.py create mode 100644 src/Python/Unittests/test_trimesh_collapse.py create mode 100644 src/Python/Unittests/test_trimesh_garbage_collection.py create mode 100755 src/Python/Unittests/test_trimesh_iterators.py create mode 100644 src/Python/Unittests/test_trimesh_normal_calculations.py create mode 100644 src/Python/Unittests/test_trimesh_others.py create mode 100644 src/Python/Unittests/test_vector_type.py create mode 100644 src/Python/Vector.hh diff --git a/src/Python/Bindings.cc b/src/Python/Bindings.cc new file mode 100644 index 00000000..2015618e --- /dev/null +++ b/src/Python/Bindings.cc @@ -0,0 +1,148 @@ +#include "Python/Bindings.hh" +#include "Python/Vector.hh" +#include "Python/Mesh.hh" +#include "Python/Iterator.hh" +#include "Python/Circulator.hh" +#include "Python/PropertyManager.hh" +#include "Python/InputOutput.hh" + +namespace OpenMesh { +namespace Python { + +/** + * Expose mesh items to %Python. + */ +void expose_items() { + class_("Vertex"); + class_("Halfedge"); + class_("Edge"); + class_("Face"); +} + +/** + * Expose item and property handles to %Python. + */ +void expose_handles() { + class_("BaseHandle", init >()) + .def("idx", &BaseHandle::idx) + .def("is_valid", &BaseHandle::is_valid) + .def("reset", &BaseHandle::reset) + .def("invalidate", &BaseHandle::invalidate) + .def(self == self) + .def(self != self) + .def(self < self) + ; + + class_ >("VertexHandle", init >()); + class_ >("HalfedgeHandle", init >()); + class_ >("EdgeHandle", init >()); + class_ >("FaceHandle", init >()); + + class_, bases >("BasePropHandle", init >()); + + class_, bases > >("VPropHandle", init >()) + .def(init&>()); + class_, bases > >("HPropHandle", init >()) + .def(init&>()); + class_, bases > >("EPropHandle", init >()) + .def(init&>()); + class_, bases > >("FPropHandle", init >()) + .def(init&>()); + class_, bases > >("MPropHandle", init >()) + .def(init&>()); +} + + +/** + * Expose the StatusBits enum and StatusInfo class to %Python. + */ +void expose_status_bits_and_info() { + using OpenMesh::Attributes::StatusBits; + using OpenMesh::Attributes::StatusInfo; + + enum_("StatusBits") + .value("DELETED", OpenMesh::Attributes::DELETED) + .value("LOCKED", OpenMesh::Attributes::LOCKED) + .value("SELECTED", OpenMesh::Attributes::SELECTED) + .value("HIDDEN", OpenMesh::Attributes::HIDDEN) + .value("FEATURE", OpenMesh::Attributes::FEATURE) + .value("TAGGED", OpenMesh::Attributes::TAGGED) + .value("TAGGED2", OpenMesh::Attributes::TAGGED2) + .value("FIXEDNONMANIFOLD", OpenMesh::Attributes::FIXEDNONMANIFOLD) + .value("UNUSED", OpenMesh::Attributes::UNUSED) + ; + + class_("StatusInfo") + .def("deleted", &StatusInfo::deleted) + .def("set_deleted", &StatusInfo::set_deleted) + .def("locked", &StatusInfo::locked) + .def("set_locked", &StatusInfo::set_locked) + .def("selected", &StatusInfo::selected) + .def("set_selected", &StatusInfo::set_selected) + .def("hidden", &StatusInfo::hidden) + .def("set_hidden", &StatusInfo::set_hidden) + .def("feature", &StatusInfo::feature) + .def("set_feature", &StatusInfo::set_feature) + .def("tagged", &StatusInfo::tagged) + .def("set_tagged", &StatusInfo::set_tagged) + .def("tagged2", &StatusInfo::tagged2) + .def("set_tagged2", &StatusInfo::set_tagged2) + .def("fixed_nonmanifold", &StatusInfo::fixed_nonmanifold) + .def("set_fixed_nonmanifold", &StatusInfo::set_fixed_nonmanifold) + .def("bits", &StatusInfo::bits) + .def("set_bits", &StatusInfo::set_bits) + .def("is_bit_set", &StatusInfo::is_bit_set) + .def("set_bit", &StatusInfo::set_bit) + .def("unset_bit", &StatusInfo::unset_bit) + .def("change_bit", &StatusInfo::change_bit) + ; +} + +BOOST_PYTHON_MODULE(openmesh) { + expose_items(); + expose_handles(); + expose_status_bits_and_info(); + + expose_vec("Vec2f"); + expose_vec("Vec3f"); + expose_vec("Vec4f"); + expose_vec("Vec2d"); + expose_vec("Vec3d"); + expose_vec("Vec4d"); + + expose_mesh("PolyMesh"); + expose_mesh("TriMesh"); + + expose_iterator("VertexIter"); + expose_iterator("HalfedgeIter"); + expose_iterator("EdgeIter"); + expose_iterator("FaceIter"); + + expose_circulator("VertexVertexIter"); + expose_circulator("VertexIHalfedgeIter"); + expose_circulator("VertexOHalfedgeIter"); + expose_circulator("VertexEdgeIter"); + expose_circulator("VertexFaceIter"); + + expose_circulator("FaceVertexIter"); + expose_circulator("FaceHalfedgeIter"); + expose_circulator("FaceEdgeIter"); + expose_circulator("FaceFaceIter"); + + expose_circulator("HalfedgeLoopIter"); + + typedef IteratorWrapperT VertexIterWrapper; + typedef IteratorWrapperT HalfedgeIterWrapper; + typedef IteratorWrapperT EdgeIterWrapper; + typedef IteratorWrapperT FaceIterWrapper; + + expose_property_manager, VertexHandle, VertexIterWrapper>("VPropertyManager"); + expose_property_manager, HalfedgeHandle, HalfedgeIterWrapper>("HPropertyManager"); + expose_property_manager, EdgeHandle, EdgeIterWrapper>("EPropertyManager"); + expose_property_manager, FaceHandle, FaceIterWrapper>("FPropertyManager"); + + expose_io(); +} + +} // namespace Python +} // namespace OpenMesh diff --git a/src/Python/Bindings.hh b/src/Python/Bindings.hh new file mode 100644 index 00000000..0d37cf5c --- /dev/null +++ b/src/Python/Bindings.hh @@ -0,0 +1,48 @@ +/** @file */ + +#ifndef OPENMESH_PYTHON_BINDINGS_HH +#define OPENMESH_PYTHON_BINDINGS_HH + +#include +#include +#include +#include + +#include "OpenMesh/Core/IO/MeshIO.hh" +#include "OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh" +#include "OpenMesh/Core/Mesh/PolyMesh_ArrayKernelT.hh" + +using namespace boost::python; + +namespace OpenMesh { + +/** + * This namespace contains classes and functions that are used to expose + * %OpenMesh to %Python. + */ +namespace Python { + +/** + * Return value policy for functions that return references to objects that are + * managed by %OpenMesh. + */ +#define OPENMESH_PYTHON_DEFAULT_POLICY return_value_policy() + +struct MeshTraits : public OpenMesh::DefaultTraits { + /** Use double precision points */ + typedef OpenMesh::Vec3d Point; + + /** Use double precision normals */ + typedef OpenMesh::Vec3d Normal; + + /** Use RGBA colors */ + typedef OpenMesh::Vec4f Color; +}; + +typedef OpenMesh::TriMesh_ArrayKernelT TriMesh; +typedef OpenMesh::PolyMesh_ArrayKernelT PolyMesh; + +} // namespace OpenMesh +} // namespace Python + +#endif diff --git a/src/Python/CMakeLists.txt b/src/Python/CMakeLists.txt new file mode 100644 index 00000000..b791cc8f --- /dev/null +++ b/src/Python/CMakeLists.txt @@ -0,0 +1,202 @@ +IF(NOT DEFINED OPENMESH_BUILD_PYTHON_BINDINGS) + SET(OPENMESH_BUILD_PYTHON_BINDINGS TRUE CACHE BOOL "Enable or disable building the Python Bindings.") +ENDIF() + +IF(NOT DEFINED OPENMESH_BUILD_PYTHON_UNIT_TESTS) + SET(OPENMESH_BUILD_PYTHON_UNIT_TESTS FALSE CACHE BOOL "Enable or disable building the Python unit tests.") +ENDIF() + +IF(NOT DEFINED OPENMESH_PYTHON_VERSION) + SET(OPENMESH_PYTHON_VERSION "2.7" CACHE STRING "Choose the Python version that is used to build the Python Bindings.") +ENDIF() + +IF(OPENMESH_BUILD_PYTHON_BINDINGS) + # Look for the python libs + MESSAGE(STATUS "Looking for Python") + FIND_PACKAGE(PythonLibs ${OPENMESH_PYTHON_VERSION} QUIET) + + IF(PYTHONLIBS_FOUND) + MESSAGE(STATUS "Looking for Python -- found") + + # Determine the name of the python component + STRING(REGEX MATCH "^[0-9]+\\.[0-9]+" PYTHON_VERSION ${PYTHONLIBS_VERSION_STRING}) + STRING(REGEX REPLACE "\\." "" PYTHON_VERSION ${PYTHON_VERSION}) + + SET(BOOST_PYTHON_COMPONENT "python-py${PYTHON_VERSION}") + + # Check known boost versions for the python component + SET(BOOST_KNOWN_VERSIONS + "1.56.0" "1.56" "1.55.0" "1.55" "1.54.0" "1.54" + "1.53.0" "1.53" "1.52.0" "1.52" "1.51.0" "1.51" + "1.50.0" "1.50" "1.49.0" "1.49" "1.48.0" "1.48" "1.47.0" "1.47" "1.46.1" + "1.46.0" "1.46" "1.45.0" "1.45" "1.44.0" "1.44" "1.43.0" "1.43" "1.42.0" "1.42" + "1.41.0" "1.41" "1.40.0" "1.40" "1.39.0" "1.39" "1.38.0" "1.38" "1.37.0" "1.37" + "1.36.1" "1.36.0" "1.36" "1.35.1" "1.35.0" "1.35" "1.34.1" "1.34.0" + "1.34" "1.33.1" "1.33.0" "1.33" + ) + + MESSAGE(STATUS "Looking for Boost Python") + + # Look for version specific python component + FOREACH(VERSION ${BOOST_KNOWN_VERSIONS}) + IF(Boost_FOUND) + BREAK() + ENDIF() + FIND_PACKAGE(Boost ${VERSION} EXACT QUIET COMPONENTS ${BOOST_PYTHON_COMPONENT}) + ENDFOREACH() + + # Look for any other python component + FOREACH(VERSION ${BOOST_KNOWN_VERSIONS}) + IF(Boost_FOUND) + BREAK() + ENDIF() + FIND_PACKAGE(Boost ${VERSION} EXACT QUIET COMPONENTS python) + ENDFOREACH() + + IF(Boost_FOUND) + MESSAGE(STATUS "Looking for Boost Python -- found") + + MESSAGE(STATUS "Checking the Boost Python configuration") + + TRY_COMPILE( + COMPILE_WORKS + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/Example/ + Example + CMAKE_FLAGS + "-DINCLUDE_DIRECTORIES:STRING=${PYTHON_INCLUDE_DIRS};${Boost_INCLUDE_DIRS}" + "-DLINK_LIBRARIES:STRING=${PYTHON_LIBRARIES};${Boost_LIBRARIES}" + ) + + IF(COMPILE_WORKS) + # Look for the python interpreter to check if the example works + FIND_PACKAGE(PythonInterp ${PYTHONLIBS_VERSION_STRING} QUIET) + + IF(PYTHONINTERP_FOUND) + EXECUTE_PROCESS( + COMMAND ${PYTHON_EXECUTABLE} -c "from example import *; greet(); planet = World()" + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + RESULT_VARIABLE PYTHON_WORKS + OUTPUT_QUIET + ERROR_QUIET + ) + + IF(PYTHON_WORKS EQUAL 0) + + ### EVERYTHING WORKS ### + + MESSAGE(STATUS "Checking the Boost Python configuration -- done") + + IF("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" AND ${Boost_VERSION} VERSION_LESS 105600) + MESSAGE("There are known issues with Clang and Boost Python 1.55 and below.") + MESSAGE("Please consider updating Boost Python.") + ENDIF() + + SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/Build/python/) + + FILE(GLOB SOURCES *.cc *hh) + ADD_LIBRARY(openmesh SHARED ${SOURCES}) + + INCLUDE_DIRECTORIES(${PYTHON_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS} ../) + + TARGET_LINK_LIBRARIES( + openmesh + OpenMeshCore + OpenMeshTools + ${Boost_LIBRARIES} + ${PYTHON_LIBRARIES} + ) + + SET_TARGET_PROPERTIES( + openmesh + PROPERTIES + PREFIX "" + DEBUG_POSTFIX "" + RELEASE_POSTFIX "" + ) + + IF(APPLE) + SET_TARGET_PROPERTIES(openmesh PROPERTIES SUFFIX ".so") + ENDIF() + + IF(WIN32) + SET_TARGET_PROPERTIES(openmesh PROPERTIES SUFFIX ".pyd") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj") + + SET(OUTPUTS openmesh.exp openmesh.lib openmesh.pyd) + + FOREACH(FILE ${OUTPUTS}) + ADD_CUSTOM_COMMAND( + TARGET openmesh POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${FILE} + ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} + ) + ENDFOREACH() + ENDIF() + + IF(OPENMESH_BUILD_PYTHON_UNIT_TESTS) + SET(UNITTEST_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/Python-Unittests/) + + # Copy unit tests + FILE(GLOB UNITTESTS Unittests/*.py) + FOREACH(TEST ${UNITTESTS}) + FILE(COPY ${TEST} DESTINATION ${UNITTEST_OUTPUT_DIRECTORY}) + ENDFOREACH() + + # Copy test files + FILE(GLOB TESTFILES ${PROJECT_SOURCE_DIR}/src/Unittests/TestFiles/*(.off|.obj|.mtl|.stl|.ply|.om)) + FOREACH(FILE ${TESTFILES}) + FILE(COPY ${FILE} DESTINATION ${UNITTEST_OUTPUT_DIRECTORY}) + ENDFOREACH() + + # Copy library + IF(WIN32) + FOREACH(FILE ${OUTPUTS}) + ADD_CUSTOM_COMMAND( + TARGET openmesh POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${FILE} + ${UNITTEST_OUTPUT_DIRECTORY} + ) + ENDFOREACH() + ELSE() + ADD_CUSTOM_COMMAND( + TARGET openmesh POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_BINARY_DIR}/Build/python/openmesh.so + ${UNITTEST_OUTPUT_DIRECTORY} + ) + ENDIF() + + ADD_TEST( + NAME Python_tests + WORKING_DIRECTORY ${UNITTEST_OUTPUT_DIRECTORY} + COMMAND ${PYTHON_EXECUTABLE} -m unittest discover --verbose + ) + ENDIF() + + ELSE() + MESSAGE("Checking the Boost Python configuration failed!") + MESSAGE("Reason: An error occurred while running a small Boost Python test project.") + MESSAGE("Make sure that your Python and Boost Python libraries match.") + MESSAGE("Skipping Python Bindings.") + ENDIF() + ELSE() + MESSAGE("Checking the Boost Python configuration failed!") + MESSAGE("Reason: Python Interpreter ${PYTHONLIBS_VERSION_STRING} not found.") + MESSAGE("Skipping Python Bindings.") + ENDIF() + ELSE() + MESSAGE("Checking the Boost Python configuration failed!") + MESSAGE("Reason: Building a small Boost Python test project failed.") + MESSAGE("Make sure that your Python and Boost Python libraries match.") + MESSAGE("Skipping Python Bindings.") + ENDIF() + ELSE() + MESSAGE("Boost Python not found! Skipping Python Bindings.") + ENDIF() + ELSE() + MESSAGE("Python not found! Skipping Python Bindings.") + ENDIF() +ENDIF() diff --git a/src/Python/Circulator.hh b/src/Python/Circulator.hh new file mode 100644 index 00000000..5a11e4e2 --- /dev/null +++ b/src/Python/Circulator.hh @@ -0,0 +1,98 @@ +#ifndef OPENMESH_PYTHON_CIRCULATOR_HH +#define OPENMESH_PYTHON_CIRCULATOR_HH + +#include "Python/Bindings.hh" + +namespace OpenMesh { +namespace Python { + +/** + * Wrapper for circulators. + * + * This class template is used to wrap circulators for %Python. It implements + * %Python's iterator protocol (the magic methods \_\_iter\_\_ and + * \_\_next\_\_). + * + * @tparam Circulator A circulator type. + */ +template +class CirculatorWrapperT { + public: + + /** + * Constructor + * + * @param _mesh The mesh that contains the items to iterate over. + * @param _center The handle to the center item. + */ + CirculatorWrapperT(PolyMesh& _mesh, CenterEntityHandle _center) : + circulator_(_mesh, _center) { + } + + /** + * Constructor + * + * @param _mesh The mesh that contains the items to iterate over. + * @param _center The handle to the center item. + */ + CirculatorWrapperT(TriMesh& _mesh, CenterEntityHandle _center) : + circulator_(_mesh, _center) { + } + + /** + * Implementation of %Python's \_\_iter\_\_ magic method. + * + * @return This circulator. + */ + CirculatorWrapperT iter() const { + return *this; + } + + /** + * Implementation of %Python's \_\_next\_\_ magic method. + * + * @return The next item. Raises a %Python StopIteration exception if + * there are no more items. + */ + typename Circulator::value_type next() { + if (circulator_.is_valid()) { + typename Circulator::value_type res = *circulator_; + ++circulator_; + return res; + } + else { + PyErr_SetString(PyExc_StopIteration, "No more data."); + boost::python::throw_error_already_set(); + } + return typename Circulator::value_type(); + } + + private: + Circulator circulator_; +}; + +/** + * Expose a circulator type to %Python. + * + * @tparam Circulator A circulator type. + * + * @param _name The name of the circulator type to be exposed. + * + * @note Circulators are wrapped by CirculatorWrapperT before they are exposed + * to %Python, i.e. they are not exposed directly. This means that circulators + * that are passed from %Python to C++ are instances of CirculatorWrapperT. + */ +template +void expose_circulator(const char *_name) { + class_ >(_name, init()) + .def(init()) + .def("__iter__", &CirculatorWrapperT::iter) + .def("__next__", &CirculatorWrapperT::next) + .def("next", &CirculatorWrapperT::next) + ; +} + +} // namespace OpenMesh +} // namespace Python + +#endif diff --git a/src/Python/Example/CMakeLists.txt b/src/Python/Example/CMakeLists.txt new file mode 100644 index 00000000..53ae0b64 --- /dev/null +++ b/src/Python/Example/CMakeLists.txt @@ -0,0 +1,27 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +PROJECT(Example) + +FILE(GLOB SOURCES *.cc *hh) + +ADD_LIBRARY(example SHARED ${SOURCES}) + +INCLUDE_DIRECTORIES(${INCLUDE_DIRECTORIES}) + +TARGET_LINK_LIBRARIES(example ${LINK_LIBRARIES}) + +SET_TARGET_PROPERTIES( + example + PROPERTIES + PREFIX "" + DEBUG_POSTFIX "" + RELEASE_POSTFIX "" +) + +IF(APPLE) + SET_TARGET_PROPERTIES(example PROPERTIES SUFFIX ".so") +ENDIF() + +IF(WIN32) + SET_TARGET_PROPERTIES(example PROPERTIES SUFFIX ".pyd") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj") +ENDIF() diff --git a/src/Python/Example/Example.cc b/src/Python/Example/Example.cc new file mode 100644 index 00000000..ed1f4b81 --- /dev/null +++ b/src/Python/Example/Example.cc @@ -0,0 +1,22 @@ +#include + +char const * greet() { + return "hello, world"; +} + +struct World { + void set(std::string msg) { this->msg = msg; } + std::string greet() { return msg; } + std::string msg; +}; + +BOOST_PYTHON_MODULE(example) { + using namespace boost::python; + + def("greet", greet); + + class_("World") + .def("greet", &World::greet) + .def("set", &World::set) + ; +} diff --git a/src/Python/InputOutput.hh b/src/Python/InputOutput.hh new file mode 100644 index 00000000..054452c9 --- /dev/null +++ b/src/Python/InputOutput.hh @@ -0,0 +1,115 @@ +#ifndef OPENMESH_PYTHON_INPUTOUTPUT_HH +#define OPENMESH_PYTHON_INPUTOUTPUT_HH + +#include "Python/Bindings.hh" + +namespace OpenMesh { +namespace Python { + +const IO::Options::Flag FLAG_DEFAULT = IO::Options::Default; +const IO::Options::Flag FLAG_BINARY = IO::Options::Binary; +const IO::Options::Flag FLAG_MSB = IO::Options::MSB; +const IO::Options::Flag FLAG_LSB = IO::Options::LSB; +const IO::Options::Flag FLAG_SWAP = IO::Options::Swap; +const IO::Options::Flag FLAG_VERTEXNORMAL = IO::Options::VertexNormal; +const IO::Options::Flag FLAG_VERTEXCOLOR = IO::Options::VertexColor; +const IO::Options::Flag FLAG_VERTEXTEXCOORD = IO::Options::VertexTexCoord; +const IO::Options::Flag FLAG_EDGECOLOR = IO::Options::EdgeColor; +const IO::Options::Flag FLAG_FACENORMAL = IO::Options::FaceNormal; +const IO::Options::Flag FLAG_FACECOLOR = IO::Options::FaceColor; +const IO::Options::Flag FLAG_FACETEXCOORD = IO::Options::FaceTexCoord; +const IO::Options::Flag FLAG_COLORALPHA = IO::Options::ColorAlpha; +const IO::Options::Flag FLAG_COLORFLOAT = IO::Options::ColorFloat; + +BOOST_PYTHON_FUNCTION_OVERLOADS(read_mesh_overloads, IO::read_mesh, 3, 4) +BOOST_PYTHON_FUNCTION_OVERLOADS(write_mesh_overloads, IO::write_mesh, 2, 4) + +/** + * Expose the input/output functions and options to Python. + */ +void expose_io() { + + //====================================================================== + // Functions + //====================================================================== + + bool (*read_mesh_poly )(PolyMesh&, const std::string& ) = &IO::read_mesh; + bool (*read_mesh_poly_options)(PolyMesh&, const std::string&, IO::Options&, bool) = &IO::read_mesh; + bool (*read_mesh_tri )(TriMesh&, const std::string& ) = &IO::read_mesh; + bool (*read_mesh_tri_options )(TriMesh&, const std::string&, IO::Options&, bool) = &IO::read_mesh; + + bool (*write_mesh_poly)(const PolyMesh&, const std::string&, IO::Options, std::streamsize) = &IO::write_mesh; + bool (*write_mesh_tri )(const TriMesh&, const std::string&, IO::Options, std::streamsize) = &IO::write_mesh; + + def("read_mesh", read_mesh_poly); + def("read_mesh", read_mesh_poly_options, read_mesh_overloads()); + def("read_mesh", read_mesh_tri); + def("read_mesh", read_mesh_tri_options, read_mesh_overloads()); + + def("write_mesh", write_mesh_poly, write_mesh_overloads()); + def("write_mesh", write_mesh_tri, write_mesh_overloads()); + + //====================================================================== + // Options + //====================================================================== + + scope scope_options = class_("Options") + .def(init()) + .def("cleanup", &IO::Options::cleanup) + .def("clear", &IO::Options::clear) + .def("is_empty", &IO::Options::is_empty) + .def("check", &IO::Options::check) + .def("is_binary", &IO::Options::is_binary) + .def("vertex_has_normal", &IO::Options::vertex_has_normal) + .def("vertex_has_color", &IO::Options::vertex_has_color) + .def("vertex_has_texcoord", &IO::Options::vertex_has_texcoord) + .def("edge_has_color", &IO::Options::edge_has_color) + .def("face_has_normal", &IO::Options::face_has_normal) + .def("face_has_color", &IO::Options::face_has_color) + .def("face_has_texcoord", &IO::Options::face_has_texcoord) + .def("color_has_alpha", &IO::Options::color_has_alpha) + .def("color_is_float", &IO::Options::color_is_float) + + .def(self == self) + .def(self != self) + .def(self -= IO::Options::Flag()) + .def(self += IO::Options::Flag()) + + .def_readonly("Default", &FLAG_DEFAULT) + .def_readonly("Binary", &FLAG_BINARY) + .def_readonly("MSB", &FLAG_MSB) + .def_readonly("LSB", &FLAG_LSB) + .def_readonly("Swap", &FLAG_SWAP) + .def_readonly("VertexNormal", &FLAG_VERTEXNORMAL) + .def_readonly("VertexColor", &FLAG_VERTEXCOLOR) + .def_readonly("VertexTexCoord", &FLAG_VERTEXTEXCOORD) + .def_readonly("EdgeColor", &FLAG_EDGECOLOR) + .def_readonly("FaceNormal", &FLAG_FACENORMAL) + .def_readonly("FaceColor", &FLAG_FACECOLOR) + .def_readonly("FaceTexCoord", &FLAG_FACETEXCOORD) + .def_readonly("ColorAlpha", &FLAG_COLORALPHA) + .def_readonly("ColorFloat", &FLAG_COLORFLOAT) + ; + + enum_("Flag") + .value("Default", IO::Options::Default) + .value("Binary", IO::Options::Binary) + .value("MSB", IO::Options::MSB) + .value("LSB", IO::Options::LSB) + .value("Swap", IO::Options::Swap) + .value("VertexNormal", IO::Options::VertexNormal) + .value("VertexColor", IO::Options::VertexColor) + .value("VertexTexCoord", IO::Options::VertexTexCoord) + .value("EdgeColor", IO::Options::EdgeColor) + .value("FaceNormal", IO::Options::FaceNormal) + .value("FaceColor", IO::Options::FaceColor) + .value("FaceTexCoord", IO::Options::FaceTexCoord) + .value("ColorAlpha", IO::Options::ColorAlpha) + .value("ColorFloat", IO::Options::ColorFloat) + ; +} + +} // namespace OpenMesh +} // namespace Python + +#endif diff --git a/src/Python/Iterator.hh b/src/Python/Iterator.hh new file mode 100644 index 00000000..848653ad --- /dev/null +++ b/src/Python/Iterator.hh @@ -0,0 +1,121 @@ +#ifndef OPENMESH_PYTHON_ITERATOR_HH +#define OPENMESH_PYTHON_ITERATOR_HH + +#include "Python/Bindings.hh" + +namespace OpenMesh { +namespace Python { + +/** + * Wrapper for mesh item iterators. + * + * This class template is used to wrap mesh item iterators for %Python. It + * implements %Python's iterator protocol (the magic methods \_\_iter\_\_ and + * \_\_next\_\_). + * + * @tparam Iterator An iterator type. + * @tparam n_items A member function pointer that points to the mesh function + * that returns the number of items to iterate over (e.g. n_vertices). + */ +template +class IteratorWrapperT { + public: + + /** + * Constructor + * + * @param _mesh The mesh that contains the items to iterate over. + * @param _hnd The handle of the first item to iterate over. + * @param _skip Specifies if deleted/hidden elements are skipped. + */ + IteratorWrapperT(const PolyMesh& _mesh, typename Iterator::value_type _hnd, bool _skip = false) : + mesh_(_mesh), n_items_(n_items), + iterator_(_mesh, _hnd, _skip), + iterator_end_(_mesh, typename Iterator::value_type(int((_mesh.*n_items)()))) { + } + + /** + * Constructor + * + * @param _mesh The mesh that contains the items to iterate over. + * @param _hnd The handle of the first item to iterate over. + * @param _skip Specifies if deleted/hidden elements are skipped. + */ + IteratorWrapperT(const TriMesh& _mesh, typename Iterator::value_type _hnd, bool _skip = false) : + mesh_(_mesh), n_items_(n_items), + iterator_(_mesh, _hnd, _skip), + iterator_end_(_mesh, typename Iterator::value_type(int((_mesh.*n_items)()))) { + } + + /** + * Implementation of %Python's \_\_iter\_\_ magic method. + * + * @return This iterator. + */ + IteratorWrapperT iter() const { + return *this; + } + + /** + * Implementation of %Python's \_\_next\_\_ magic method. + * + * @return The next item. Raises a %Python StopIteration exception if + * there are no more items. + */ + typename Iterator::value_type next() { + if (iterator_ != iterator_end_) { + typename Iterator::value_type res = *iterator_; + ++iterator_; + return res; + } + else { + PyErr_SetString(PyExc_StopIteration, "No more data."); + boost::python::throw_error_already_set(); + } + return typename Iterator::value_type(); + } + + /** + * Implementation of %Python's \_\_len\_\_ magic method. + * + * @return The number of items in the mesh. + */ + unsigned int len() const { + return (mesh_.*n_items_)(); + } + + private: + const OpenMesh::PolyConnectivity& mesh_; + size_t (OpenMesh::ArrayKernel::*n_items_)() const; + Iterator iterator_; + Iterator iterator_end_; +}; + +/** + * Expose an iterator type to %Python. + * + * @tparam Iterator An iterator type. + * @tparam n_items A member function pointer that points to the mesh function + * that returns the number of items to iterate over (e.g. n_vertices). + * + * @param _name The name of the iterator type to be exposed. + * + * @note %Iterators are wrapped by IteratorWrapperT before they are exposed to + * %Python, i.e. they are not exposed directly. This means that iterators + * that are passed from %Python to C++ are instances of IteratorWrapperT. + */ +template +void expose_iterator(const char *_name) { + class_ >(_name, init >()) + .def(init >()) + .def("__iter__", &IteratorWrapperT::iter) + .def("__next__", &IteratorWrapperT::next) + .def("__len__", &IteratorWrapperT::len) + .def("next", &IteratorWrapperT::next) + ; +} + +} // namespace OpenMesh +} // namespace Python + +#endif diff --git a/src/Python/Mesh.hh b/src/Python/Mesh.hh new file mode 100644 index 00000000..257bd6cf --- /dev/null +++ b/src/Python/Mesh.hh @@ -0,0 +1,931 @@ +#ifndef OPENMESH_PYTHON_MESH_HH +#define OPENMESH_PYTHON_MESH_HH + +#include "Python/Bindings.hh" +#include "Python/Iterator.hh" +#include "Python/Circulator.hh" + +#include + +namespace OpenMesh { +namespace Python { + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(garbage_collection_overloads, garbage_collection, 0, 3) +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(add_property_overloads, add_property, 1, 2) + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(copy_all_properties_overloads, copy_all_properties, 2, 3) + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(delete_vertex_overloads, delete_vertex, 1, 2) +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(delete_edge_overloads, delete_edge, 1, 2) +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(delete_face_overloads, delete_face, 1, 2) +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(is_boundary_overloads, is_boundary, 1, 2) + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(find_feature_edges_overloads, find_feature_edges, 0, 1) +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(update_normal_overloads, update_normal, 1, 2) +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(update_halfedge_normals_overloads, update_halfedge_normals, 0, 1) +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(calc_halfedge_normal_overloads, calc_halfedge_normal, 1, 2) + +/** + * Set the status of an item. + * + * @tparam Mesh A mesh type. + * @tparam PropHandle A handle type. + * + * @param _self The mesh instance that is to be used. + * @param _h The handle of the item whose status is to be set. + * @param _info The status to be set. + * + * Depending on @ref OPENMESH_PYTHON_DEFAULT_POLICY, Mesh::status may + * return by value instead of reference. This function ensures that the + * status of an item can be changed nonetheless. + */ +template +void set_status(Mesh& _self, IndexHandle _h, const OpenMesh::Attributes::StatusInfo& _info) { + _self.status(_h) = _info; +} + +/** + * Set the value of a property of an item. + * + * @tparam Mesh A mesh type. + * @tparam PropHandle A property handle type. + * @tparam IndexHandle The appropriate handle type. + * + * @param _self The mesh instance that is to be used. + * @param _ph The property that is to be set. + * @param _h The handle of the item whose property is to be set. + * @param _value The value to be set. + * + * Depending on @ref OPENMESH_PYTHON_DEFAULT_POLICY, Mesh::property may + * return by value instead of reference. This function ensures that the + * property value of an item can be changed nonetheless. + */ +template +void set_property(Mesh& _self, PropHandle _ph, IndexHandle _h, const object& _value) { + _self.property(_ph, _h) = _value; +} + +/** + * Set the value of a mesh property. + * + * @tparam Mesh A mesh type. + * @tparam PropHandle A property handle type. + * + * @param _self The mesh instance that is to be used. + * @param _ph The property that is to be set. + * @param _value The value to be set. + * + * Depending on @ref OPENMESH_PYTHON_DEFAULT_POLICY, Mesh::property may + * return by value instead of reference. This function ensures that the + * property value of an item can be changed nonetheless. + */ +template +void set_property(Mesh& _self, PropHandle _ph, const object& _value) { + _self.property(_ph) = _value; +} + +/** + * Thin wrapper for assign_connectivity. + * + * @tparam Mesh A mesh type. + * @tparam OtherMesh A mesh type. + * + * @param _self The mesh instance that is to be used. + * @param _other The mesh from which the connectivity is to be copied. + */ +template +void assign_connectivity(Mesh& _self, const OtherMesh& _other) { + _self.assign_connectivity(_other); +} + +/** + * Get an iterator. + */ +template +IteratorWrapperT get_iterator(Mesh& _self) { + return IteratorWrapperT(_self, typename Iterator::value_type(0)); +} + +/** + * Get a skipping iterator. + */ +template +IteratorWrapperT get_skipping_iterator(Mesh& _self) { + return IteratorWrapperT(_self, typename Iterator::value_type(0), true); +} + +/** + * Get a circulator. + * + * @tparam Mesh A Mesh type. + * @tparam Circulator A circulator type. + * @tparam CenterEntityHandle The appropriate handle type. + * + * @param _self The mesh instance that is to be used. + * @param _handle The handle of the item to circulate around. + */ +template +CirculatorWrapperT get_circulator(Mesh& _self, CenterEntityHandle _handle) { + return CirculatorWrapperT(_self, _handle); +} + +/** + * Garbage collection using lists instead of vectors to keep track of a set of + * handles. + * + * @tparam Mesh A Mesh type. + * + * @param _self The mesh instance that is to be used. + * @param _vh_to_update The list of vertex handles to be updated. + * @param _hh_to_update The list of halfedge handles to be updated. + * @param _fh_to_update The list of face handles to be updated. + * @param _v Remove deleted vertices? + * @param _e Remove deleted edges? + * @param _f Remove deleted faces? + */ +template +void garbage_collection(Mesh& _self, list& _vh_to_update, list& _hh_to_update, list& _fh_to_update, bool _v = true, bool _e = true, bool _f = true) { + // Convert list of handles to vector of pointers + stl_input_iterator vh_begin(_vh_to_update); + stl_input_iterator vh_end; + std::vector vh_vector; + vh_vector.insert(vh_vector.end(), vh_begin, vh_end); + + // Convert list of handles to vector of pointers + stl_input_iterator hh_begin(_hh_to_update); + stl_input_iterator hh_end; + std::vector hh_vector; + hh_vector.insert(hh_vector.end(), hh_begin, hh_end); + + // Convert list of handles to vector of pointers + stl_input_iterator fh_begin(_fh_to_update); + stl_input_iterator fh_end; + std::vector fh_vector; + fh_vector.insert(fh_vector.end(), fh_begin, fh_end); + + // Call garbage collection + _self.garbage_collection(vh_vector, hh_vector, fh_vector, _v, _e, _f); +} + +/** + * Add a new face from a %Python list of vertex handles. + * + * @tparam Mesh A Mesh type. + * + * @param _self The mesh instance that is to be used. + * @param _vhandles The list of vertex handles. + */ +template +FaceHandle add_face(Mesh& _self, const list& _vhandles) { + stl_input_iterator begin(_vhandles); + stl_input_iterator end; + + std::vector vector; + vector.insert(vector.end(), begin, end); + + return _self.add_face(vector); +} + +/** + * This function template is used to expose mesh member functions that are only + * available for a specific type of mesh (i.e. they are available for polygon + * meshes or triangle meshes, but not both). + * + * @tparam Class A boost::python::class type. + * + * @param _class The boost::python::class instance for which the member + * functions are to be defined. + */ +template +void expose_type_specific_functions(Class& _class) { + // See the template specializations below +} + +/** + * Function template specialization for polygon meshes. + */ +template <> +void expose_type_specific_functions(class_& _class) { + typedef PolyMesh::Scalar Scalar; + typedef PolyMesh::Point Point; + typedef PolyMesh::Normal Normal; + typedef PolyMesh::Color Color; + + FaceHandle (PolyMesh::*add_face_3_vh)(VertexHandle, VertexHandle, VertexHandle ) = &PolyMesh::add_face; + FaceHandle (PolyMesh::*add_face_4_vh)(VertexHandle, VertexHandle, VertexHandle, VertexHandle) = &PolyMesh::add_face; + FaceHandle (*add_face_list)(PolyMesh&, const list&) = &add_face; + + void (PolyMesh::*split_eh_pt)(EdgeHandle, const Point&) = &PolyMesh::split; + void (PolyMesh::*split_eh_vh)(EdgeHandle, VertexHandle) = &PolyMesh::split; + void (PolyMesh::*split_fh_pt)(FaceHandle, const Point&) = &PolyMesh::split; + void (PolyMesh::*split_fh_vh)(FaceHandle, VertexHandle) = &PolyMesh::split; + + Normal (PolyMesh::*calc_face_normal_pt)(const Point&, const Point&, const Point&) const = &PolyMesh::calc_face_normal; + + _class + .def("add_face", add_face_3_vh) + .def("add_face", add_face_4_vh) + .def("add_face", add_face_list) + + .def("split", split_eh_pt) + .def("split", split_eh_vh) + .def("split", split_fh_pt) + .def("split", split_fh_vh) + + .def("split_copy", &PolyMesh::split_copy) + + .def("calc_face_normal", calc_face_normal_pt) + + .def("insert_edge", &PolyMesh::insert_edge) + ; +} + +/** + * Function template specialization for triangle meshes. + */ +template <> +void expose_type_specific_functions(class_& _class) { + typedef TriMesh::Scalar Scalar; + typedef TriMesh::Point Point; + typedef TriMesh::Normal Normal; + typedef TriMesh::Color Color; + + FaceHandle (TriMesh::*add_face_3_vh)(VertexHandle, VertexHandle, VertexHandle) = &TriMesh::add_face; + FaceHandle (*add_face_list)(TriMesh&, const list&) = &add_face; + + VertexHandle (TriMesh::*split_eh_pt)(EdgeHandle, const Point&) = &TriMesh::split; + void (TriMesh::*split_eh_vh)(EdgeHandle, VertexHandle) = &TriMesh::split; + VertexHandle (TriMesh::*split_fh_pt)(FaceHandle, const Point&) = &TriMesh::split; + void (TriMesh::*split_fh_vh)(FaceHandle, VertexHandle) = &TriMesh::split; + + VertexHandle (TriMesh::*split_copy_eh_pt)(EdgeHandle, const Point&) = &TriMesh::split_copy; + void (TriMesh::*split_copy_eh_vh)(EdgeHandle, VertexHandle) = &TriMesh::split_copy; + VertexHandle (TriMesh::*split_copy_fh_pt)(FaceHandle, const Point&) = &TriMesh::split_copy; + void (TriMesh::*split_copy_fh_vh)(FaceHandle, VertexHandle) = &TriMesh::split_copy; + + HalfedgeHandle (TriMesh::*vertex_split_pt)(Point, VertexHandle, VertexHandle, VertexHandle) = &TriMesh::vertex_split; + HalfedgeHandle (TriMesh::*vertex_split_vh)(VertexHandle, VertexHandle, VertexHandle, VertexHandle) = &TriMesh::vertex_split; + + _class + .def("add_face", add_face_3_vh) + .def("add_face", add_face_list) + + .def("split", split_eh_pt) + .def("split", split_eh_vh) + .def("split", split_fh_pt) + .def("split", split_fh_vh) + + .def("split_copy", split_copy_eh_pt) + .def("split_copy", split_copy_eh_vh) + .def("split_copy", split_copy_fh_pt) + .def("split_copy", split_copy_fh_vh) + + .def("opposite_vh", &TriMesh::opposite_vh) + .def("opposite_he_opposite_vh", &TriMesh::opposite_he_opposite_vh) + + .def("vertex_split", vertex_split_pt) + .def("vertex_split", vertex_split_vh) + + .def("is_flip_ok", &TriMesh::is_flip_ok) + .def("flip", &TriMesh::flip) + ; +} + + +/** + * Expose a mesh type to %Python. + * + * @tparam Mesh A mesh type. + * + * @param _name The name of the mesh type to be exposed. + */ +template +void expose_mesh(const char *_name) { + using OpenMesh::Attributes::StatusInfo; + + typedef typename Mesh::Scalar Scalar; + typedef typename Mesh::Point Point; + typedef typename Mesh::Normal Normal; + typedef typename Mesh::Color Color; + + //====================================================================== + // KernelT Function Pointers + //====================================================================== + + // Get the i'th item + VertexHandle (Mesh::*vertex_handle_uint )(unsigned int) const = &Mesh::vertex_handle; + HalfedgeHandle (Mesh::*halfedge_handle_uint)(unsigned int) const = &Mesh::halfedge_handle; + EdgeHandle (Mesh::*edge_handle_uint )(unsigned int) const = &Mesh::edge_handle; + FaceHandle (Mesh::*face_handle_uint )(unsigned int) const = &Mesh::face_handle; + + // Delete items + void (Mesh::*garbage_collection_bools)(bool, bool, bool) = &Mesh::garbage_collection; + void (*garbage_collection_lists_bools)(Mesh&, list&, list&, list&, bool, bool, bool) = &garbage_collection; + + // Vertex connectivity + HalfedgeHandle (Mesh::*halfedge_handle_vh)(VertexHandle) const = &Mesh::halfedge_handle; + HalfedgeHandle (Mesh::*halfedge_handle_fh)(FaceHandle ) const = &Mesh::halfedge_handle; + + // Halfedge connectivity + FaceHandle (Mesh::*face_handle_hh )(HalfedgeHandle) const = &Mesh::face_handle; + HalfedgeHandle (Mesh::*prev_halfedge_handle_hh)(HalfedgeHandle) const = &Mesh::prev_halfedge_handle; + EdgeHandle (Mesh::*edge_handle_hh )(HalfedgeHandle) const = &Mesh::edge_handle; + + // Edge connectivity + HalfedgeHandle (Mesh::*halfedge_handle_eh_uint)(EdgeHandle, unsigned int) const = &Mesh::halfedge_handle; + + // Set halfedge + void (Mesh::*set_halfedge_handle_vh_hh)(VertexHandle, HalfedgeHandle) = &Mesh::set_halfedge_handle; + void (Mesh::*set_halfedge_handle_fh_hh)(FaceHandle, HalfedgeHandle ) = &Mesh::set_halfedge_handle; + + // Handle -> Item + const typename Mesh::Vertex& (Mesh::*vertex )(VertexHandle ) const = &Mesh::vertex; + const typename Mesh::Halfedge& (Mesh::*halfedge)(HalfedgeHandle) const = &Mesh::halfedge; + const typename Mesh::Edge& (Mesh::*edge )(EdgeHandle ) const = &Mesh::edge; + const typename Mesh::Face& (Mesh::*face )(FaceHandle ) const = &Mesh::face; + + // Item -> Handle + VertexHandle (Mesh::*handle_v)(const typename Mesh::Vertex& ) const = &Mesh::handle; + HalfedgeHandle (Mesh::*handle_h)(const typename Mesh::Halfedge&) const = &Mesh::handle; + EdgeHandle (Mesh::*handle_e)(const typename Mesh::Edge& ) const = &Mesh::handle; + FaceHandle (Mesh::*handle_f)(const typename Mesh::Face& ) const = &Mesh::handle; + + // Get value of a standard property (point, normal, color) + const typename Mesh::Point& (Mesh::*point_vh )(VertexHandle ) const = &Mesh::point; + const typename Mesh::Normal& (Mesh::*normal_vh)(VertexHandle ) const = &Mesh::normal; + const typename Mesh::Normal& (Mesh::*normal_hh)(HalfedgeHandle) const = &Mesh::normal; + const typename Mesh::Normal& (Mesh::*normal_fh)(FaceHandle ) const = &Mesh::normal; + const typename Mesh::Color& (Mesh::*color_vh )(VertexHandle ) const = &Mesh::color; + const typename Mesh::Color& (Mesh::*color_hh )(HalfedgeHandle) const = &Mesh::color; + const typename Mesh::Color& (Mesh::*color_eh )(EdgeHandle ) const = &Mesh::color; + const typename Mesh::Color& (Mesh::*color_fh )(FaceHandle ) const = &Mesh::color; + + // Get value of a standard property (texture coordinate) + const typename Mesh::TexCoord1D& (Mesh::*texcoord1D_vh)(VertexHandle ) const = &Mesh::texcoord1D; + const typename Mesh::TexCoord1D& (Mesh::*texcoord1D_hh)(HalfedgeHandle) const = &Mesh::texcoord1D; + const typename Mesh::TexCoord2D& (Mesh::*texcoord2D_vh)(VertexHandle ) const = &Mesh::texcoord2D; + const typename Mesh::TexCoord2D& (Mesh::*texcoord2D_hh)(HalfedgeHandle) const = &Mesh::texcoord2D; + const typename Mesh::TexCoord3D& (Mesh::*texcoord3D_vh)(VertexHandle ) const = &Mesh::texcoord3D; + const typename Mesh::TexCoord3D& (Mesh::*texcoord3D_hh)(HalfedgeHandle) const = &Mesh::texcoord3D; + + // Get value of a standard property (status) + const StatusInfo& (Mesh::*status_vh)(VertexHandle ) const = &Mesh::status; + const StatusInfo& (Mesh::*status_hh)(HalfedgeHandle) const = &Mesh::status; + const StatusInfo& (Mesh::*status_eh)(EdgeHandle ) const = &Mesh::status; + const StatusInfo& (Mesh::*status_fh)(FaceHandle ) const = &Mesh::status; + + // Set value of a standard property (point, normal, color) + void (Mesh::*set_normal_vh)(VertexHandle, const typename Mesh::Normal&) = &Mesh::set_normal; + void (Mesh::*set_normal_hh)(HalfedgeHandle, const typename Mesh::Normal&) = &Mesh::set_normal; + void (Mesh::*set_normal_fh)(FaceHandle, const typename Mesh::Normal&) = &Mesh::set_normal; + void (Mesh::*set_color_vh )(VertexHandle, const typename Mesh::Color& ) = &Mesh::set_color; + void (Mesh::*set_color_hh )(HalfedgeHandle, const typename Mesh::Color& ) = &Mesh::set_color; + void (Mesh::*set_color_eh )(EdgeHandle, const typename Mesh::Color& ) = &Mesh::set_color; + void (Mesh::*set_color_fh )(FaceHandle, const typename Mesh::Color& ) = &Mesh::set_color; + + // Set value of a standard property (texture coordinate) + void (Mesh::*set_texcoord1D_vh)(VertexHandle, const typename Mesh::TexCoord1D&) = &Mesh::set_texcoord1D; + void (Mesh::*set_texcoord1D_hh)(HalfedgeHandle, const typename Mesh::TexCoord1D&) = &Mesh::set_texcoord1D; + void (Mesh::*set_texcoord2D_vh)(VertexHandle, const typename Mesh::TexCoord2D&) = &Mesh::set_texcoord2D; + void (Mesh::*set_texcoord2D_hh)(HalfedgeHandle, const typename Mesh::TexCoord2D&) = &Mesh::set_texcoord2D; + void (Mesh::*set_texcoord3D_vh)(VertexHandle, const typename Mesh::TexCoord3D&) = &Mesh::set_texcoord3D; + void (Mesh::*set_texcoord3D_hh)(HalfedgeHandle, const typename Mesh::TexCoord3D&) = &Mesh::set_texcoord3D; + + // Set value of a standard property (status) + void (*set_status_vh)(Mesh&, VertexHandle, const StatusInfo&) = &set_status; + void (*set_status_hh)(Mesh&, HalfedgeHandle, const StatusInfo&) = &set_status; + void (*set_status_eh)(Mesh&, EdgeHandle, const StatusInfo&) = &set_status; + void (*set_status_fh)(Mesh&, FaceHandle, const StatusInfo&) = &set_status; + + // Property management - add property + void (Mesh::*add_property_vph)(VPropHandleT&, const std::string&) = &Mesh::add_property; + void (Mesh::*add_property_eph)(EPropHandleT&, const std::string&) = &Mesh::add_property; + void (Mesh::*add_property_hph)(HPropHandleT&, const std::string&) = &Mesh::add_property; + void (Mesh::*add_property_fph)(FPropHandleT&, const std::string&) = &Mesh::add_property; + void (Mesh::*add_property_mph)(MPropHandleT&, const std::string&) = &Mesh::add_property; + + // Property management - remove property + void (Mesh::*remove_property_vph)(VPropHandleT&) = &Mesh::remove_property; + void (Mesh::*remove_property_eph)(EPropHandleT&) = &Mesh::remove_property; + void (Mesh::*remove_property_hph)(HPropHandleT&) = &Mesh::remove_property; + void (Mesh::*remove_property_fph)(FPropHandleT&) = &Mesh::remove_property; + void (Mesh::*remove_property_mph)(MPropHandleT&) = &Mesh::remove_property; + + // Property management - get property by name + bool (Mesh::*get_property_handle_vph)(VPropHandleT&, const std::string&) const = &Mesh::get_property_handle; + bool (Mesh::*get_property_handle_eph)(EPropHandleT&, const std::string&) const = &Mesh::get_property_handle; + bool (Mesh::*get_property_handle_hph)(HPropHandleT&, const std::string&) const = &Mesh::get_property_handle; + bool (Mesh::*get_property_handle_fph)(FPropHandleT&, const std::string&) const = &Mesh::get_property_handle; + bool (Mesh::*get_property_handle_mph)(MPropHandleT&, const std::string&) const = &Mesh::get_property_handle; + + // Property management - get property value for an item + const object& (Mesh::*property_vertex )(VPropHandleT, VertexHandle ) const = &Mesh::property; + const object& (Mesh::*property_edge )(EPropHandleT, EdgeHandle ) const = &Mesh::property; + const object& (Mesh::*property_halfedge)(HPropHandleT, HalfedgeHandle) const = &Mesh::property; + const object& (Mesh::*property_face )(FPropHandleT, FaceHandle ) const = &Mesh::property; + const object& (Mesh::*property_mesh )(MPropHandleT ) const = &Mesh::property; + + // Property management - set property value for an item + void (*set_property_vertex )(Mesh&, VPropHandleT, VertexHandle, const object&) = &set_property; + void (*set_property_edge )(Mesh&, EPropHandleT, EdgeHandle, const object&) = &set_property; + void (*set_property_halfedge)(Mesh&, HPropHandleT, HalfedgeHandle, const object&) = &set_property; + void (*set_property_face )(Mesh&, FPropHandleT, FaceHandle, const object&) = &set_property; + void (*set_property_mesh )(Mesh&, MPropHandleT, const object&) = &set_property; + + // Low-level adding new items + VertexHandle (Mesh::*new_vertex_void )(void ) = &Mesh::new_vertex; + VertexHandle (Mesh::*new_vertex_point)(const typename Mesh::Point& ) = &Mesh::new_vertex; + FaceHandle (Mesh::*new_face_void )(void ) = &Mesh::new_face; + FaceHandle (Mesh::*new_face_face )(const typename Mesh::Face& ) = &Mesh::new_face; + + // Kernel item iterators + IteratorWrapperT (*vertices )(Mesh&) = &get_iterator; + IteratorWrapperT (*halfedges)(Mesh&) = &get_iterator; + IteratorWrapperT (*edges )(Mesh&) = &get_iterator; + IteratorWrapperT (*faces )(Mesh&) = &get_iterator; + + IteratorWrapperT (*svertices )(Mesh&) = &get_skipping_iterator; + IteratorWrapperT (*shalfedges)(Mesh&) = &get_skipping_iterator; + IteratorWrapperT (*sedges )(Mesh&) = &get_skipping_iterator; + IteratorWrapperT (*sfaces )(Mesh&) = &get_skipping_iterator; + + //====================================================================== + // BaseKernel Function Pointers + //====================================================================== + + // Copy property + void (Mesh::*copy_property_vprop)(VPropHandleT&, VertexHandle, VertexHandle ) = &Mesh::copy_property; + void (Mesh::*copy_property_hprop)(HPropHandleT, HalfedgeHandle, HalfedgeHandle) = &Mesh::copy_property; + void (Mesh::*copy_property_eprop)(EPropHandleT, EdgeHandle, EdgeHandle ) = &Mesh::copy_property; + void (Mesh::*copy_property_fprop)(FPropHandleT, FaceHandle, FaceHandle ) = &Mesh::copy_property; + + // Copy all properties + void (Mesh::*copy_all_properties_vh_vh_bool)(VertexHandle, VertexHandle, bool) = &Mesh::copy_all_properties; + void (Mesh::*copy_all_properties_hh_hh_bool)(HalfedgeHandle, HalfedgeHandle, bool) = &Mesh::copy_all_properties; + void (Mesh::*copy_all_properties_eh_eh_bool)(EdgeHandle, EdgeHandle, bool) = &Mesh::copy_all_properties; + void (Mesh::*copy_all_properties_fh_fh_bool)(FaceHandle, FaceHandle, bool) = &Mesh::copy_all_properties; + + //====================================================================== + // PolyConnectivity Function Pointers + //====================================================================== + + // Assign connectivity + void (*assign_connectivity_poly)(Mesh&, const PolyMesh&) = &assign_connectivity; + void (*assign_connectivity_tri )(Mesh&, const TriMesh& ) = &assign_connectivity; + + // Vertex and face valence + unsigned int (Mesh::*valence_vh)(VertexHandle) const = &Mesh::valence; + unsigned int (Mesh::*valence_fh)(FaceHandle ) const = &Mesh::valence; + + // Triangulate face or mesh + void (Mesh::*triangulate_fh )(FaceHandle) = &Mesh::triangulate; + void (Mesh::*triangulate_void)( ) = &Mesh::triangulate; + + // Deleting mesh items and other connectivity/topology modifications + void (Mesh::*delete_vertex)(VertexHandle, bool) = &Mesh::delete_vertex; + void (Mesh::*delete_edge )(EdgeHandle, bool) = &Mesh::delete_edge; + void (Mesh::*delete_face )(FaceHandle, bool) = &Mesh::delete_face; + + // Vertex and Face circulators + CirculatorWrapperT (*vv )(Mesh&, VertexHandle ) = &get_circulator; + CirculatorWrapperT (*vih)(Mesh&, VertexHandle ) = &get_circulator; + CirculatorWrapperT (*voh)(Mesh&, VertexHandle ) = &get_circulator; + CirculatorWrapperT (*ve )(Mesh&, VertexHandle ) = &get_circulator; + CirculatorWrapperT (*vf )(Mesh&, VertexHandle ) = &get_circulator; + CirculatorWrapperT (*fv )(Mesh&, FaceHandle ) = &get_circulator; + CirculatorWrapperT (*fh )(Mesh&, FaceHandle ) = &get_circulator; + CirculatorWrapperT (*fe )(Mesh&, FaceHandle ) = &get_circulator; + CirculatorWrapperT (*ff )(Mesh&, FaceHandle ) = &get_circulator; + CirculatorWrapperT (*hl )(Mesh&, HalfedgeHandle) = &get_circulator; + + // Boundary and manifold tests + bool (Mesh::*is_boundary_hh)(HalfedgeHandle ) const = &Mesh::is_boundary; + bool (Mesh::*is_boundary_eh)(EdgeHandle ) const = &Mesh::is_boundary; + bool (Mesh::*is_boundary_vh)(VertexHandle ) const = &Mesh::is_boundary; + bool (Mesh::*is_boundary_fh)(FaceHandle, bool) const = &Mesh::is_boundary; + + // Generic handle derefertiation + const typename Mesh::Vertex& (Mesh::*deref_vh)(VertexHandle ) const = &Mesh::deref; + const typename Mesh::Halfedge& (Mesh::*deref_hh)(HalfedgeHandle) const = &Mesh::deref; + const typename Mesh::Edge& (Mesh::*deref_eh)(EdgeHandle ) const = &Mesh::deref; + const typename Mesh::Face& (Mesh::*deref_fh)(FaceHandle ) const = &Mesh::deref; + + //====================================================================== + // PolyMeshT Function Pointers + //====================================================================== + + void (Mesh::*calc_edge_vector_eh_normal)(EdgeHandle, Normal&) const = &Mesh::calc_edge_vector; + void (Mesh::*calc_edge_vector_hh_normal)(HalfedgeHandle, Normal&) const = &Mesh::calc_edge_vector; + + Normal (Mesh::*calc_edge_vector_eh)(EdgeHandle ) const = &Mesh::calc_edge_vector; + Normal (Mesh::*calc_edge_vector_hh)(HalfedgeHandle) const = &Mesh::calc_edge_vector; + + Scalar (Mesh::*calc_edge_length_eh)(EdgeHandle ) const = &Mesh::calc_edge_length; + Scalar (Mesh::*calc_edge_length_hh)(HalfedgeHandle) const = &Mesh::calc_edge_length; + + Scalar (Mesh::*calc_edge_sqr_length_eh)(EdgeHandle ) const = &Mesh::calc_edge_sqr_length; + Scalar (Mesh::*calc_edge_sqr_length_hh)(HalfedgeHandle) const = &Mesh::calc_edge_sqr_length; + + Scalar (Mesh::*calc_dihedral_angle_fast_hh)(HalfedgeHandle) const = &Mesh::calc_dihedral_angle_fast; + Scalar (Mesh::*calc_dihedral_angle_fast_eh)(EdgeHandle ) const = &Mesh::calc_dihedral_angle_fast; + + Scalar (Mesh::*calc_dihedral_angle_hh)(HalfedgeHandle) const = &Mesh::calc_dihedral_angle; + Scalar (Mesh::*calc_dihedral_angle_eh)(EdgeHandle ) const = &Mesh::calc_dihedral_angle; + + unsigned int (Mesh::*find_feature_edges)(Scalar) = &Mesh::find_feature_edges; + + void (Mesh::*split_fh_vh)(FaceHandle, VertexHandle) = &Mesh::split; + void (Mesh::*split_eh_vh)(EdgeHandle, VertexHandle) = &Mesh::split; + + void (Mesh::*update_normal_fh)(FaceHandle ) = &Mesh::update_normal; + void (Mesh::*update_normal_hh)(HalfedgeHandle, double) = &Mesh::update_normal; + void (Mesh::*update_normal_vh)(VertexHandle ) = &Mesh::update_normal; + + void (Mesh::*update_halfedge_normals)(double) = &Mesh::update_halfedge_normals; + + Normal (Mesh::*calc_face_normal )(FaceHandle ) const = &Mesh::calc_face_normal; + Normal (Mesh::*calc_halfedge_normal)(HalfedgeHandle, double) const = &Mesh::calc_halfedge_normal; + + void (Mesh::*calc_face_centroid_fh_point)(FaceHandle, Point&) const = &Mesh::calc_face_centroid; + Point (Mesh::*calc_face_centroid_fh )(FaceHandle ) const = &Mesh::calc_face_centroid; + + //====================================================================== + // Mesh Type + //====================================================================== + + class_ class_mesh(_name); + + class_mesh + + //====================================================================== + // KernelT + //====================================================================== + + .def("reserve", &Mesh::reserve) + + .def("vertex", vertex, return_value_policy()) + .def("halfedge", halfedge, return_value_policy()) + .def("edge", edge, return_value_policy()) + .def("face", face, return_value_policy()) + + .def("handle", handle_v) + .def("handle", handle_h) + .def("handle", handle_e) + .def("handle", handle_f) + + .def("vertex_handle", vertex_handle_uint) + .def("halfedge_handle", halfedge_handle_uint) + .def("edge_handle", edge_handle_uint) + .def("face_handle", face_handle_uint) + + .def("clear", &Mesh::clear) + .def("clean", &Mesh::clean) + .def("garbage_collection", garbage_collection_bools, garbage_collection_overloads()) + .def("garbage_collection", garbage_collection_lists_bools) + + .def("n_vertices", &Mesh::n_vertices) + .def("n_halfedges", &Mesh::n_halfedges) + .def("n_edges", &Mesh::n_edges) + .def("n_faces", &Mesh::n_faces) + .def("vertices_empty", &Mesh::vertices_empty) + .def("halfedges_empty", &Mesh::halfedges_empty) + .def("edges_empty", &Mesh::edges_empty) + .def("faces_empty", &Mesh::faces_empty) + + .def("halfedge_handle", halfedge_handle_vh) + .def("set_halfedge_handle", set_halfedge_handle_vh_hh) + + .def("to_vertex_handle", &Mesh::to_vertex_handle) + .def("from_vertex_handle", &Mesh::from_vertex_handle) + .def("set_vertex_handle", &Mesh::set_vertex_handle) + .def("face_handle", face_handle_hh) + .def("set_face_handle", &Mesh::set_face_handle) + .def("next_halfedge_handle", &Mesh::next_halfedge_handle) + .def("set_next_halfedge_handle", &Mesh::set_next_halfedge_handle) + .def("prev_halfedge_handle", prev_halfedge_handle_hh) + .def("opposite_halfedge_handle", &Mesh::opposite_halfedge_handle) + .def("ccw_rotated_halfedge_handle", &Mesh::ccw_rotated_halfedge_handle) + .def("cw_rotated_halfedge_handle", &Mesh::cw_rotated_halfedge_handle) + .def("edge_handle", edge_handle_hh) + + .def("halfedge_handle", halfedge_handle_eh_uint) + + .def("halfedge_handle", halfedge_handle_fh) + .def("set_halfedge_handle", set_halfedge_handle_fh_hh) + + .def("point", point_vh, OPENMESH_PYTHON_DEFAULT_POLICY) + .def("set_point", &Mesh::set_point) + .def("normal", normal_vh, OPENMESH_PYTHON_DEFAULT_POLICY) + .def("set_normal", set_normal_vh) + .def("normal", normal_hh, OPENMESH_PYTHON_DEFAULT_POLICY) + .def("set_normal", set_normal_hh) + .def("color", color_vh, OPENMESH_PYTHON_DEFAULT_POLICY) + .def("set_color", set_color_vh) + .def("texcoord1D", texcoord1D_vh, OPENMESH_PYTHON_DEFAULT_POLICY) + .def("set_texcoord1D", set_texcoord1D_vh) + .def("texcoord2D", texcoord2D_vh, OPENMESH_PYTHON_DEFAULT_POLICY) + .def("set_texcoord2D", set_texcoord2D_vh) + .def("texcoord3D", texcoord3D_vh, OPENMESH_PYTHON_DEFAULT_POLICY) + .def("set_texcoord3D", set_texcoord3D_vh) + .def("texcoord1D", texcoord1D_hh, OPENMESH_PYTHON_DEFAULT_POLICY) + .def("set_texcoord1D", set_texcoord1D_hh) + .def("texcoord2D", texcoord2D_hh, OPENMESH_PYTHON_DEFAULT_POLICY) + .def("set_texcoord2D", set_texcoord2D_hh) + .def("texcoord3D", texcoord3D_hh, OPENMESH_PYTHON_DEFAULT_POLICY) + .def("set_texcoord3D", set_texcoord3D_hh) + .def("status", status_vh, OPENMESH_PYTHON_DEFAULT_POLICY) + .def("set_status", set_status_vh) + .def("status", status_hh, OPENMESH_PYTHON_DEFAULT_POLICY) + .def("set_status", set_status_hh) + .def("color", color_hh, OPENMESH_PYTHON_DEFAULT_POLICY) + .def("set_color", set_color_hh) + .def("color", color_eh, OPENMESH_PYTHON_DEFAULT_POLICY) + .def("set_color", set_color_eh) + .def("status", status_eh, OPENMESH_PYTHON_DEFAULT_POLICY) + .def("set_status", set_status_eh) + .def("normal", normal_fh, OPENMESH_PYTHON_DEFAULT_POLICY) + .def("set_normal", set_normal_fh) + .def("color", color_fh, OPENMESH_PYTHON_DEFAULT_POLICY) + .def("set_color", set_color_fh) + .def("status", status_fh, OPENMESH_PYTHON_DEFAULT_POLICY) + .def("set_status", set_status_fh) + + .def("request_vertex_normals", &Mesh::request_vertex_normals) + .def("request_vertex_colors", &Mesh::request_vertex_colors) + .def("request_vertex_texcoords1D", &Mesh::request_vertex_texcoords1D) + .def("request_vertex_texcoords2D", &Mesh::request_vertex_texcoords2D) + .def("request_vertex_texcoords3D", &Mesh::request_vertex_texcoords3D) + .def("request_vertex_status", &Mesh::request_vertex_status) + .def("request_halfedge_status", &Mesh::request_halfedge_status) + .def("request_halfedge_normals", &Mesh::request_halfedge_normals) + .def("request_halfedge_colors", &Mesh::request_halfedge_colors) + .def("request_halfedge_texcoords1D", &Mesh::request_halfedge_texcoords1D) + .def("request_halfedge_texcoords2D", &Mesh::request_halfedge_texcoords2D) + .def("request_halfedge_texcoords3D", &Mesh::request_halfedge_texcoords3D) + .def("request_edge_status", &Mesh::request_edge_status) + .def("request_edge_colors", &Mesh::request_edge_colors) + .def("request_face_normals", &Mesh::request_face_normals) + .def("request_face_colors", &Mesh::request_face_colors) + .def("request_face_status", &Mesh::request_face_status) + .def("request_face_texture_index", &Mesh::request_face_texture_index) + + .def("release_vertex_normals", &Mesh::release_vertex_normals) + .def("release_vertex_colors", &Mesh::release_vertex_colors) + .def("release_vertex_texcoords1D", &Mesh::release_vertex_texcoords1D) + .def("release_vertex_texcoords2D", &Mesh::release_vertex_texcoords2D) + .def("release_vertex_texcoords3D", &Mesh::release_vertex_texcoords3D) + .def("release_vertex_status", &Mesh::release_vertex_status) + .def("release_halfedge_status", &Mesh::release_halfedge_status) + .def("release_halfedge_normals", &Mesh::release_halfedge_normals) + .def("release_halfedge_colors", &Mesh::release_halfedge_colors) + .def("release_halfedge_texcoords1D", &Mesh::release_halfedge_texcoords1D) + .def("release_halfedge_texcoords2D", &Mesh::release_halfedge_texcoords2D) + .def("release_halfedge_texcoords3D", &Mesh::release_halfedge_texcoords3D) + .def("release_edge_status", &Mesh::release_edge_status) + .def("release_edge_colors", &Mesh::release_edge_colors) + .def("release_face_normals", &Mesh::release_face_normals) + .def("release_face_colors", &Mesh::release_face_colors) + .def("release_face_status", &Mesh::release_face_status) + .def("release_face_texture_index", &Mesh::release_face_texture_index) + + .def("has_vertex_normals", &Mesh::has_vertex_normals) + .def("has_vertex_colors", &Mesh::has_vertex_colors) + .def("has_vertex_texcoords1D", &Mesh::has_vertex_texcoords1D) + .def("has_vertex_texcoords2D", &Mesh::has_vertex_texcoords2D) + .def("has_vertex_texcoords3D", &Mesh::has_vertex_texcoords3D) + .def("has_vertex_status", &Mesh::has_vertex_status) + .def("has_halfedge_status", &Mesh::has_halfedge_status) + .def("has_halfedge_normals", &Mesh::has_halfedge_normals) + .def("has_halfedge_colors", &Mesh::has_halfedge_colors) + .def("has_halfedge_texcoords1D", &Mesh::has_halfedge_texcoords1D) + .def("has_halfedge_texcoords2D", &Mesh::has_halfedge_texcoords2D) + .def("has_halfedge_texcoords3D", &Mesh::has_halfedge_texcoords3D) + .def("has_edge_status", &Mesh::has_edge_status) + .def("has_edge_colors", &Mesh::has_edge_colors) + .def("has_face_normals", &Mesh::has_face_normals) + .def("has_face_colors", &Mesh::has_face_colors) + .def("has_face_status", &Mesh::has_face_status) + .def("has_face_texture_index", &Mesh::has_face_texture_index) + + .def("add_property", add_property_vph, add_property_overloads()) + .def("add_property", add_property_eph, add_property_overloads()) + .def("add_property", add_property_hph, add_property_overloads()) + .def("add_property", add_property_fph, add_property_overloads()) + .def("add_property", add_property_mph, add_property_overloads()) + + .def("remove_property", remove_property_vph) + .def("remove_property", remove_property_eph) + .def("remove_property", remove_property_hph) + .def("remove_property", remove_property_fph) + .def("remove_property", remove_property_mph) + + .def("get_property_handle", get_property_handle_vph) + .def("get_property_handle", get_property_handle_eph) + .def("get_property_handle", get_property_handle_hph) + .def("get_property_handle", get_property_handle_fph) + .def("get_property_handle", get_property_handle_mph) + + .def("property", property_vertex, OPENMESH_PYTHON_DEFAULT_POLICY) + .def("property", property_edge, OPENMESH_PYTHON_DEFAULT_POLICY) + .def("property", property_halfedge, OPENMESH_PYTHON_DEFAULT_POLICY) + .def("property", property_face, OPENMESH_PYTHON_DEFAULT_POLICY) + .def("property", property_mesh, OPENMESH_PYTHON_DEFAULT_POLICY) + + .def("set_property", set_property_vertex) + .def("set_property", set_property_edge) + .def("set_property", set_property_halfedge) + .def("set_property", set_property_face) + .def("set_property", set_property_mesh) + + .def("new_vertex", new_vertex_void) + .def("new_vertex", new_vertex_point) + .def("new_edge", &Mesh::new_edge) + .def("new_face", new_face_void) + .def("new_face", new_face_face) + + .def("vertices", vertices) + .def("halfedges", halfedges) + .def("edges", edges) + .def("faces", faces) + + .def("svertices", svertices) + .def("shalfedges", shalfedges) + .def("sedges", sedges) + .def("sfaces", sfaces) + + //====================================================================== + // BaseKernel + //====================================================================== + + .def("copy_property", copy_property_vprop) + .def("copy_property", copy_property_hprop) + .def("copy_property", copy_property_eprop) + .def("copy_property", copy_property_fprop) + + .def("copy_all_properties", copy_all_properties_vh_vh_bool, copy_all_properties_overloads()) + .def("copy_all_properties", copy_all_properties_hh_hh_bool, copy_all_properties_overloads()) + .def("copy_all_properties", copy_all_properties_eh_eh_bool, copy_all_properties_overloads()) + .def("copy_all_properties", copy_all_properties_fh_fh_bool, copy_all_properties_overloads()) + + //====================================================================== + // PolyConnectivity + //====================================================================== + + .def("assign_connectivity", assign_connectivity_poly) + .def("assign_connectivity", assign_connectivity_tri) + + .def("opposite_face_handle", &Mesh::opposite_face_handle) + .def("adjust_outgoing_halfedge", &Mesh::adjust_outgoing_halfedge) + .def("find_halfedge", &Mesh::find_halfedge) + .def("valence", valence_vh) + .def("valence", valence_fh) + .def("collapse", &Mesh::collapse) + .def("is_simple_link", &Mesh::is_simple_link) + .def("is_simply_connected", &Mesh::is_simply_connected) + .def("remove_edge", &Mesh::remove_edge) + .def("reinsert_edge", &Mesh::reinsert_edge) + .def("triangulate", triangulate_fh) + .def("triangulate", triangulate_void) + .def("split_edge", &Mesh::split_edge) + .def("split_edge_copy", &Mesh::split_edge_copy) + + .def("add_vertex", &Mesh::add_vertex) + + .def("is_collapse_ok", &Mesh::is_collapse_ok) + .def("delete_vertex", delete_vertex, delete_vertex_overloads()) + .def("delete_edge", delete_edge, delete_edge_overloads()) + .def("delete_face", delete_face, delete_face_overloads()) + + .def("vv", vv) + .def("vih", vih) + .def("voh", voh) + .def("ve", ve) + .def("vf", vf) + + .def("fv", fv) + .def("fh", fh) + .def("fe", fe) + .def("ff", ff) + + .def("hl", hl) + + .def("is_boundary", is_boundary_hh) + .def("is_boundary", is_boundary_eh) + .def("is_boundary", is_boundary_vh) + .def("is_boundary", is_boundary_fh, is_boundary_overloads()) + .def("is_manifold", &Mesh::is_manifold) + + .def("deref", deref_vh, return_value_policy()) + .def("deref", deref_hh, return_value_policy()) + .def("deref", deref_eh, return_value_policy()) + .def("deref", deref_fh, return_value_policy()) + + .def("is_triangles", &Mesh::is_triangles) + .staticmethod("is_triangles") + + .def_readonly("InvalidVertexHandle", &Mesh::InvalidVertexHandle) + .def_readonly("InvalidHalfedgeHandle", &Mesh::InvalidHalfedgeHandle) + .def_readonly("InvalidEdgeHandle", &Mesh::InvalidEdgeHandle) + .def_readonly("InvalidFaceHandle", &Mesh::InvalidFaceHandle) + + //====================================================================== + // PolyMeshT + //====================================================================== + + .def("add_vertex", &Mesh::add_vertex) + + .def("calc_edge_vector", calc_edge_vector_eh_normal) + .def("calc_edge_vector", calc_edge_vector_eh) + .def("calc_edge_vector", calc_edge_vector_hh_normal) + .def("calc_edge_vector", calc_edge_vector_hh) + + .def("calc_edge_length", calc_edge_length_eh) + .def("calc_edge_length", calc_edge_length_hh) + .def("calc_edge_sqr_length", calc_edge_sqr_length_eh) + .def("calc_edge_sqr_length", calc_edge_sqr_length_hh) + + .def("calc_sector_vectors", &Mesh::calc_sector_vectors) + .def("calc_sector_angle", &Mesh::calc_sector_angle) + .def("calc_sector_normal", &Mesh::calc_sector_normal) + .def("calc_sector_area", &Mesh::calc_sector_area) + + .def("calc_dihedral_angle_fast", calc_dihedral_angle_fast_hh) + .def("calc_dihedral_angle_fast", calc_dihedral_angle_fast_eh) + .def("calc_dihedral_angle", calc_dihedral_angle_hh) + .def("calc_dihedral_angle", calc_dihedral_angle_eh) + + .def("find_feature_edges", find_feature_edges, find_feature_edges_overloads()) + + .def("split", split_fh_vh) + .def("split", split_eh_vh) + + .def("update_normals", &Mesh::update_normals) + .def("update_normal", update_normal_fh) + .def("update_face_normals", &Mesh::update_face_normals) + + .def("calc_face_normal", calc_face_normal) + + .def("calc_face_centroid", calc_face_centroid_fh_point) + .def("calc_face_centroid", calc_face_centroid_fh) + + .def("update_normal", update_normal_hh, update_normal_overloads()) + .def("update_halfedge_normals", update_halfedge_normals, update_halfedge_normals_overloads()) + + .def("calc_halfedge_normal", calc_halfedge_normal, calc_halfedge_normal_overloads()) + + .def("is_estimated_feature_edge", &Mesh::is_estimated_feature_edge) + + .def("update_normal", update_normal_vh) + .def("update_vertex_normals", &Mesh::update_vertex_normals) + + .def("calc_vertex_normal", &Mesh::calc_vertex_normal) + .def("calc_vertex_normal_fast", &Mesh::calc_vertex_normal_fast) + .def("calc_vertex_normal_correct", &Mesh::calc_vertex_normal_correct) + .def("calc_vertex_normal_loop", &Mesh::calc_vertex_normal_loop) + + .def("is_polymesh", &Mesh::is_polymesh) + .staticmethod("is_polymesh") + + .def("is_trimesh", &Mesh::is_trimesh) + .staticmethod("is_trimesh") + ; + + expose_type_specific_functions(class_mesh); + + //====================================================================== + // Nested Types + //====================================================================== + + // Enter mesh scope + scope scope_mesh = class_mesh; + + // Point + const boost::python::type_info point_info = type_id(); + const converter::registration * point_registration = converter::registry::query(point_info); + scope_mesh.attr("Point") = handle<>(point_registration->m_class_object); + + // Normal + const boost::python::type_info normal_info = type_id(); + const converter::registration * normal_registration = converter::registry::query(normal_info); + scope_mesh.attr("Normal") = handle<>(normal_registration->m_class_object); + + // Color + const boost::python::type_info color_info = type_id(); + const converter::registration * color_registration = converter::registry::query(color_info); + scope_mesh.attr("Color") = handle<>(color_registration->m_class_object); + + // TexCoord2D + const boost::python::type_info texcoord2d_info = type_id(); + const converter::registration * texcoord2d_registration = converter::registry::query(texcoord2d_info); + scope_mesh.attr("TexCoord2D") = handle<>(texcoord2d_registration->m_class_object); + + // TexCoord3D + const boost::python::type_info texcoord3d_info = type_id(); + const converter::registration * texcoord3d_registration = converter::registry::query(texcoord3d_info); + scope_mesh.attr("TexCoord3D") = handle<>(texcoord3d_registration->m_class_object); +} + +} // namespace OpenMesh +} // namespace Python + +#endif diff --git a/src/Python/PropertyManager.hh b/src/Python/PropertyManager.hh new file mode 100644 index 00000000..515d5cc6 --- /dev/null +++ b/src/Python/PropertyManager.hh @@ -0,0 +1,143 @@ +#ifndef OPENMESH_PYTHON_PROPERTYMANAGER_HH +#define OPENMESH_PYTHON_PROPERTYMANAGER_HH + +#include "Python/Bindings.hh" +#include "OpenMesh/Core/Utils/PropertyManager.hh" + +namespace OpenMesh { +namespace Python { + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(retain_overloads, retain, 0, 1) + +/** + * Implementation of %Python's \_\_getitem\_\_ magic method. + * + * @tparam PropertyManager A property manager type. + * @tparam IndexHandle The appropriate handle type. + * + * @param _self The property manager instance that is to be used. + * @param _handle The index of the property value to be returned. + * + * @return The requested property value. + */ +template +object propman_get_item(PropertyManager& _self, IndexHandle _handle) { + return _self[_handle]; +} + +/** + * Implementation of %Python's \_\_setitem\_\_ magic method. + * + * @tparam PropertyManager A property manager type. + * @tparam IndexHandle The appropriate handle type. + * + * @param _self The property manager instance that is to be used. + * @param _handle The index of the property value to be set. + * @param _value The property value to be set. + */ +template +void propman_set_item(PropertyManager& _self, IndexHandle _handle, object _value) { + _self[_handle] = _value; +} + +/** + * Conveniently set the property value for an entire range of mesh items + * using a %Python iterator. + * + * @tparam PropertyManager A property manager type. + * @tparam Iterator A %Python iterator type. + * + * @param _self The property manager instance that is to be used. + * @param _it An iterator that iterates over the items in the range. + * @param _value The value the range will be set to. + */ +template +void propman_set_range(PropertyManager& _self, Iterator _it, object _value) { + try { + while (true) { + _self[_it.next()] = _value; + } + } + catch (error_already_set exception) { + // This is expected behavior + PyErr_Clear(); + } +} + +/** + * Thin wrapper for propertyExists. + * + * @tparam PropertyManager A property manager type. + * @tparam Mesh A mesh type. + * + * @param _mesh The mesh that is used to check if the property exists. + * @param _propname The name of the property. + */ +template +bool property_exists(Mesh& _mesh, const char *_propname) { + return PropertyManager::propertyExists(_mesh, _propname); +} + +/** + * Expose a property manager type to %Python. + * + * This function template is used to expose property managers to %Python. The + * template parameters are used to instantiate the appropriate property manager + * type. + * + * @tparam PropHandle A property handle type (e.g. %VPropHandle\). + * @tparam IndexHandle The appropriate handle type (e.g. %VertexHandle for + * %VPropHandle\). + * @tparam Iterator A %Python iterator type. This type is used to instantiate + * the propman_set_range function. + * + * @param _name The name of the property manager type to be exposed. + */ +template +void expose_property_manager(const char *_name) { + // Convenience typedef + typedef PropertyManager PropertyManager; + + // Function pointers + void (PropertyManager::*retain)(bool) = &PropertyManager::retain; + + object (*getitem)(PropertyManager&, IndexHandle ) = &propman_get_item; + void (*setitem)(PropertyManager&, IndexHandle, object) = &propman_set_item; + + void (*set_range)(PropertyManager&, Iterator, object) = &propman_set_range; + + bool (*property_exists_poly)(PolyMesh&, const char *) = &property_exists; + bool (*property_exists_tri )(TriMesh&, const char *) = &property_exists; + + // Expose property manager + class_(_name) + .def(init >()) + .def(init >()) + + .def("swap", &PropertyManager::swap) + .def("is_valid", &PropertyManager::isValid) + + .def("__bool__", &PropertyManager::operator bool) + .def("__nonzero__", &PropertyManager::operator bool) + + .def("get_raw_property", &PropertyManager::getRawProperty, return_value_policy()) + .def("get_name", &PropertyManager::getName, return_value_policy()) + .def("get_mesh", &PropertyManager::getMesh, return_value_policy()) + + .def("retain", retain, retain_overloads()) + + .def("__getitem__", getitem) + .def("__setitem__", setitem) + + .def("set_range", set_range) + + .def("property_exists", property_exists_poly) + .def("property_exists", property_exists_tri) + .staticmethod("property_exists") + ; +} + +} // namespace OpenMesh +} // namespace Python + +#endif diff --git a/src/Python/Unittests/test_add_face.py b/src/Python/Unittests/test_add_face.py new file mode 100644 index 00000000..faeadea1 --- /dev/null +++ b/src/Python/Unittests/test_add_face.py @@ -0,0 +1,333 @@ +import unittest +import openmesh + +class AddFace(unittest.TestCase): + + def test_add_triangles_to_trimesh(self): + self.mesh = openmesh.TriMesh() + self.vhandle = [] + + # Add some vertices + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 0, 0))) + + # Add two faces + face_vhandles = [] + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[0]) + + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[3]) + + self.mesh.add_face(face_vhandles) + + # Test setup: + # 1 === 2 + # | / | + # | / | + # | / | + # 0 === 3 + + # Check setup + self.assertEqual(self.mesh.n_vertices(), 4) + self.assertEqual(self.mesh.n_faces(), 2) + + def test_add_quad_to_trimesh(self): + self.mesh = openmesh.TriMesh() + self.vhandle = [] + + # Add some vertices + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 0, 0))) + + # Add two faces + face_vhandles = [] + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[3]) + + self.mesh.add_face(face_vhandles) + + # Test setup: + # 1 === 2 + # | / | + # | / | + # | / | + # 0 === 3 + + # Check setup + self.assertEqual(self.mesh.n_vertices(), 4) + self.assertEqual(self.mesh.n_faces(), 2) + + def test_create_triangle_mesh_cube(self): + self.mesh = openmesh.TriMesh() + self.vhandle = [] + + # Add some vertices + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(-1, -1, 1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 1, -1, 1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 1, 1, 1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(-1, 1, 1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(-1, -1, -1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 1, -1, -1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 1, 1, -1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(-1, 1, -1))) + + # Add six faces to form a cube + face_vhandles = [] + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[3]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[3]) + self.mesh.add_face(face_vhandles) + + #======================= + + face_vhandles = [] + face_vhandles.append(self.vhandle[7]) + face_vhandles.append(self.vhandle[6]) + face_vhandles.append(self.vhandle[5]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + face_vhandles.append(self.vhandle[7]) + face_vhandles.append(self.vhandle[5]) + face_vhandles.append(self.vhandle[4]) + self.mesh.add_face(face_vhandles) + + #======================= + + face_vhandles = [] + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[4]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[4]) + face_vhandles.append(self.vhandle[5]) + self.mesh.add_face(face_vhandles) + + #======================= + + face_vhandles = [] + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[5]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[5]) + face_vhandles.append(self.vhandle[6]) + self.mesh.add_face(face_vhandles) + + #======================= + + face_vhandles = [] + face_vhandles.append(self.vhandle[3]) + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[6]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + face_vhandles.append(self.vhandle[3]) + face_vhandles.append(self.vhandle[6]) + face_vhandles.append(self.vhandle[7]) + self.mesh.add_face(face_vhandles) + + #======================= + + face_vhandles = [] + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[3]) + face_vhandles.append(self.vhandle[7]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[7]) + face_vhandles.append(self.vhandle[4]) + self.mesh.add_face(face_vhandles) + + # Test setup: + # + # 3 ======== 2 + # / /| + # / / | z + # 0 ======== 1 | | + # | | | | y + # | 7 | 6 | / + # | | / | / + # | |/ |/ + # 4 ======== 5 -------> x + + # Check setup + self.assertEqual(self.mesh.n_edges(), 18) + self.assertEqual(self.mesh.n_halfedges(), 36) + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_faces(), 12) + + def test_create_strange_config(self): + self.mesh = openmesh.TriMesh() + self.vhandle = [] + + # 2 x-----------x 1 + # \ / + # \ / + # \ / + # \ / + # \ / + # 0 x ---x 6 + # /|\ | + # / | \ | + # / | \ | + # / | \| + # x----x x + # 3 4 5 + + # Add some vertices + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 1, 1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(2, 2, 2))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(3, 3, 3))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(4, 4, 4))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(5, 5, 5))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(6, 6, 6))) + + self.mesh.add_face(self.vhandle[0], self.vhandle[1], self.vhandle[2]) + self.mesh.add_face(self.vhandle[0], self.vhandle[3], self.vhandle[4]) + self.mesh.add_face(self.vhandle[0], self.vhandle[5], self.vhandle[6]) + + # non-manifold! + self.mesh.add_face(self.vhandle[3], self.vhandle[0], self.vhandle[4]) + + # Check setup + self.assertEqual(self.mesh.n_vertices(), 7) + self.assertEqual(self.mesh.n_faces(), 4) + + def test_add_quad_to_polymesh(self): + self.mesh = openmesh.PolyMesh() + self.vhandle = [] + + # Add some vertices + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 0, 0))) + + # Add one face + face_vhandles = [] + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[3]) + + self.mesh.add_face(face_vhandles) + + # Test setup: + # 1 === 2 + # | | + # | | + # | | + # 0 === 3 + + # Check setup + self.assertEqual(self.mesh.n_vertices(), 4) + self.assertEqual(self.mesh.n_faces(), 1) + + def test_create_poly_mesh_cube(self): + self.mesh = openmesh.PolyMesh() + self.vhandle = [] + + # Add some vertices + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(-1, -1, 1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 1, -1, 1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 1, 1, 1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(-1, 1, 1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(-1, -1, -1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 1, -1, -1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 1, 1, -1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(-1, 1, -1))) + + # Add six faces to form a cube + face_vhandles = [] + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[3]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + face_vhandles.append(self.vhandle[7]) + face_vhandles.append(self.vhandle[6]) + face_vhandles.append(self.vhandle[5]) + face_vhandles.append(self.vhandle[4]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[4]) + face_vhandles.append(self.vhandle[5]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[5]) + face_vhandles.append(self.vhandle[6]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + face_vhandles.append(self.vhandle[3]) + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[6]) + face_vhandles.append(self.vhandle[7]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[3]) + face_vhandles.append(self.vhandle[7]) + face_vhandles.append(self.vhandle[4]) + self.mesh.add_face(face_vhandles) + + # Test setup: + # + # 3 ======== 2 + # / /| + # / / | z + # 0 ======== 1 | | + # | | | | y + # | 7 | 6 | / + # | | / | / + # | |/ |/ + # 4 ======== 5 -------> x + + # Check setup + self.assertEqual(self.mesh.n_edges(), 12) + self.assertEqual(self.mesh.n_halfedges(), 24) + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_faces(), 6) + + +if __name__ == '__main__': + suite = unittest.TestLoader().loadTestsFromTestCase(AddFace) + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/src/Python/Unittests/test_boundary.py b/src/Python/Unittests/test_boundary.py new file mode 100644 index 00000000..fde5ea35 --- /dev/null +++ b/src/Python/Unittests/test_boundary.py @@ -0,0 +1,86 @@ +import unittest +import openmesh + +class BoundaryTriangleMesh(unittest.TestCase): + + def setUp(self): + self.mesh = openmesh.TriMesh() + + # Add some vertices + self.vhandle = [] + + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(2, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0,-1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(2,-1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(3, 0, 0))) + + # Single point + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0,-2, 0))) + + # Add five faces + self.fhandle = [] + + face_vhandles = [] + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[2]) + self.fhandle.append(self.mesh.add_face(face_vhandles)) + + face_vhandles = [] + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[3]) + face_vhandles.append(self.vhandle[4]) + self.fhandle.append(self.mesh.add_face(face_vhandles)) + + face_vhandles = [] + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[3]) + face_vhandles.append(self.vhandle[1]) + self.fhandle.append(self.mesh.add_face(face_vhandles)) + + face_vhandles = [] + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[4]) + self.fhandle.append(self.mesh.add_face(face_vhandles)) + + face_vhandles = [] + face_vhandles.append(self.vhandle[5]) + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[4]) + self.fhandle.append(self.mesh.add_face(face_vhandles)) + + # Test setup: + # 0 ==== 2 + # |\ 0 /|\ + # | \ / | \ + # |2 1 3|4 5 + # | / \ | / + # |/ 1 \|/ + # 3 ==== 4 + # + # Vertex 6 single + + def test_boundary_vertex(self): + self.assertTrue (self.mesh.is_boundary(self.vhandle[0])) + self.assertFalse(self.mesh.is_boundary(self.vhandle[1])) + self.assertTrue (self.mesh.is_boundary(self.vhandle[2])) + self.assertTrue (self.mesh.is_boundary(self.vhandle[3])) + self.assertTrue (self.mesh.is_boundary(self.vhandle[4])) + self.assertTrue (self.mesh.is_boundary(self.vhandle[5])) + + self.assertTrue (self.mesh.is_boundary(self.vhandle[6])) + + def test_boundary_face(self): + self.assertTrue (self.mesh.is_boundary(self.fhandle[0])) + self.assertTrue (self.mesh.is_boundary(self.fhandle[1])) + self.assertTrue (self.mesh.is_boundary(self.fhandle[2])) + self.assertFalse(self.mesh.is_boundary(self.fhandle[3])) + self.assertTrue (self.mesh.is_boundary(self.fhandle[4])) + + +if __name__ == '__main__': + suite = unittest.TestLoader().loadTestsFromTestCase(BoundaryTriangleMesh) + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/src/Python/Unittests/test_delete_face.py b/src/Python/Unittests/test_delete_face.py new file mode 100644 index 00000000..5359cfde --- /dev/null +++ b/src/Python/Unittests/test_delete_face.py @@ -0,0 +1,530 @@ +import unittest +import openmesh + +class DeleteFaceTriangleMesh(unittest.TestCase): + + def test_delete_half_triangle_mesh_cube_no_edge_status(self): + self.mesh = openmesh.TriMesh() + self.vhandle = [] + + # Add some vertices + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(-1, -1, 1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 1, -1, 1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 1, 1, 1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(-1, 1, 1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(-1, -1, -1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 1, -1, -1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 1, 1, -1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(-1, 1, -1))) + + # Add six faces to form a cube + face_vhandles = [] + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[3]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[3]) + self.mesh.add_face(face_vhandles) + + #======================= + + face_vhandles = [] + face_vhandles.append(self.vhandle[7]) + face_vhandles.append(self.vhandle[6]) + face_vhandles.append(self.vhandle[5]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + face_vhandles.append(self.vhandle[7]) + face_vhandles.append(self.vhandle[5]) + face_vhandles.append(self.vhandle[4]) + self.mesh.add_face(face_vhandles) + + #======================= + + face_vhandles = [] + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[4]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[4]) + face_vhandles.append(self.vhandle[5]) + self.mesh.add_face(face_vhandles) + + #======================= + + face_vhandles = [] + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[5]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[5]) + face_vhandles.append(self.vhandle[6]) + self.mesh.add_face(face_vhandles) + + #======================= + + face_vhandles = [] + face_vhandles.append(self.vhandle[3]) + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[6]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + face_vhandles.append(self.vhandle[3]) + face_vhandles.append(self.vhandle[6]) + face_vhandles.append(self.vhandle[7]) + self.mesh.add_face(face_vhandles) + + #======================= + + face_vhandles = [] + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[3]) + face_vhandles.append(self.vhandle[7]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[7]) + face_vhandles.append(self.vhandle[4]) + self.mesh.add_face(face_vhandles) + + # Test setup: + # + # 3 ======== 2 + # / /| + # / / | z + # 0 ======== 1 | | + # | | | | y + # | 7 | 6 | / + # | | / | / + # | |/ |/ + # 4 ======== 5 -------> x + + # Check setup + self.assertEqual(self.mesh.n_edges(), 18) + self.assertEqual(self.mesh.n_halfedges(), 36) + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_faces(), 12) + + # ===================================================== + # Now we delete half of the mesh + # ===================================================== + self.mesh.request_face_status() + self.mesh.request_vertex_status() + self.mesh.request_halfedge_status() + + n_face_to_delete = self.mesh.n_faces() / 2 + + # Check the variable + self.assertEqual(n_face_to_delete, 6) + + for i in range(int(n_face_to_delete)): + self.mesh.delete_face(self.mesh.face_handle(i)) + + # ===================================================== + # Check config afterwards + # ===================================================== + + self.assertEqual(self.mesh.n_edges(), 18) + self.assertEqual(self.mesh.n_halfedges(), 36) + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_faces(), 12) + + # ===================================================== + # Cleanup and recheck + # ===================================================== + + self.mesh.garbage_collection() + + self.assertEqual(self.mesh.n_edges(), 18) + self.assertEqual(self.mesh.n_halfedges(), 36) + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_faces(), 6) + + def test_delete_half_triangle_mesh_cube_with_edge_status(self): + self.mesh = openmesh.TriMesh() + self.vhandle = [] + + # Add some vertices + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(-1, -1, 1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 1, -1, 1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 1, 1, 1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(-1, 1, 1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(-1, -1, -1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 1, -1, -1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 1, 1, -1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(-1, 1, -1))) + + # Add six faces to form a cube + face_vhandles = [] + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[3]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[3]) + self.mesh.add_face(face_vhandles) + + #======================= + + face_vhandles = [] + face_vhandles.append(self.vhandle[7]) + face_vhandles.append(self.vhandle[6]) + face_vhandles.append(self.vhandle[5]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + face_vhandles.append(self.vhandle[7]) + face_vhandles.append(self.vhandle[5]) + face_vhandles.append(self.vhandle[4]) + self.mesh.add_face(face_vhandles) + + #======================= + + face_vhandles = [] + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[4]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[4]) + face_vhandles.append(self.vhandle[5]) + self.mesh.add_face(face_vhandles) + + #======================= + + face_vhandles = [] + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[5]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[5]) + face_vhandles.append(self.vhandle[6]) + self.mesh.add_face(face_vhandles) + + #======================= + + face_vhandles = [] + face_vhandles.append(self.vhandle[3]) + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[6]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + face_vhandles.append(self.vhandle[3]) + face_vhandles.append(self.vhandle[6]) + face_vhandles.append(self.vhandle[7]) + self.mesh.add_face(face_vhandles) + + #======================= + + face_vhandles = [] + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[3]) + face_vhandles.append(self.vhandle[7]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[7]) + face_vhandles.append(self.vhandle[4]) + self.mesh.add_face(face_vhandles) + + # Test setup: + # + # 3 ======== 2 + # / /| + # / / | z + # 0 ======== 1 | | + # | | | | y + # | 7 | 6 | / + # | | / | / + # | |/ |/ + # 4 ======== 5 -------> x + + # Check setup + self.assertEqual(self.mesh.n_edges(), 18) + self.assertEqual(self.mesh.n_halfedges(), 36) + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_faces(), 12) + + # ===================================================== + # Now we delete half of the mesh + # ===================================================== + self.mesh.request_face_status() + self.mesh.request_vertex_status() + self.mesh.request_edge_status() + self.mesh.request_halfedge_status() + + n_face_to_delete = self.mesh.n_faces() / 2 + + # Check the variable + self.assertEqual(n_face_to_delete, 6) + + for i in range(int(n_face_to_delete)): + self.mesh.delete_face(self.mesh.face_handle(i)) + + # ===================================================== + # Check config afterwards + # ===================================================== + + self.assertEqual(self.mesh.n_edges(), 18) + self.assertEqual(self.mesh.n_halfedges(), 36) + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_faces(), 12) + + # ===================================================== + # Cleanup and recheck + # ===================================================== + + self.mesh.garbage_collection() + + self.assertEqual(self.mesh.n_edges(), 13) + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_faces(), 6) + + def test_deletete_half_poly_mesh_cube_without_edge_status(self): + self.mesh = openmesh.PolyMesh() + self.vhandle = [] + + # Add some vertices + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(-1, -1, 1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 1, -1, 1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 1, 1, 1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(-1, 1, 1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(-1, -1, -1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 1, -1, -1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 1, 1, -1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(-1, 1, -1))) + + # Add six faces to form a cube + face_vhandles = [] + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[3]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + face_vhandles.append(self.vhandle[7]) + face_vhandles.append(self.vhandle[6]) + face_vhandles.append(self.vhandle[5]) + face_vhandles.append(self.vhandle[4]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[4]) + face_vhandles.append(self.vhandle[5]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[5]) + face_vhandles.append(self.vhandle[6]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + face_vhandles.append(self.vhandle[3]) + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[6]) + face_vhandles.append(self.vhandle[7]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[3]) + face_vhandles.append(self.vhandle[7]) + face_vhandles.append(self.vhandle[4]) + self.mesh.add_face(face_vhandles) + + # Test setup: + # + # 3 ======== 2 + # / /| + # / / | z + # 0 ======== 1 | | + # | | | | y + # | 7 | 6 | / + # | | / | / + # | |/ |/ + # 4 ======== 5 -------> x + + # Check setup + self.assertEqual(self.mesh.n_edges(), 12) + self.assertEqual(self.mesh.n_halfedges(), 24) + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_faces(), 6) + + # ===================================================== + # Now we delete half of the mesh + # ===================================================== + self.mesh.request_face_status() + self.mesh.request_vertex_status() + self.mesh.request_halfedge_status() + + n_face_to_delete = self.mesh.n_faces() / 2 + + # Check the variable + self.assertEqual(n_face_to_delete, 3) + + for i in range(int(n_face_to_delete)): + self.mesh.delete_face(self.mesh.face_handle(i)) + + # ===================================================== + # Check config afterwards + # ===================================================== + + self.assertEqual(self.mesh.n_edges(), 12) + self.assertEqual(self.mesh.n_halfedges(), 24) + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_faces(), 6) + + # ===================================================== + # Cleanup and recheck + # ===================================================== + + self.mesh.garbage_collection() + + self.assertEqual(self.mesh.n_edges(), 12) + self.assertEqual(self.mesh.n_halfedges(), 24) + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_faces(), 3) + + def test_deletete_half_poly_mesh_cube_with_edge_status(self): + self.mesh = openmesh.PolyMesh() + self.vhandle = [] + + # Add some vertices + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(-1, -1, 1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 1, -1, 1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 1, 1, 1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(-1, 1, 1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(-1, -1, -1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 1, -1, -1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 1, 1, -1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(-1, 1, -1))) + + # Add six faces to form a cube + face_vhandles = [] + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[3]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + face_vhandles.append(self.vhandle[7]) + face_vhandles.append(self.vhandle[6]) + face_vhandles.append(self.vhandle[5]) + face_vhandles.append(self.vhandle[4]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[4]) + face_vhandles.append(self.vhandle[5]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[5]) + face_vhandles.append(self.vhandle[6]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + face_vhandles.append(self.vhandle[3]) + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[6]) + face_vhandles.append(self.vhandle[7]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[3]) + face_vhandles.append(self.vhandle[7]) + face_vhandles.append(self.vhandle[4]) + self.mesh.add_face(face_vhandles) + + # Test setup: + # + # 3 ======== 2 + # / /| + # / / | z + # 0 ======== 1 | | + # | | | | y + # | 7 | 6 | / + # | | / | / + # | |/ |/ + # 4 ======== 5 -------> x + + # Check setup + self.assertEqual(self.mesh.n_edges(), 12) + self.assertEqual(self.mesh.n_halfedges(), 24) + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_faces(), 6) + + # ===================================================== + # Now we delete half of the mesh + # ===================================================== + self.mesh.request_face_status() + self.mesh.request_vertex_status() + self.mesh.request_edge_status() + self.mesh.request_halfedge_status() + + n_face_to_delete = self.mesh.n_faces() / 2 + + # Check the variable + self.assertEqual(n_face_to_delete, 3) + + for i in range(int(n_face_to_delete)): + self.mesh.delete_face(self.mesh.face_handle(i)) + + # ===================================================== + # Check config afterwards + # ===================================================== + + self.assertEqual(self.mesh.n_edges(), 12) + self.assertEqual(self.mesh.n_halfedges(), 24) + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_faces(), 6) + + # ===================================================== + # Cleanup and recheck + # ===================================================== + + self.mesh.garbage_collection() + + self.assertEqual(self.mesh.n_edges(), 10) + self.assertEqual(self.mesh.n_halfedges(), 20) + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_faces(), 3) + + +if __name__ == '__main__': + suite = unittest.TestLoader().loadTestsFromTestCase(DeleteFaceTriangleMesh) + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/src/Python/Unittests/test_property.py b/src/Python/Unittests/test_property.py new file mode 100755 index 00000000..61d3cbd7 --- /dev/null +++ b/src/Python/Unittests/test_property.py @@ -0,0 +1,289 @@ +import unittest +import openmesh + +class Property(unittest.TestCase): + + def setUp(self): + self.mesh = openmesh.TriMesh() + self.vhandle = [] + + def test_vertex_property_copy_properties_int(self): + # Add some vertices + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 0, 0))) + + # Add two faces + face_vhandles = [] + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[0]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[3]) + self.mesh.add_face(face_vhandles) + + # Test setup: + # 1 === 2 + # | / | + # | / | + # | / | + # 0 === 3 + + # Check setup + self.assertEqual(self.mesh.n_vertices(), 4) + self.assertEqual(self.mesh.n_faces(), 2) + + # Add a vertex property + intHandle = openmesh.VPropHandle() + self.assertFalse(self.mesh.get_property_handle(intHandle, "intProp")) + self.mesh.add_property(intHandle, "intProp") + self.assertTrue(self.mesh.get_property_handle(intHandle, "intProp")) + + # Fill property + for vh in self.mesh.vertices(): + self.mesh.set_property(intHandle, vh, vh.idx()) + + # Check if property it is ok + v_it = self.mesh.vertices() + self.assertEqual(self.mesh.property(intHandle, v_it.next()), 0) + self.assertEqual(self.mesh.property(intHandle, v_it.next()), 1) + self.assertEqual(self.mesh.property(intHandle, v_it.next()), 2) + self.assertEqual(self.mesh.property(intHandle, v_it.next()), 3) + + # Check vertex positions + v_it = self.mesh.vertices() + vh = v_it.next() + self.assertEqual(self.mesh.point(vh)[0], 0) + self.assertEqual(self.mesh.point(vh)[1], 0) + self.assertEqual(self.mesh.point(vh)[2], 0) + vh = v_it.next() + self.assertEqual(self.mesh.point(vh)[0], 0) + self.assertEqual(self.mesh.point(vh)[1], 1) + self.assertEqual(self.mesh.point(vh)[2], 0) + vh = v_it.next() + self.assertEqual(self.mesh.point(vh)[0], 1) + self.assertEqual(self.mesh.point(vh)[1], 1) + self.assertEqual(self.mesh.point(vh)[2], 0) + vh = v_it.next() + self.assertEqual(self.mesh.point(vh)[0], 1) + self.assertEqual(self.mesh.point(vh)[1], 0) + self.assertEqual(self.mesh.point(vh)[2], 0) + + # Copy from vertex 1 to 0, with skipping build in properties + self.mesh.copy_all_properties(self.vhandle[1], self.vhandle[0]) + v_it = self.mesh.vertices() + vh = v_it.next() + self.assertEqual(self.mesh.point(vh)[0], 0) + self.assertEqual(self.mesh.point(vh)[1], 0) + self.assertEqual(self.mesh.point(vh)[2], 0) + vh = v_it.next() + self.assertEqual(self.mesh.point(vh)[0], 0) + self.assertEqual(self.mesh.point(vh)[1], 1) + self.assertEqual(self.mesh.point(vh)[2], 0) + vh = v_it.next() + self.assertEqual(self.mesh.point(vh)[0], 1) + self.assertEqual(self.mesh.point(vh)[1], 1) + self.assertEqual(self.mesh.point(vh)[2], 0) + vh = v_it.next() + self.assertEqual(self.mesh.point(vh)[0], 1) + self.assertEqual(self.mesh.point(vh)[1], 0) + self.assertEqual(self.mesh.point(vh)[2], 0) + v_it = self.mesh.vertices() + self.assertEqual(self.mesh.property(intHandle, v_it.next()), 1) + self.assertEqual(self.mesh.property(intHandle, v_it.next()), 1) + self.assertEqual(self.mesh.property(intHandle, v_it.next()), 2) + self.assertEqual(self.mesh.property(intHandle, v_it.next()), 3) + + # Copy from vertex 2 to 3, including build in properties + self.mesh.copy_all_properties(self.vhandle[2], self.vhandle[3], True) + v_it = self.mesh.vertices() + vh = v_it.next() + self.assertEqual(self.mesh.point(vh)[0], 0) + self.assertEqual(self.mesh.point(vh)[1], 0) + self.assertEqual(self.mesh.point(vh)[2], 0) + vh = v_it.next() + self.assertEqual(self.mesh.point(vh)[0], 0) + self.assertEqual(self.mesh.point(vh)[1], 1) + self.assertEqual(self.mesh.point(vh)[2], 0) + vh = v_it.next() + self.assertEqual(self.mesh.point(vh)[0], 1) + self.assertEqual(self.mesh.point(vh)[1], 1) + self.assertEqual(self.mesh.point(vh)[2], 0) + vh = v_it.next() + self.assertEqual(self.mesh.point(vh)[0], 1) + self.assertEqual(self.mesh.point(vh)[1], 1) + self.assertEqual(self.mesh.point(vh)[2], 0) + v_it = self.mesh.vertices() + self.assertEqual(self.mesh.property(intHandle, v_it.next()), 1) + self.assertEqual(self.mesh.property(intHandle, v_it.next()), 1) + self.assertEqual(self.mesh.property(intHandle, v_it.next()), 2) + self.assertEqual(self.mesh.property(intHandle, v_it.next()), 2) + + def test_check_status_properties_halfedge_edge_all_deleted(self): + self.mesh.request_vertex_status() + self.mesh.request_face_status() + self.mesh.request_halfedge_status() + self.mesh.request_edge_status() + + # Define positions + p1 = openmesh.Vec3d(0, 0, 0) + p2 = openmesh.Vec3d(0, 1, 0) + p3 = openmesh.Vec3d(1, 1, 0) + p4 = openmesh.Vec3d(0, 0, 1) + + # Add some vertices + vh1 = self.mesh.add_vertex(p1) + vh2 = self.mesh.add_vertex(p2) + vh3 = self.mesh.add_vertex(p3) + vh4 = self.mesh.add_vertex(p4) + + # Add some faces + f1 = self.mesh.add_face(vh1, vh3, vh2) + f2 = self.mesh.add_face(vh1, vh2, vh4) + f3 = self.mesh.add_face(vh2, vh3, vh4) + f4 = self.mesh.add_face(vh3, vh1, vh4) + + # Delete all faces + self.mesh.delete_face(f1) + self.mesh.delete_face(f2) + self.mesh.delete_face(f3) + self.mesh.delete_face(f4) + + for heh in self.mesh.halfedges(): + self.assertTrue(self.mesh.status(self.mesh.edge_handle(heh)).deleted()) + self.assertTrue(self.mesh.status(heh).deleted()) + + def test_copy_all_properties_vertex_after_remove_of_property(self): + # Add some vertices + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 0, 0))) + + # Add two faces + face_vhandles = [] + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[0]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[3]) + self.mesh.add_face(face_vhandles) + + # Test setup: + # 1 === 2 + # | / | + # | / | + # | / | + # 0 === 3 + + # Check setup + self.assertEqual(self.mesh.n_vertices(), 4) + self.assertEqual(self.mesh.n_faces(), 2) + + # Add a double vertex property + doubleHandle = openmesh.VPropHandle() + self.assertFalse(self.mesh.get_property_handle(doubleHandle, "doubleProp")) + self.mesh.add_property(doubleHandle, "doubleProp") + self.assertTrue(self.mesh.get_property_handle(doubleHandle, "doubleProp")) + + # Add a int vertex property + intHandle = openmesh.VPropHandle() + self.assertFalse(self.mesh.get_property_handle(intHandle, "intProp")) + self.mesh.add_property(intHandle, "intProp") + self.assertTrue(self.mesh.get_property_handle(intHandle, "intProp")) + + # Now remove the double property again + self.mesh.remove_property(doubleHandle) + + # Fill int property + for vh in self.mesh.vertices(): + self.mesh.set_property(intHandle, vh, vh.idx()) + + # Check if property it is ok + v_it = self.mesh.vertices() + self.assertEqual(self.mesh.property(intHandle, v_it.next()), 0) + self.assertEqual(self.mesh.property(intHandle, v_it.next()), 1) + self.assertEqual(self.mesh.property(intHandle, v_it.next()), 2) + self.assertEqual(self.mesh.property(intHandle, v_it.next()), 3) + + # Check vertex positions + v_it = self.mesh.vertices() + vh = v_it.next() + self.assertEqual(self.mesh.point(vh)[0], 0) + self.assertEqual(self.mesh.point(vh)[1], 0) + self.assertEqual(self.mesh.point(vh)[2], 0) + vh = v_it.next() + self.assertEqual(self.mesh.point(vh)[0], 0) + self.assertEqual(self.mesh.point(vh)[1], 1) + self.assertEqual(self.mesh.point(vh)[2], 0) + vh = v_it.next() + self.assertEqual(self.mesh.point(vh)[0], 1) + self.assertEqual(self.mesh.point(vh)[1], 1) + self.assertEqual(self.mesh.point(vh)[2], 0) + vh = v_it.next() + self.assertEqual(self.mesh.point(vh)[0], 1) + self.assertEqual(self.mesh.point(vh)[1], 0) + self.assertEqual(self.mesh.point(vh)[2], 0) + + # Copy from vertex 1 to 0, with skipping build in properties + self.mesh.copy_all_properties(self.vhandle[1], self.vhandle[0]) + v_it = self.mesh.vertices() + vh = v_it.next() + self.assertEqual(self.mesh.point(vh)[0], 0) + self.assertEqual(self.mesh.point(vh)[1], 0) + self.assertEqual(self.mesh.point(vh)[2], 0) + vh = v_it.next() + self.assertEqual(self.mesh.point(vh)[0], 0) + self.assertEqual(self.mesh.point(vh)[1], 1) + self.assertEqual(self.mesh.point(vh)[2], 0) + vh = v_it.next() + self.assertEqual(self.mesh.point(vh)[0], 1) + self.assertEqual(self.mesh.point(vh)[1], 1) + self.assertEqual(self.mesh.point(vh)[2], 0) + vh = v_it.next() + self.assertEqual(self.mesh.point(vh)[0], 1) + self.assertEqual(self.mesh.point(vh)[1], 0) + self.assertEqual(self.mesh.point(vh)[2], 0) + v_it = self.mesh.vertices() + self.assertEqual(self.mesh.property(intHandle, v_it.next()), 1) + self.assertEqual(self.mesh.property(intHandle, v_it.next()), 1) + self.assertEqual(self.mesh.property(intHandle, v_it.next()), 2) + self.assertEqual(self.mesh.property(intHandle, v_it.next()), 3) + + # Copy from vertex 2 to 3, including build in properties + self.mesh.copy_all_properties(self.vhandle[2], self.vhandle[3], True) + v_it = self.mesh.vertices() + vh = v_it.next() + self.assertEqual(self.mesh.point(vh)[0], 0) + self.assertEqual(self.mesh.point(vh)[1], 0) + self.assertEqual(self.mesh.point(vh)[2], 0) + vh = v_it.next() + self.assertEqual(self.mesh.point(vh)[0], 0) + self.assertEqual(self.mesh.point(vh)[1], 1) + self.assertEqual(self.mesh.point(vh)[2], 0) + vh = v_it.next() + self.assertEqual(self.mesh.point(vh)[0], 1) + self.assertEqual(self.mesh.point(vh)[1], 1) + self.assertEqual(self.mesh.point(vh)[2], 0) + vh = v_it.next() + self.assertEqual(self.mesh.point(vh)[0], 1) + self.assertEqual(self.mesh.point(vh)[1], 1) + self.assertEqual(self.mesh.point(vh)[2], 0) + v_it = self.mesh.vertices() + self.assertEqual(self.mesh.property(intHandle, v_it.next()), 1) + self.assertEqual(self.mesh.property(intHandle, v_it.next()), 1) + self.assertEqual(self.mesh.property(intHandle, v_it.next()), 2) + self.assertEqual(self.mesh.property(intHandle, v_it.next()), 2) + +if __name__ == '__main__': + suite = unittest.TestLoader().loadTestsFromTestCase(Property) + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/src/Python/Unittests/test_python.py b/src/Python/Unittests/test_python.py new file mode 100644 index 00000000..f7d75492 --- /dev/null +++ b/src/Python/Unittests/test_python.py @@ -0,0 +1,71 @@ +import unittest +import openmesh + +class Python(unittest.TestCase): + + def setUp(self): + self.mesh = openmesh.TriMesh() + + # Add some vertices + self.vhandle = [] + + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(2, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0,-1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(2,-1, 0))) + + # Add four faces using Python lists + vertex_list = [self.vhandle[0], self.vhandle[1], self.vhandle[2]] + self.mesh.add_face(vertex_list) + vertex_list = [self.vhandle[1], self.vhandle[3], self.vhandle[4]] + self.mesh.add_face(vertex_list) + vertex_list = [self.vhandle[0], self.vhandle[3], self.vhandle[1]] + self.mesh.add_face(vertex_list) + vertex_list = [self.vhandle[2], self.vhandle[1], self.vhandle[4]] + self.mesh.add_face(vertex_list) + + # Test setup: + # 0 ==== 2 + # |\ 0 /| + # | \ / | + # |2 1 3| + # | / \ | + # |/ 1 \| + # 3 ==== 4 + + def test_python_iterator(self): + # Iterate over all vertices + indices = [0, 1, 2, 3, 4] + for v, idx in zip(self.mesh.vertices(), indices): + self.assertEqual(v.idx(), idx) + + def test_python_circulator(self): + # Iterate around vertex 1 at the middle + indices = [4, 3, 0, 2] + for v, idx in zip(self.mesh.vv(self.vhandle[1]), indices): + self.assertEqual(v.idx(), idx) + + def test_property_manager(self): + # Check if vertex property exists + self.assertFalse(openmesh.VPropertyManager.property_exists(self.mesh, "prop")) + + # Create a new vertex property + propman = openmesh.VPropertyManager(self.mesh, "prop") + self.assertTrue(propman.property_exists(self.mesh, "prop")) + + # Check initial property values + for v in self.mesh.vertices(): + self.assertEqual(propman[v], None) + + # Set property values + propman.set_range(self.mesh.vertices(), 0.0) + + # Check again + for v in self.mesh.vertices(): + self.assertEqual(propman[v], 0.0) + + +if __name__ == '__main__': + suite = unittest.TestLoader().loadTestsFromTestCase(Python) + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/src/Python/Unittests/test_read_write_obj.py b/src/Python/Unittests/test_read_write_obj.py new file mode 100644 index 00000000..da2bfa41 --- /dev/null +++ b/src/Python/Unittests/test_read_write_obj.py @@ -0,0 +1,229 @@ +import unittest +import openmesh + +class ReadWriteOBJ(unittest.TestCase): + + def setUp(self): + self.mesh = openmesh.TriMesh() + + def test_load_simple_obj(self): + ok = openmesh.read_mesh(self.mesh, "cube-minimal.obj") + + self.assertTrue(ok) + + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_edges(), 18) + self.assertEqual(self.mesh.n_faces(), 12) + + def test_load_simple_obj_check_halfedge_and_vertex_normals(self): + self.mesh.request_halfedge_normals() + self.mesh.request_vertex_normals() + + options = openmesh.Options() + options += openmesh.Options.VertexNormal + + file_name = "cube-minimal.obj" + + ok = openmesh.read_mesh(self.mesh, file_name, options) + + self.assertTrue(ok) + + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_edges(), 18) + self.assertEqual(self.mesh.n_faces(), 12) + self.assertEqual(self.mesh.n_halfedges(), 36) + + # ===================================================== + # Check vertex normals + # ===================================================== + + self.assertEqual(self.mesh.normal(self.mesh.vertex_handle(0))[0], 0.0) + self.assertEqual(self.mesh.normal(self.mesh.vertex_handle(0))[1], -1.0) + self.assertEqual(self.mesh.normal(self.mesh.vertex_handle(0))[2], 0.0) + + self.assertEqual(self.mesh.normal(self.mesh.vertex_handle(3))[0], 0.0) + self.assertEqual(self.mesh.normal(self.mesh.vertex_handle(3))[1], 0.0) + self.assertEqual(self.mesh.normal(self.mesh.vertex_handle(3))[2], 1.0) + + self.assertEqual(self.mesh.normal(self.mesh.vertex_handle(4))[0], 0.0) + self.assertEqual(self.mesh.normal(self.mesh.vertex_handle(4))[1], -1.0) + self.assertEqual(self.mesh.normal(self.mesh.vertex_handle(4))[2], 0.0) + + self.assertEqual(self.mesh.normal(self.mesh.vertex_handle(7))[0], 0.0) + self.assertEqual(self.mesh.normal(self.mesh.vertex_handle(7))[1], 0.0) + self.assertEqual(self.mesh.normal(self.mesh.vertex_handle(7))[2], 1.0) + + # ===================================================== + # Check halfedge normals + # ===================================================== + + self.assertEqual(self.mesh.normal(self.mesh.halfedge_handle( 0))[0], 0.0) + self.assertEqual(self.mesh.normal(self.mesh.halfedge_handle( 0))[1], 0.0) + self.assertEqual(self.mesh.normal(self.mesh.halfedge_handle( 0))[2], -1.0) + + self.assertEqual(self.mesh.normal(self.mesh.halfedge_handle(10))[0], -1.0) + self.assertEqual(self.mesh.normal(self.mesh.halfedge_handle(10))[1], 0.0) + self.assertEqual(self.mesh.normal(self.mesh.halfedge_handle(10))[2], 0.0) + + self.assertEqual(self.mesh.normal(self.mesh.halfedge_handle(19))[0], 0.0) + self.assertEqual(self.mesh.normal(self.mesh.halfedge_handle(19))[1], 1.0) + self.assertEqual(self.mesh.normal(self.mesh.halfedge_handle(19))[2], 0.0) + + self.assertEqual(self.mesh.normal(self.mesh.halfedge_handle(24))[0], 1.0) + self.assertEqual(self.mesh.normal(self.mesh.halfedge_handle(24))[1], 0.0) + self.assertEqual(self.mesh.normal(self.mesh.halfedge_handle(24))[2], 0.0) + + self.assertEqual(self.mesh.normal(self.mesh.halfedge_handle(30))[0], 0.0) + self.assertEqual(self.mesh.normal(self.mesh.halfedge_handle(30))[1], -1.0) + self.assertEqual(self.mesh.normal(self.mesh.halfedge_handle(30))[2], 0.0) + + self.assertEqual(self.mesh.normal(self.mesh.halfedge_handle(35))[0], 0.0) + self.assertEqual(self.mesh.normal(self.mesh.halfedge_handle(35))[1], 0.0) + self.assertEqual(self.mesh.normal(self.mesh.halfedge_handle(35))[2], 1.0) + + self.mesh.release_vertex_normals() + self.mesh.release_halfedge_normals() + + def test_load_simple_obj_force_vertex_colors_although_not_available(self): + self.mesh.request_vertex_colors() + + file_name = "cube-minimal.obj" + + options = openmesh.Options() + options += openmesh.Options.VertexColor + + ok = openmesh.read_mesh(self.mesh, file_name, options) + + self.assertTrue(ok) + + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_edges(), 18) + self.assertEqual(self.mesh.n_faces(), 12) + self.assertEqual(self.mesh.n_halfedges(), 36) + + def test_load_simple_obj_check_texcoords(self): + self.mesh.request_halfedge_texcoords2D() + + options = openmesh.Options() + options += openmesh.Options.FaceTexCoord + + file_name = "cube-minimal-texCoords.obj" + + ok = openmesh.read_mesh(self.mesh, file_name, options) + + self.assertTrue(ok) + + self.assertEqual(self.mesh.texcoord2D(self.mesh.halfedge_handle( 0))[0], 1.0) + self.assertEqual(self.mesh.texcoord2D(self.mesh.halfedge_handle( 0))[1], 1.0) + + self.assertEqual(self.mesh.texcoord2D(self.mesh.halfedge_handle(10))[0], 3.0) + self.assertEqual(self.mesh.texcoord2D(self.mesh.halfedge_handle(10))[1], 3.0) + + self.assertEqual(self.mesh.texcoord2D(self.mesh.halfedge_handle(19))[0], 6.0) + self.assertEqual(self.mesh.texcoord2D(self.mesh.halfedge_handle(19))[1], 6.0) + + self.assertEqual(self.mesh.texcoord2D(self.mesh.halfedge_handle(24))[0], 7.0) + self.assertEqual(self.mesh.texcoord2D(self.mesh.halfedge_handle(24))[1], 7.0) + + self.assertEqual(self.mesh.texcoord2D(self.mesh.halfedge_handle(30))[0], 9.0) + self.assertEqual(self.mesh.texcoord2D(self.mesh.halfedge_handle(30))[1], 9.0) + + self.assertEqual(self.mesh.texcoord2D(self.mesh.halfedge_handle(35))[0], 12.0) + self.assertEqual(self.mesh.texcoord2D(self.mesh.halfedge_handle(35))[1], 12.0) + + self.mesh.release_halfedge_texcoords2D() + + def test_load_obj_with_material(self): + self.mesh.request_face_colors() + + options = openmesh.Options() + options += openmesh.Options.FaceColor + + file_name = "square_material.obj" + + ok = openmesh.read_mesh(self.mesh, file_name, options) + + self.assertTrue(ok) + + fh = self.mesh.face_handle(self.mesh.halfedge_handle(0)) + + self.assertTrue(fh.is_valid()) + + self.assertAlmostEqual(self.mesh.color(fh)[0], 0.5, 2) + self.assertAlmostEqual(self.mesh.color(fh)[1], 0.5, 2) + self.assertAlmostEqual(self.mesh.color(fh)[2], 0.5, 2) + + self.mesh.release_face_colors() + + def test_load_simple_obj_with_vertex_colors_after_vertices(self): + self.mesh.request_vertex_colors() + + options = openmesh.Options() + options += openmesh.Options.VertexColor + + file_name = "cube-minimal-vertex-colors-after-vertex-definition.obj" + + ok = openmesh.read_mesh(self.mesh, file_name, options) + + self.assertTrue(ok) + + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_edges(), 18) + self.assertEqual(self.mesh.n_faces(), 12) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(0))[0], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(0))[1], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(0))[2], 0.0) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(3))[0], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(3))[1], 1.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(3))[2], 1.0) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(4))[0], 1.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(4))[1], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(4))[2], 0.0) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(7))[0], 1.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(7))[1], 1.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(7))[2], 1.0) + + self.mesh.release_vertex_colors() + + def test_load_simple_obj_with_vertex_colors_as_vc_lines(self): + self.mesh.request_vertex_colors() + + options = openmesh.Options() + options += openmesh.Options.VertexColor + + file_name = "cube-minimal-vertex-colors-as-vc-lines.obj" + + ok = openmesh.read_mesh(self.mesh, file_name, options) + + self.assertTrue(ok) + + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_edges(), 18) + self.assertEqual(self.mesh.n_faces(), 12) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(0))[0], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(0))[1], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(0))[2], 0.0) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(3))[0], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(3))[1], 1.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(3))[2], 1.0) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(4))[0], 1.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(4))[1], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(4))[2], 0.0) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(7))[0], 1.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(7))[1], 1.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(7))[2], 1.0) + + self.mesh.release_vertex_colors() + + +if __name__ == '__main__': + suite = unittest.TestLoader().loadTestsFromTestCase(ReadWriteOBJ) + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/src/Python/Unittests/test_read_write_off.py b/src/Python/Unittests/test_read_write_off.py new file mode 100644 index 00000000..a2d50b05 --- /dev/null +++ b/src/Python/Unittests/test_read_write_off.py @@ -0,0 +1,163 @@ +import unittest +import openmesh + +class ReadWriteOFF(unittest.TestCase): + + def setUp(self): + self.mesh = openmesh.TriMesh() + + def test_load_simple_off_file(self): + ok = openmesh.read_mesh(self.mesh, "cube1.off") + + self.assertTrue(ok) + + self.assertEqual(self.mesh.n_vertices(), 7526) + self.assertEqual(self.mesh.n_edges(), 22572) + self.assertEqual(self.mesh.n_faces(), 15048) + + def test_write_and_read_vertex_colors_to_and_from_off_file(self): + self.mesh.request_vertex_colors() + + self.mesh.add_vertex(openmesh.Vec3d(0, 0, 1)) + self.mesh.add_vertex(openmesh.Vec3d(0, 1, 0)) + self.mesh.add_vertex(openmesh.Vec3d(0, 1, 1)) + self.mesh.add_vertex(openmesh.Vec3d(1, 0, 1)) + + # Using the default openmesh Python color type + testColor = openmesh.Vec4f(1.0, 0.5, 0.25, 1.0) + + # Setting colors (different from black) + for v in self.mesh.vertices(): + self.mesh.set_color(v, testColor) + + # Check if the colors are correctly set + count = 0 + for v in self.mesh.vertices(): + color = self.mesh.color(v) + if color[0] != testColor[0] or color[1] != testColor[1] or color[2] != testColor[2]: + count += 1 + + self.assertEqual(count, 0) + + options = openmesh.Options() + options += openmesh.Options.VertexColor + options += openmesh.Options.ColorFloat + + openmesh.write_mesh(self.mesh, "temp.off", options) + openmesh.read_mesh(self.mesh, "temp.off", options) + + # Check if vertices still have the same color + count = 0 + for v in self.mesh.vertices(): + color = self.mesh.color(v) + if color[0] != testColor[0] or color[1] != testColor[1] or color[2] != testColor[2]: + count += 1 + + self.assertEqual(count, 0) + + self.mesh.release_vertex_colors() + + def test_write_and_read_float_vertex_colors_to_and_from_off_file(self): + self.mesh.request_vertex_colors() + + options = openmesh.Options(openmesh.Options.VertexColor) + + ok = openmesh.read_mesh(self.mesh, "meshlab.ply", options) + + self.assertTrue(ok) + + options.clear() + options += openmesh.Options.VertexColor + options += openmesh.Options.ColorFloat + + # Write the mesh + ok = openmesh.write_mesh(self.mesh, "cube_floating.off", options) + self.assertTrue(ok) + ok = openmesh.read_mesh(self.mesh, "cube_floating.off", options) + self.assertTrue(ok) + + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_edges(), 18) + self.assertEqual(self.mesh.n_faces(), 12) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(0))[0], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(0))[1], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(0))[2], 1.0) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(3))[0], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(3))[1], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(3))[2], 1.0) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(4))[0], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(4))[1], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(4))[2], 1.0) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(7))[0], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(7))[1], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(7))[2], 1.0) + + self.assertFalse(options.vertex_has_normal()) + self.assertFalse(options.vertex_has_texcoord()) + self.assertTrue(options.vertex_has_color()) + self.assertTrue(options.color_is_float()) + + self.mesh.release_vertex_colors() + + def test_write_and_read_binary_float_vertex_colors_to_and_from_off_file(self): + self.mesh.request_vertex_colors() + + options = openmesh.Options(openmesh.Options.VertexColor) + + ok = openmesh.read_mesh(self.mesh, "meshlab.ply", options) + + self.assertTrue(ok) + + options.clear() + options += openmesh.Options.VertexColor + options += openmesh.Options.Binary + options += openmesh.Options.ColorFloat + + # Write the mesh + ok = openmesh.write_mesh(self.mesh, "cube_floating_binary.off", options) + self.assertTrue(ok) + self.mesh.clear() + options.clear() + options += openmesh.Options.VertexColor + options += openmesh.Options.Binary + options += openmesh.Options.ColorFloat + ok = openmesh.read_mesh(self.mesh, "cube_floating_binary.off", options) + self.assertTrue(ok) + + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_edges(), 18) + self.assertEqual(self.mesh.n_faces(), 12) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(0))[0], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(0))[1], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(0))[2], 1.0) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(3))[0], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(3))[1], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(3))[2], 1.0) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(4))[0], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(4))[1], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(4))[2], 1.0) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(7))[0], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(7))[1], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(7))[2], 1.0) + + self.assertFalse(options.vertex_has_normal()) + self.assertFalse(options.vertex_has_texcoord()) + self.assertFalse(options.face_has_color()) + self.assertTrue(options.vertex_has_color()) + self.assertTrue(options.color_is_float()) + self.assertTrue(options.is_binary()) + + self.mesh.release_vertex_colors() + + +if __name__ == '__main__': + suite = unittest.TestLoader().loadTestsFromTestCase(ReadWriteOFF) + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/src/Python/Unittests/test_read_write_om.py b/src/Python/Unittests/test_read_write_om.py new file mode 100644 index 00000000..dc8887ec --- /dev/null +++ b/src/Python/Unittests/test_read_write_om.py @@ -0,0 +1,201 @@ +import unittest +import openmesh +import os + +class ReadWriteOM(unittest.TestCase): + + def setUp(self): + self.mesh = openmesh.TriMesh() + + def test_load_simple_om_force_vertex_colors_although_not_available(self): + self.mesh.request_vertex_colors() + + file_name = "cube-minimal.om" + + options = openmesh.Options() + options += openmesh.Options.VertexColor + + ok = openmesh.read_mesh(self.mesh, file_name, options) + + self.assertTrue(ok) + + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_edges(), 18) + self.assertEqual(self.mesh.n_faces(), 12) + self.assertEqual(self.mesh.n_halfedges(), 36) + + self.assertFalse(options.vertex_has_normal()) + self.assertFalse(options.vertex_has_texcoord()) + self.assertFalse(options.vertex_has_color()) + + def test_load_simple_om_with_texcoords(self): + self.mesh.request_vertex_texcoords2D() + + options = openmesh.Options() + options += openmesh.Options.VertexTexCoord + + ok = openmesh.read_mesh(self.mesh, "cube-minimal-texCoords.om", options) + + self.assertTrue(ok) + + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_edges(), 18) + self.assertEqual(self.mesh.n_faces(), 12) + + self.assertEqual(self.mesh.texcoord2D(self.mesh.vertex_handle(0))[0], 10.0) + self.assertEqual(self.mesh.texcoord2D(self.mesh.vertex_handle(0))[1], 10.0) + + self.assertEqual(self.mesh.texcoord2D(self.mesh.vertex_handle(2))[0], 6.0) + self.assertEqual(self.mesh.texcoord2D(self.mesh.vertex_handle(2))[1], 6.0) + + self.assertEqual(self.mesh.texcoord2D(self.mesh.vertex_handle(4))[0], 9.0) + self.assertEqual(self.mesh.texcoord2D(self.mesh.vertex_handle(4))[1], 9.0) + + self.assertEqual(self.mesh.texcoord2D(self.mesh.vertex_handle(7))[0], 12.0) + self.assertEqual(self.mesh.texcoord2D(self.mesh.vertex_handle(7))[1], 12.0) + + self.assertFalse(options.vertex_has_normal()) + self.assertTrue(options.vertex_has_texcoord()) + self.assertFalse(options.vertex_has_color()) + + self.mesh.release_vertex_texcoords2D() + + def test_load_simple_om_with_vertex_colors(self): + self.mesh.request_vertex_colors() + + options = openmesh.Options() + options += openmesh.Options.VertexColor + + ok = openmesh.read_mesh(self.mesh, "cube-minimal-vertexColors.om", options) + + self.assertTrue(ok) + + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_edges(), 18) + self.assertEqual(self.mesh.n_faces(), 12) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(0))[0], 1.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(0))[1], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(0))[2], 0.0) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(3))[0], 1.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(3))[1], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(3))[2], 0.0) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(4))[0], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(4))[1], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(4))[2], 1.0) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(7))[0], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(7))[1], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(7))[2], 1.0) + + self.assertFalse(options.vertex_has_normal()) + self.assertFalse(options.vertex_has_texcoord()) + self.assertTrue(options.vertex_has_color()) + + self.mesh.release_vertex_colors() + + def test_write_triangle(self): + filename = "triangle-minimal.om"; + + # Generate data + v1 = self.mesh.add_vertex(openmesh.Vec3d(1.0, 0.0, 0.0)) + v2 = self.mesh.add_vertex(openmesh.Vec3d(0.0, 1.0, 0.0)) + v3 = self.mesh.add_vertex(openmesh.Vec3d(0.0, 0.0, 1.0)) + self.mesh.add_face(v1, v2, v3) + + # Save + ok = openmesh.write_mesh(self.mesh, filename) + self.assertTrue(ok) + + # Reset + self.mesh.clear() + + # Load + ok = openmesh.read_mesh(self.mesh, filename) + self.assertTrue(ok) + + # Compare + self.assertEqual(self.mesh.n_vertices(), 3) + self.assertEqual(self.mesh.n_edges(), 3) + self.assertEqual(self.mesh.n_faces(), 1) + + self.assertEqual(self.mesh.point(v1), openmesh.Vec3d(1.0, 0.0, 0.0)) + self.assertEqual(self.mesh.point(v2), openmesh.Vec3d(0.0, 1.0, 0.0)) + self.assertEqual(self.mesh.point(v3), openmesh.Vec3d(0.0, 0.0, 1.0)) + + # Cleanup + os.remove(filename) + + def test_write_triangle_vertex_integer_color(self): + self.mesh.request_vertex_colors() + + options = openmesh.Options() + options += openmesh.Options.VertexColor + options += openmesh.Options.ColorFloat + + filename = "triangle-minimal-ColorsPerVertex.om" + + # Generate data + v1 = self.mesh.add_vertex(openmesh.Vec3d(1.0, 0.0, 0.0)) + v2 = self.mesh.add_vertex(openmesh.Vec3d(0.0, 1.0, 0.0)) + v3 = self.mesh.add_vertex(openmesh.Vec3d(0.0, 0.0, 1.0)) + self.mesh.add_face(v1, v2, v3) + + c1 = openmesh.Vec4f(0.00, 0.00, 0.50, 1.00) + c2 = openmesh.Vec4f(0.25, 0.00, 0.00, 1.00) + c3 = openmesh.Vec4f(0.00, 0.75, 0.00, 1.00) + + self.mesh.set_color(v1, c1) + self.mesh.set_color(v2, c2) + self.mesh.set_color(v3, c3) + + # Save + ok = openmesh.write_mesh(self.mesh, filename, options) + self.assertTrue(ok) + + self.mesh.release_vertex_colors() + + # Load + cmpMesh = openmesh.TriMesh() + cmpMesh.request_vertex_colors() + ok = openmesh.read_mesh(cmpMesh, filename, options) + self.assertTrue(ok) + + self.assertTrue(cmpMesh.has_vertex_colors()) + + # Compare + self.assertEqual(self.mesh.n_vertices(), 3) + self.assertEqual(self.mesh.n_edges(), 3) + self.assertEqual(self.mesh.n_faces(), 1) + + self.assertEqual(cmpMesh.point(v1), openmesh.Vec3d(1.0, 0.0, 0.0)) + self.assertEqual(cmpMesh.point(v2), openmesh.Vec3d(0.0, 1.0, 0.0)) + self.assertEqual(cmpMesh.point(v3), openmesh.Vec3d(0.0, 0.0, 1.0)) + + self.assertAlmostEqual(cmpMesh.color(v1)[0], c1[0], 2) + self.assertAlmostEqual(cmpMesh.color(v1)[1], c1[1], 2) + self.assertAlmostEqual(cmpMesh.color(v1)[2], c1[2], 2) + self.assertAlmostEqual(cmpMesh.color(v1)[3], c1[3], 2) + + self.assertAlmostEqual(cmpMesh.color(v2)[0], c2[0], 2) + self.assertAlmostEqual(cmpMesh.color(v2)[1], c2[1], 2) + self.assertAlmostEqual(cmpMesh.color(v2)[2], c2[2], 2) + self.assertAlmostEqual(cmpMesh.color(v2)[3], c2[3], 2) + + self.assertAlmostEqual(cmpMesh.color(v3)[0], c3[0], 2) + self.assertAlmostEqual(cmpMesh.color(v3)[1], c3[1], 2) + self.assertAlmostEqual(cmpMesh.color(v3)[2], c3[2], 2) + self.assertAlmostEqual(cmpMesh.color(v3)[3], c3[3], 2) + + # Clean up + cmpMesh.release_vertex_colors() + os.remove(filename) + + # TODO property tests + + +if __name__ == '__main__': + suite = unittest.TestLoader().loadTestsFromTestCase(ReadWriteOM) + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/src/Python/Unittests/test_read_write_ply.py b/src/Python/Unittests/test_read_write_ply.py new file mode 100644 index 00000000..7de00b1e --- /dev/null +++ b/src/Python/Unittests/test_read_write_ply.py @@ -0,0 +1,342 @@ +import unittest +import openmesh + +class ReadWritePLY(unittest.TestCase): + + def setUp(self): + self.mesh = openmesh.TriMesh() + + def test_load_simple_point_ply_file_with_bad_encoding(self): + ok = openmesh.read_mesh(self.mesh, "pointCloudBadEncoding.ply") + + self.assertTrue(ok) + + self.assertEqual(self.mesh.n_vertices(), 10) + self.assertEqual(self.mesh.n_edges(), 0) + self.assertEqual(self.mesh.n_faces(), 0) + + def test_load_simple_point_ply_file_with_good_encoding(self): + ok = openmesh.read_mesh(self.mesh, "pointCloudGoodEncoding.ply") + + self.assertTrue(ok) + + self.assertEqual(self.mesh.n_vertices(), 10) + self.assertEqual(self.mesh.n_edges(), 0) + self.assertEqual(self.mesh.n_faces(), 0) + + def test_load_simple_ply(self): + ok = openmesh.read_mesh(self.mesh, "cube-minimal.ply") + + self.assertTrue(ok) + + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_edges(), 18) + self.assertEqual(self.mesh.n_faces(), 12) + + def test_load_simple_ply_force_vertex_colors_although_not_available(self): + self.mesh.request_vertex_colors() + + file_name = "cube-minimal.ply" + + options = openmesh.Options() + options += openmesh.Options.VertexColor + + ok = openmesh.read_mesh(self.mesh, file_name, options) + + self.assertTrue(ok) + + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_edges(), 18) + self.assertEqual(self.mesh.n_faces(), 12) + self.assertEqual(self.mesh.n_halfedges(), 36) + + self.assertFalse(options.vertex_has_normal()) + self.assertFalse(options.vertex_has_texcoord()) + self.assertFalse(options.vertex_has_color()) + + def test_load_simple_ply_with_vertex_colors(self): + self.mesh.request_vertex_colors() + + file_name = "cube-minimal.ply" + + options = openmesh.Options() + options += openmesh.Options.VertexColor + + ok = openmesh.read_mesh(self.mesh, "cube-minimal-vertexColors.ply", options) + + self.assertTrue(ok) + + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_edges(), 18) + self.assertEqual(self.mesh.n_faces(), 12) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(0))[0], 1.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(0))[1], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(0))[2], 0.0) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(3))[0], 1.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(3))[1], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(3))[2], 0.0) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(4))[0], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(4))[1], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(4))[2], 1.0) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(7))[0], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(7))[1], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(7))[2], 1.0) + + self.assertFalse(options.vertex_has_normal()) + self.assertFalse(options.vertex_has_texcoord()) + self.assertTrue(options.vertex_has_color()) + + self.mesh.release_vertex_colors() + + def test_load_ply_from_mesh_lab_with_vertex_colors(self): + self.mesh.request_vertex_colors() + + options = openmesh.Options() + options += openmesh.Options.VertexColor + + ok = openmesh.read_mesh(self.mesh, "meshlab.ply", options) + + self.assertTrue(ok) + + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_edges(), 18) + self.assertEqual(self.mesh.n_faces(), 12) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(0))[0], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(0))[1], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(0))[2], 1.0) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(3))[0], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(3))[1], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(3))[2], 1.0) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(4))[0], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(4))[1], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(4))[2], 1.0) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(7))[0], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(7))[1], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(7))[2], 1.0) + + self.assertFalse(options.vertex_has_normal()) + self.assertFalse(options.vertex_has_texcoord()) + self.assertTrue(options.vertex_has_color()) + + self.mesh.release_vertex_colors() + + def test_write_and_read_binary_ply_with_vertex_colors(self): + self.mesh.request_vertex_colors() + + options = openmesh.Options() + options += openmesh.Options.VertexColor + + ok = openmesh.read_mesh(self.mesh, "meshlab.ply", options) + + self.assertTrue(ok) + + options += openmesh.Options.Binary + + ok = openmesh.write_mesh(self.mesh, "meshlab_binary.ply", options) + self.assertTrue(ok) + + self.mesh.clear + + ok = openmesh.read_mesh(self.mesh, "meshlab_binary.ply", options) + self.assertTrue(ok) + + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_edges(), 18) + self.assertEqual(self.mesh.n_faces(), 12) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(0))[0], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(0))[1], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(0))[2], 1.0) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(3))[0], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(3))[1], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(3))[2], 1.0) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(4))[0], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(4))[1], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(4))[2], 1.0) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(7))[0], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(7))[1], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(7))[2], 1.0) + + self.assertFalse(options.vertex_has_normal()) + self.assertFalse(options.vertex_has_texcoord()) + self.assertTrue(options.vertex_has_color()) + + self.mesh.release_vertex_colors() + + def test_write_and_read_ply_with_float_vertex_colors(self): + self.mesh.request_vertex_colors() + + options = openmesh.Options() + options += openmesh.Options.VertexColor + + ok = openmesh.read_mesh(self.mesh, "meshlab.ply", options) + + self.assertTrue(ok) + + options += openmesh.Options.ColorFloat + + ok = openmesh.write_mesh(self.mesh, "meshlab_float.ply", options) + self.assertTrue(ok) + + self.mesh.clear + ok = openmesh.read_mesh(self.mesh, "meshlab_float.ply", options) + self.assertTrue(ok) + + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_edges(), 18) + self.assertEqual(self.mesh.n_faces(), 12) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(0))[0], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(0))[1], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(0))[2], 1.0) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(3))[0], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(3))[1], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(3))[2], 1.0) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(4))[0], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(4))[1], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(4))[2], 1.0) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(7))[0], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(7))[1], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(7))[2], 1.0) + + self.assertFalse(options.vertex_has_normal()) + self.assertFalse(options.vertex_has_texcoord()) + self.assertTrue(options.vertex_has_color()) + self.assertTrue(options.color_is_float()) + + self.mesh.release_vertex_colors() + + def test_write_and_read_binary_ply_with_float_vertex_colors(self): + self.mesh.request_vertex_colors() + + options = openmesh.Options() + options += openmesh.Options.VertexColor + + ok = openmesh.read_mesh(self.mesh, "meshlab.ply", options) + + self.assertTrue(ok) + + options += openmesh.Options.ColorFloat + options += openmesh.Options.Binary + + ok = openmesh.write_mesh(self.mesh, "meshlab_binary_float.ply", options) + self.assertTrue(ok) + + self.mesh.clear + ok = openmesh.read_mesh(self.mesh, "meshlab_binary_float.ply", options) + self.assertTrue(ok) + + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_edges(), 18) + self.assertEqual(self.mesh.n_faces(), 12) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(0))[0], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(0))[1], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(0))[2], 1.0) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(3))[0], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(3))[1], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(3))[2], 1.0) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(4))[0], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(4))[1], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(4))[2], 1.0) + + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(7))[0], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(7))[1], 0.0) + self.assertEqual(self.mesh.color(self.mesh.vertex_handle(7))[2], 1.0) + + self.assertFalse(options.vertex_has_normal()) + self.assertFalse(options.vertex_has_texcoord()) + self.assertTrue(options.vertex_has_color()) + self.assertTrue(options.color_is_float()) + self.assertTrue(options.is_binary()) + + self.mesh.release_vertex_colors() + + def test_load_simple_ply_with_texcoords(self): + self.mesh.request_vertex_texcoords2D() + + options = openmesh.Options() + options += openmesh.Options.VertexTexCoord + + ok = openmesh.read_mesh(self.mesh, "cube-minimal-texCoords.ply", options) + + self.assertTrue(ok) + + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_edges(), 18) + self.assertEqual(self.mesh.n_faces(), 12) + + self.assertEqual(self.mesh.texcoord2D(self.mesh.vertex_handle(0))[0], 10.0) + self.assertEqual(self.mesh.texcoord2D(self.mesh.vertex_handle(0))[1], 10.0) + + self.assertEqual(self.mesh.texcoord2D(self.mesh.vertex_handle(2))[0], 6.0) + self.assertEqual(self.mesh.texcoord2D(self.mesh.vertex_handle(2))[1], 6.0) + + self.assertEqual(self.mesh.texcoord2D(self.mesh.vertex_handle(4))[0], 9.0) + self.assertEqual(self.mesh.texcoord2D(self.mesh.vertex_handle(4))[1], 9.0) + + self.assertEqual(self.mesh.texcoord2D(self.mesh.vertex_handle(7))[0], 12.0) + self.assertEqual(self.mesh.texcoord2D(self.mesh.vertex_handle(7))[1], 12.0) + + self.assertFalse(options.vertex_has_normal()) + self.assertTrue(options.vertex_has_texcoord()) + self.assertFalse(options.vertex_has_color()) + + self.mesh.release_vertex_texcoords2D() + + def test_load_simple_ply_with_normals(self): + self.mesh.request_vertex_normals() + + options = openmesh.Options() + options += openmesh.Options.VertexNormal + + ok = openmesh.read_mesh(self.mesh, "cube-minimal-normals.ply", options) + + self.assertTrue(ok) + + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_edges(), 18) + self.assertEqual(self.mesh.n_faces(), 12) + + self.assertTrue(options.vertex_has_normal()) + self.assertFalse(options.vertex_has_texcoord()) + self.assertFalse(options.vertex_has_color()) + + self.assertEqual(self.mesh.normal(self.mesh.vertex_handle(0))[0], 0.0) + self.assertEqual(self.mesh.normal(self.mesh.vertex_handle(0))[1], 0.0) + self.assertEqual(self.mesh.normal(self.mesh.vertex_handle(0))[2], 1.0) + + self.assertEqual(self.mesh.normal(self.mesh.vertex_handle(3))[0], 1.0) + self.assertEqual(self.mesh.normal(self.mesh.vertex_handle(3))[1], 0.0) + self.assertEqual(self.mesh.normal(self.mesh.vertex_handle(3))[2], 0.0) + + self.assertEqual(self.mesh.normal(self.mesh.vertex_handle(4))[0], 1.0) + self.assertEqual(self.mesh.normal(self.mesh.vertex_handle(4))[1], 0.0) + self.assertEqual(self.mesh.normal(self.mesh.vertex_handle(4))[2], 1.0) + + self.assertEqual(self.mesh.normal(self.mesh.vertex_handle(7))[0], 1.0) + self.assertEqual(self.mesh.normal(self.mesh.vertex_handle(7))[1], 1.0) + self.assertEqual(self.mesh.normal(self.mesh.vertex_handle(7))[2], 2.0) + + self.mesh.release_vertex_normals() + + +if __name__ == '__main__': + suite = unittest.TestLoader().loadTestsFromTestCase(ReadWritePLY) + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/src/Python/Unittests/test_read_write_stl.py b/src/Python/Unittests/test_read_write_stl.py new file mode 100644 index 00000000..b2ede2e0 --- /dev/null +++ b/src/Python/Unittests/test_read_write_stl.py @@ -0,0 +1,75 @@ +import unittest +import openmesh + +class ReadWriteSTL(unittest.TestCase): + + def setUp(self): + self.mesh = openmesh.TriMesh() + + def test_load_simple_stl_file(self): + ok = openmesh.read_mesh(self.mesh, "cube1.stl") + + self.assertTrue(ok) + + self.assertEqual(self.mesh.n_vertices(), 7526) + self.assertEqual(self.mesh.n_edges(), 22572) + self.assertEqual(self.mesh.n_faces(), 15048) + + def test_load_simple_stl_file_with_normals(self): + self.mesh.request_face_normals() + + options = openmesh.Options() + options += openmesh.Options.FaceNormal + + ok = openmesh.read_mesh(self.mesh, "cube1.stl", options) + + self.assertTrue(ok) + + self.assertAlmostEqual(self.mesh.normal(self.mesh.face_handle(0))[0], -0.038545) + self.assertAlmostEqual(self.mesh.normal(self.mesh.face_handle(0))[1], -0.004330) + self.assertAlmostEqual(self.mesh.normal(self.mesh.face_handle(0))[2], 0.999247) + + self.assertEqual(self.mesh.n_vertices(), 7526) + self.assertEqual(self.mesh.n_edges(), 22572) + self.assertEqual(self.mesh.n_faces(), 15048) + + self.mesh.release_face_normals() + + def test_load_simple_stl_binary_file(self): + ok = openmesh.read_mesh(self.mesh, "cube1Binary.stl") + + self.assertTrue(ok) + + self.assertEqual(self.mesh.n_vertices(), 7526) + self.assertEqual(self.mesh.n_edges(), 22572) + self.assertEqual(self.mesh.n_faces(), 15048) + + def test_load_simple_stl_binary_file_with_normals(self): + self.mesh.request_face_normals() + + options = openmesh.Options() + options += openmesh.Options.FaceNormal + options += openmesh.Options.Binary + + ok = openmesh.read_mesh(self.mesh, "cube1Binary.stl", options) + + self.assertTrue(ok) + + self.assertTrue(options.is_binary()) + self.assertTrue(options.face_has_normal()) + self.assertFalse(options.vertex_has_normal()) + + self.assertAlmostEqual(self.mesh.normal(self.mesh.face_handle(0))[0], -0.038545, 5) + self.assertAlmostEqual(self.mesh.normal(self.mesh.face_handle(0))[1], -0.004330, 5) + self.assertAlmostEqual(self.mesh.normal(self.mesh.face_handle(0))[2], 0.999247, 5) + + self.assertEqual(self.mesh.n_vertices(), 7526) + self.assertEqual(self.mesh.n_edges(), 22572) + self.assertEqual(self.mesh.n_faces(), 15048) + + self.mesh.release_face_normals() + + +if __name__ == '__main__': + suite = unittest.TestLoader().loadTestsFromTestCase(ReadWriteSTL) + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/src/Python/Unittests/test_split_copy.py b/src/Python/Unittests/test_split_copy.py new file mode 100644 index 00000000..0252f593 --- /dev/null +++ b/src/Python/Unittests/test_split_copy.py @@ -0,0 +1,87 @@ +import unittest +import openmesh + +class SplitCopy(unittest.TestCase): + + def test_split_copy_triangle_mesh(self): + self.mesh = openmesh.TriMesh() + self.vhandle = [] + + # Add some vertices + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0.25, 0.25, 0))) + + # Add one face + face_vhandles = [] + + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[0]) + + fh = self.mesh.add_face(face_vhandles) + + # Test setup: + # 1 === 2 + # | / + # | / + # | / + # 0 + + # Set property + fprop_int = openmesh.FPropHandle() + self.mesh.add_property(fprop_int) + self.mesh.set_property(fprop_int, fh, 999) + + # Split face with new vertex + self.mesh.split_copy(fh, self.vhandle[3]) + + # Check setup + for f in self.mesh.faces(): + self.assertEqual(self.mesh.property(fprop_int, f), 999) + + def test_split_copy_polymesh(self): + self.mesh = openmesh.PolyMesh() + self.vhandle = [] + + # Add some vertices + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0.5, 0.5, 0))) + + # Add one face + face_vhandles = [] + + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[3]) + + fh = self.mesh.add_face(face_vhandles) + + # Test setup: + # 1 === 2 + # | | + # | | + # | | + # 0 === 3 + + # Set property + fprop_int = openmesh.FPropHandle() + self.mesh.add_property(fprop_int) + self.mesh.set_property(fprop_int, fh, 999) + + # Split face with new vertex + self.mesh.split_copy(fh, self.vhandle[4]) + + # Check setup + for f in self.mesh.faces(): + self.assertEqual(self.mesh.property(fprop_int, f), 999) + + +if __name__ == '__main__': + suite = unittest.TestLoader().loadTestsFromTestCase(SplitCopy) + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/src/Python/Unittests/test_trimesh_circulator_current_halfedge_handle_replacement.py b/src/Python/Unittests/test_trimesh_circulator_current_halfedge_handle_replacement.py new file mode 100644 index 00000000..8232c0ce --- /dev/null +++ b/src/Python/Unittests/test_trimesh_circulator_current_halfedge_handle_replacement.py @@ -0,0 +1,269 @@ +import unittest +import openmesh + +class TrimeshCirculatorCurrentHalfedgeHandleReplacement(unittest.TestCase): + + def setUp(self): + self.mesh = openmesh.TriMesh() + self.vhandle = [] + + def test_dereference(self): + # Add some vertices + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(2, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, -1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(2, -1, 0))) + + # Add four faces + face_vhandles = [] + + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[2]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[3]) + face_vhandles.append(self.vhandle[4]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[3]) + face_vhandles.append(self.vhandle[1]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[4]) + self.mesh.add_face(face_vhandles) + + # Test setup: + # 0 ==== 2 + # |\ 0 /| + # | \ / | + # |2 1 3| + # | / \ | + # |/ 1 \| + # 3 ==== 4 + # Starting vertex is 1->4 + + # output from fh_it.current_halfedge_handle() + current_halfedge_handles = [4, 0, 2, 10, 6, 8, 1, 12, 7, 14, 3, 11] + + i = 0 + for f in self.mesh.faces(): + for he in self.mesh.fh(f): + self.assertEqual(he.idx(), current_halfedge_handles[i]) + i += 1 + + def test_vv_iter(self): + # Add some vertices + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(2, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, -1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(2, -1, 0))) + + # Add four faces + face_vhandles = [] + + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[2]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[3]) + face_vhandles.append(self.vhandle[4]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[3]) + face_vhandles.append(self.vhandle[1]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[4]) + self.mesh.add_face(face_vhandles) + + # Test setup: + # 0 ==== 2 + # |\ 0 /| + # | \ / | + # |2 1 3| + # | / \ | + # |/ 1 \| + # 3 ==== 4 + # Starting vertex is 1->4 + + # output from vv_it.current_halfedge_handle() + current_halfedge_handles = [5, 0, 12, 11, 6, 1, 2, 15, 3, 4, 13, 7, 8, 9, 10, 14] + + eh0 = [] + eh1 = [] + + i = 0 + + for v in self.mesh.vertices(): + for vv in self.mesh.vv(v): + he = openmesh.HalfedgeHandle(current_halfedge_handles[i]) + eh0.append(self.mesh.edge_handle(he)) + i += 1 + for v in self.mesh.vertices(): + for he in self.mesh.voh(v): + eh1.append(self.mesh.edge_handle(he)) + + self.assertEqual(len(eh0), len(eh1)) + for i in range(len(eh0)): + self.assertEqual(eh0[i], eh1[i]) + + def test_fe_iter(self): + # Add some vertices + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(2, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, -1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(2, -1, 0))) + + # Add four faces + face_vhandles = [] + + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[2]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[3]) + face_vhandles.append(self.vhandle[4]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[3]) + face_vhandles.append(self.vhandle[1]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[4]) + self.mesh.add_face(face_vhandles) + + # Test setup: + # 0 ==== 2 + # |\ 0 /| + # | \ / | + # |2 1 3| + # | / \ | + # |/ 1 \| + # 3 ==== 4 + # Starting vertex is 1->4 + + # output from fe_it.current_halfedge_handle() + current_halfedge_handles = [4, 0, 2, 10, 6, 8, 1, 12, 7, 14, 3, 11] + + heh0 = [] + heh1 = [] + + i = 0 + + for f in self.mesh.faces(): + for e in self.mesh.fe(f): + heh0.append(openmesh.HalfedgeHandle(current_halfedge_handles[i])) + i += 1 + for f in self.mesh.faces(): + for he in self.mesh.fh(f): + heh1.append(he) + + self.assertEqual(len(heh0), len(heh1)) + for i in range(len(heh0)): + self.assertEqual(heh0[i], heh1[i]) + + def test_vf_iter_boundary(self): + # Add some vertices + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(2, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(3, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(4, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(2, -1, 0))) + + # Add three faces + face_vhandles = [] + + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[2]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[3]) + face_vhandles.append(self.vhandle[4]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[5]) + face_vhandles.append(self.vhandle[3]) + self.mesh.add_face(face_vhandles) + + # Test setup: + # + # 0 ------ 2 ------ 4 + # \ / \ / + # \ 0 / \ 1 / + # \ / \ / + # 1 ------- 3 + # \ / + # \ 2 / + # \ / + # \ / + # 5 + + # output from fe_it.current_halfedge_handle() + current_halfedge_handles = [0, 2, 12, 4, 6, 8, 16, 10, 14] + + fh0 = [] + fh1 = [] + + i = 0 + + for v in self.mesh.vertices(): + for f in self.mesh.vf(v): + he = openmesh.HalfedgeHandle(current_halfedge_handles[i]) + fh0.append(self.mesh.face_handle(he)) + i += 1 + for v in self.mesh.vertices(): + for f in self.mesh.vf(v): + fh1.append(f) + + self.assertEqual(len(fh0), len(fh1)) + for i in range(len(fh0)): + self.assertEqual(fh0[i], fh1[i]) + + +if __name__ == '__main__': + suite = unittest.TestLoader().loadTestsFromTestCase(TrimeshCirculatorCurrentHalfedgeHandleReplacement) + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/src/Python/Unittests/test_trimesh_circulator_face_edge.py b/src/Python/Unittests/test_trimesh_circulator_face_edge.py new file mode 100644 index 00000000..91df16a7 --- /dev/null +++ b/src/Python/Unittests/test_trimesh_circulator_face_edge.py @@ -0,0 +1,50 @@ +import unittest +import openmesh + +class TriMeshCirculatorFaceEdge(unittest.TestCase): + + def setUp(self): + self.mesh = openmesh.TriMesh() + + # Add some vertices + self.vhandle = [] + + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(2, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(3, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(4, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(2,-1, 0))) + + # Add four faces + self.mesh.add_face(self.vhandle[0], self.vhandle[1], self.vhandle[2]) + self.mesh.add_face(self.vhandle[2], self.vhandle[1], self.vhandle[3]) + self.mesh.add_face(self.vhandle[2], self.vhandle[3], self.vhandle[4]) + self.mesh.add_face(self.vhandle[1], self.vhandle[5], self.vhandle[3]) + + ''' + Test setup: + 0 ------ 2 ------ 4 + \ / \ / + \ 0 / \ 2 / + \ / 1 \ / + 1 ------- 3 + \ / + \ 3 / + \ / + \ / + 5 + ''' + + def test_face_edge_iter_without_holes_increment(self): + # Iterate around face 1 at the middle + fe_it = openmesh.FaceEdgeIter(self.mesh, self.mesh.face_handle(1)) + self.assertEqual(fe_it.__next__().idx(), 4) + self.assertEqual(fe_it.__next__().idx(), 1) + self.assertEqual(fe_it.__next__().idx(), 3) + self.assertRaises(StopIteration, fe_it.__next__) + + +if __name__ == '__main__': + suite = unittest.TestLoader().loadTestsFromTestCase(TriMeshCirculatorFaceEdge) + unittest.TextTestRunner(verbosity=2).run(suite) \ No newline at end of file diff --git a/src/Python/Unittests/test_trimesh_circulator_face_face.py b/src/Python/Unittests/test_trimesh_circulator_face_face.py new file mode 100644 index 00000000..293c39c2 --- /dev/null +++ b/src/Python/Unittests/test_trimesh_circulator_face_face.py @@ -0,0 +1,156 @@ +import unittest +import openmesh + +class TrimeshCirculatorFaceFace(unittest.TestCase): + + def setUp(self): + self.mesh = openmesh.TriMesh() + self.vhandle = [] + + def test_face_face_iter_with_holes(self): + # Add some vertices + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(2, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(3, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(4, 1, 0))) + + # Add three faces + face_vhandles = [] + + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[2]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[3]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[3]) + face_vhandles.append(self.vhandle[4]) + self.mesh.add_face(face_vhandles) + + # Test setup: + # + # 0 ------ 2 ------ 4 + # \ / \ / + # \ 0 / \ 2 / + # \ / 1 \ / + # 1 ------- 3 + + ff_it = self.mesh.ff(self.mesh.face_handle(1)) + + self.assertEqual(ff_it.__next__().idx(), 2) + self.assertEqual(ff_it.__next__().idx(), 0) + self.assertRaises(StopIteration, ff_it.__next__) + + def test_face_face_iter_without_holes(self): + # Add some vertices + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(2, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(3, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(4, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(2, -1, 0))) + + # Add four faces + face_vhandles = [] + + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[2]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[3]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[3]) + face_vhandles.append(self.vhandle[4]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[5]) + face_vhandles.append(self.vhandle[3]) + self.mesh.add_face(face_vhandles) + + # Test setup: + # + # 0 ------ 2 ------ 4 + # \ / \ / + # \ 0 / \ 2 / + # \ / 1 \ / + # 1 ------- 3 + # \ / + # \ 3 / + # \ / + # \ / + # 5 + + ff_it = self.mesh.ff(self.mesh.face_handle(1)) + + self.assertEqual(ff_it.__next__().idx(), 2) + self.assertEqual(ff_it.__next__().idx(), 0) + self.assertEqual(ff_it.__next__().idx(), 3) + self.assertRaises(StopIteration, ff_it.__next__) + + def test_face_face_iter_without_holes(self): + # Add some vertices + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 0, 0))) + + # Add two faces + face_vhandles = [] + + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[1]) + fh1 = self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[3]) + face_vhandles.append(self.vhandle[2]) + fh2 = self.mesh.add_face(face_vhandles) + + # Test setup: + # + # 1 -------- 2 + # | f0 / | + # | / f1 | + # 0 -------- 3 + + # Check setup + self.assertEqual(self.mesh.n_vertices(), 4) + self.assertEqual(self.mesh.n_faces(), 2) + + face_iter = self.mesh.ff(fh1) + + # Get the face via the handle + faceHandle1 = face_iter.__next__() + face1 = self.mesh.face(faceHandle1) + + self.assertEqual(faceHandle1.idx(), 1) + + +if __name__ == '__main__': + suite = unittest.TestLoader().loadTestsFromTestCase(TrimeshCirculatorFaceFace) + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/src/Python/Unittests/test_trimesh_circulator_face_halfedge.py b/src/Python/Unittests/test_trimesh_circulator_face_halfedge.py new file mode 100644 index 00000000..2da994ed --- /dev/null +++ b/src/Python/Unittests/test_trimesh_circulator_face_halfedge.py @@ -0,0 +1,50 @@ +import unittest +import openmesh + +class TriMeshCirculatorFaceHalfEdge(unittest.TestCase): + + def setUp(self): + self.mesh = openmesh.TriMesh() + + # Add some vertices + self.vhandle = [] + + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(2, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(3, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(4, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(2,-1, 0))) + + # Add four faces + self.mesh.add_face(self.vhandle[0], self.vhandle[1], self.vhandle[2]) + self.mesh.add_face(self.vhandle[2], self.vhandle[1], self.vhandle[3]) + self.mesh.add_face(self.vhandle[2], self.vhandle[3], self.vhandle[4]) + self.mesh.add_face(self.vhandle[1], self.vhandle[5], self.vhandle[3]) + + ''' + Test setup: + 0 ------ 2 ------ 4 + \ / \ / + \ 0 / \ 2 / + \ / 1 \ / + 1 ------- 3 + \ / + \ 3 / + \ / + \ / + 5 + ''' + + def test_face_halfedge_iter_without_holes_increment(self): + # Iterate around face 1 at the middle + fh_it = openmesh.FaceHalfedgeIter(self.mesh, self.mesh.face_handle(1)) + self.assertEqual(fh_it.__next__().idx(), 8) + self.assertEqual(fh_it.__next__().idx(), 3) + self.assertEqual(fh_it.__next__().idx(), 6) + self.assertRaises(StopIteration, fh_it.__next__) + + +if __name__ == '__main__': + suite = unittest.TestLoader().loadTestsFromTestCase(TriMeshCirculatorFaceHalfEdge) + unittest.TextTestRunner(verbosity=2).run(suite) \ No newline at end of file diff --git a/src/Python/Unittests/test_trimesh_circulator_face_vertex.py b/src/Python/Unittests/test_trimesh_circulator_face_vertex.py new file mode 100644 index 00000000..68161bcf --- /dev/null +++ b/src/Python/Unittests/test_trimesh_circulator_face_vertex.py @@ -0,0 +1,48 @@ +import unittest +import openmesh + +class TriMeshCirculatorFaceVertex(unittest.TestCase): + + def setUp(self): + self.mesh = openmesh.TriMesh() + + # Add some vertices + self.vhandle = [] + + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(2, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0,-1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(2,-1, 0))) + + # Add four faces + self.fh0 = self.mesh.add_face(self.vhandle[0], self.vhandle[1], self.vhandle[2]) + self.mesh.add_face(self.vhandle[1], self.vhandle[3], self.vhandle[4]) + self.mesh.add_face(self.vhandle[0], self.vhandle[3], self.vhandle[1]) + self.mesh.add_face(self.vhandle[2], self.vhandle[1], self.vhandle[4]) + + ''' + Test setup: + 0 ==== 2 + |\ 0 /| + | \ / | + |2 1 3| + | / \ | + |/ 1 \| + 3 ==== 4 + ''' + + def test_face_vertex_iter_without_increment(self): + self.assertEqual(self.fh0.idx(), 0) + + # Iterate around face 0 at the top + fv_it = openmesh.FaceVertexIter(self.mesh, self.fh0) + self.assertEqual(fv_it.__next__().idx(), 0) + self.assertEqual(fv_it.__next__().idx(), 1) + self.assertEqual(fv_it.__next__().idx(), 2) + self.assertRaises(StopIteration, fv_it.__next__) + + +if __name__ == '__main__': + suite = unittest.TestLoader().loadTestsFromTestCase(TriMeshCirculatorFaceVertex) + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/src/Python/Unittests/test_trimesh_circulator_halfedge_loop.py b/src/Python/Unittests/test_trimesh_circulator_halfedge_loop.py new file mode 100644 index 00000000..794f7646 --- /dev/null +++ b/src/Python/Unittests/test_trimesh_circulator_halfedge_loop.py @@ -0,0 +1,131 @@ +import unittest +import openmesh + +class TrimeshCirculatorHalfedgeLoop(unittest.TestCase): + + def setUp(self): + self.mesh = openmesh.TriMesh() + self.vhandle = [] + + def test_halfedge_loop_with_face(self): + # Add some vertices + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(2, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(3, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(4, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(2, -1, 0))) + + # Add four faces + face_vhandles = [] + + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[2]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[3]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[3]) + face_vhandles.append(self.vhandle[4]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[5]) + face_vhandles.append(self.vhandle[3]) + self.mesh.add_face(face_vhandles) + + # Test setup: + # + # edge x => halfedge x/x+1 + # i.e. edge 0 => halfedge 0/1 + # + # 0 --4--- 2 ------ 4 + # \ / \ / + # 0 0 2 6 2 / + # \ / 1 \ / + # 1 ---8--- 3 + # \ / + # \ 3 / + # \ / + # \ / + # 5 + + # Circle around face 1 + hl_it = self.mesh.hl(self.mesh.halfedge_handle(3)) + + self.assertEqual(hl_it.__next__().idx(), 3) + self.assertEqual(hl_it.__next__().idx(), 6) + self.assertEqual(hl_it.__next__().idx(), 8) + self.assertRaises(StopIteration, hl_it.__next__) + + def test_halfedge_loop_without_face(self): + # Add some vertices + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(2, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(3, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(4, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(2, -1, 0))) + + # Add three faces + face_vhandles = [] + + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[2]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[3]) + face_vhandles.append(self.vhandle[4]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[5]) + face_vhandles.append(self.vhandle[3]) + self.mesh.add_face(face_vhandles) + + # Test setup: + # + # H => hole (no face) + # fx => face #x + # edge 0 => halfedge 0/1 + # + # 0 --4--- 2 -10--- 4 + # \ / \ / + # 0 f0 2 6 f2 8 + # \ / H \ / + # 1 ---16---3 + # \ / + # 12 f3 14 + # \ / + # \ / + # 5 + + # Circle around the hole + hl_it = self.mesh.hl(self.mesh.halfedge_handle(3)) + + self.assertEqual(hl_it.__next__().idx(), 3) + self.assertEqual(hl_it.__next__().idx(), 17) + self.assertEqual(hl_it.__next__().idx(), 7) + self.assertRaises(StopIteration, hl_it.__next__) + + +if __name__ == '__main__': + suite = unittest.TestLoader().loadTestsFromTestCase(TrimeshCirculatorHalfedgeLoop) + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/src/Python/Unittests/test_trimesh_circulator_vertex_edge.py b/src/Python/Unittests/test_trimesh_circulator_vertex_edge.py new file mode 100644 index 00000000..5792e472 --- /dev/null +++ b/src/Python/Unittests/test_trimesh_circulator_vertex_edge.py @@ -0,0 +1,55 @@ +import unittest +import openmesh + +class TriMeshCirculatorVertexEdge(unittest.TestCase): + + def setUp(self): + self.mesh = openmesh.TriMesh() + + # Add some vertices + self.vhandle = [] + + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(2, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0,-1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(2,-1, 0))) + + # Add four faces + self.mesh.add_face(self.vhandle[0], self.vhandle[1], self.vhandle[2]) + self.mesh.add_face(self.vhandle[1], self.vhandle[3], self.vhandle[4]) + self.mesh.add_face(self.vhandle[0], self.vhandle[3], self.vhandle[1]) + self.mesh.add_face(self.vhandle[2], self.vhandle[1], self.vhandle[4]) + + ''' + Test setup: + 0 ==== 2 + |\ 0 /| + | \ / | + |2 1 3| + | / \ | + |/ 1 \| + 3 ==== 4 + ''' + + def test_vertex_edge_iter_without_holes_increment(self): + # Iterate around vertex 1 at the middle + ve_it = openmesh.VertexEdgeIter(self.mesh, self.vhandle[1]) + self.assertEqual(ve_it.__next__().idx(), 5) + self.assertEqual(ve_it.__next__().idx(), 3) + self.assertEqual(ve_it.__next__().idx(), 0) + self.assertEqual(ve_it.__next__().idx(), 1) + self.assertRaises(StopIteration, ve_it.__next__) + + def test_vertex_edge_iter_boundary_increment(self): + # Iterate around vertex 2 at the boundary + ve_it = openmesh.VertexEdgeIter(self.mesh, self.vhandle[2]) + self.assertEqual(ve_it.__next__().idx(), 7) + self.assertEqual(ve_it.__next__().idx(), 1) + self.assertEqual(ve_it.__next__().idx(), 2) + self.assertRaises(StopIteration, ve_it.__next__) + + +if __name__ == '__main__': + suite = unittest.TestLoader().loadTestsFromTestCase(TriMeshCirculatorVertexEdge) + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/src/Python/Unittests/test_trimesh_circulator_vertex_face.py b/src/Python/Unittests/test_trimesh_circulator_vertex_face.py new file mode 100644 index 00000000..4a58f0f7 --- /dev/null +++ b/src/Python/Unittests/test_trimesh_circulator_vertex_face.py @@ -0,0 +1,93 @@ +import unittest +import openmesh + +class TriMeshCirculatorVertexFace(unittest.TestCase): + + def setUp(self): + self.mesh = openmesh.TriMesh() + + # Add some vertices + self.vhandle = [] + + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(2, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0,-1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(2,-1, 0))) + + def test_vertex_face_iter_with_holes_increment(self): + # Add two faces + self.mesh.add_face(self.vhandle[0], self.vhandle[1], self.vhandle[2]) + self.mesh.add_face(self.vhandle[1], self.vhandle[3], self.vhandle[4]) + + ''' + Test setup: + 0 ==== 2 + \ / + \ / + 1 + / \ + / \ + 3 ==== 4 + ''' + + # Iterate around vertex 1 at the middle (with holes in between) + vf_it = openmesh.VertexFaceIter(self.mesh, self.vhandle[1]) + self.assertEqual(vf_it.__next__().idx(), 0) + self.assertEqual(vf_it.__next__().idx(), 1) + self.assertRaises(StopIteration, vf_it.__next__) + + def test_vertex_face_iter_without_holes_increment(self): + # Add four faces + self.mesh.add_face(self.vhandle[0], self.vhandle[1], self.vhandle[2]) + self.mesh.add_face(self.vhandle[1], self.vhandle[3], self.vhandle[4]) + self.mesh.add_face(self.vhandle[0], self.vhandle[3], self.vhandle[1]) + self.mesh.add_face(self.vhandle[2], self.vhandle[1], self.vhandle[4]) + + ''' + Test setup: + 0 ==== 2 + |\ 0 /| + | \ / | + |2 1 3| + | / \ | + |/ 1 \| + 3 ==== 4 + ''' + + # Iterate around vertex 1 at the middle (without holes in between) + vf_it = openmesh.VertexFaceIter(self.mesh, self.vhandle[1]) + self.assertEqual(vf_it.__next__().idx(), 3) + self.assertEqual(vf_it.__next__().idx(), 1) + self.assertEqual(vf_it.__next__().idx(), 2) + self.assertEqual(vf_it.__next__().idx(), 0) + self.assertRaises(StopIteration, vf_it.__next__) + + def test_vertex_face_iter_boundary_increment(self): + # Add four faces + self.mesh.add_face(self.vhandle[0], self.vhandle[1], self.vhandle[2]) + self.mesh.add_face(self.vhandle[1], self.vhandle[3], self.vhandle[4]) + self.mesh.add_face(self.vhandle[0], self.vhandle[3], self.vhandle[1]) + self.mesh.add_face(self.vhandle[2], self.vhandle[1], self.vhandle[4]) + + ''' + Test setup: + 0 ==== 2 + |\ 0 /| + | \ / | + |2 1 3| + | / \ | + |/ 1 \| + 3 ==== 4 + ''' + + # Iterate around vertex 2 at the boundary (without holes in between) + vf_it = openmesh.VertexFaceIter(self.mesh, self.vhandle[2]) + self.assertEqual(vf_it.__next__().idx(), 3) + self.assertEqual(vf_it.__next__().idx(), 0) + self.assertRaises(StopIteration, vf_it.__next__) + + +if __name__ == '__main__': + suite = unittest.TestLoader().loadTestsFromTestCase(TriMeshCirculatorVertexFace) + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/src/Python/Unittests/test_trimesh_circulator_vertex_ihalfedge.py b/src/Python/Unittests/test_trimesh_circulator_vertex_ihalfedge.py new file mode 100644 index 00000000..96b0950f --- /dev/null +++ b/src/Python/Unittests/test_trimesh_circulator_vertex_ihalfedge.py @@ -0,0 +1,80 @@ +import unittest +import openmesh + +class TriMeshCirculatorVertexIHalfEdge(unittest.TestCase): + + def setUp(self): + self.mesh = openmesh.TriMesh() + + # Add some vertices + self.vhandle = [] + + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(2, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0,-1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(2,-1, 0))) + + # Add four faces + self.mesh.add_face(self.vhandle[0], self.vhandle[1], self.vhandle[2]) + self.mesh.add_face(self.vhandle[1], self.vhandle[3], self.vhandle[4]) + self.mesh.add_face(self.vhandle[0], self.vhandle[3], self.vhandle[1]) + self.mesh.add_face(self.vhandle[2], self.vhandle[1], self.vhandle[4]) + + ''' + Test setup: + 0 ==== 2 + |\ 0 /| + | \ / | + |2 1 3| + | / \ | + |/ 1 \| + 3 ==== 4 + Starting halfedge is 1->4 + ''' + + def test_vertex_incoming_halfedge_without_holes_increment(self): + # Iterate around vertex 1 at the middle + vih_it = openmesh.VertexIHalfedgeIter(self.mesh, self.vhandle[1]) + heh = vih_it.__next__() + self.assertEqual(heh.idx(), 10) + self.assertEqual(self.mesh.face_handle(heh).idx(), 1) + heh = vih_it.__next__() + self.assertEqual(heh.idx(), 7) + self.assertEqual(self.mesh.face_handle(heh).idx(), 2) + heh = vih_it.__next__() + self.assertEqual(heh.idx(), 0) + self.assertEqual(self.mesh.face_handle(heh).idx(), 0) + heh = vih_it.__next__() + self.assertEqual(heh.idx(), 3) + self.assertEqual(self.mesh.face_handle(heh).idx(), 3) + self.assertRaises(StopIteration, vih_it.__next__) + + def test_vertex_incoming_halfedge_boundary_increment(self): + # Iterate around vertex 2 at the boundary + vih_it = openmesh.VertexIHalfedgeIter(self.mesh, self.vhandle[2]) + heh = vih_it.__next__() + self.assertEqual(heh.idx(), 14) + self.assertEqual(self.mesh.face_handle(heh).idx(), 3) + heh = vih_it.__next__() + self.assertEqual(heh.idx(), 2) + self.assertEqual(self.mesh.face_handle(heh).idx(), 0) + heh = vih_it.__next__() + self.assertEqual(heh.idx(), 5) + self.assertEqual(self.mesh.face_handle(heh).idx(), -1) + self.assertRaises(StopIteration, vih_it.__next__) + + def test_vertex_incoming_halfedge_dereference_increment(self): + # Iterate around vertex 1 at the middle + vih_it = openmesh.VertexIHalfedgeIter(self.mesh, self.vhandle[1]) + heh = vih_it.__next__() + eh = self.mesh.edge_handle(heh) + vh = self.mesh.to_vertex_handle(heh) + self.assertEqual(heh.idx(), 10) + self.assertEqual(eh.idx(), 5) + self.assertEqual(vh.idx(), 1) + + +if __name__ == '__main__': + suite = unittest.TestLoader().loadTestsFromTestCase(TriMeshCirculatorVertexIHalfEdge) + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/src/Python/Unittests/test_trimesh_circulator_vertex_ohalfedge.py b/src/Python/Unittests/test_trimesh_circulator_vertex_ohalfedge.py new file mode 100644 index 00000000..2d68a6be --- /dev/null +++ b/src/Python/Unittests/test_trimesh_circulator_vertex_ohalfedge.py @@ -0,0 +1,80 @@ +import unittest +import openmesh + +class TriMeshCirculatorVertexOHalfEdge(unittest.TestCase): + + def setUp(self): + self.mesh = openmesh.TriMesh() + + # Add some vertices + self.vhandle = [] + + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(2, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0,-1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(2,-1, 0))) + + # Add four faces + self.mesh.add_face(self.vhandle[0], self.vhandle[1], self.vhandle[2]) + self.mesh.add_face(self.vhandle[1], self.vhandle[3], self.vhandle[4]) + self.mesh.add_face(self.vhandle[0], self.vhandle[3], self.vhandle[1]) + self.mesh.add_face(self.vhandle[2], self.vhandle[1], self.vhandle[4]) + + ''' + Test setup: + 0 ==== 2 + |\ 0 /| + | \ / | + |2 1 3| + | / \ | + |/ 1 \| + 3 ==== 4 + Starting halfedge is 1->4 + ''' + + def test_vertex_outgoing_halfedge_without_holes_increment(self): + # Iterate around vertex 1 at the middle + voh_it = openmesh.VertexOHalfedgeIter(self.mesh, self.vhandle[1]) + heh = voh_it.__next__() + self.assertEqual(heh.idx(), 11) + self.assertEqual(self.mesh.face_handle(heh).idx(), 3) + heh = voh_it.__next__() + self.assertEqual(heh.idx(), 6) + self.assertEqual(self.mesh.face_handle(heh).idx(), 1) + heh = voh_it.__next__() + self.assertEqual(heh.idx(), 1) + self.assertEqual(self.mesh.face_handle(heh).idx(), 2) + heh = voh_it.__next__() + self.assertEqual(heh.idx(), 2) + self.assertEqual(self.mesh.face_handle(heh).idx(), 0) + self.assertRaises(StopIteration, voh_it.__next__) + + def test_vertex_outgoing_halfedge_boundary_increment(self): + # Iterate around vertex 2 at the boundary + voh_it = openmesh.VertexOHalfedgeIter(self.mesh, self.vhandle[2]) + heh = voh_it.__next__() + self.assertEqual(heh.idx(), 15) + self.assertEqual(self.mesh.face_handle(heh).idx(), -1) + heh = voh_it.__next__() + self.assertEqual(heh.idx(), 3) + self.assertEqual(self.mesh.face_handle(heh).idx(), 3) + heh = voh_it.__next__() + self.assertEqual(heh.idx(), 4) + self.assertEqual(self.mesh.face_handle(heh).idx(), 0) + self.assertRaises(StopIteration, voh_it.__next__) + + def test_vertex_outgoing_halfedge_dereference_increment(self): + # Iterate around vertex 1 at the middle + voh_it = openmesh.VertexOHalfedgeIter(self.mesh, self.vhandle[1]) + heh = voh_it.__next__() + eh = self.mesh.edge_handle(heh) + vh = self.mesh.to_vertex_handle(heh) + self.assertEqual(heh.idx(), 11) + self.assertEqual(eh.idx(), 5) + self.assertEqual(vh.idx(), 4) + + +if __name__ == '__main__': + suite = unittest.TestLoader().loadTestsFromTestCase(TriMeshCirculatorVertexOHalfEdge) + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/src/Python/Unittests/test_trimesh_circulator_vertex_vertex.py b/src/Python/Unittests/test_trimesh_circulator_vertex_vertex.py new file mode 100644 index 00000000..c0d10d7d --- /dev/null +++ b/src/Python/Unittests/test_trimesh_circulator_vertex_vertex.py @@ -0,0 +1,56 @@ +import unittest +import openmesh + +class TriMeshCirculatorVertexVertex(unittest.TestCase): + + def setUp(self): + self.mesh = openmesh.TriMesh() + + # Add some vertices + self.vhandle = [] + + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(2, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0,-1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(2,-1, 0))) + + # Add four faces + self.mesh.add_face(self.vhandle[0], self.vhandle[1], self.vhandle[2]) + self.mesh.add_face(self.vhandle[1], self.vhandle[3], self.vhandle[4]) + self.mesh.add_face(self.vhandle[0], self.vhandle[3], self.vhandle[1]) + self.mesh.add_face(self.vhandle[2], self.vhandle[1], self.vhandle[4]) + + ''' + Test setup: + 0 ==== 2 + |\ 0 /| + | \ / | + |2 1 3| + | / \ | + |/ 1 \| + 3 ==== 4 + Starting vertex is 1->4 + ''' + + def test_vertex_vertex_increment(self): + # Iterate around vertex 1 at the middle + vv_it = openmesh.VertexVertexIter(self.mesh, self.vhandle[1]) + self.assertEqual(vv_it.__next__().idx(), 4) + self.assertEqual(vv_it.__next__().idx(), 3) + self.assertEqual(vv_it.__next__().idx(), 0) + self.assertEqual(vv_it.__next__().idx(), 2) + self.assertRaises(StopIteration, vv_it.__next__) + + def test_vertex_vertex_boundary_increment(self): + # Iterate around vertex 2 at the boundary + vv_it = openmesh.VertexVertexIter(self.mesh, self.vhandle[2]) + self.assertEqual(vv_it.__next__().idx(), 4) + self.assertEqual(vv_it.__next__().idx(), 1) + self.assertEqual(vv_it.__next__().idx(), 0) + self.assertRaises(StopIteration, vv_it.__next__) + + +if __name__ == '__main__': + suite = unittest.TestLoader().loadTestsFromTestCase(TriMeshCirculatorVertexVertex) + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/src/Python/Unittests/test_trimesh_collapse.py b/src/Python/Unittests/test_trimesh_collapse.py new file mode 100644 index 00000000..8c3a48c8 --- /dev/null +++ b/src/Python/Unittests/test_trimesh_collapse.py @@ -0,0 +1,516 @@ +import unittest +import openmesh + +class Collapse(unittest.TestCase): + + def setUp(self): + self.mesh = openmesh.TriMesh() + self.vhandle = [] + + def test_collapse_quad_with_center(self): + + # 0--------1 + # |\ /| + # | \ / | + # | \ / | + # | 2 | + # | / \ | + # | / \ | + # 3--------4 + + # Add some vertices + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(2, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 2, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(2, 2, 0))) + + # Add four faces + face_vhandles = [] + + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[1]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[3]) + face_vhandles.append(self.vhandle[2]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[4]) + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[3]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[4]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[2]) + self.mesh.add_face(face_vhandles) + + self.mesh.request_vertex_status() + self.mesh.request_edge_status() + self.mesh.request_face_status() + + # Get the halfedge + v2v1 = self.mesh.find_halfedge(self.vhandle[2], self.vhandle[1]) + + self.assertTrue(v2v1.is_valid()) + self.assertTrue(self.mesh.is_collapse_ok(v2v1)) + + # Execute it as a crash test + self.mesh.collapse(v2v1) + + def test_collapse_tetrahedron_complex(self): + # Add some vertices + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 0, 0))) + + # Add four faces + face_vhandles = [] + + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[2]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[3]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[3]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[3]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[0]) + self.mesh.add_face(face_vhandles) + + self.mesh.request_vertex_status() + self.mesh.request_edge_status() + self.mesh.request_face_status() + + v0v1 = self.mesh.halfedge_handle(0) + v1v0 = self.mesh.opposite_halfedge_handle(v0v1) + + v1vL = self.mesh.next_halfedge_handle(v0v1) + vLv1 = self.mesh.opposite_halfedge_handle(v1vL) + vLv0 = self.mesh.next_halfedge_handle(v1vL) + v0vL = self.mesh.opposite_halfedge_handle(vLv0) + + vLvR = self.mesh.next_halfedge_handle(v0vL) + vRvL = self.mesh.opposite_halfedge_handle(vLvR) + + v0vR = self.mesh.next_halfedge_handle(v1v0) + vRv0 = self.mesh.opposite_halfedge_handle(v0vR) + vRv1 = self.mesh.next_halfedge_handle(v0vR) + v1vR = self.mesh.opposite_halfedge_handle(vRv1) + + v0 = self.mesh.from_vertex_handle(v0v1) + v1 = self.mesh.to_vertex_handle(v0v1) + vL = self.mesh.to_vertex_handle(self.mesh.next_halfedge_handle(v0v1)) + vR = self.mesh.to_vertex_handle(self.mesh.next_halfedge_handle(v1v0)) + + # =================================================================== + # Check preconditions + # =================================================================== + + self.assertTrue(self.mesh.is_collapse_ok(v0v1)) + self.assertTrue(self.mesh.is_collapse_ok(v1v0)) + + # Test the Vertex indices + self.assertEqual(v0.idx(), 0) + self.assertEqual(v1.idx(), 1) + self.assertEqual(vL.idx(), 2) + self.assertEqual(vR.idx(), 3) + + # Check the halfedges + self.assertEqual(v0v1.idx(), 0) + self.assertEqual(v1v0.idx(), 1) + + self.assertEqual(v1vL.idx(), 2) + self.assertEqual(vLv1.idx(), 3) + self.assertEqual(vLv0.idx(), 4) + self.assertEqual(v0vL.idx(), 5) + + self.assertEqual(vLvR.idx(), 6) + self.assertEqual(vRvL.idx(), 7) + + self.assertEqual(vRv0.idx(), 8) + self.assertEqual(v0vR.idx(), 9) + + self.assertEqual(v1vR.idx(), 10) + self.assertEqual(vRv1.idx(), 11) + + # =================================================================== + # Execute collapse + # =================================================================== + + self.mesh.collapse(v0v1) + + # =================================================================== + # Check configuration afterwards + # =================================================================== + + # Now the configuration should look like this: + # The numbers at the side denote the halfedges + # 1 + # / \ + # / \ + # // \\ + # 3/2 11\10 + # // \\ + # / 6--> \ + # 2 ----------- 3 + # <--7 + + self.assertEqual(self.mesh.n_faces(), 4) + + # Check if the right vertices got deleted + self.assertTrue(self.mesh.status(self.mesh.face_handle(0)).deleted()) + self.assertFalse(self.mesh.status(self.mesh.face_handle(1)).deleted()) + self.assertFalse(self.mesh.status(self.mesh.face_handle(2)).deleted()) + self.assertTrue(self.mesh.status(self.mesh.face_handle(3)).deleted()) + + # Check the vertices of the two remaining faces + fh_1 = self.mesh.face_handle(1) + fh_2 = self.mesh.face_handle(2) + + fv_it = self.mesh.fv(fh_1) + + self.assertTrue(fv_it.__next__().idx(), 1) + self.assertTrue(fv_it.__next__().idx(), 2) + self.assertTrue(fv_it.__next__().idx(), 3) + + fv_it = self.mesh.fv(fh_2) + + self.assertTrue(fv_it.__next__().idx(), 2) + self.assertTrue(fv_it.__next__().idx(), 1) + self.assertTrue(fv_it.__next__().idx(), 3) + + # Get the first halfedge of face 1 + fh_1_he = self.mesh.halfedge_handle(fh_1) + + self.assertEqual(fh_1_he.idx(), 11) + self.assertEqual(self.mesh.to_vertex_handle(fh_1_he).idx(), 1) + + next = self.mesh.next_halfedge_handle(fh_1_he) + self.assertEqual(next.idx(), 2) + self.assertEqual(self.mesh.to_vertex_handle(next).idx(), 2) + + next = self.mesh.next_halfedge_handle(next) + self.assertEqual(next.idx(), 6) + self.assertEqual(self.mesh.to_vertex_handle(next).idx(), 3) + + # Get the first halfedge of face 2 + fh_2_he = self.mesh.halfedge_handle(fh_2) + + self.assertEqual(fh_2_he.idx(), 7) + self.assertEqual(self.mesh.to_vertex_handle(fh_2_he).idx(), 2) + + next = self.mesh.next_halfedge_handle(fh_2_he) + self.assertEqual(next.idx(), 3) + self.assertEqual(self.mesh.to_vertex_handle(next).idx(), 1) + + next = self.mesh.next_halfedge_handle(next) + self.assertEqual(next.idx(), 10) + self.assertEqual(self.mesh.to_vertex_handle(next).idx(), 3) + + # Vertex 1 outgoing + voh_it = self.mesh.voh(self.mesh.vertex_handle(1)) + self.assertEqual(voh_it.__next__().idx(), 10) + self.assertEqual(voh_it.__next__().idx(), 2) + self.assertRaises(StopIteration, voh_it.__next__) + + # Vertex 2 outgoing + voh_it = self.mesh.voh(self.mesh.vertex_handle(2)) + self.assertEqual(voh_it.__next__().idx(), 3) + self.assertEqual(voh_it.__next__().idx(), 6) + self.assertRaises(StopIteration, voh_it.__next__) + + # Vertex 2 outgoing + voh_it = self.mesh.voh(self.mesh.vertex_handle(3)) + self.assertEqual(voh_it.__next__().idx(), 11) + self.assertEqual(voh_it.__next__().idx(), 7) + self.assertRaises(StopIteration, voh_it.__next__) + + # =================================================================== + # Cleanup + # =================================================================== + self.mesh.garbage_collection() + + # =================================================================== + # Check configuration afterwards + # =================================================================== + + # Now the configuration should look like this: + # The numbers at the side denote the halfedges + # 0 + # / \ + # / \ + # // \\ + # 4/5 0\1 + # // \\ + # / 3--> \ + # 2 ----------- 1 + # <--2 + + self.assertEqual(self.mesh.n_faces(), 2) + + # Check the vertices of the two remaining faces + fh_0 = self.mesh.face_handle(0) + fh_1 = self.mesh.face_handle(1) + + fv_it = self.mesh.fv(fh_0) + + self.assertEqual(fv_it.__next__().idx(), 2) + self.assertEqual(fv_it.__next__().idx(), 1) + self.assertEqual(fv_it.__next__().idx(), 0) + + fv_it = self.mesh.fv(fh_1) + + self.assertEqual(fv_it.__next__().idx(), 1) + self.assertEqual(fv_it.__next__().idx(), 2) + self.assertEqual(fv_it.__next__().idx(), 0) + + # Get the first halfedge of face 1 + fh_0_he = self.mesh.halfedge_handle(fh_0) + + self.assertEqual(fh_0_he.idx(), 5) + self.assertEqual(self.mesh.to_vertex_handle(fh_0_he).idx(), 2) + + next = self.mesh.next_halfedge_handle(fh_0_he) + self.assertEqual(next.idx(), 3) + self.assertEqual(self.mesh.to_vertex_handle(next).idx(), 1) + + next = self.mesh.next_halfedge_handle(next) + self.assertEqual(next.idx(), 0) + self.assertEqual(self.mesh.to_vertex_handle(next).idx(), 0) + + # Get the first halfedge of face 1 + fh_1_he = self.mesh.halfedge_handle(fh_1) + + self.assertEqual(fh_1_he.idx(), 1) + self.assertEqual(self.mesh.to_vertex_handle(fh_1_he).idx(), 1) + + next = self.mesh.next_halfedge_handle(fh_1_he) + self.assertEqual(next.idx(), 2) + self.assertEqual(self.mesh.to_vertex_handle(next).idx(), 2) + + next = self.mesh.next_halfedge_handle(next) + self.assertEqual(next.idx(), 4) + self.assertEqual(self.mesh.to_vertex_handle(next).idx(), 0) + + # Vertex 0 outgoing + voh_it = self.mesh.voh(self.mesh.vertex_handle(0)) + self.assertEqual(voh_it.__next__().idx(), 1) + self.assertEqual(voh_it.__next__().idx(), 5) + self.assertRaises(StopIteration, voh_it.__next__) + + # Vertex 1 outgoing + voh_it = self.mesh.voh(self.mesh.vertex_handle(1)) + self.assertEqual(voh_it.__next__().idx(), 0) + self.assertEqual(voh_it.__next__().idx(), 2) + self.assertRaises(StopIteration, voh_it.__next__) + + # Vertex 2 outgoing + voh_it = self.mesh.voh(self.mesh.vertex_handle(2)) + self.assertEqual(voh_it.__next__().idx(), 3) + self.assertEqual(voh_it.__next__().idx(), 4) + self.assertRaises(StopIteration, voh_it.__next__) + + self.assertFalse(self.mesh.is_collapse_ok(self.mesh.halfedge_handle(0))) + self.assertFalse(self.mesh.is_collapse_ok(self.mesh.halfedge_handle(1))) + self.assertFalse(self.mesh.is_collapse_ok(self.mesh.halfedge_handle(2))) + self.assertFalse(self.mesh.is_collapse_ok(self.mesh.halfedge_handle(3))) + self.assertFalse(self.mesh.is_collapse_ok(self.mesh.halfedge_handle(4))) + self.assertFalse(self.mesh.is_collapse_ok(self.mesh.halfedge_handle(5))) + + def test_collapse_tetrahedron(self): + # Add some vertices + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 0, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 1, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 0, -1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 0, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(-1, 0, 0))) + + # Add six faces + face_vhandles = [] + + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[4]) + face_vhandles.append(self.vhandle[2]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[3]) + face_vhandles.append(self.vhandle[4]) + face_vhandles.append(self.vhandle[0]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[4]) + face_vhandles.append(self.vhandle[3]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[0]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[3]) + self.mesh.add_face(face_vhandles) + + self.mesh.request_vertex_status() + self.mesh.request_edge_status() + self.mesh.request_face_status() + + # ============================================= + # Collapse halfedge from 0 to 4 + # ============================================= + + heh_collapse1 = self.mesh.halfedge_handle(0) + + self.assertEqual(self.mesh.to_vertex_handle(heh_collapse1).idx(), 4) + self.assertEqual(self.mesh.from_vertex_handle(heh_collapse1).idx(), 0) + + self.assertTrue(self.mesh.is_collapse_ok(heh_collapse1)) + self.mesh.collapse(heh_collapse1) + + heh_collapse2 = self.mesh.halfedge_handle(2) + + self.assertEqual(self.mesh.to_vertex_handle(heh_collapse2).idx(), 2) + self.assertEqual(self.mesh.from_vertex_handle(heh_collapse2).idx(), 4) + + self.assertTrue(self.mesh.is_collapse_ok(heh_collapse2)) + self.mesh.collapse(heh_collapse2) + + heh_collapse3 = self.mesh.halfedge_handle(6) + + self.assertEqual(self.mesh.to_vertex_handle(heh_collapse3).idx(), 2) + self.assertEqual(self.mesh.from_vertex_handle(heh_collapse3).idx(), 3) + + self.assertFalse(self.mesh.is_collapse_ok(heh_collapse3)) + + def test_large_collapse_halfedge(self): + # Add some vertices + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 0, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 1, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 2, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 0, -1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 2, -1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(-1, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 3, 0, 0))) + + # Add six faces + face_vhandles = [] + + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[5]) + face_vhandles.append(self.vhandle[1]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[5]) + face_vhandles.append(self.vhandle[3]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[2]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[3]) + face_vhandles.append(self.vhandle[4]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[4]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[4]) + face_vhandles.append(self.vhandle[6]) + self.mesh.add_face(face_vhandles) + + # Test setup: + # 0 ==== 2 + # / \ /|\ + # / \ / | \ + # 5 --- 1 | 6 + # \ / \ | / + # \ / \|/ + # 3 ==== 4 + + # Request the status bits + self.mesh.request_vertex_status() + self.mesh.request_edge_status() + self.mesh.request_face_status() + + # ============================================= + # Collapse halfedge from 1 to 4 + # ============================================= + + heh_collapse = openmesh.HalfedgeHandle() + + for he in self.mesh.halfedges(): + if self.mesh.from_vertex_handle(he).idx() == 1 and self.mesh.to_vertex_handle(he).idx() == 4: + heh_collapse = he + + # Check our halfedge + self.assertEqual(self.mesh.to_vertex_handle(heh_collapse).idx(), 4) + self.assertEqual(self.mesh.from_vertex_handle(heh_collapse).idx(), 1) + self.assertTrue(self.mesh.is_collapse_ok(heh_collapse)) + + # Remember the end vertices + vh_from = self.mesh.from_vertex_handle(heh_collapse) + vh_to = self.mesh.to_vertex_handle(heh_collapse) + + # Collapse it + self.mesh.collapse(heh_collapse) + + self.assertTrue(self.mesh.status(vh_from).deleted()) + self.assertFalse(self.mesh.status(vh_to).deleted()) + + +if __name__ == '__main__': + suite = unittest.TestLoader().loadTestsFromTestCase(Collapse) + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/src/Python/Unittests/test_trimesh_garbage_collection.py b/src/Python/Unittests/test_trimesh_garbage_collection.py new file mode 100644 index 00000000..07c820b2 --- /dev/null +++ b/src/Python/Unittests/test_trimesh_garbage_collection.py @@ -0,0 +1,168 @@ +import unittest +import openmesh + +class TriMeshGarbageCollection(unittest.TestCase): + + def setUp(self): + self.mesh = openmesh.TriMesh() + + self.mesh.request_vertex_status() + self.mesh.request_edge_status() + self.mesh.request_halfedge_status() + self.mesh.request_face_status() + + # Add some vertices + self.vhandle = [] + + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(-1, -1, 1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 1, -1, 1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 1, 1, 1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(-1, 1, 1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(-1, -1, -1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 1, -1, -1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 1, 1, -1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(-1, 1, -1))) + + # Add six faces to form a cube + self.mesh.add_face(self.vhandle[0], self.vhandle[1], self.vhandle[3]) + self.mesh.add_face(self.vhandle[1], self.vhandle[2], self.vhandle[3]) + self.mesh.add_face(self.vhandle[7], self.vhandle[6], self.vhandle[5]) + self.mesh.add_face(self.vhandle[7], self.vhandle[5], self.vhandle[4]) + self.mesh.add_face(self.vhandle[1], self.vhandle[0], self.vhandle[4]) + self.mesh.add_face(self.vhandle[1], self.vhandle[4], self.vhandle[5]) + self.mesh.add_face(self.vhandle[2], self.vhandle[1], self.vhandle[5]) + self.mesh.add_face(self.vhandle[2], self.vhandle[5], self.vhandle[6]) + self.mesh.add_face(self.vhandle[3], self.vhandle[2], self.vhandle[6]) + self.mesh.add_face(self.vhandle[3], self.vhandle[6], self.vhandle[7]) + self.mesh.add_face(self.vhandle[0], self.vhandle[3], self.vhandle[7]) + self.mesh.add_face(self.vhandle[0], self.vhandle[7], self.vhandle[4]) + + # Test setup: + # + # 3 ======== 2 + # / /| + # / / | z + # 0 ======== 1 | | + # | | | | y + # | 7 | 6 | / + # | | / | / + # | |/ |/ + # 4 ======== 5 -------> x + + def test_standard_garbage_collection(self): + # Check setup + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_faces(), 12) + + self.mesh.delete_vertex(self.vhandle[0]) + + # Check setup + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_faces(), 12) + + self.mesh.garbage_collection() + + # Check setup + self.assertEqual(self.mesh.n_vertices(), 7) + self.assertEqual(self.mesh.n_faces(), 8) + + def test_tracked_garbage_collection(self): + # Check setup + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_faces(), 12) + + #================================================== + # Create lists containing the current handles + #================================================== + + vertexHandles = [] + for v in self.mesh.vertices(): + vertexHandles.append(v) + + halfedgeHandles = [] + for he in self.mesh.halfedges(): + halfedgeHandles.append(he) + + faceHandles = [] + for f in self.mesh.faces(): + faceHandles.append(f) + + # Deleting vertex 0 + self.mesh.delete_vertex(self.vhandle[0]) + + # Check setup + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_faces(), 12) + + self.mesh.garbage_collection(vertexHandles, halfedgeHandles, faceHandles, True, True, True) + + # Check setup + self.assertEqual(self.mesh.n_vertices(), 7) + self.assertEqual(self.mesh.n_faces(), 8) + + # Check setup of vertices + self.assertEqual(vertexHandles[0].idx(), -1) + self.assertEqual(vertexHandles[1].idx(), 1) + self.assertEqual(vertexHandles[2].idx(), 2) + self.assertEqual(vertexHandles[3].idx(), 3) + self.assertEqual(vertexHandles[4].idx(), 4) + self.assertEqual(vertexHandles[5].idx(), 5) + self.assertEqual(vertexHandles[6].idx(), 6) + self.assertEqual(vertexHandles[7].idx(), 0) + + # Check setup of halfedge handles + self.assertEqual(halfedgeHandles[0 ].idx(), -1) + self.assertEqual(halfedgeHandles[1 ].idx(), -1) + self.assertEqual(halfedgeHandles[2 ].idx(), 2) + self.assertEqual(halfedgeHandles[3 ].idx(), 3) + self.assertEqual(halfedgeHandles[4 ].idx(), -1) + self.assertEqual(halfedgeHandles[5 ].idx(), -1) + self.assertEqual(halfedgeHandles[6 ].idx(), 6) + self.assertEqual(halfedgeHandles[7 ].idx(), 7) + self.assertEqual(halfedgeHandles[8 ].idx(), 8) + self.assertEqual(halfedgeHandles[9 ].idx(), 9) + self.assertEqual(halfedgeHandles[10].idx(), 10) + self.assertEqual(halfedgeHandles[11].idx(), 11) + self.assertEqual(halfedgeHandles[12].idx(), 12) + self.assertEqual(halfedgeHandles[13].idx(), 13) + self.assertEqual(halfedgeHandles[14].idx(), 14) + self.assertEqual(halfedgeHandles[15].idx(), 15) + self.assertEqual(halfedgeHandles[16].idx(), 16) + self.assertEqual(halfedgeHandles[17].idx(), 17) + self.assertEqual(halfedgeHandles[18].idx(), 18) + self.assertEqual(halfedgeHandles[19].idx(), 19) + self.assertEqual(halfedgeHandles[20].idx(), -1) + self.assertEqual(halfedgeHandles[21].idx(), -1) + self.assertEqual(halfedgeHandles[22].idx(), 22) + self.assertEqual(halfedgeHandles[23].idx(), 23) + self.assertEqual(halfedgeHandles[24].idx(), 24) + self.assertEqual(halfedgeHandles[25].idx(), 25) + self.assertEqual(halfedgeHandles[26].idx(), 26) + self.assertEqual(halfedgeHandles[27].idx(), 27) + self.assertEqual(halfedgeHandles[28].idx(), 20) + self.assertEqual(halfedgeHandles[29].idx(), 21) + self.assertEqual(halfedgeHandles[30].idx(), 4) + self.assertEqual(halfedgeHandles[31].idx(), 5) + self.assertEqual(halfedgeHandles[32].idx(), 0) + self.assertEqual(halfedgeHandles[33].idx(), 1) + self.assertEqual(halfedgeHandles[34].idx(), -1) + self.assertEqual(halfedgeHandles[35].idx(), -1) + + # Check setup of faces + self.assertEqual(faceHandles[0 ].idx(), -1) + self.assertEqual(faceHandles[1 ].idx(), 1) + self.assertEqual(faceHandles[2 ].idx(), 2) + self.assertEqual(faceHandles[3 ].idx(), 3) + self.assertEqual(faceHandles[4 ].idx(), -1) + self.assertEqual(faceHandles[5 ].idx(), 5) + self.assertEqual(faceHandles[6 ].idx(), 6) + self.assertEqual(faceHandles[7 ].idx(), 7) + self.assertEqual(faceHandles[8 ].idx(), 4) + self.assertEqual(faceHandles[9 ].idx(), 0) + self.assertEqual(faceHandles[10].idx(), -1) + self.assertEqual(faceHandles[11].idx(), -1) + + +if __name__ == '__main__': + suite = unittest.TestLoader().loadTestsFromTestCase(TriMeshGarbageCollection) + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/src/Python/Unittests/test_trimesh_iterators.py b/src/Python/Unittests/test_trimesh_iterators.py new file mode 100755 index 00000000..6bd48c35 --- /dev/null +++ b/src/Python/Unittests/test_trimesh_iterators.py @@ -0,0 +1,396 @@ +import unittest +import openmesh + +class TriMeshIterators(unittest.TestCase): + + def setUp(self): + self.mesh = openmesh.TriMesh() + self.vhandle = [] + + def test_vertex_iter(self): + # Add some vertices + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 0, 0))) + + # Add two faces + self.mesh.add_face(self.vhandle[2], self.vhandle[1], self.vhandle[0]) + self.mesh.add_face(self.vhandle[2], self.vhandle[0], self.vhandle[3]) + + # Test setup: + # 1 === 2 + # | / | + # | / | + # | / | + # 0 === 3 + + v_it = self.mesh.vertices() + + self.assertEqual(v_it.__next__().idx(), 0) + self.assertEqual(v_it.__next__().idx(), 1) + self.assertEqual(v_it.__next__().idx(), 2) + self.assertEqual(v_it.__next__().idx(), 3) + + self.assertRaises(StopIteration, v_it.__next__) + + def test_vertex_iter_start_position(self): + # Add some vertices + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 0, 0))) + + # Add two faces + self.mesh.add_face(self.vhandle[2], self.vhandle[1], self.vhandle[0]) + self.mesh.add_face(self.vhandle[2], self.vhandle[0], self.vhandle[3]) + + # Test setup: + # 1 === 2 + # | / | + # | / | + # | / | + # 0 === 3 + + v_it = openmesh.VertexIter(self.mesh, self.mesh.vertex_handle(2)) + + self.assertEqual(v_it.__next__().idx(), 2) + self.assertEqual(v_it.__next__().idx(), 3) + + self.assertRaises(StopIteration, v_it.__next__) + + def test_edge_iter(self): + # Add some vertices + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 0, 0))) + + # Add two faces + self.mesh.add_face(self.vhandle[2], self.vhandle[1], self.vhandle[0]) + self.mesh.add_face(self.vhandle[2], self.vhandle[0], self.vhandle[3]) + + # Test setup: + # 1 === 2 + # | / | + # | / | + # | / | + # 0 === 3 + + e_it = self.mesh.edges() + + e = e_it.__next__() + self.assertEqual(e.idx(), 0) + + he = self.mesh.halfedge_handle(e, 0) + self.assertEqual(self.mesh.to_vertex_handle(he).idx(), 1) + self.assertEqual(self.mesh.from_vertex_handle(he).idx(), 2) + he = self.mesh.halfedge_handle(e, 1) + self.assertEqual(self.mesh.to_vertex_handle(he).idx(), 2) + self.assertEqual(self.mesh.from_vertex_handle(he).idx(), 1) + + e = e_it.__next__() + self.assertEqual(e.idx(), 1) + + he = self.mesh.halfedge_handle(e, 0) + self.assertEqual(self.mesh.to_vertex_handle(he).idx(), 0) + self.assertEqual(self.mesh.from_vertex_handle(he).idx(), 1) + he = self.mesh.halfedge_handle(e, 1) + self.assertEqual(self.mesh.to_vertex_handle(he).idx(), 1) + self.assertEqual(self.mesh.from_vertex_handle(he).idx(), 0) + + e = e_it.__next__() + self.assertEqual(e.idx(), 2) + + he = self.mesh.halfedge_handle(e, 0) + self.assertEqual(self.mesh.to_vertex_handle(he).idx(), 2) + self.assertEqual(self.mesh.from_vertex_handle(he).idx(), 0) + he = self.mesh.halfedge_handle(e, 1) + self.assertEqual(self.mesh.to_vertex_handle(he).idx(), 0) + self.assertEqual(self.mesh.from_vertex_handle(he).idx(), 2) + + e = e_it.__next__() + self.assertEqual(e.idx(), 3) + + he = self.mesh.halfedge_handle(e, 0) + self.assertEqual(self.mesh.to_vertex_handle(he).idx(), 3) + self.assertEqual(self.mesh.from_vertex_handle(he).idx(), 0) + he = self.mesh.halfedge_handle(e, 1) + self.assertEqual(self.mesh.to_vertex_handle(he).idx(), 0) + self.assertEqual(self.mesh.from_vertex_handle(he).idx(), 3) + + e = e_it.__next__() + self.assertEqual(e.idx(), 4) + + he = self.mesh.halfedge_handle(e, 0) + self.assertEqual(self.mesh.to_vertex_handle(he).idx(), 2) + self.assertEqual(self.mesh.from_vertex_handle(he).idx(), 3) + he = self.mesh.halfedge_handle(e, 1) + self.assertEqual(self.mesh.to_vertex_handle(he).idx(), 3) + self.assertEqual(self.mesh.from_vertex_handle(he).idx(), 2) + + def test_halfedge_iter_skipping(self): + # Add some vertices + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(-1, -1, 1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 1, -1, 1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 1, 1, 1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(-1, 1, 1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(-1, -1, -1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 1, -1, -1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 1, 1, -1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(-1, 1, -1))) + + # Add six faces to form a cube + self.mesh.add_face(self.vhandle[0], self.vhandle[1], self.vhandle[3]) + self.mesh.add_face(self.vhandle[1], self.vhandle[2], self.vhandle[3]) + self.mesh.add_face(self.vhandle[7], self.vhandle[6], self.vhandle[5]) + self.mesh.add_face(self.vhandle[7], self.vhandle[5], self.vhandle[4]) + self.mesh.add_face(self.vhandle[1], self.vhandle[0], self.vhandle[4]) + self.mesh.add_face(self.vhandle[1], self.vhandle[4], self.vhandle[5]) + self.mesh.add_face(self.vhandle[2], self.vhandle[1], self.vhandle[5]) + self.mesh.add_face(self.vhandle[2], self.vhandle[5], self.vhandle[6]) + self.mesh.add_face(self.vhandle[3], self.vhandle[2], self.vhandle[6]) + self.mesh.add_face(self.vhandle[3], self.vhandle[6], self.vhandle[7]) + self.mesh.add_face(self.vhandle[0], self.vhandle[3], self.vhandle[7]) + self.mesh.add_face(self.vhandle[0], self.vhandle[7], self.vhandle[4]) + + # Test setup: + # + # 3 ======== 2 + # / /| + # / / | z + # 0 ======== 1 | | + # | | | | y + # | 7 | 6 | / + # | | / | / + # | |/ |/ + # 4 ======== 5 -------> x + + # Check setup + self.assertEqual(self.mesh.n_edges(), 18) + self.assertEqual(self.mesh.n_halfedges(), 36) + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_faces(), 12) + + # Run over all halfedges + heCounter = 0 + + self.mesh.request_face_status() + self.mesh.request_vertex_status() + self.mesh.request_halfedge_status() + + # Get second edge + eh = self.mesh.edge_handle(2) + + # Delete one edge + self.mesh.delete_edge(eh) + + # Check setup ( No garbage collection, so nothing should change!) + self.assertEqual(self.mesh.n_edges(), 18) + self.assertEqual(self.mesh.n_halfedges(), 36) + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_faces(), 12) + + # ===================================================== + # Check skipping iterator + # ===================================================== + + ok_4 = True + ok_5 = True + + count = 0 + + for he in self.mesh.shalfedges(): + if he.idx() == 4: + ok_4 = False + if he.idx() == 5: + ok_5 = False + count += 1 + + self.assertEqual(count, 34) + self.assertTrue(ok_4) + self.assertTrue(ok_5) + + # ===================================================== + # Check non skipping iterator + # ===================================================== + + ok_4 = False + ok_5 = False + + count = 0 + + for he in self.mesh.halfedges(): + if he.idx() == 4: + ok_4 = True + if he.idx() == 5: + ok_5 = True + count += 1 + + self.assertEqual(count, 36) + self.assertTrue(ok_4) + self.assertTrue(ok_5) + + def test_halfedge_iter_skipping_low_level(self): + # Add some vertices + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(-1, -1, 1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 1, -1, 1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 1, 1, 1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(-1, 1, 1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(-1, -1, -1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 1, -1, -1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d( 1, 1, -1))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(-1, 1, -1))) + + # Add six faces to form a cube + self.mesh.add_face(self.vhandle[0], self.vhandle[1], self.vhandle[3]) + self.mesh.add_face(self.vhandle[1], self.vhandle[2], self.vhandle[3]) + self.mesh.add_face(self.vhandle[7], self.vhandle[6], self.vhandle[5]) + self.mesh.add_face(self.vhandle[7], self.vhandle[5], self.vhandle[4]) + self.mesh.add_face(self.vhandle[1], self.vhandle[0], self.vhandle[4]) + self.mesh.add_face(self.vhandle[1], self.vhandle[4], self.vhandle[5]) + self.mesh.add_face(self.vhandle[2], self.vhandle[1], self.vhandle[5]) + self.mesh.add_face(self.vhandle[2], self.vhandle[5], self.vhandle[6]) + self.mesh.add_face(self.vhandle[3], self.vhandle[2], self.vhandle[6]) + self.mesh.add_face(self.vhandle[3], self.vhandle[6], self.vhandle[7]) + self.mesh.add_face(self.vhandle[0], self.vhandle[3], self.vhandle[7]) + self.mesh.add_face(self.vhandle[0], self.vhandle[7], self.vhandle[4]) + + # Test setup: + # + # 3 ======== 2 + # / /| + # / / | z + # 0 ======== 1 | | + # | | | | y + # | 7 | 6 | / + # | | / | / + # | |/ |/ + # 4 ======== 5 -------> x + + # Check setup + self.assertEqual(self.mesh.n_edges(), 18) + self.assertEqual(self.mesh.n_halfedges(), 36) + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_faces(), 12) + + # Run over all halfedges + heCounter = 0 + + self.mesh.request_face_status() + self.mesh.request_vertex_status() + self.mesh.request_halfedge_status() + + # Get second edge + eh = self.mesh.edge_handle(2) + + # Delete one edge + self.mesh.delete_edge(eh) + + # Check setup ( No garbage collection, so nothing should change!) + self.assertEqual(self.mesh.n_edges(), 18) + self.assertEqual(self.mesh.n_halfedges(), 36) + self.assertEqual(self.mesh.n_vertices(), 8) + self.assertEqual(self.mesh.n_faces(), 12) + + # ===================================================== + # Try to add low level edge with invalid incidents and + # check skipping iterator + # ===================================================== + + # Add a low level edge without handles + eh_test = self.mesh.edge_handle(self.mesh.new_edge(openmesh.VertexHandle(), openmesh.VertexHandle())) + + count = 0 + found_4 = False + found_5 = False + found_36 = False + found_37 = False + + for he in self.mesh.shalfedges(): + if he.idx() == 4: + found_4 = True + if he.idx() == 5: + found_5 = True + if he.idx() == 36: + found_36 = True + if he.idx() == 37: + found_37 = True + count += 1 + + self.assertEqual(count, 36) + self.assertFalse(found_4) + self.assertFalse(found_5) + self.assertTrue(found_36) + self.assertTrue(found_37) + + # ===================================================== + # Try to delete one edge with invalid incidents and + # check skipping iterator + # ===================================================== + + # Delete one edge and recheck (Halfedges 4 and 5) + self.mesh.delete_edge(eh_test) + + count = 0 + found_4 = False + found_5 = False + found_36 = False + found_37 = False + + for he in self.mesh.shalfedges(): + if he.idx() == 4: + found_4 = True + if he.idx() == 5: + found_5 = True + if he.idx() == 36: + found_36 = True + if he.idx() == 37: + found_37 = True + count += 1 + + self.assertEqual(count, 34) + self.assertFalse(found_4) + self.assertFalse(found_5) + self.assertFalse(found_36) + self.assertFalse(found_37) + + def test_face_iter_empty_mesh_one_deleted_face(self): + # Request delete_face capability + self.mesh.request_vertex_status() + self.mesh.request_edge_status() + self.mesh.request_face_status() + + # Add some vertices + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 1, 0))) + + # Add one face + fh = self.mesh.add_face(self.vhandle[2], self.vhandle[1], self.vhandle[0]) + + is_delete_isolated_vertex = False + self.mesh.delete_face(fh, is_delete_isolated_vertex) + + # Test setup: + # 1 === 2 + # | / + # | / + # | / + # 0 + + # Normal iterators + f_it = self.mesh.faces() + + self.assertEqual(f_it.__next__().idx(), 0) + self.assertRaises(StopIteration, f_it.__next__) + + # Same with skipping iterators + f_it = self.mesh.sfaces() + + self.assertRaises(StopIteration, f_it.__next__) + + +if __name__ == '__main__': + suite = unittest.TestLoader().loadTestsFromTestCase(TriMeshIterators) + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/src/Python/Unittests/test_trimesh_normal_calculations.py b/src/Python/Unittests/test_trimesh_normal_calculations.py new file mode 100644 index 00000000..68c8a7dc --- /dev/null +++ b/src/Python/Unittests/test_trimesh_normal_calculations.py @@ -0,0 +1,103 @@ +import unittest +import openmesh + +class Normals(unittest.TestCase): + + def setUp(self): + self.mesh = openmesh.TriMesh() + + # Add some vertices + self.vhandle = [] + + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 0, 1))) + + # Add four faces + face_vhandles = [] + + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[2]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[3]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[3]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[3]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[0]) + self.mesh.add_face(face_vhandles) + + def test_normal_calculations(self): + # Check one Request only vertex normals + # Face normals are required for vertex and halfedge normals, so + # that prevent access to non existing properties are in place + + self.mesh.request_vertex_normals() + self.mesh.request_halfedge_normals() + + # Check blocks + self.mesh.update_normals() + + # Request required face normals + self.mesh.request_face_normals() + + # Automatically compute all normals + # As only vertex normals are requested and no face normals, this will compute nothing. + self.mesh.update_normals() + + # Face normals alone + self.mesh.update_face_normals() + + # Vertex normals alone (require valid face normals) + self.mesh.update_vertex_normals() + + # Halfedge normals alone (require valid face normals) + self.mesh.update_halfedge_normals() + + def test_calc_vertex_normal_fast(self): + self.mesh.request_vertex_normals() + self.mesh.request_halfedge_normals() + self.mesh.request_face_normals() + + normal = openmesh.Vec3d() + + self.mesh.calc_vertex_normal_fast(self.vhandle[2], normal) + + def test_calc_vertex_normal_correct(self): + self.mesh.request_vertex_normals() + self.mesh.request_halfedge_normals() + self.mesh.request_face_normals() + + normal = openmesh.Vec3d() + + self.mesh.calc_vertex_normal_correct(self.vhandle[2], normal) + + def test_calc_vertex_normal_loop(self): + self.mesh.request_vertex_normals() + self.mesh.request_halfedge_normals() + self.mesh.request_face_normals() + + normal = openmesh.Vec3d() + + self.mesh.calc_vertex_normal_loop(self.vhandle[2], normal) + + +if __name__ == '__main__': + suite = unittest.TestLoader().loadTestsFromTestCase(Normals) + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/src/Python/Unittests/test_trimesh_others.py b/src/Python/Unittests/test_trimesh_others.py new file mode 100644 index 00000000..bd559195 --- /dev/null +++ b/src/Python/Unittests/test_trimesh_others.py @@ -0,0 +1,129 @@ +import unittest +import openmesh + +from math import pi, fabs + +class Others(unittest.TestCase): + + def setUp(self): + self.mesh = openmesh.TriMesh() + self.vhandle = [] + + def test_is_estimated_feature_edge(self): + # Add some vertices + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 0, 1))) + + # Add four faces + face_vhandles = [] + + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[2]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[3]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[3]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[3]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[0]) + self.mesh.add_face(face_vhandles) + + # =============================================== + # Setup complete + # =============================================== + + + # Check one Request only vertex normals + # Face normals are required for vertex and halfedge normals, so + # that prevent access to non existing properties are in place + + self.mesh.request_vertex_normals() + self.mesh.request_halfedge_normals() + self.mesh.request_face_normals() + + # Automatically compute all normals + # As only vertex normals are requested and no face normals, this will compute nothing. + self.mesh.update_normals() + + he = self.mesh.halfedges().__next__() + + self.assertTrue(self.mesh.is_estimated_feature_edge(he, 0.0)) + self.assertTrue(self.mesh.is_estimated_feature_edge(he, 0.125 * pi)) + self.assertTrue(self.mesh.is_estimated_feature_edge(he, 0.250 * pi)) + self.assertTrue(self.mesh.is_estimated_feature_edge(he, 0.375 * pi)) + self.assertTrue(self.mesh.is_estimated_feature_edge(he, 0.500 * pi)) + self.assertFalse(self.mesh.is_estimated_feature_edge(he, 0.625 * pi)) + self.assertFalse(self.mesh.is_estimated_feature_edge(he, 0.750 * pi)) + self.assertFalse(self.mesh.is_estimated_feature_edge(he, 0.875 * pi)) + self.assertFalse(self.mesh.is_estimated_feature_edge(he, 1.000 * pi)) + + def test_is_estimated_feature_edge(self): + # Test setup: + # 1 -- 2 + # | / | + # | / | + # 0 -- 3 + + # Add some vertices + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 0, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(0, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 1, 0))) + self.vhandle.append(self.mesh.add_vertex(openmesh.Vec3d(1, 0, 0))) + + # Add two faces + face_vhandles = [] + + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[1]) + face_vhandles.append(self.vhandle[2]) + self.mesh.add_face(face_vhandles) + + face_vhandles = [] + + face_vhandles.append(self.vhandle[0]) + face_vhandles.append(self.vhandle[2]) + face_vhandles.append(self.vhandle[3]) + self.mesh.add_face(face_vhandles) + + # =============================================== + # Setup complete + # =============================================== + + he = self.mesh.halfedge_handle(4) + + self.assertEqual(self.mesh.to_vertex_handle(he).idx(), 0) + self.assertEqual(self.mesh.from_vertex_handle(he).idx(), 2) + self.assertEqual(self.mesh.edge_handle(he).idx(), 2) + + eh = self.mesh.edge_handle(he) + self.assertEqual(self.mesh.calc_dihedral_angle(eh), 0.0) + + # Modify point + tmp = (openmesh.Vec3d(0.0, 0.0, -1.0) + openmesh.Vec3d(1.0, 1.0, -1.0)) * 0.5 + self.mesh.set_point(self.vhandle[2], tmp) + + difference = fabs(1.36944 - self.mesh.calc_dihedral_angle(eh)) + + self.assertTrue(difference < 0.00001) + + +if __name__ == '__main__': + suite = unittest.TestLoader().loadTestsFromTestCase(Others) + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/src/Python/Unittests/test_vector_type.py b/src/Python/Unittests/test_vector_type.py new file mode 100644 index 00000000..72a1fb95 --- /dev/null +++ b/src/Python/Unittests/test_vector_type.py @@ -0,0 +1,30 @@ +import unittest +import openmesh + +class VectorTest(unittest.TestCase): + + def test_compute_triangle_surface_with_cross_product(self): + # vec1 + # x + # | + # | + # | + # x------>x vec2 + + vec1 = openmesh.Vec3d(0.0, 1.0, 0.0) + vec2 = openmesh.Vec3d(1.0, 0.0, 0.0) + + area = 0.5 * openmesh.cross(vec1, vec2).norm() + self.assertEqual(area, 0.5) + + area = 0.5 * (vec1 % vec2).norm() + self.assertEqual(area, 0.5) + + def test_abs_test(self): + vec1 = openmesh.Vec3d(0.5, 0.5, -0.5) + self.assertEqual(vec1.l8_norm(), 0.5) + + +if __name__ == '__main__': + suite = unittest.TestLoader().loadTestsFromTestCase(VectorTest) + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/src/Python/Vector.hh b/src/Python/Vector.hh new file mode 100644 index 00000000..f43cd14b --- /dev/null +++ b/src/Python/Vector.hh @@ -0,0 +1,173 @@ +#ifndef OPENMESH_PYTHON_VECTOR_HH +#define OPENMESH_PYTHON_VECTOR_HH + +#include "Python/Bindings.hh" + +namespace OpenMesh { +namespace Python { + +template +void set_item(Vector& _vec, int _index, Scalar _value) { + if (_index < 0) { + _index += _vec.size(); + } + + if ((size_t)_index < _vec.size()) { + _vec[_index] = _value; + } + else { + PyErr_SetString(PyExc_IndexError, "Index out of range."); + throw_error_already_set(); + } +} + +template +Scalar get_item(Vector& _vec, int _index) { + if (_index < 0) { + _index += _vec.size(); + } + + if ((size_t)_index < _vec.size()) { + return _vec[_index]; + } + else { + PyErr_SetString(PyExc_IndexError, "Index out of range."); + throw_error_already_set(); + } + + return 0.0; +} + +/** + * Expose a vector type to %Python. + * + * This function template is used to expose vectors to %Python. The template + * parameters are used to instantiate the appropriate vector type. + * + * @tparam Scalar A scalar type. + * @tparam N The dimension of the vector. + * + * @param _name The name of the vector type to be exposed. + * + * @note N must be either 2, 3 or 4. + */ +template +void expose_vec(const char *_name) { + typedef OpenMesh::VectorT Vector; + + Scalar (Vector::*min_void)() const = &Vector::min; + Scalar (Vector::*max_void)() const = &Vector::max; + + Vector (Vector::*max_vector)(const Vector&) const = &Vector::max; + Vector (Vector::*min_vector)(const Vector&) const = &Vector::min; + + class_ classVector = class_(_name); + + classVector + .def("__setitem__", &set_item) + .def("__getitem__", &get_item) + .def(self == self) + .def(self != self) + .def(self *= Scalar()) + .def(self /= Scalar()) + .def(self * Scalar()) + .def(Scalar() * self) + .def(self / Scalar()) + .def(self *= self) + .def(self /= self) + .def(self -= self) + .def(self += self) + .def(self * self) + .def(self / self) + .def(self + self) + .def(self - self) + .def(-self) + .def(self | self) + .def("vectorize", &Vector::vectorize, return_internal_reference<>()) + .def(self < self) + + .def("norm", &Vector::norm) + .def("length", &Vector::length) + .def("sqrnorm", &Vector::sqrnorm) + .def("normalize", &Vector::normalize, return_internal_reference<>()) + .def("normalized", &Vector::normalized) + .def("normalize_cond", &Vector::normalize_cond, return_internal_reference<>()) + + .def("l1_norm", &Vector::l1_norm) + .def("l8_norm", &Vector::l8_norm) + + .def("max", max_void) + .def("max_abs", &Vector::max_abs) + .def("min", min_void) + .def("min_abs", &Vector::min_abs) + .def("mean", &Vector::mean) + .def("mean_abs", &Vector::mean_abs) + .def("minimize", &Vector::minimize, return_internal_reference<>()) + .def("minimized", &Vector::minimized) + .def("maximize", &Vector::maximize, return_internal_reference<>()) + .def("maximized", &Vector::maximized) + .def("min", min_vector) + .def("max", max_vector) + + .def("size", &Vector::size) + .staticmethod("size") + .def("vectorized", &Vector::vectorized) + .staticmethod("vectorized") + ; + + typedef OpenMesh::VectorT Vector2; + typedef OpenMesh::VectorT Vector3; + typedef OpenMesh::VectorT Vector4; + + struct Factory { + static Vector2 *vec2_default() { + return new Vector2(Scalar(), Scalar()); + } + static Vector2 *vec2_user_defined(const Scalar& _v0, const Scalar& _v1) { + return new Vector2(_v0, _v1); + } + static Vector3 *vec3_default() { + return new Vector3(Scalar(), Scalar(), Scalar()); + } + static Vector3 *vec3_user_defined(const Scalar& _v0, const Scalar& _v1, const Scalar& _v2) { + return new Vector3(_v0, _v1, _v2); + } + static Vector4 *vec4_default() { + return new Vector4(Scalar(), Scalar(), Scalar(), Scalar()); + } + static Vector4 *vec4_user_defined(const Scalar& _v0, const Scalar& _v1, const Scalar& _v2, const Scalar& _v3) { + return new Vector4(_v0, _v1, _v2, _v3); + } + }; + + if (N == 2) { + classVector + .def("__init__", make_constructor(&Factory::vec2_default)) + .def("__init__", make_constructor(&Factory::vec2_user_defined)) + ; + } + + if (N == 3) { + classVector + .def("__init__", make_constructor(&Factory::vec3_default)) + .def("__init__", make_constructor(&Factory::vec3_user_defined)) + .def("__mod__", &Vector3::operator%) + ; + + def("cross", &Vector3::operator%); + } + + if (N == 4) { + classVector + .def("__init__", make_constructor(&Factory::vec4_default)) + .def("__init__", make_constructor(&Factory::vec4_user_defined)) + ; + } + + def("dot", &Vector::operator|); +} + +} // namespace OpenMesh +} // namespace Python + +#endif