trimesh
trimesh copied to clipboard
Add angled sidewall features for extrude_triangulation
This PR allows to extrude Polygons with some sidewall angle (other than 90°). For 90° (default) behaves exactly as before, for any other angle it calculates the shifted vertices in the extruded face and uses the previous logic as much as possible.
It is mostly motivated by integrating into GDSFactory for rendering waveguides with angled sidewalls.
Awesome thanks for the PR! A few quick notes:
- Can we change the parameter from
sidewall_angle: float->taper: float | None?- Angle should be in radians rather than degrees
- Making it None-able avoids the
== 90.0check which probably would have to be changed tonp.isclose(taper_angle, np.pi / 2.0), butif taper is Noneis preferable.
- it looks like interiors aren't handled? I was seeing
Failed with index -4592643848549385702 is out of bounds for axis 0 with size 112in the quick test below. We probably have to handle those. - How does this deal with self-intersections? Not handling these is fine, we should just probably note it in the docstring. If we did want to support that nicely the easiest way to handle them would probably be to do this as
shapely.Polygon.bufferoperations as then they handle the "did an interior disappear at a particular inflation level" problem and this function would have to match the inflated edges with the original edges.
Quick test:
import numpy as np
from shapely.geometry import Point
import trimesh
if __name__ == "__main__":
polygons = [
trimesh.load_path("models/2D/wrench.dxf").polygons_full[0],
Point(10.0, 10.0).buffer(5.0),
]
for polygon in polygons:
# (2,) axis aligned bounding box size
extents = np.ptp(np.reshape(polygon.bounds, (2, 2)), axis=0)
# try at different heights relative to the polygon scale
for height in np.linspace(extents.min() / 10.0, extents.max() * 10.0, 10):
# try at different angles
for angle in np.linspace(50.0, 140.0, 20):
print(f"attempting: {height}, {angle}")
try:
extrusion = trimesh.creation.extrude_polygon(
polygon, height=height, sidewall_angle=angle
)
except BaseException as E:
print(f"Failed with {E}")
I think this is a bit more complicated to handle for general inputs, particularly if the taper results in topology changes, e.g:
- taper is positive and there are interiors that may be closed
- taper is positive and exterior has locally non-convex sections that close
- taper is positive and exterior has locally convex sections that are properly inflated to a radius around the vertex
- negative taper
I was discussing this with a friend and we thought that for positive taper "union of cones at each vertex and wedges on every edge for every interior and exterior" gave you the correct answer, although it's kinda slow. A quick prototype gave pretty good results on a "circle with a concentric interior." For negative taper it might be just original.difference(wedges) although I wouldn't swear to it haha. It would also be nice if there was some way of doing this more cheaply!
I'm going to close this as I think we need to handle and test topology changes before merging but open to the feature!