Toolpath Strategies¶
A strategy answers one question: given this geometry, what sequence of positions should the tool visit?
All strategies inherit from ToolpathStrategy and return a ToolpathCollection.
FollowCurveStrategy¶
Generates a toolpath that follows a predefined curve point-by-point. The simplest and most fundamental strategy.
from toolpath_engine import FollowCurveStrategy, Curve
helix = Curve.helix(center=(0, 0, 0), radius=40, pitch=5, turns=4)
paths = FollowCurveStrategy().generate(
curve=helix,
feed_rate=600, # mm/min
spacing=1.0, # resample curve to this point spacing (mm); 0 = use as-is
path_type="deposit", # stored as metadata on each point
)
Use this for:
- Weld paths, deposition paths, laser scan lines
- Any process where you've already defined the exact path geometry
RasterFillStrategy¶
Generates parallel raster passes across a surface — like the infill pattern in a 3D printer slicer, or a lawn-mowing pattern.
from toolpath_engine import RasterFillStrategy, Surface
top_plate = Surface.plane(origin=(0, 0, 80), normal=(0, 0, 1), size=60)
paths = RasterFillStrategy().generate(
surface=top_plate,
spacing=3.0, # distance between parallel passes (mm)
feed_rate=500,
step_size=0.5, # point spacing along each pass (mm)
path_type="deposit",
)
Use this for:
- Area coverage: coating, cladding, surface finishing
- Layer-by-layer additive deposition on flat surfaces
ContourParallelStrategy¶
Generates offset contour passes inward (or outward) from a boundary — like the perimeter shells in a 3D printer.
from toolpath_engine import ContourParallelStrategy, Curve
boundary = Curve.circle(center=(0, 0, 80), radius=30, num_points=64)
paths = ContourParallelStrategy().generate(
boundary=boundary,
stepover=3.0, # offset distance between passes (mm)
num_passes=4, # number of inward passes
feed_rate=700,
path_type="deposit",
)
Use this for:
- Building up walls around a boundary
- Island filling from the outside in
- Spiral-in patterns for circular features
How it works¶
Each pass is generated by _offset_curve_2d(), which offsets the boundary curve in the XY plane using a simple inward normal calculation:
- For each point on the boundary, compute the inward-facing normal (perpendicular to the curve tangent, rotated 90° in XY)
- Translate the point by
stepover * pass_indexalong that normal - Resample the offset curve to the requested point spacing
The Z coordinate of all points is taken from the input boundary — the strategy does not vary Z between passes.
Parameters¶
| Parameter | Type | Default | Description |
|---|---|---|---|
boundary |
Curve |
required | The outer (or inner) boundary to offset from |
stepover |
float | 3.0 | Distance between each offset pass (mm) |
num_passes |
int | 4 | Number of offset passes to generate |
feed_rate |
float | 500 | Feed rate for all points (mm/min) |
step_size |
float | 0.5 | Point spacing along each pass (mm) |
path_type |
str | "deposit" |
Metadata tag stored on each point |
Current limitations¶
- 2D offset only —
_offset_curve_2doperates in XY. It does not follow 3D surface curvature. For curved surfaces, the offset passes will drift from the surface normal as the curve bends in Z. - No hole support — offset passes can self-intersect for highly concave boundaries. No clipping or pruning of self-intersections is currently implemented.
- Fixed Z — all passes share the Z of the input boundary. Use
FollowCurveStrategywith a pre-computed 3D path if you need Z variation between passes.
Setting process parameters on a collection¶
After generating paths, attach process parameters to all points at once:
paths.set_param("wire_feed", 3.0)
paths.set_param("laser_power", 2000)
paths.set_param("shielding_gas_flow", 15.0)
These are stored in point.process_params and mapped to G-code output letters via PostConfig.param_codes.
Writing a custom strategy¶
Subclass ToolpathStrategy and implement generate():
from toolpath_engine.strategies.base import ToolpathStrategy
from toolpath_engine.core.toolpath import Toolpath, ToolpathPoint, ToolpathCollection
from toolpath_engine.core.primitives import Position, Orientation
class SpiralStrategy(ToolpathStrategy):
def generate(self, radius, pitch, turns, feed_rate=500, **kwargs):
import math
points = []
n = int(turns * 72)
for i in range(n):
t = i / 72 # turns elapsed
angle = 2 * math.pi * t
x = radius * math.cos(angle)
y = radius * math.sin(angle)
z = pitch * t
points.append(ToolpathPoint(
position=Position(x, y, z),
orientation=Orientation(0, 0, -1),
feed_rate=feed_rate,
))
tp = Toolpath(points=points)
return ToolpathCollection(toolpaths=[tp])