marcomusy / vedo

A python module for scientific analysis of 3D data based on VTK and Numpy
https://vedo.embl.es
MIT License
1.98k stars 257 forks source link

How to flatten mesh to 2D and get its internal and external boundaries? #1147

Open JeffreyWardman opened 3 days ago

JeffreyWardman commented 3 days ago

The below doesn't work:

import vedo

path = "model.stl"
mesh = vedo.Mesh(path)
v = mesh.vertices
v[:, -1] = 0

boundaries = mesh.boundaries().split()
vedo.show(mesh, mesh.boundaries().c("black"), axes=1).close()

Likewise with:

mesh = mesh.project_on_plane("z")
marcomusy commented 2 days ago

Flattening a 3D mesh cannot change the boundaries.

from vedo import *

path = "data/panther.stl"
mesh = Mesh(path).cut_with_plane().c("white")
v = mesh.vertices
# v[:, -1] = 0

boundaries = mesh.boundaries().split()
pmesh = mesh.clone().project_on_plane("z")
silhouette = pmesh.silhouette([0,0,1]).lw(2)
show(mesh, pmesh, boundaries, silhouette, axes=1).close()

image

JeffreyWardman commented 2 days ago

How would I get the boundaries of pmesh, the projection on the z plane?

Also how do I plot it so all things in the projection have the same colour? I thought updating the backface would resolve it:

pmesh.c("red")
pmesh.backcolor("red")
JeffreyWardman commented 2 days ago

Basically this: https://examples.vtk.org/site/Cxx/PolyData/ExternalContour/ assuming it also contains internal boundaries (e.g. if a cylinder was cut through the middle, creating a hole.

marcomusy commented 1 day ago

I dont think that is a very good way of doing it especially if you have other objects in the scene. Also ai think this is not completely trivial.. this a possible way to go:

from vedo import *

shape = Mesh(dataurl+'bunny.obj').c('blue9', 0.1)
shape.subdivide()

mx = shape.clone().project_on_plane("x")
mx.alpha(1).c("red")

msh = mx.generate_delaunay2d(mode="fit", alpha=0.003)
boundary = msh.boundaries().join().print()

show(shape, msh, boundary, axes=1)
Screenshot 2024-06-28 at 00 01 17
JeffreyWardman commented 1 day ago

Hmm that didn't work for my use-case. Projecting on plane is great but I want it to only project the outer boundary. Too much detail gets retained and it essentially looks like canny edges. Is there a way to remove the shading information in the plotter instance when calling the silhouette function?

jo-mueller commented 23 hours ago

but I want it to only project the outer boundary

Would it maybe be possible to iteratively cut the mesh with a plane orthogonal to the desired direction of projection? Something like this:

import vedo

bunny_mesh = vedo.load(vedo.dataurl + 'bunny.obj')

boundaries = bunny_mesh.bounds()
cut_planes = np.linspace(boundaries[0], boundaries[1], 20)

cuts = []
for cut_plane in cut_planes:
    mesh_copy = bunny_mesh.clone()
    cut_plane = vedo.Plane(pos=(cut_plane, 0, 0), normal=(1, 0, 0))
    cut = mesh_copy.intersect_with(cut_plane)
    viewer.add_points(cut.vertices, size=0.005)

So far, what's missing is that the intersect_with function only returns a point collection, which would have to be assembled into an actually filled area. The desired projection would then be the union of all of these.