question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Split mesh using a splitting plane

See original GitHub issue

I want to split a mesh using a plane, similar to cross_section but returning both halves of the mesh as closed meshes instead of the polyline boundary of the intersection surface. I wrote the following code to achieve this:

def split_mesh(mesh, plane_origin, plane_normal):
    """
    Splits mesh by intersection with a plane, 
    returns closed mesh for positive and negative side of plane 
    """
    plane_normal = unit_vector(plane_normal)
    
    polyline, triangles_edge = trimesh.intersections.mesh_plane(bbox_mesh, 
                                                           plane_origin= plane_origin, 
                                                           plane_normal= plane_normal, 
                                                           return_faces=True)
    
    vertices_negative = []
    vertices_positive = []
    for vertex_id, vertex in enumerate(bbox_mesh.vertices):
        # TODO: add threshold
        signed_distance = np.dot(plane_normal, np.subtract(vertex, plane_origin))
        if signed_distance < 0:
            vertices_negative.append(vertex_id)
        else:
            vertices_positive.append(vertex_id)
    
    triangles_negative = []
    triangles_positive = []
    # For each triangle check how many points on either side of the plane
    for triangle in bbox_mesh.faces:
        triangle_negative_check = [vertex_id in vertices_negative for vertex_id in triangle]
        # If all points on the negative side this triangle is not affected
        if all(triangle_negative_check):
            triangles_negative.append(np.array(bbox_mesh.vertices[triangle]))
        elif any(triangle_negative_check):
            # Get section line for this triangle on edge
            section = polyline[np.all(np.isin(bbox_mesh.faces[triangles_edge], np.array(triangle)), axis=1)][0]
            assert section.shape == (2,3)
            # If two points on negative side
            if sum(triangle_negative_check) == 2:
                vertex_1, vertex_2 = bbox_mesh.vertices[triangle[triangle_negative_check]]
                triangles_negative.extend(unordered_to_triangles([section[0], section[1], vertex_1, vertex_2]))
                vertex_3 = bbox_mesh.vertices[triangle[np.logical_not(triangle_negative_check)]]
                triangles_positive.append(np.vstack([section[0], section[1], vertex_3]))
            elif sum(triangle_negative_check) == 1:
                vertex_3 = bbox_mesh.vertices[triangle[triangle_negative_check]]
                triangles_negative.append(np.vstack([section[0], section[1], vertex_3]))
                vertex_1, vertex_2 = bbox_mesh.vertices[triangle[np.logical_not(triangle_negative_check)]]
                triangles_positive.extend(unordered_to_triangles([section[0], section[1], vertex_1, vertex_2]))
        # If all points on the positive side this triangle is not affected
        else:
            triangles_positive.append(np.array(bbox_mesh.vertices[triangle]))
            
    # Get triangulation of the split surface
    vertices_splitsurface = []
    for a, b in polyline:
        for vertex in [a,b]:
            if tuple(vertex) not in vertices_splitsurface:
                vertices_splitsurface.append(tuple(vertex))

    triangles_splitsurface = unordered_to_triangles(vertices_splitsurface)
    
    # Add triangles of split surface to both the positive and negative side
    triangles_negative.extend(triangles_splitsurface)
    triangles_positive.extend(triangles_splitsurface)
    
    # Create Trimesh from triangles for positive and negative
    mesh_negative = trimesh.Trimesh(*to_face_indices(triangles_negative))
    mesh_positive = trimesh.Trimesh(*to_face_indices(triangles_positive))
    
    return mesh_positive, mesh_negative

Would it be useful to improve and create a pull request, or is there a much more simple way to achieve this with trimesh?

Looking forward to your reply!

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Reactions:1
  • Comments:17 (9 by maintainers)

github_iconTop GitHub Comments

1reaction
mikedhcommented, Oct 26, 2018

BTW the test script to generate those images:

In [1]: ws = 'POLYGON Z ((12 3203 77.16970170640745, 12 3268.200439631
   ...: 641 76.69979984979925, 12 9204 33.92028410374769, 29.596979605
   ...: 48745 9204 34.05965652518653, 4502 9204 69.48219224307377, 450
   ...: 2 9145.442381733686 69.90421905297444, 4502 3203 112.731609845
   ...: 7339, 4427.295926242265 3203 112.1399351444852, 12 3203 77.169
   ...: 70170640745))'

In [2]: from shapely.wkt import loads

In [3]: poly = loads(ws)

In [5]: import trimesh

In [6]: v,f =trimesh.creation.triangulate_polygon(poly)

In [7]: v.shape
Out[7]: (94, 2)

In [8]: f.shape
Out[8]: (133, 3)

In [10]: import matplotlib.pyplot as plt

In [11]: for i in v[trimesh.geometry.faces_to_edges(f)]:
    ...:     plt.plot(*i.T, color='k')
    ...:     

In [12]: trimesh.path.polygons.plot_polygon(poly)

1reaction
mikedhcommented, Oct 26, 2018

Oh, you probably don’t have to implement linestrings_to_polygon yourself, if you just do trimesh.load_path(sections) it will give you a Path3D object (or if you want to call the specific function, it’s trimesh.path.io.misc.lines_to_path). If you transform the sections onto the plane either manually or through path.to_planar, It will then have path.polygons_full constructed for you.

As for the result of triangulate_polygon, that looks correct- the vertices are 2D because it’s coming from a planar polygon, you just need to np.column_stack it with zeros to use it in a 3D mesh.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Split a mesh with a plane - Blender Stack Exchange
Add a plane · Grab and move the plane to where I want the cut an existing mesh · Select the plane, then...
Read more >
Split Mesh with Plane
The Split Mesh with Plane command enables you to cut triangular meshes with a plane that can be defined in an interactive way....
Read more >
Mesh Splitting and Booleans (Add, Intersect, Subtract)
The Split Mesh tool will divide a mesh into two portions based on a cutting plane. Split Mesh is an ideal tool for...
Read more >
Mesh Split Plane - Parametric House
In this grasshopper example file you can create a stellated mesh and split it with a parametric plane.
Read more >
MeshSplit | Rhino 3-D modeling
If a curve is selected as splitter, it is "extruded" in the z-direction of the current CPlane to intersect and split the mesh....
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found