vedo icon indicating copy to clipboard operation
vedo copied to clipboard

Some subdivide functions return meshes with 0 points

Open haesleinhuepf opened this issue 2 years ago • 7 comments

I'm investigating an issue with mesh.subdivide. Depending on the method (e.g. linear, loop, butterfly), the resulting mesh has zero vertices. I'm wondering if this is a bug or has something to do with my example data. If the data is the problem, is there a way of fixing it? Do meshes have to fulfill some constraints when being passed to these functions?

Code to reproduce:

import vedo
print("Vedo version", vedo.__version__)

filename = "https://github.com/haesleinhuepf/napari-process-points-and-surfaces/raw/main/napari_process_points_and_surfaces/data/gastruloid.ply"


for method in range(0, 5):
    mesh_in = vedo.load(filename)
    print("Mesh in points", mesh_in.npoints)

    mesh_out = mesh_in.subdivide(n=1, method=method)
    print("Mesh out points", mesh_out.npoints, f"(Method {method})")

The output shows that three of the five methods result in meshes with 0 vertices:

Vedo version 2023.4.3
Mesh in points 3324
Mesh out points 0 (Method 0)
Mesh in points 3324
Mesh out points 0 (Method 1)
Mesh in points 3324
Mesh out points 231686 (Method 2)
Mesh in points 3324
Mesh out points 0 (Method 3)
Mesh in points 3324
Mesh out points 9967 (Method 4)

See also:

  • https://github.com/haesleinhuepf/napari-process-points-and-surfaces/issues/57

haesleinhuepf avatar Feb 19 '23 09:02 haesleinhuepf

Hi Robert You seem to have a non-manifold edge in your mesh:

import vedo

filename = "https://github.com/haesleinhuepf/napari-process-points-and-surfaces/raw/main/napari_process_points_and_surfaces/data/gastruloid.ply"

mesh_in = vedo.Mesh(filename)
non_manifold_boundaries = mesh_in.boundaries(
    non_manifold_edges=True, 
    boundary_edges=False,
)
print("Mesh in points", mesh_in.npoints)

vedo.show(mesh_in, non_manifold_boundaries)
Screenshot 2023-02-19 at 11 41 34

some subdivision methods are insensitive to it but others may not be...

marcomusy avatar Feb 19 '23 10:02 marcomusy

PS: to spot the cell you can click on a region of the mesh then press . repeatedly to fly to it then press i and check the printout at cellID. Finally adding


mesh_in.delete_cells([3721])
mesh_in.clean()

print("Mesh in points", mesh_in.npoints)
mesh_out = mesh_in.subdivide(n=1, method=0)
print("Mesh out points", mesh_out.npoints)

vedo.show(mesh_out, non_manifold_boundaries).close()

cures the problem. Would be cool to have some split_non_manifold_edges() function to have this programmatically..

marcomusy avatar Feb 19 '23 11:02 marcomusy

Thanks for the rapid response @marcomusy !

Would be cool to have some split_non_manifold_edges() function to have this programmatically..

Yes, agreed! We'll look into this. We already use mesh.clean() to remove duplicated edges after running marching cubes. So maybe, this additional cleanup step is necessary, too. Btw. just for completeness: This is how we created the discussed dataset (full details):

  • Marching cubes (scikit-image)
  • Vedo's mesh.clean()
  • Vedo's mesh.decimate(method='quadric',...)
  • Vedo's mesh.smooth()

Would it make sense to remove non-manifold edges after any of those steps in general? We had a similar discussion about mesh.clean() after marching cubes...

Thanks again!

haesleinhuepf avatar Feb 19 '23 13:02 haesleinhuepf

..i've been playing with it this afternoon and this is what i've come up with (you need the dev16 version in master branch):

from vedo import *

filename = "https://github.com/haesleinhuepf/napari-process-points-and-surfaces/raw/main/napari_process_points_and_surfaces/data/gastruloid.ply"

msh = Mesh(filename)
print("msh is_manifold:", msh.is_manifold())

msh.non_manifold_faces(collapse_edges=False, remove_boundary=False, remove_all=True)
print("msh is_manifold:", msh.is_manifold())

msh.fill_holes()

msh.backcolor("p5").linewidth(1)

show(msh, msh.boundaries(), axes=1)
Screenshot 2023-02-20 at 00 37 48

Implemented in https://github.com/marcomusy/vedo/blob/0bc914d0074054bd62305a39f781bf14ee35d36a/vedo/mesh.py#L745

  • remove_all kills all the problematic cells leaving out a hole which can be cured by fill_holes()
  • remove_boundary only removes cells touching any mesh boundaries
  • collapse_edges collapses all non-manifold edges into a single point and the final degenerate faces are then removed.

marcomusy avatar Feb 20 '23 00:02 marcomusy

..in any case the marching cube algorithm should not generate non manifold isosurfaces...

marcomusy avatar Feb 20 '23 00:02 marcomusy

Thanks again for your time and effort!

..in any case the marching cube algorithm should not generate non manifold isosurfaces...

I was suspecting the scikit-image version to be buggy when was saw it produces duplicate vertices. Are there alternatives? When searching the vedo documentation for "marching" it doesn't find any...

Also let me get @jo-mueller in the loop as he's more knowledgeable in the mesh context than I am: Johannes could you do me a favor and check if the above solution can be used to fix the issue in nppas? Or shall we replace marching cubes? Looking forward to hear what you think.

  • https://github.com/haesleinhuepf/napari-process-points-and-surfaces/issues/57

Big thanks to you both!

haesleinhuepf avatar Feb 20 '23 08:02 haesleinhuepf

Hi, the method doing the marching cube is called isosurface() and it's documented here: https://vedo.embl.es/autodocs/content/vedo/vedo/base.html#BaseGrid.isosurface it has a fast implementation activated by default, which you can disable by setting flying_edges=False.

marcomusy avatar Feb 20 '23 10:02 marcomusy