diff --git a/src/Mesh.hh b/src/Mesh.hh index 743766a385a57444cb071d5e47d02f230cadd357..d015aedf2d8318a9c503a1ccee2d4cac245dbf14 100644 --- a/src/Mesh.hh +++ b/src/Mesh.hh @@ -164,8 +164,17 @@ py::array_t face_vertex_indices_trimesh(TriMesh& _self) { if (_self.n_faces() == 0) { return py::array_t(); } + + const bool has_status = _self.has_face_status(); + int *indices = new int[_self.n_faces() * 3]; + py::capsule base = free_when_done(indices); + for (auto fh : _self.all_faces()) { + if (has_status && _self.status(fh).deleted()) { + PyErr_SetString(PyExc_RuntimeError, "Mesh has deleted items. Please call garbage_collection() first."); + throw py::error_already_set(); + } auto fv_it = _self.fv_iter(fh); indices[fh.idx() * 3 + 0] = fv_it->idx(); ++fv_it; indices[fh.idx() * 3 + 1] = fv_it->idx(); ++fv_it; @@ -173,25 +182,6 @@ py::array_t face_vertex_indices_trimesh(TriMesh& _self) { } const auto shape = {_self.n_faces(), size_t(3)}; const auto strides = {3 * sizeof(int), sizeof(int)}; - py::capsule base = free_when_done(indices); - return py::array_t(shape, strides, indices, base); -} - -template -py::array_t halfedge_vertex_indices(Mesh& _self) { - if (_self.n_halfedges() == 0) { - return py::array_t(); - } - int *indices = new int[_self.n_halfedges() * 2]; - for (auto heh : _self.all_halfedges()) { - auto vh1 = _self.from_vertex_handle(heh); - auto vh2 = _self.to_vertex_handle(heh); - indices[heh.idx() * 2 + 0] = vh1.idx(); - indices[heh.idx() * 2 + 1] = vh2.idx(); - } - const auto shape = {_self.n_halfedges(), size_t(2)}; - const auto strides = {2 * sizeof(int), sizeof(int)}; - py::capsule base = free_when_done(indices); return py::array_t(shape, strides, indices, base); } @@ -223,24 +213,36 @@ struct FuncHalfedgeToVertex { static void call(const OM::ArrayKernel& _mesh, OM::HalfedgeHandle _heh, int *_ptr) { *_ptr = _mesh.to_vertex_handle(_heh).idx(); } + static size_t dim() { return 1; } }; struct FuncHalfedgeFromVertex { static void call(const OM::ArrayKernel& _mesh, OM::HalfedgeHandle _heh, int *_ptr) { *_ptr = _mesh.from_vertex_handle(_heh).idx(); } + static size_t dim() { return 1; } }; struct FuncHalfedgeFace { static void call(const OM::ArrayKernel& _mesh, OM::HalfedgeHandle _heh, int *_ptr) { *_ptr = _mesh.face_handle(_heh).idx(); } + static size_t dim() { return 1; } }; struct FuncHalfedgeEdge { static void call(const OM::ArrayKernel& _mesh, OM::HalfedgeHandle _heh, int *_ptr) { *_ptr = _mesh.edge_handle(_heh).idx(); } + static size_t dim() { return 1; } +}; + +struct FuncHalfedgeVertex { + static void call(const OM::ArrayKernel& _mesh, OM::HalfedgeHandle _heh, int *_ptr) { + _ptr[0] = _mesh.from_vertex_handle(_heh).idx(); + _ptr[1] = _mesh.to_vertex_handle(_heh).idx(); + } + static size_t dim() { return 2; } }; template @@ -248,13 +250,21 @@ py::array_t edge_other_indices(Mesh& _self) { if (_self.n_edges() == 0) { return py::array_t(); } + + const bool has_status = _self.has_edge_status(); + int *indices = new int[_self.n_edges() * 2]; + py::capsule base = free_when_done(indices); + for (auto eh : _self.all_edges()) { + if (has_status && _self.status(eh).deleted()) { + PyErr_SetString(PyExc_RuntimeError, "Mesh has deleted items. Please call garbage_collection() first."); + throw py::error_already_set(); + } CopyFunc::call(_self, eh, &indices[eh.idx() * 2]); } const auto shape = {_self.n_edges(), size_t(2)}; const auto strides = {2 * sizeof(int), sizeof(int)}; - py::capsule base = free_when_done(indices); return py::array_t(shape, strides, indices, base); } @@ -263,13 +273,32 @@ py::array_t halfedge_other_indices(Mesh& _self) { if (_self.n_halfedges() == 0) { return py::array_t(); } - int *indices = new int[_self.n_halfedges()]; + + const bool has_status = _self.has_halfedge_status(); + const size_t dim = CopyFunc::dim(); + + int *indices = new int[_self.n_halfedges() * dim]; + py::capsule base = free_when_done(indices); + for (auto heh : _self.all_halfedges()) { - CopyFunc::call(_self, heh, &indices[heh.idx()]); + if (has_status && _self.status(heh).deleted()) { + PyErr_SetString(PyExc_RuntimeError, "Mesh has deleted items. Please call garbage_collection() first."); + throw py::error_already_set(); + } + CopyFunc::call(_self, heh, &indices[heh.idx() * dim]); } - const auto shape = {_self.n_halfedges()}; - const auto strides = {sizeof(int)}; - py::capsule base = free_when_done(indices); + + std::vector shape; + std::vector strides; + if (dim == 1) { + shape = {_self.n_halfedges()}; + strides = {sizeof(int)}; + } + else { + shape = {_self.n_halfedges(), dim}; + strides = {dim * sizeof(int), sizeof(int)}; + } + return py::array_t(shape, strides, indices, base); } @@ -278,11 +307,18 @@ py::array_t indices(Mesh& _self) { const size_t n = _self.py_n_items(Handle()); if (n == 0) return py::array_t(); - // find max valence + const bool has_status = _self.py_has_status(Handle()); + + // find max valence and check status int max_valence = 0; for (size_t i = 0; i < n; ++i) { + Handle hnd(i); + if (has_status && _self.status(hnd).deleted()) { + PyErr_SetString(PyExc_RuntimeError, "Mesh has deleted items. Please call garbage_collection() first."); + throw py::error_already_set(); + } int valence = 0; - for (auto it = Circulator(_self, Handle(i)); it.is_valid(); ++it) { + for (auto it = Circulator(_self, hnd); it.is_valid(); ++it) { valence++; } max_valence = std::max(max_valence, valence); @@ -1209,8 +1245,8 @@ void expose_mesh(py::module& m, const char *_name) { .def("edge_halfedge_indices", &edge_other_indices) .def("eh_indices", &edge_other_indices) - .def("halfedge_vertex_indices", &halfedge_vertex_indices) - .def("hv_indices", &halfedge_vertex_indices) + .def("halfedge_vertex_indices", &halfedge_other_indices) + .def("hv_indices", &halfedge_other_indices) .def("halfedge_to_vertex_indices", &halfedge_other_indices) .def("htv_indices", &halfedge_other_indices) diff --git a/src/MeshTypes.hh b/src/MeshTypes.hh index a7364fd97927b18780a0e90aac88745b6de3af6f..58b3886a78d465b1e6b3e8a6577a4a6a0dd8a547 100644 --- a/src/MeshTypes.hh +++ b/src/MeshTypes.hh @@ -179,6 +179,11 @@ public: size_t py_n_items(OpenMesh::EdgeHandle) const { return Mesh::n_edges(); } size_t py_n_items(OpenMesh::FaceHandle) const { return Mesh::n_faces(); } + size_t py_has_status(OpenMesh::VertexHandle) const { return Mesh::has_vertex_status(); } + size_t py_has_status(OpenMesh::HalfedgeHandle) const { return Mesh::has_halfedge_status(); } + size_t py_has_status(OpenMesh::EdgeHandle) const { return Mesh::has_edge_status(); } + size_t py_has_status(OpenMesh::FaceHandle) const { return Mesh::has_face_status(); } + private: template diff --git a/tests/test_indices.py b/tests/test_indices.py index f20bc969750e1491e167c9e7e445dafdbf4c3c52..9cb75ef839098ca64c93b3d2fce17a6ad8a5d148 100644 --- a/tests/test_indices.py +++ b/tests/test_indices.py @@ -30,6 +30,10 @@ class Python(unittest.TestCase): # | | | / | / | # 0 === 1 4 === 5 8 === 9 + def delete_vertices(self): + for vh in self.mesh.vertices(): + self.mesh.delete_vertex(vh) + def test_vertex_vertex_indices(self): indices1 = self.mesh.vertex_vertex_indices() indices2 = self.mesh.vv_indices() @@ -43,6 +47,11 @@ class Python(unittest.TestCase): self.assertEqual(indices1.shape, (self.mesh.n_vertices(), 3)) self.assertEqual(indices2.shape, (self.mesh.n_vertices(), 3)) + def test_vertex_vertex_indices_delete(self): + self.delete_vertices() + self.assertRaises(RuntimeError, self.mesh.vertex_vertex_indices) + self.assertRaises(RuntimeError, self.mesh.vv_indices) + def test_vertex_face_indices(self): indices1 = self.mesh.vertex_face_indices() indices2 = self.mesh.vf_indices() @@ -56,6 +65,11 @@ class Python(unittest.TestCase): self.assertEqual(indices1.shape, (self.mesh.n_vertices(), 2)) self.assertEqual(indices2.shape, (self.mesh.n_vertices(), 2)) + def test_vertex_face_indices_delete(self): + self.delete_vertices() + self.assertRaises(RuntimeError, self.mesh.vertex_face_indices) + self.assertRaises(RuntimeError, self.mesh.vf_indices) + def test_vertex_edge_indices(self): indices1 = self.mesh.vertex_edge_indices() indices2 = self.mesh.ve_indices() @@ -69,6 +83,11 @@ class Python(unittest.TestCase): self.assertEqual(indices1.shape, (self.mesh.n_vertices(), 3)) self.assertEqual(indices2.shape, (self.mesh.n_vertices(), 3)) + def test_vertex_edge_indices_delete(self): + self.delete_vertices() + self.assertRaises(RuntimeError, self.mesh.vertex_edge_indices) + self.assertRaises(RuntimeError, self.mesh.ve_indices) + def test_vertex_outgoing_halfedge_indices(self): indices1 = self.mesh.vertex_outgoing_halfedge_indices() indices2 = self.mesh.voh_indices() @@ -82,6 +101,11 @@ class Python(unittest.TestCase): self.assertEqual(indices1.shape, (self.mesh.n_vertices(), 3)) self.assertEqual(indices2.shape, (self.mesh.n_vertices(), 3)) + def test_vertex_outgoing_halfedge_indices_delete(self): + self.delete_vertices() + self.assertRaises(RuntimeError, self.mesh.vertex_outgoing_halfedge_indices) + self.assertRaises(RuntimeError, self.mesh.voh_indices) + def test_vertex_incoming_halfedge_indices(self): indices1 = self.mesh.vertex_incoming_halfedge_indices() indices2 = self.mesh.vih_indices() @@ -95,9 +119,13 @@ class Python(unittest.TestCase): self.assertEqual(indices1.shape, (self.mesh.n_vertices(), 3)) self.assertEqual(indices2.shape, (self.mesh.n_vertices(), 3)) + def test_vertex_incoming_halfedge_indices_delete(self): + self.delete_vertices() + self.assertRaises(RuntimeError, self.mesh.vertex_incoming_halfedge_indices) + self.assertRaises(RuntimeError, self.mesh.vih_indices) + def test_face_vertex_indices_trimesh(self): self.mesh = openmesh.read_trimesh('TestFiles/cube-minimal.obj') - indices1 = self.mesh.face_vertex_indices() indices2 = self.mesh.fv_indices() for fh in self.mesh.faces(): @@ -113,7 +141,13 @@ class Python(unittest.TestCase): self.assertEqual(indices2[fh.idx(), 2], vh3.idx()) self.assertEqual(indices1.shape, (self.mesh.n_faces(), 3)) self.assertEqual(indices2.shape, (self.mesh.n_faces(), 3)) - + + def test_face_vertex_indices_trimesh_delete(self): + self.mesh = openmesh.read_trimesh('TestFiles/cube-minimal.obj') + self.delete_vertices() + self.assertRaises(RuntimeError, self.mesh.face_vertex_indices) + self.assertRaises(RuntimeError, self.mesh.fv_indices) + def test_face_vertex_indices_polymesh(self): indices1 = self.mesh.face_vertex_indices() indices2 = self.mesh.fv_indices() @@ -127,6 +161,11 @@ class Python(unittest.TestCase): self.assertEqual(indices1.shape, (self.mesh.n_faces(), 4)) self.assertEqual(indices2.shape, (self.mesh.n_faces(), 4)) + def test_face_vertex_indices_polymesh_delete(self): + self.delete_vertices() + self.assertRaises(RuntimeError, self.mesh.face_vertex_indices) + self.assertRaises(RuntimeError, self.mesh.fv_indices) + def test_face_face_indices(self): indices1 = self.mesh.face_face_indices() indices2 = self.mesh.ff_indices() @@ -140,6 +179,11 @@ class Python(unittest.TestCase): self.assertEqual(indices1.shape, (self.mesh.n_faces(), 1)) self.assertEqual(indices2.shape, (self.mesh.n_faces(), 1)) + def test_face_face_indices_delete(self): + self.delete_vertices() + self.assertRaises(RuntimeError, self.mesh.face_face_indices) + self.assertRaises(RuntimeError, self.mesh.ff_indices) + def test_face_edge_indices(self): indices1 = self.mesh.face_edge_indices() indices2 = self.mesh.fe_indices() @@ -153,6 +197,11 @@ class Python(unittest.TestCase): self.assertEqual(indices1.shape, (self.mesh.n_faces(), 4)) self.assertEqual(indices2.shape, (self.mesh.n_faces(), 4)) + def test_face_edge_indices_delete(self): + self.delete_vertices() + self.assertRaises(RuntimeError, self.mesh.face_edge_indices) + self.assertRaises(RuntimeError, self.mesh.fe_indices) + def test_face_halfedge_indices(self): indices1 = self.mesh.face_halfedge_indices() indices2 = self.mesh.fh_indices() @@ -165,7 +214,12 @@ class Python(unittest.TestCase): self.assertTrue(np.array_equal(indices2[h1.idx()], correct)) self.assertEqual(indices1.shape, (self.mesh.n_faces(), 4)) self.assertEqual(indices2.shape, (self.mesh.n_faces(), 4)) - + + def test_face_halfedge_indices_delete(self): + self.delete_vertices() + self.assertRaises(RuntimeError, self.mesh.face_halfedge_indices) + self.assertRaises(RuntimeError, self.mesh.fh_indices) + def test_edge_vertex_indices(self): indices1 = self.mesh.edge_vertex_indices() indices2 = self.mesh.ev_indices() @@ -180,6 +234,11 @@ class Python(unittest.TestCase): self.assertEqual(indices1.shape, (self.mesh.n_edges(), 2)) self.assertEqual(indices2.shape, (self.mesh.n_edges(), 2)) + def test_edge_vertex_indices_delete(self): + self.delete_vertices() + self.assertRaises(RuntimeError, self.mesh.edge_vertex_indices) + self.assertRaises(RuntimeError, self.mesh.ev_indices) + def test_edge_face_indices(self): indices1 = self.mesh.edge_face_indices() indices2 = self.mesh.ef_indices() @@ -195,6 +254,11 @@ class Python(unittest.TestCase): self.assertEqual(indices1.shape, (self.mesh.n_edges(), 2)) self.assertEqual(indices2.shape, (self.mesh.n_edges(), 2)) + def test_edge_face_indices_delete(self): + self.delete_vertices() + self.assertRaises(RuntimeError, self.mesh.edge_face_indices) + self.assertRaises(RuntimeError, self.mesh.ef_indices) + def test_edge_halfedge_indices(self): indices1 = self.mesh.edge_halfedge_indices() indices2 = self.mesh.eh_indices() @@ -207,6 +271,11 @@ class Python(unittest.TestCase): self.assertEqual(indices2[eh.idx(), 1], heh2.idx()) self.assertEqual(indices1.shape, (self.mesh.n_edges(), 2)) self.assertEqual(indices2.shape, (self.mesh.n_edges(), 2)) + + def test_edge_halfedge_indices_delete(self): + self.delete_vertices() + self.assertRaises(RuntimeError, self.mesh.edge_halfedge_indices) + self.assertRaises(RuntimeError, self.mesh.eh_indices) def test_halfedge_vertex_indices(self): indices1 = self.mesh.halfedge_vertex_indices() @@ -221,6 +290,11 @@ class Python(unittest.TestCase): self.assertEqual(indices1.shape, (self.mesh.n_halfedges(), 2)) self.assertEqual(indices2.shape, (self.mesh.n_halfedges(), 2)) + def test_halfedge_vertex_indices_delete(self): + self.delete_vertices() + self.assertRaises(RuntimeError, self.mesh.halfedge_vertex_indices) + self.assertRaises(RuntimeError, self.mesh.hv_indices) + def test_halfedge_to_vertex_indices(self): indices1 = self.mesh.halfedge_to_vertex_indices() indices2 = self.mesh.htv_indices() @@ -231,6 +305,11 @@ class Python(unittest.TestCase): self.assertEqual(indices1.shape, (self.mesh.n_halfedges(),)) self.assertEqual(indices2.shape, (self.mesh.n_halfedges(),)) + def test_halfedge_to_vertex_indices_delete(self): + self.delete_vertices() + self.assertRaises(RuntimeError, self.mesh.halfedge_to_vertex_indices) + self.assertRaises(RuntimeError, self.mesh.htv_indices) + def test_halfedge_from_vertex_indices(self): indices1 = self.mesh.halfedge_from_vertex_indices() indices2 = self.mesh.hfv_indices() @@ -241,6 +320,11 @@ class Python(unittest.TestCase): self.assertEqual(indices1.shape, (self.mesh.n_halfedges(),)) self.assertEqual(indices2.shape, (self.mesh.n_halfedges(),)) + def test_halfedge_from_vertex_indices_delete(self): + self.delete_vertices() + self.assertRaises(RuntimeError, self.mesh.halfedge_from_vertex_indices) + self.assertRaises(RuntimeError, self.mesh.hfv_indices) + def test_halfedge_face_indices(self): indices1 = self.mesh.halfedge_face_indices() indices2 = self.mesh.hf_indices() @@ -251,6 +335,11 @@ class Python(unittest.TestCase): self.assertEqual(indices1.shape, (self.mesh.n_halfedges(),)) self.assertEqual(indices2.shape, (self.mesh.n_halfedges(),)) + def test_halfedge_face_indices_delete(self): + self.delete_vertices() + self.assertRaises(RuntimeError, self.mesh.halfedge_face_indices) + self.assertRaises(RuntimeError, self.mesh.hf_indices) + def test_halfedge_edge_indices(self): indices1 = self.mesh.halfedge_edge_indices() indices2 = self.mesh.he_indices() @@ -260,6 +349,11 @@ class Python(unittest.TestCase): self.assertEqual(indices2[heh.idx()], eh.idx()) self.assertEqual(indices1.shape, (self.mesh.n_halfedges(),)) self.assertEqual(indices2.shape, (self.mesh.n_halfedges(),)) + + def test_halfedge_edge_indices_delete(self): + self.delete_vertices() + self.assertRaises(RuntimeError, self.mesh.halfedge_edge_indices) + self.assertRaises(RuntimeError, self.mesh.he_indices) if __name__ == '__main__':