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

Some subdivide functions return meshes with 0 points #813

Open
haesleinhuepf opened this issue Feb 19, 2023 · 7 comments
Open

Some subdivide functions return meshes with 0 points #813

haesleinhuepf opened this issue Feb 19, 2023 · 7 comments

Comments

@haesleinhuepf
Copy link

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:

@marcomusy
Copy link
Owner

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
Copy link
Owner

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..

@haesleinhuepf
Copy link
Author

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!

@marcomusy
Copy link
Owner

..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

def non_manifold_faces(self, remove_all=False, remove_boundary=False, collapse_edges=False):

  • 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
Copy link
Owner

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

@haesleinhuepf
Copy link
Author

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.

Big thanks to you both!

@marcomusy
Copy link
Owner

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants