Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added preserve_border argument to lossless method. #37

Merged
merged 8 commits into from
Oct 23, 2024
3 changes: 2 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,14 @@ Parameters of the **`simplify\_mesh\_lossless`** method that can be tuned.
Maximal error after which a vertex is not deleted.
- **max\_iterations**
Maximum number of iterations.
- **preserve\_border**
Flag for preserving the vertices situated on open borders. Applies the method described in `this issue <https://github.com/sp4cerat/Fast-Quadric-Mesh-Simplification/issues/14>`__.

Note
~~~~

- The **`simplify\_mesh\_lossless`** method is different from the **`simplify\_mesh`** method with the lossless flag enabled, and should be prefered when quality is the aim and not a precise number of target triangles.
- Tests have shown that the **threshold\_lossless** argument has little to no influence on the reduction of the meshes.
- On the other hand, only the basic algorithm has the option (yet) to preserve open borders.


Implications of the parameters for the threshold growth rate :
Expand Down
10 changes: 7 additions & 3 deletions pyfqmr/Simplify.h
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,7 @@ namespace Simplify
compact_mesh();
} //simplify_mesh()

void simplify_mesh_lossless(void (*log)(char*, int)=NULL, double epsilon=1e-3, int max_iterations = 9999)
void simplify_mesh_lossless(void (*log)(char*, int)=NULL, double epsilon=1e-3, int max_iterations = 9999, bool preserve_border = false)
{
// init
loopi(0,triangles.size())
Expand Down Expand Up @@ -499,8 +499,12 @@ namespace Simplify
int i0=t.v[ j ]; Vertex &v0 = vertices[i0];
int i1=t.v[(j+1)%3]; Vertex &v1 = vertices[i1];

// Border check
if(v0.border != v1.border) continue;
// Border check //Added preserve_border method from issue 14 for lossless
if(preserve_border){
if (v0.border || v1.border) continue; // should keep border vertices
}
else
if (v0.border != v1.border) continue; // base behaviour

// Compute vertex to collapse to
vec3f p;
Expand Down
15 changes: 10 additions & 5 deletions pyfqmr/Simplify.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ cdef extern from "Simplify.h" namespace "Simplify" :
void simplify_mesh( int target_count, int update_rate, double aggressiveness,
void (*log)(char*, int), int max_iterations,double alpha, int K,
bool lossless, double threshold_lossless, bool preserve_border)
void simplify_mesh_lossless(void (*log)(char*, int), double epsilon, int max_iterations)
void simplify_mesh_lossless(void (*log)(char*, int), double epsilon, int max_iterations, bool preserve_border)
void setMeshFromExt(vector[vector[double]] vertices, vector[vector[int]] faces)
vector[vector[int]] getFaces()
vector[vector[double]] getVertices()
Expand Down Expand Up @@ -107,7 +107,12 @@ cdef class Simplify :
self.triangles_cpp.clear()
self.vertices_cpp.clear()
self.normals_cpp.clear()

# Here we will need some checks, just to make sure the right objets are passed
# Maybe other checks like max(faces)==len(vertices) could be added
if not np.issubdtype(faces.dtype, np.integer):
raise TypeError("faces array must be of integer type")

self.faces_mv = faces.astype(dtype="int32", subok=False, copy=False)
self.vertices_mv = vertices.astype(dtype="float64", subok=False, copy=False)
self.triangles_cpp = setFacesNogil(self.faces_mv, self.triangles_cpp)
Expand Down Expand Up @@ -171,7 +176,7 @@ cdef class Simplify :
round(t_end-t_start,4), N_start, N_end)
)

cpdef void simplify_mesh_lossless(self, bool verbose = True, double epsilon=1e-3, int max_iterations=9999):
cpdef void simplify_mesh_lossless(self, bool verbose = True, double epsilon=1e-3, int max_iterations=9999, bool preserve_border = False):
"""Simplify mesh using lossless method

Parameters
Expand All @@ -191,7 +196,7 @@ cdef class Simplify :

N_start = self.faces_mv.shape[0]
t_start = _time()
simplify_mesh_lossless(log, epsilon, max_iterations)
simplify_mesh_lossless(log, epsilon, max_iterations, preserve_border)
t_end = _time()
N_end = getFaces().size()

Expand All @@ -205,7 +210,7 @@ cdef class Simplify :
@cython.boundscheck(False)
@cython.wraparound(False) # turn off negative index wrapping for entire function
@cython.nonecheck(False)
cdef vector[vector[double]] setVerticesNogil(double[:,:] vertices, vector[vector[double]] vector_vertices )nogil:
cdef vector[vector[double]] setVerticesNogil(double[:,:] vertices, vector[vector[double]] vector_vertices )nogil noexcept:
"""nogil function for filling the vector of vertices, "vector_vertices",
with the data found in the memory view of the array "vertices"
"""
Expand All @@ -224,7 +229,7 @@ cdef vector[vector[double]] setVerticesNogil(double[:,:] vertices, vector[vector
@cython.boundscheck(False)
@cython.wraparound(False) # turn off negative index wrapping for entire function
@cython.nonecheck(False)
cdef vector[vector[int]] setFacesNogil(int[:,:] faces, vector[vector[int]] vector_faces )nogil:
cdef vector[vector[int]] setFacesNogil(int[:,:] faces, vector[vector[int]] vector_faces )nogil noexcept:
"""nogil function for filling the vector of faces, "vector_faces",
with the data found in the memory view of the array "faces"
"""
Expand Down
4 changes: 2 additions & 2 deletions tests/test_simplify.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def test_example():

def test_empty():
verts = np.zeros((0,3), dtype=np.float32)
faces = np.zeros((0,3), dtype=np.float32)
faces = np.zeros((0,3), dtype=np.int32)

simp = pyfqmr.Simplify()
simp.setMesh(verts, faces)
Expand All @@ -50,7 +50,7 @@ def test_example_lossless():

def test_empty_lossless():
verts = np.zeros((0,3), dtype=np.float32)
faces = np.zeros((0,3), dtype=np.float32)
faces = np.zeros((0,3), dtype=np.int32)

simp = pyfqmr.Simplify()
simp.setMesh(verts, faces)
Expand Down
Loading