This page explains how each slicing algorithm works under the hood.
All slicers share a common pattern:
- Input: A triangulated mesh
- Process: Generate cutting surfaces and find intersections
- Output: Layers containing Paths (contours)
The key difference is what cutting surfaces are used:
| Slicer | Cutting Surface | Result |
|---|---|---|
PlanarSlicer |
Horizontal planes at fixed Z heights | Parallel horizontal contours |
InterpolationSlicer |
Isosurfaces of geodesic distance field | Contours that follow mesh curvature |
ScalarFieldSlicer |
Isosurfaces of user-defined scalar field | Custom contour patterns |
The simplest approach - intersect the mesh with horizontal planes:
flowchart TD
A[Mesh] --> B[Compute Z bounds]
B --> C[Generate planes at layer_height intervals]
C --> D[CGAL mesh-plane intersection]
D --> E[Extract polylines from intersection]
E --> F[Create Layer per plane]
- Compute bounds: Find min/max Z coordinates of mesh vertices
- Generate planes: Create horizontal planes from
z_mintoz_maxspaced bylayer_height - Intersect: Use CGAL's
slicer()function for robust mesh-plane intersection - Extract contours: CGAL returns polylines for each connected intersection
from compas_slicer.slicers import PlanarSlicer
slicer = PlanarSlicer(mesh, layer_height=0.4)
slicer.generate_paths()
# Result: slicer.layers contains one Layer per plane
# Each Layer contains one or more Paths (contours)CGAL (Computational Geometry Algorithms Library) provides:
- Robustness: Handles degenerate cases (edges exactly on plane, etc.)
- Speed: Optimized C++ implementation
- Correctness: Proper handling of mesh topology
The intersection is computed via compas_cgal.slicer.slice_mesh().
| Parameter | Type | Description |
|---|---|---|
layer_height |
float | Distance between planes in mm |
slice_height_range |
tuple | Optional (z_start, z_end) to slice only part of mesh |
Instead of horizontal planes, generate contours that interpolate between two boundary curves. This creates non-planar toolpaths that follow the mesh surface.
flowchart TD
A[Mesh + Boundaries] --> B[Compute geodesic distances]
B --> C[Create distance field on vertices]
C --> D[Extract isocurves at regular intervals]
D --> E[Non-planar Layers]
- Define boundaries: Mark two sets of mesh vertices as
target_LOWandtarget_HIGH - Compute distances: Calculate geodesic distance from each vertex to both boundaries
- Interpolate: For each vertex, compute interpolated distance:
d = t * d_low + (1-t) * d_high - Extract isocurves: Find contours where the interpolated field equals zero
The key insight: by varying t from 0 to 1, the zero-isocurve sweeps from one boundary to the other.
from compas_slicer.slicers import InterpolationSlicer
from compas_slicer.pre_processing import InterpolationSlicingPreprocessor
# 1. Define boundaries
preprocessor = InterpolationSlicingPreprocessor(mesh, ...)
preprocessor.create_compound_targets()
# 2. Slice with interpolation
slicer = InterpolationSlicer(mesh, preprocessor)
slicer.generate_paths()For a mesh with vertices
-
$d_{low}(v)$ = geodesic distance from vertex$v$ to lower boundary -
$d_{high}(v)$ = geodesic distance from vertex$v$ to upper boundary
The interpolated field at parameter
The zero-level set
- Domes and shells: Toolpaths follow curvature for better adhesion
- Overhangs: Reduce support by printing along surface
- Aesthetic parts: Visible layer lines follow form
The most general approach - extract isocurves of any scalar field defined on mesh vertices.
flowchart TD
A[Mesh + Scalar Field] --> B[Assign field to vertices]
B --> C[Find edges with sign change]
C --> D[Interpolate crossing points]
D --> E[Connect into contours]
- Define scalar field: Assign one float value per vertex
- Find zero crossings: For each edge, check if field changes sign
- Interpolate position: Find exact crossing point via linear interpolation
- Build contours: Connect crossing points around faces to form polylines
from compas_slicer.slicers import ScalarFieldSlicer
# scalar_field: one value per vertex
slicer = ScalarFieldSlicer(mesh, scalar_field, no_of_isocurves=50)
slicer.generate_paths()For an edge with vertices
-
Test: Edge is crossed if
$f_u \cdot f_v \leq 0$ (different signs) -
Interpolate: Crossing point at parameter
$t = \frac{|f_u|}{|f_u| + |f_v|}$ -
Position:
$p = (1-t) \cdot p_u + t \cdot p_v$
f_u = -2 f_v = +3
u ●─────────────● v
↑
crossing at t = 2/5
To build connected contours:
- Start at any crossed edge
- Find the face containing this edge
- Find the other crossed edge in this face
- Move to adjacent face sharing that edge
- Repeat until returning to start (closed) or reaching boundary (open)
- Custom layer patterns: Any scalar field you can compute
- Distance-based: Contours equidistant from a feature
- Curvature-based: Highlight geometric features
- Stress fields: From FEA analysis
All slicers produce contours via ScalarFieldContours, which uses CGAL's isoline extraction:
flowchart LR
A[Scalar field on vertices] --> B[CGAL isolines]
B --> C[Sorted polylines]
C --> D[Path objects]
The CGAL backend (compas_cgal.isolines) handles:
- Edge crossing detection: Finding zero-crossings on mesh edges
- Polyline assembly: Connecting crossings into coherent curves
- Multiple contours: Holes, disconnected regions, branching
- Open/closed detection: Identifying boundary-hitting paths
- Fast: Single CGAL call handles all planes
- Scales well: O(n) in number of faces
- Memory efficient: Processes planes in batch
- Slower: One contour extraction per isocurve
- Preprocessing cost: Geodesic distance computation
- Mesh quality matters: Irregular tessellation → irregular contours
- Clean mesh: Remove degenerate faces, weld vertices
- Appropriate resolution: More faces ≠ better results
- Layer height: Fewer layers = faster slicing
| Aspect | Planar | Interpolation | Scalar Field |
|---|---|---|---|
| Speed | Fast | Medium | Medium |
| Setup | Simple | Requires boundaries | Requires field |
| Paths | Horizontal only | Follow surface | Arbitrary |
| Use case | Standard FDM | Shells, domes | Custom patterns |
- Architecture - Data structures overview
- Print Organization - Adding fabrication parameters
- Examples - Complete working code