marcomusy/vedo

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

JeffreyWardman opened this issue · 8 comments

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")

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

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")

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.

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

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?

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.

Unfortunately this won't be accurate without thin slices, which is quite computationally costly.

In addtion to what Johannes sugested a possiblity could be to parse all the points in the sihlouette polyline and use mesh.intersect_with_line(camerapos, meshsilhoupt).
If hits more than 1 you remove the cell (in this case a line segment).