Section#

A demonstration of mesh-plane cross-sections, commonly referred to as “slicing” in the context of 3D printing.

import trimesh
import numpy as np
from shapely.geometry import LineString
%pylab inline
%config InlineBackend.figure_format = 'svg'
%pylab is deprecated, use %matplotlib inline and import the required libraries.
Populating the interactive namespace from numpy and matplotlib
# load the mesh from filename
# file objects are also supported
mesh = trimesh.load_mesh('../models/featuretype.STL')
# get a single cross section of the mesh
slice = mesh.section(plane_origin=mesh.centroid,
                     plane_normal=[0,0,1])
# the section will be in the original mesh frame
slice.show()
# we can move the 3D curve to a Path2D object easily
slice_2D, to_3D = slice.to_planar()
slice_2D.show()
_images/section_5_0.svg
# if we wanted to take a bunch of parallel slices, like for a 3D printer
# we can do that easily with the section_multiplane method
# we're going to slice the mesh into evenly spaced chunks along z
# this takes the (2,3) bounding box and slices it into [minz, maxz]
z_extents = mesh.bounds[:,2]
# slice every .125 model units (eg, inches)
z_levels  = np.arange(*z_extents, step=.125)
# find a bunch of parallel cross sections
sections = mesh.section_multiplane(plane_origin=mesh.bounds[0],
                                   plane_normal=[0,0,1],
                                   heights=z_levels)
sections
[<trimesh.Path2D(vertices.shape=(304, 2), len(entities)=9)>,
 <trimesh.Path2D(vertices.shape=(636, 2), len(entities)=9)>,
 <trimesh.Path2D(vertices.shape=(636, 2), len(entities)=9)>,
 <trimesh.Path2D(vertices.shape=(616, 2), len(entities)=10)>,
 <trimesh.Path2D(vertices.shape=(610, 2), len(entities)=10)>,
 <trimesh.Path2D(vertices.shape=(739, 2), len(entities)=10)>,
 <trimesh.Path2D(vertices.shape=(547, 2), len(entities)=9)>,
 <trimesh.Path2D(vertices.shape=(735, 2), len(entities)=10)>,
 <trimesh.Path2D(vertices.shape=(14, 2), len(entities)=1)>,
 <trimesh.Path2D(vertices.shape=(18, 2), len(entities)=1)>,
 <trimesh.Path2D(vertices.shape=(186, 2), len(entities)=4)>]
# summing the array of Path2D objects will put all of the curves
# into one Path2D object, which we can plot easily
combined = np.sum(sections)
combined.show()
/home/user/.local/lib/python3.12/site-packages/shapely/predicates.py:526: RuntimeWarning: invalid value encountered in contains
  return lib.contains(a, b, **kwargs)
_images/section_8_1.svg
# if we want to intersect a line with this 2D polygon, we can use shapely methods
polygon = slice_2D.polygons_full[0]
# intersect line with one of the polygons
hits = polygon.intersection(LineString([[-4,-1], [3,0]]))
# check what class the intersection returned
hits.__class__
shapely.geometry.multilinestring.MultiLineString
# we can plot the intersection (red) and our original geometry(black and green)
ax = plt.gca()
for h in hits.geoms:
    ax.plot(*h.xy, color='r')
slice_2D.show()
_images/section_10_0.svg
# the medial axis is available for closed Path2D objects
(slice_2D + slice_2D.medial_axis()).show()
_images/section_11_0.svg