Skip to content

Commit

Permalink
Merge pull request #37 from Kramer84/preserve-border-lossless
Browse files Browse the repository at this point in the history
Added preserve_border argument to lossless method, and bit stricter type checking for add_mesh method.
  • Loading branch information
Kramer84 authored Oct 23, 2024
2 parents 9be728c + e0b12db commit e3732d3
Show file tree
Hide file tree
Showing 4 changed files with 21 additions and 11 deletions.
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

0 comments on commit e3732d3

Please sign in to comment.