pyvista / pyacvd

Python implementation of surface mesh resampling algorithm ACVD
MIT License
202 stars 16 forks source link

Clustering fails due to vtkSubdivisionFilter #5

Closed supersubscript closed 5 years ago

supersubscript commented 5 years ago

I have a mesh, that I'm trying to resample with the ACVD algorithm. While the mesh is manifold, the subdivision filter employed by the ACVD algorithm appears to return a non-manifold mesh, that makes the algorithm crash mid-way.

In particular:

import pyvista as pv
from PyACVD import Clustering

mesh = pv.PolyData('shoot_manifold_acvd_crash.ply')
non_manifold_edges = mesh.extract_edges(feature_edges=False, boundary_edges=False, 
                                        manifold_edges=False, non_manifold_edges=True)
print(non_manifold_edges.n_cells, non_manifold_edges.n_points)
0 0

The step that fails is the generation of the clusters:

cobj = Clustering.Cluster(mesh)
cobj.GenClusters(mesh.n_points)
Subdividing mesh with 3 subdivision(s)
ERROR:root:Dataset is non-manifold and cannot be subdivided. Edge shared by 4 cells
ERROR:root:Subdivision failed.
ERROR:root:vtkInformation (0x55df794fb890)
Traceback (most recent call last):

  File "<ipython-input-2-be1bf5dcec33>", line 2, in <module>
    cobj.GenClusters(mesh.n_points)

  File "/home/henrik/.local/anaconda2/envs/surface/lib/python3.7/site-packages/PyACVD/Clustering.py", line 184, in GenClusters
    self.PrepareMesh(nclus, subratio, verbose)

  File "/home/henrik/.local/anaconda2/envs/surface/lib/python3.7/site-packages/PyACVD/Clustering.py", line 139, in PrepareMesh
    v = VN.vtk_to_numpy(self.mesh.GetPoints().GetData()).astype(np.float)

AttributeError: 'NoneType' object has no attribute 'GetData'

Digging deeper shows that this is due to the subdivision that is happening inside the mesh preparation step:

mesh.subdivide(3, 'loop')
ERROR:root:Dataset is non-manifold and cannot be subdivided. Edge shared by 4 cells
ERROR:root:Subdivision failed.
ERROR:root:vtkInformation (0x55df796458b0)
PolyData (0x7ff0b08cab28)
  N Cells:  0
  N Points: 0
  X Bounds: 1.000e+00, -1.000e+00
  Y Bounds: 1.000e+00, -1.000e+00
  Z Bounds: 1.000e+00, -1.000e+00
  N Scalars:    0
mesh.subdivide(2, 'loop')
mesh.subdivide(2, 'loop')
ERROR:root:Dataset is non-manifold and cannot be subdivided. Edge shared by 4 cells
ERROR:root:Subdivision failed.
ERROR:root:vtkInformation (0x55df79436670)
PolyData (0x7ff0b08caa68)
  N Cells:  0
  N Points: 0
  X Bounds: 1.000e+00, -1.000e+00
  Y Bounds: 1.000e+00, -1.000e+00
  Z Bounds: 1.000e+00, -1.000e+00
  N Scalars:    0

Notably, the issue doesn't arise for when only a single subdivision is needed, but the output of that is non-manifold.

mesh = mesh.subdivide(1, 'loop')
non_manifold_edges = mesh.extract_edges(feature_edges=False, boundary_edges=False, 
                                        manifold_edges=False, non_manifold_edges=True)
print(non_manifold_edges.n_cells, non_manifold_edges.n_points)
18 18
akaszynski commented 5 years ago

Thanks for pointing this out. I'm in the process of refactoring this module and I'll have an update soon.

akaszynski commented 5 years ago

There's some sort of bug with the subdivision filter, and I've found that I've had better luck just creating my own method. The following code snippet should work for you with the latest version of pyacvd

import pyvista as pv
from pyacvd import Clustering

pv.set_plot_theme('document')

mesh = pv.read('shoot_manifold_acvd_crash.ply')

clus = Clustering(mesh)
clus.subdivide(3)  # 2 also works
clus.cluster(10000)
remesh = clus.create_mesh()
remesh.plot(color='w', show_edges=True, smooth_shading=True)

Visualization Toolkit - OpenGL_079