Skip to content

Commit

Permalink
Fixes #317 (2D reach for the spheres) (#139)
Browse files Browse the repository at this point in the history
* 2d-test

* merge meshes 2d
  • Loading branch information
sgsellan authored Jul 18, 2024
1 parent 8fc4575 commit de33a51
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 18 deletions.
53 changes: 36 additions & 17 deletions src/gpytoolbox/reach_for_the_spheres.py
Original file line number Diff line number Diff line change
Expand Up @@ -664,7 +664,19 @@ def reach_for_the_spheres_iteration(state,
state.V = sp.sparse.linalg.spsolve(Q,b)

# catching flow singularities so we fail gracefully
if np.any((np.isnan(state.V))) or np.any(doublearea(state.V, state.F)==0) or len(non_manifold_edges(state.F))>0 :

there_are_non_manifold_edges = False
if dim==3:
there_are_non_manifold_edges = len(non_manifold_edges(state.F))>0
elif dim==2:
he_nm = np.sort(state.F, axis=1)
# print(he)
he_u_nm = np.unique(he_nm, axis=0, return_counts=True)
# print(he)
ne_nm = he_u_nm[0][he_u_nm[1]>2]
there_are_non_manifold_edges = len(ne_nm)>0

if np.any((np.isnan(state.V))) or np.any(doublearea(state.V, state.F)==0) or there_are_non_manifold_edges :

if verbose:
print("we found a flow singularity. Returning the last converged solution.")
Expand Down Expand Up @@ -1034,20 +1046,27 @@ def _sample_sdf(sdf,

def _merge_meshes(V_active, F_active, V_inactive, F_inactive):
""" Combines the active and inactive mesh while making sure to not merge any *interior* active vertex with a boundary active vertex, avoiding the creation of a non-manifold mesh as shown in gh issue 100 """
bd_active = boundary_vertices(F_active) # boundary
interior_active = np.setdiff1d(np.arange(V_active.shape[0]), bd_active)
V_for_zipping = np.vstack((V_inactive, V_active[bd_active,:]))

# now we will merge the boundary vertices of the active mesh with the inactive mesh
_, zipped_indices, zipped_indices_inverse = np.unique(np.round(V_for_zipping/np.sqrt(np.finfo(V_active.dtype).eps)),return_index=True,return_inverse=True,axis=0)
# add the interior vertices of the active mesh manually into the index list, making sure they are not merged with anything else:
unique_num = zipped_indices.shape[0] # number of unique vertices
zipped_indices = np.concatenate((zipped_indices, interior_active + V_inactive.shape[0]))
# inverse map update
zipped_indices_inverse = np.concatenate((zipped_indices_inverse, np.arange(interior_active.shape[0]) + unique_num))
# now use the index maps to get the final mesh
V = np.vstack((V_inactive, V_active))
F_for_zipping = np.vstack((F_inactive, F_active + V_inactive.shape[0]))
F = zipped_indices_inverse[F_for_zipping]
V = V[zipped_indices,:]
dim = V_active.shape[1]

if dim==3:
bd_active = boundary_vertices(F_active) # boundary
interior_active = np.setdiff1d(np.arange(V_active.shape[0]), bd_active)
V_for_zipping = np.vstack((V_inactive, V_active[bd_active,:]))

# now we will merge the boundary vertices of the active mesh with the inactive mesh
_, zipped_indices, zipped_indices_inverse = np.unique(np.round(V_for_zipping/np.sqrt(np.finfo(V_active.dtype).eps)),return_index=True,return_inverse=True,axis=0)
# add the interior vertices of the active mesh manually into the index list, making sure they are not merged with anything else:
unique_num = zipped_indices.shape[0] # number of unique vertices
zipped_indices = np.concatenate((zipped_indices, interior_active + V_inactive.shape[0]))
# inverse map update
zipped_indices_inverse = np.concatenate((zipped_indices_inverse, np.arange(interior_active.shape[0]) + unique_num))
# now use the index maps to get the final mesh
V = np.vstack((V_inactive, V_active))
F_for_zipping = np.vstack((F_inactive, F_active + V_inactive.shape[0]))
F = zipped_indices_inverse[F_for_zipping]
V = V[zipped_indices,:]
elif dim==2:
V = np.vstack((V_inactive, V_active))
F = np.vstack((F_inactive, F_active + V_inactive.shape[0]))
V, _, _, F = remove_duplicate_vertices(V, faces=F, epsilon=np.sqrt(np.finfo(V.dtype).eps))
return V,F
40 changes: 39 additions & 1 deletion test/test_reach_for_the_spheres.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from .context import gpytoolbox as gpy
from .context import numpy as np
from .context import unittest

import matplotlib.pyplot as plt

class TestReachForTheSpheres(unittest.TestCase):
def test_beat_marching_cubes_low_res(self):
Expand All @@ -26,6 +26,44 @@ def test_beat_marching_cubes_low_res(self):
# print(f"reach_for_the_spheres h: {h_ours}, MC h: {h_mc} for {mesh} with n={n}")
self.assertTrue(h_ours < h_mc)

def test_beat_marching_cubes_2d(self):
png_paths = ["test/unit_tests_data/switzerland.png"]
ns = [10, 20, 30, 50]
for png_path in png_paths:
vv = gpy.png2poly(png_path)[0]
vv = gpy.normalize_points(vv)
vv = 1.0*vv
ec = gpy.edge_indices(vv.shape[0], closed=True)
for n in ns:
gx, gy = np.meshgrid(np.linspace(-1.0, 1.0, n+1), np.linspace(-1.0, 1.0, n+1))
GV = np.vstack((gx.flatten(), gy.flatten())).T
S = gpy.signed_distance(GV, vv, ec)[0]
# plt.scatter(GV[:,0], GV[:,1], c=S)
# plt.plot(vv[:,0], vv[:,1], 'r-')
# plt.colorbar()
# plt.show()
vv_mc, ee_mc = gpy.marching_squares(S, GV, n+1, n+1)

# plot vv, ee edge by edge
# for i in range(ee_mc.shape[0]):
# plt.plot([vv_mc[ee_mc[i,0],0], vv_mc[ee_mc[i,1],0]], [vv_mc[ee_mc[i,0],1], vv_mc[ee_mc[i,1],1]], 'k-')

# now run rfts
vv_rfts, ee_rfts = gpy.regular_circle_polyline(10)
sdf = lambda x: gpy.signed_distance(x, vv, ec)[0]
vv_rfts, ee_rfts = gpy.reach_for_the_spheres(GV, sdf, V=vv_rfts, F=ee_rfts, S=S, min_h = np.clip(1.5/n, 0.001, 0.1))
# plot vv, ee edge by edge
# for i in range(ee_rfts.shape[0]):
# plt.plot([vv_rfts[ee_rfts[i,0],0], vv_rfts[ee_rfts[i,1],0]], [vv_rfts[ee_rfts[i,0],1], vv_rfts[ee_rfts[i,1],1]], 'g-')

h_mc = gpy.approximate_hausdorff_distance(vv_mc, ee_mc.astype(np.int32), vv, ec.astype(np.int32))
h_rfts = gpy.approximate_hausdorff_distance(vv_rfts, ee_rfts.astype(np.int32), vv, ec.astype(np.int32))
# print(f"reach_for_the_spheres h: {h_rfts}, MC h: {h_mc} for {png_path} with n={n}")
# plt.axis('equal')
# plt.show()
self.assertTrue(h_rfts < h_mc)


def test_noop(self):
meshes = ["bunny_oded.obj", "spot.obj", "teddy.obj"]
for mesh in meshes:
Expand Down
Binary file added test/unit_tests_data/switzerland.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit de33a51

Please sign in to comment.