marcomusy / vedo

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

Close an extruded mesh with a cover #1155

Open FPerezHernandez92 opened 4 months ago

FPerezHernandez92 commented 4 months ago

I am working with a kind of mesh that is a bit complicated. At the edges they might have intersections. Therefore, when it comes to extruding and putting a cover, I have problems. For example, I can't generate the cover with "Delaney". Or if I paste a slice the mesh is not closed (I use the is_closed method and it returns False).

example_lower.stl.zip

# Python 3.11.9
import vedo  # 2024.5.2
import time

def close_with_cover_reply(mesh, value_z):
    extrude_mesh = mesh.extrude(zshift=value_z, rotation=0, cap=False, dr=0, res=10)
    cut_mesh = extrude_mesh.cut_with_plane(origin=(0, 0, value_z), normal=(0, 0, 1))

    extrude_cut_mesh = vedo.merge(mesh, cut_mesh)
    extrude_cut_mesh.clean()

    cover_mesh = extrude_cut_mesh.copy()
    cover_mesh.vertices[:, 2:] = value_z

    closed_mesh = vedo.merge(extrude_cut_mesh, cover_mesh)
    closed_mesh.clean()
    return closed_mesh

def close_with_delaney(mesh, value_z):
    extrude_mesh = mesh.extrude(zshift=value_z, rotation=0, cap=False, dr=0, res=10)
    cut_mesh = extrude_mesh.cut_with_plane(origin=(0, 0, value_z), normal=(0, 0, 1))

    extrude_cut_mesh = vedo.merge(mesh, cut_mesh)
    extrude_cut_mesh.clean()

    boundarie = extrude_cut_mesh.boundaries()
    cover_delaney = boundarie.generate_delaunay2d()

    closed_mesh = vedo.merge(extrude_cut_mesh, cover_delaney)
    closed_mesh.clean()
    return closed_mesh

name_mesh = "example_lower.stl"
mesh = vedo.Mesh(name_mesh)
value_z: int = -20
# vedo.show(mesh, new=True)

st_time = time.time()
closed_mesh = close_with_cover_reply(mesh, value_z)
print(time.time() - st_time, closed_mesh.is_closed())  # 0.58 seconds and closed

st_time = time.time()
closed_mesh = close_with_delaney(mesh, value_z)
print(time.time() - st_time, closed_mesh.is_closed())  # 0.71 seconds but not closed

I have extruded the mesh and then cut the extrusion to have a plane surface.

Captura de pantalla 2024-07-10 a las 16 02 30

My problem comes to generate the cover. Option 1. Function "close_with_cover_reply". I have tried to replicate the mesh at the desired Z coordinate. This way I get a closed mesh, but at the cost of obtaining a mesh with twice the number of points, which is not very elegant.

Captura de pantalla 2024-07-10 a las 16 04 24

Option 2. Function "close_with_delaney". I tried to get the boundary of the extracted mesh. Then I generate the cover with Delaney, but, apart from the fact that it generates triangles where it does not correspond, the mesh would not be closed.

Captura de pantalla 2024-07-10 a las 16 05 10

I wouldn't mind modifying the morphology of the edge, so I could make a smooth of the edge and join it as it is done in @https://github.com/marcomusy/vedo/issues/1019 and then extrude to simplify it, but it is not enough.

I have used the "cap" parameter of the extrude function but it generates a copy of the mesh points, which I am trying to avoid.

My goal would be to have something similar to this and also, the is_closed function would return True:

Captura de pantalla 2024-07-10 a las 16 15 07

Thanks for the help and congratulations for the work, I love vedo.

marcomusy commented 4 months ago

Hi Francisco, I think it is very difficult problem because the original mesh is very irregular and even at the very first step you run into the problem of having the extrusion of the boundary to intersect your mesh mesh:

import vedo 

def cap(mesh, value_z):
    msh = mesh.boundaries().join().extrude(zshift=value_z).lw(0)
    msh.cut_with_plane(origin=(0, 0, value_z), normal=(0, 0, 1))
    return msh

name_mesh = "data/example_lower.stl"
mesh = vedo.Mesh(name_mesh)
value_z: int = -20

closed_mesh = cap(mesh, value_z)
vedo.show(mesh, closed_mesh).close()

Screenshot from 2024-07-10 22-06-31 (note how the purple on the bottom right hits the yellow mesh)

I'm afraid you will need an extra step of cleaning up the mesh before proceeding to close it.

marcomusy commented 4 months ago

Or you can try with pymeshfix but it takes a LONG time to process it (1 min):

pip install pymeshfix

then

import vedo  # 2024.5.2
import time
import pymeshfix

def close_with_cover_reply(mesh, value_z):
    extrude_mesh = mesh.extrude(zshift=value_z, cap=False, res=10)
    cut_mesh = extrude_mesh.cut_with_plane(origin=(0, 0, value_z), normal=(0, 0, 1))

    extrude_cut_mesh = vedo.merge(mesh, cut_mesh)
    extrude_cut_mesh.clean()

    cover_mesh = extrude_cut_mesh.copy()
    cover_mesh.vertices[:, 2:] = value_z

    closed_mesh = vedo.merge(extrude_cut_mesh, cover_mesh)
    closed_mesh.clean()
    meshfix = pymeshfix.MeshFix(closed_mesh.vertices, closed_mesh.cells)
    meshfix.repair()
    return vedo.Mesh(meshfix.mesh)

name_mesh = "data/example_lower.stl"
mesh = vedo.Mesh(name_mesh)
value_z: int = -20

st_time = time.time()
closed_mesh = close_with_cover_reply(mesh, value_z)
print(time.time() - st_time, closed_mesh.is_closed(), closed_mesh.is_manifold()) 

vedo.show(closed_mesh, axes=1)

image

FPerezHernandez92 commented 4 months ago

Thank you very much for your answers. Unfortunately I can't use pymeshfix because it takes too long. I need to solve the problem in a very small amount of time.

Indeed the type of meshes present many difficulties. I have thought of an alternative that could be the one that was done here https://github.com/marcomusy/vedo/issues/1019#issuecomment-1921652457

But my idea would be to make smooth to the edge or to take a subsampling of the edge to lose a little its morphology. The problem would be that with the subsampling I would not be able to use the to_strips function because the number of lines is different. Perhaps in this way I could clean the edge that apparently is the biggest impediment. Would it be an option?

marcomusy commented 4 months ago

Sorry I overlooked your question.. SInce the main issue seems to be the self-intersecting vertices in the extrusion, you may try to detect them before extrusion with intersect_with_line() described here If positive you can delete the face .delete_cells() and then call .clean() to remove the point too.