From 1b738239b8ff3fe5aef2fcb20e675a7bacc752d3 Mon Sep 17 00:00:00 2001 From: Oded Stein Date: Thu, 14 Mar 2024 10:04:00 -0700 Subject: [PATCH] add qslim decimation feature --- src/cpp/binding_decimate.cpp | 17 ++++++++++-- src/gpytoolbox/decimate.py | 19 ++++++++++++-- test/test_decimate.py | 50 +++++++++++++++++++----------------- 3 files changed, 58 insertions(+), 28 deletions(-) diff --git a/src/cpp/binding_decimate.cpp b/src/cpp/binding_decimate.cpp index 22d3ea07..bcf358b2 100644 --- a/src/cpp/binding_decimate.cpp +++ b/src/cpp/binding_decimate.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -13,12 +14,24 @@ using EigenDRef = Ref; //allows passing column/row void binding_decimate(py::module& m) { m.def("_decimate_cpp_impl",[](EigenDRef v, - EigenDRef f, int num_faces) + EigenDRef f, + int num_faces, + int method) { Eigen::MatrixXd SV; Eigen::MatrixXi SF; Eigen::VectorXi J, I; - igl::decimate(v,f,num_faces,SV,SF,I,J); + if(method==0) { + igl::decimate(v,f,num_faces, + //This will be required when we bump the libigl version. + //true, + SV,SF,I,J); + } else if(method==1) { + igl::qslim(v,f,num_faces, + //This will be required when we bump the libigl version. + //true, + SV,SF,I,J); + } return std::make_tuple(SV,SF,I,J); }); diff --git a/src/gpytoolbox/decimate.py b/src/gpytoolbox/decimate.py index 0a84b3f2..f896f933 100644 --- a/src/gpytoolbox/decimate.py +++ b/src/gpytoolbox/decimate.py @@ -1,6 +1,9 @@ import numpy as np -def decimate(V,F,face_ratio=0.1,num_faces=None): +def decimate(V,F, + face_ratio=0.1, + num_faces=None, + method='shortest_edge'): """Reduce the number of faces of a triangle mesh. From a manifold triangle mesh, builds a new triangle mesh with fewer faces than the original one using libigl's decimation algorithm. @@ -15,6 +18,9 @@ def decimate(V,F,face_ratio=0.1,num_faces=None): Desired ratio of output faces to input faces num_faces : int, optional (default None) Desired number of faces in output mesh (superseeds face_ratio if set) + method : string, optional (default shortest_edge) + Which mesh decimation algorithm to use. + Options are 'shortest_edge' and 'qslim' Returns ------- @@ -53,6 +59,15 @@ def decimate(V,F,face_ratio=0.1,num_faces=None): if (num_faces is None): num_faces = np.floor(face_ratio*F.shape[0]).astype(np.int32) - v, f, i, j = _decimate_cpp_impl(V.astype(np.float64),F.astype(np.int32),num_faces) + method_int = 0 + if method == 'shortest_edge': + method_int = 0 + elif method == 'qslim': + method_int = 1 + else: + raise Exception("Not a valid decimation method.") + v, f, i, j = _decimate_cpp_impl(V.astype(np.float64),F.astype(np.int32), + num_faces, + method_int) return v,f,i,j diff --git a/test/test_decimate.py b/test/test_decimate.py index f8b0517f..1f0b9684 100644 --- a/test/test_decimate.py +++ b/test/test_decimate.py @@ -7,35 +7,37 @@ def test_armadillo(self): np.random.seed(0) v,f = gpytoolbox.read_mesh("test/unit_tests_data/armadillo.obj") for nn in range(20,2000,301): - u,g,i,j = gpytoolbox.decimate(v,f,num_faces=nn) - self.assertTrue(np.isclose(g.shape[0]-nn,0,atol=3)) - ratio = nn/f.shape[0] - u,g,i,j = gpytoolbox.decimate(v,f,face_ratio=ratio) - # print(nn) - # print(g.shape[0]) - # print(g.shape[0]/f.shape[0]) - self.assertTrue(np.isclose(ratio - (g.shape[0]/f.shape[0]),0,atol=0.001)) - # Are the outputs what they claim they are? - # Is i the size of g and j the size of u? - self.assertTrue(g.shape[0]==i.shape[0]) - self.assertTrue(u.shape[0]==j.shape[0]) - # There isn't really a good way to check that one is the birth vertex of the other... + for method in {'shortest_edge', 'qslim'}: + u,g,i,j = gpytoolbox.decimate(v,f,method=method,num_faces=nn) + self.assertTrue(np.isclose(g.shape[0]-nn,0,atol=3)) + ratio = nn/f.shape[0] + u,g,i,j = gpytoolbox.decimate(v,f,method=method,face_ratio=ratio) + # print(nn) + # print(g.shape[0]) + # print(g.shape[0]/f.shape[0]) + self.assertTrue(np.isclose(ratio - (g.shape[0]/f.shape[0]),0,atol=0.001)) + # Are the outputs what they claim they are? + # Is i the size of g and j the size of u? + self.assertTrue(g.shape[0]==i.shape[0]) + self.assertTrue(u.shape[0]==j.shape[0]) + # There isn't really a good way to check that one is the birth vertex of the other... def test_with_boundary(self): np.random.seed(0) v,f = gpytoolbox.read_mesh("test/unit_tests_data/airplane.obj") for nn in range(200,2000,301): - u,g,i,j = gpytoolbox.decimate(v,f,num_faces=nn) - self.assertTrue(np.isclose(g.shape[0]-nn,0,atol=3)) - ratio = nn/f.shape[0] - u,g,i,j = gpytoolbox.decimate(v,f,face_ratio=ratio) - self.assertTrue(np.isclose(ratio - (g.shape[0]/f.shape[0]),0,atol=0.001)) - gpytoolbox.write_mesh("output.obj",u,g) - # Are the outputs what they claim they are? - # Is i the size of g and j the size of u? - self.assertTrue(g.shape[0]==i.shape[0]) - self.assertTrue(u.shape[0]==j.shape[0]) - # There isn't really a good way to check that one is the birth vertex of the other... + for method in {'shortest_edge', 'qslim'}: + u,g,i,j = gpytoolbox.decimate(v,f,method=method,num_faces=nn) + self.assertTrue(np.isclose(g.shape[0]-nn,0,atol=3)) + ratio = nn/f.shape[0] + u,g,i,j = gpytoolbox.decimate(v,f,method=method,face_ratio=ratio) + self.assertTrue(np.isclose(ratio - (g.shape[0]/f.shape[0]),0,atol=0.001)) + gpytoolbox.write_mesh("output.obj",u,g) + # Are the outputs what they claim they are? + # Is i the size of g and j the size of u? + self.assertTrue(g.shape[0]==i.shape[0]) + self.assertTrue(u.shape[0]==j.shape[0]) + # There isn't really a good way to check that one is the birth vertex of the other... if __name__ == '__main__': unittest.main() \ No newline at end of file