diff --git a/.gitignore b/.gitignore index bcef0eee6a2f054e44a09453040c79788da7f460..a50a9e35add69243704432e9171d98e3f37cd3cc 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ build/ tests/__pycache__ tests/OutFiles *.pyc +*.so .eggs/ build-setuptools/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6034875cf027ee5b048277c70be0db3873c4cc65..add86a402fd09f1e205b59f9cda93778ed20a693 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -3,23 +3,23 @@ stages: - test - deploy -build-3.7-VS2017: +build-3.9-VS2017: stage: build tags: - VS2017 - - python37 + - Python39 variables: BUILD_PLATFORM: "VS2017" ARCHITECTURE: "x64" before_script: - git submodule sync --recursive - git submodule update --init --recursive - script: "CI\\build-3.7-VS2017.bat" + script: "CI\\build-3.9-VS2017.bat" artifacts: paths: - dist3/ - + build-3.9-linux: stage: build @@ -33,9 +33,9 @@ build-3.9-linux: - python setup.py bdist_wheel --dist-dir dist3 artifacts: paths: - - dist3/ + - dist3/ -build-3.5-macos: +build-3.9-macos: stage: build tags: @@ -44,39 +44,41 @@ build-3.5-macos: GIT_SUBMODULE_STRATEGY: recursive script: - export PATH=/opt/local/bin:$PATH - - virtualenv -p python3.5 . + - virtualenv -p python3.9 . - source bin/activate - python setup.py bdist_wheel --dist-dir dist3 artifacts: paths: - dist3/ - -build-2.7-macos: + +build-3.9-macos-m1: stage: build tags: - - Apple + - AppleM1 variables: GIT_SUBMODULE_STRATEGY: recursive script: - export PATH=/opt/local/bin:$PATH - - virtualenv -p python2.7 . + - virtualenv -p python3.9 . - source bin/activate - - python setup.py bdist_wheel --dist-dir dist2 + - python setup.py bdist_wheel --dist-dir dist3 artifacts: paths: - - dist2/ + - dist3/ -test-3.7-VS2017: +test-3.9-VS2017: stage: test tags: - VS2017 - - python37 + - Python39 dependencies: - - build-3.7-VS2017 - script: "CI\\test-3.7-VS2017.bat" + - build-3.9-VS2017 + needs: [build-3.9-VS2017] + script: "CI\\test-3.9-VS2017.bat" + test-3.9-linux: stage: test @@ -84,56 +86,60 @@ test-3.9-linux: - Linux dependencies: - build-3.9-linux + needs: [build-3.9-linux] script: - virtualenv -p python3.9 . - source bin/activate - pip install dist3/*.whl - cd tests - - python -m unittest discover + - python -m unittest discover -test-3.5-macos: +test-3.9-macos: stage: test tags: - Apple dependencies: - - build-3.5-macos + - build-3.9-macos + needs: [build-3.9-macos] script: - export PATH=/opt/local/bin:$PATH - - virtualenv -p python3.5 . + - virtualenv -p python3.9 . - source bin/activate - pip install dist3/*.whl - cd tests - python -m unittest discover -test-2.7-macos: +test-3.9-macos-m1: stage: test tags: - - Apple + - AppleM1 dependencies: - - build-2.7-macos + - build-3.9-macos-m1 + needs: [build-3.9-macos-m1] script: - export PATH=/opt/local/bin:$PATH - - virtualenv -p python2.7 . + - virtualenv -p python3.9 . - source bin/activate - - pip install dist2/*.whl + - pip install dist3/*.whl - cd tests - - python -m unittest discover + - python -m unittest discover -deploy-3.7-VS2017: +deploy-3.9-VS2017: stage: deploy tags: - VS2017 - - python37 + - Python39 dependencies: - - build-3.7-VS2017 - script: "CI\\deploy-3.7-VS2017.bat" + - build-3.9-VS2017 + needs: [build-3.9-VS2017] + script: "CI\\deploy-3.9-VS2017.bat" artifacts: paths: - release/*.whl - + deploy-3.9-linux: stage: deploy @@ -141,40 +147,43 @@ deploy-3.9-linux: - Linux dependencies: - build-3.9-linux + needs: [build-3.9-linux] script: - mkdir release - cp dist3/*.whl release artifacts: paths: - - release/*.whl + - release/*.whl -deploy-3.5-macos: +deploy-3.9-macos: stage: deploy tags: - Apple dependencies: - - build-3.5-macos + - build-3.9-macos + needs: [build-3.9-macos] script: - mkdir release - cp dist3/*.whl release artifacts: paths: - release/*.whl - -deploy-2.7-macos: + +deploy-3.9-macos-m1: stage: deploy tags: - - Apple + - AppleM1 dependencies: - - build-2.7-macos + - build-3.9-macos-m1 + needs: [build-3.9-macos-m1] script: - mkdir release - - cp dist2/*.whl release + - cp dist3/*.whl release artifacts: paths: - - release/*.whl + - release/*.whl deploy-sdist: stage: @@ -198,6 +207,7 @@ deploy-documentation: - Linux dependencies: - build-3.9-linux + needs: [build-3.9-linux] script: - virtualenv -p python3.9 . - source bin/activate diff --git a/CI/build-3.7-VS2017.bat b/CI/build-3.7-VS2017.bat deleted file mode 100644 index f23dd8d1a1db8f8b5034c12050d9a088c41653c4..0000000000000000000000000000000000000000 --- a/CI/build-3.7-VS2017.bat +++ /dev/null @@ -1,4 +0,0 @@ -set PATH=C:\Program Files\Python37;C:\Program Files\Python37\Scripts;C:\Program Files\CMake\bin;%PATH% -virtualenv -p "C:\Program Files\Python37\python.exe" . -call .\Scripts\activate -python setup.py bdist_wheel --dist-dir dist3 diff --git a/CI/build-3.9-VS2017.bat b/CI/build-3.9-VS2017.bat new file mode 100644 index 0000000000000000000000000000000000000000..ffcfd2ac7b141710d36915f3cdf0dbc896ddecb1 --- /dev/null +++ b/CI/build-3.9-VS2017.bat @@ -0,0 +1,4 @@ +set PATH=C:\Program Files\Python39;C:\Program Files\Python39\Scripts;C:\Program Files\CMake\bin;%PATH% +virtualenv.exe -p "C:\Program Files\Python39\python.exe" . +call .\Scripts\activate +python setup.py bdist_wheel --dist-dir dist3 diff --git a/CI/deploy-3.7-VS2017.bat b/CI/deploy-3.9-VS2017.bat similarity index 100% rename from CI/deploy-3.7-VS2017.bat rename to CI/deploy-3.9-VS2017.bat diff --git a/CI/test-3.7-VS2017.bat b/CI/test-3.9-VS2017.bat similarity index 57% rename from CI/test-3.7-VS2017.bat rename to CI/test-3.9-VS2017.bat index 0e50e09bfe831d6ce6b18d82124ced8c24269563..ea6b67407ad9956c38ba50ff640cdc5edbf2f62e 100644 --- a/CI/test-3.7-VS2017.bat +++ b/CI/test-3.9-VS2017.bat @@ -1,5 +1,5 @@ -set PATH=C:\Program Files\Python37;C:\Program Files\Python37\Scripts;%PATH% -virtualenv -p "C:\Program Files\Python37\python.exe" . +set PATH=C:\Program Files\Python39;C:\Program Files\Python39\Scripts;%PATH% +virtualenv -p "C:\Program Files\Python39\python.exe" . call .\Scripts\activate python -m pip install numpy==1.19.3 cd dist3 diff --git a/CMakeLists.txt b/CMakeLists.txt index 38b78674c824d54aa2a9a23cb7667d56adc02a82..569a6198135032baf0d287a80999aa3a7ad884f8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,8 +16,8 @@ if (NOT WIN32) ) endif () -acg_append_files(HEADERS "src/*.hh" .) -acg_append_files(SOURCES "src/*.cc" .) +vci_append_files(HEADERS "src/*.hh" .) +vci_append_files(SOURCES "src/*.cc" .) include_directories(${OPENMESH_INCLUDE_DIRS}) pybind11_add_module(openmesh ${HEADERS} ${SOURCES}) diff --git a/OpenMesh b/OpenMesh index 87f2365d20bd742f1a9f1dd876e9de956f584210..c36d8a304c5ab2457eb7969ad440c4b1ea74407b 160000 --- a/OpenMesh +++ b/OpenMesh @@ -1 +1 @@ -Subproject commit 87f2365d20bd742f1a9f1dd876e9de956f584210 +Subproject commit c36d8a304c5ab2457eb7969ad440c4b1ea74407b diff --git a/docs/readwrite.rst b/docs/readwrite.rst index 75dffb709c6166366dba9981b2f6dcf5f8da787c..c47743e3de741d25a7f4ac39cab62a1b9dbb51d8 100644 --- a/docs/readwrite.rst +++ b/docs/readwrite.rst @@ -16,3 +16,15 @@ OpenMesh provides functions that read and write meshes from and to files: om.write_mesh(trimesh, "bunny.ply") OpenMesh currently supports five file types: .obj, .off, .ply, .stl and .om + +For writing .obj files there is also support for textures. You can pass the path of a texture image and +optionally the suffix for the material file, default is ".mat", but some programs, e.g. Blender expect ".mtl" as suffix + +.. code:: python + + om.write_mesh( + "out.obj", + trimesh, + texture_file="moon.png", + material_file_extension=".mtl" # default is ".mat", blender needs ".mtl" + ) diff --git a/pybind11 b/pybind11 index f1abf5d9159b805674197f6bc443592e631c9130..2dd52544942be4bef80811ef18c8fcf1d3c7e246 160000 --- a/pybind11 +++ b/pybind11 @@ -1 +1 @@ -Subproject commit f1abf5d9159b805674197f6bc443592e631c9130 +Subproject commit 2dd52544942be4bef80811ef18c8fcf1d3c7e246 diff --git a/setup.py b/setup.py index f5b9888773f8c24a37321e1dae507d00dcf2808a..c916177bfa9760d92a9fc345466541fd87ce6a73 100644 --- a/setup.py +++ b/setup.py @@ -100,6 +100,7 @@ setup( 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.9', 'Topic :: Multimedia :: Graphics', ], project_urls={ diff --git a/src/InputOutput.cc b/src/InputOutput.cc index 0c840c0c915a2aa1659bb40f196467b811be8a1c..2012297f9919a6d4e913a66ace308fbb02b2e669 100644 --- a/src/InputOutput.cc +++ b/src/InputOutput.cc @@ -24,7 +24,11 @@ void def_read_mesh(py::module& m, const char *_name) { bool _face_color, bool _face_texture_index, bool _color_alpha, - bool _color_float + bool _color_float, + bool _vertex_status, + bool _halfedge_status, + bool _edge_status, + bool _face_status ) { Mesh mesh; @@ -70,6 +74,24 @@ void def_read_mesh(py::module& m, const char *_name) { if (_face_texture_index) { mesh.request_face_texture_index(); } + if (_vertex_status) { + mesh.request_vertex_status(); + } + if (_edge_status) { + mesh.request_vertex_status(); + } + if (_halfedge_status) { + mesh.request_halfedge_status(); + } + if (_edge_status) { + mesh.request_edge_status(); + } + if (_face_status) { + mesh.request_face_status(); + } + if (_face_status || _halfedge_status || _edge_status || _face_status) { + options += OM::IO::Options::Status; + } if (_color_alpha) options += OM::IO::Options::ColorAlpha; if (_color_float) options += OM::IO::Options::ColorFloat; @@ -126,7 +148,11 @@ void def_read_mesh(py::module& m, const char *_name) { py::arg("face_color")=false, py::arg("face_texture_index")=false, py::arg("color_alpha")=false, - py::arg("color_float")=false + py::arg("color_float")=false, + py::arg("vertex_status")=false, + py::arg("halfedge_status")=false, + py::arg("edge_status")=false, + py::arg("face_status")=false ); } @@ -148,7 +174,10 @@ void def_write_mesh(py::module& m) { bool _face_normal, bool _face_color, bool _color_alpha, - bool _color_float + bool _color_float, + bool _status, + const std::string& _texture_file = "", + const std::string& _material_file_extension = ".mat" ) { OM::IO::Options options; @@ -168,6 +197,10 @@ void def_write_mesh(py::module& m) { if (_color_alpha) options += OM::IO::Options::ColorAlpha; if (_color_float) options += OM::IO::Options::ColorFloat; + options.texture_file = _texture_file; + options.material_file_extension = _material_file_extension; + + if (_status) options += OM::IO::Options::Status; const bool ok = OM::IO::write_mesh(_mesh, _filename, options); @@ -191,7 +224,10 @@ void def_write_mesh(py::module& m) { py::arg("face_normal")=false, py::arg("face_color")=false, py::arg("color_alpha")=false, - py::arg("color_float")=false + py::arg("color_float")=false, + py::arg("status")=false, + py::arg("texture_file")="", + py::arg("material_file_extension")=".mat" ); } diff --git a/src/Mesh.hh b/src/Mesh.hh index 7af6043c78890c0324da9ea0bdef47293a6b0a06..0a9f6a74bb156c8cb8139acfc1ec1b05b499ffd8 100644 --- a/src/Mesh.hh +++ b/src/Mesh.hh @@ -719,10 +719,21 @@ void expose_mesh(py::module& m, const char *_name) { _self.status(_h).set_deleted(_val); }) - .def("is_deleted", [](Mesh& _self, OM::HalfedgeHandle _h) { - if (!_self.has_halfedge_status()) return false; - return _self.status(_h).deleted(); - }) + .def("is_locked", [](Mesh& _self, OM::VertexHandle _h) { + if (!_self.has_vertex_status()) return false; + return _self.status(_h).locked(); + }) + + .def("set_locked", [](Mesh& _self, OM::VertexHandle _h, bool _val) { + if (!_self.has_vertex_status()) _self.request_vertex_status(); + _self.status(_h).set_locked(_val); + + }) + + .def("is_deleted", [](Mesh& _self, OM::HalfedgeHandle _h) { + if (!_self.has_halfedge_status()) return false; + return _self.status(_h).deleted(); + }) .def("set_deleted", [](Mesh& _self, OM::HalfedgeHandle _h, bool _val) { if (!_self.has_halfedge_status()) _self.request_halfedge_status(); @@ -843,6 +854,23 @@ void expose_mesh(py::module& m, const char *_name) { } }) + .def("set_feature", [](Mesh& _self, OM::EdgeHandle _h, bool val) { + if (!_self.has_edge_status()) _self.request_edge_status(); + return _self.status(_h).set_feature(val); + }) + .def("feature", [](Mesh& _self, OM::EdgeHandle _h) { + if (!_self.has_edge_status()) _self.request_edge_status(); + return _self.status(_h).feature(); + }) + .def("set_feature", [](Mesh& _self, OM::VertexHandle _h, bool val) { + if (!_self.has_vertex_status()) _self.request_vertex_status(); + return _self.status(_h).set_feature(val); + }) + .def("feature", [](Mesh& _self, OM::VertexHandle _h) { + if (!_self.has_vertex_status()) _self.request_vertex_status(); + return _self.status(_h).feature(); + }) + //====================================================================== // BaseKernel //====================================================================== @@ -893,13 +921,21 @@ void expose_mesh(py::module& m, const char *_name) { .def("valence", valence_fh) .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("remove_edge", [](Mesh& _self, OM::EdgeHandle _eh) { + if (!_self.has_edge_status()) _self.request_edge_status(); + if (!_self.has_face_status()) _self.request_face_status(); + return _self.remove_edge(_eh); + }) + .def("reinsert_edge", [](Mesh& _self, OM::EdgeHandle _eh) { + if (!_self.has_edge_status()) _self.request_edge_status(); + if (!_self.has_face_status()) _self.request_face_status(); + _self.reinsert_edge(_eh); + }) .def("is_collapse_ok", [](Mesh& _self, OM::HalfedgeHandle _heh) { if (!_self.has_vertex_status()) _self.request_vertex_status(); if (!_self.has_halfedge_status()) _self.request_halfedge_status();