trimesh icon indicating copy to clipboard operation
trimesh copied to clipboard

Mesh non watertight after slicing with plane

Open marios-stam opened this issue 1 year ago • 3 comments

Hello all,

I have been trying to slice the following mesh that I have attached with the plane defined in the code and I get a non-watertight mesh as a result. However, if I save the sliced mesh and load it from another similar framework and save it using it, it is watertight when I load it again in trimesh.

origin = [-0.03,  0.05,  0.10]
normal = [-0.25,  0.43,  0.87]
part = trimesh.load_mesh("debugAssets/problematic.stl")
slicedMesh = trimesh.intersections.slice_mesh_plane(part, plane_normal=normal, plane_origin=origin, cap=True)
trimesh.repair.fill_holes(slicedMesh)
trimesh.repair.fix_winding(slicedMesh)
trimesh.repair.fix_inversion(slicedMesh)
trimesh.repair.fix_normals(slicedMesh)
slicedMesh =  slicedMesh.process()

print("slicedMesh watertight: ", slicedMesh.is_watertight)

Here is also a photo of how the plane and the mesh look image image

problematic.zip

marios-stam avatar Mar 10 '24 11:03 marios-stam

Hey, not totally sure why it's not watertight... I was able to get it to be watertight with triangle (free for non-commercial use, pip install triangle) but not the permissibly licensed mapbox-earcut (i.e. the default).

Might be an on-vertex hit or some case that's not handled well? PR's welcome if you figure it out! With triangle and process(validate=True) fixed it which means it removed a degenerate or otherwise bad face.

import trimesh

if __name__ == "__main__":
    s = trimesh.load("~/Downloads/problematic.zip")
    assert len(s.geometry) == 1

    origin = [-0.03, 0.05, 0.10]
    normal = [-0.25, 0.43, 0.87]

    part = next(iter(s.geometry.values()))

    viz = trimesh.Scene([part,
                         trimesh.path.creation.grid(
                             side=part.bounds.max(),
                             plane_normal=normal,
                             plane_origin=origin)])

    st = part.slice_plane(
        plane_origin=origin,
        plane_normal=normal,
        cap=True,
        engine="triangle",
        triangle_args="pY",
    ).process(validate=True)

    se = part.slice_plane(
        plane_origin=origin,
        plane_normal=normal,
        cap=True,
    ).process(validate=True)

mikedh avatar Mar 11 '24 01:03 mikedh

Hello

yes, that seemed to do the work! Thank you for the quick response and in case I find the original source of the issue I will let you know with a PR!

marios-stam avatar Mar 21 '24 08:03 marios-stam

Following the previous, after lots of testing, the "triangle" engine seems much more robust but there are still issues. For example, the following code does not provide watertight meshes.

meshFile = "partBefore.stl"
m = trimesh.load_mesh(meshFile)
p = [0.0, 0.0, 0.0]
n = [-0.71 , 0.00 ,0.71]

nNeg = [0.71 , 0.00 ,-0.71]

posMesh =  intersections.slice_mesh_plane(m, n, p, cap=True,engine="triangle").process(validate=True)
negMesh =  intersections.slice_mesh_plane(m, nNeg, p, cap=True,engine="triangle").process(validate=True)

print("posMesh.is_watertight", posMesh.is_watertight)
print("negMesh.is_watertight", negMesh.is_watertight)
sc = trimesh.Scene()
sc.add_geometry(posMesh)
sc.add_geometry(negMesh)
sc.show()
 

image

partBefore.zip

marios-stam avatar Mar 28 '24 14:03 marios-stam