Curved Slicing (Interpolation)¶
This example demonstrates non-planar slicing using geodesic interpolation between boundary curves - the signature technique from the ETH research.
What You'll Learn¶
- Defining boundary curves for interpolation
- Computing geodesic distance fields
- Finding critical points (minima, maxima, saddles)
- Non-planar slicing with
InterpolationSlicer - Variable velocity based on layer height
- Smoothing tool orientations for robotic fabrication
Why Curved Slicing?¶
Traditional planar slicing creates horizontal layers. For curved surfaces like shells and domes, this causes:
- Poor surface finish on overhangs
- Weak layer adhesion on steep angles
- Visible stair-stepping
Curved slicing generates toolpaths that follow the surface curvature:
Planar slicing: Curved slicing:
___________ _________
|___________| / \
|___________| / \
|___________| ( )
|___________| \ /
|___________| \_________/
The Pipeline¶
flowchart TD
A[Load Mesh] --> B[Define Boundaries]
B --> C[Compute Gradient Field]
C --> D[Find Critical Points]
D --> E[Interpolation Slicing]
E --> F[Simplify & Smooth]
F --> G[Create PrintPoints]
G --> H[Smooth Orientations]
H --> I[Variable Velocity]
Step-by-Step Walkthrough¶
1. Load Mesh and Boundaries¶
from pathlib import Path
from compas.datastructures import Mesh
import compas_slicer.utilities as utils
from compas_slicer.pre_processing import create_mesh_boundary_attributes
# Load mesh
mesh = Mesh.from_obj(DATA_PATH / 'mesh.obj')
# Load boundary vertex indices (pre-defined in JSON)
low_boundary_vs = utils.load_from_json(DATA_PATH, 'boundaryLOW.json')
high_boundary_vs = utils.load_from_json(DATA_PATH, 'boundaryHIGH.json')
# Mark boundaries on mesh
create_mesh_boundary_attributes(mesh, low_boundary_vs, high_boundary_vs)
What are boundaries?
Boundaries are the start and end curves for interpolation. For a dome:
- LOW boundary: Base ring (where printing starts)
- HIGH boundary: Top ring (where printing ends)
The slicer generates contours that smoothly transition between these boundaries.
2. Configure Interpolation¶
from compas_slicer.config import InterpolationConfig
avg_layer_height = 2.0
config = InterpolationConfig(
avg_layer_height=avg_layer_height,
# Other options available
)
3. Create Preprocessor and Gradient Field¶
from compas_slicer.pre_processing import InterpolationSlicingPreprocessor
preprocessor = InterpolationSlicingPreprocessor(mesh, config, DATA_PATH)
# Create compound targets from boundaries
preprocessor.create_compound_targets()
# Compute gradient field between boundaries
g_eval = preprocessor.create_gradient_evaluation(
norm_filename='gradient_norm.json',
g_filename='gradient.json',
target_1=preprocessor.target_LOW,
target_2=preprocessor.target_HIGH
)
The gradient evaluation computes:
- Geodesic distances from each vertex to both boundaries
- Gradient field showing direction of steepest descent
- Gradient magnitude for identifying critical points
4. Find Critical Points¶
preprocessor.find_critical_points(
g_eval,
output_filenames=['minima.json', 'maxima.json', 'saddles.json']
)
Critical points are where the gradient is zero:
| Type | Description | Example |
|---|---|---|
| Minima | Local low points | Bottom of a valley |
| Maxima | Local high points | Top of a bump |
| Saddles | Neither min nor max | Mountain pass |
These are important for handling branching geometry (like Y-shaped structures).
5. Slice with Interpolation¶
from compas_slicer.slicers import InterpolationSlicer
slicer = InterpolationSlicer(mesh, preprocessor, config)
slicer.slice_model()
The interpolation slicer:
- Creates a distance field that blends from LOW (0) to HIGH (1)
- Extracts isocurves at regular intervals
- Each isocurve becomes one layer (non-planar)
6. Simplify and Smooth¶
from compas_slicer.post_processing import simplify_paths_rdp, seams_smooth
simplify_paths_rdp(slicer, threshold=0.25)
seams_smooth(slicer, smooth_distance=3)
7. Create PrintPoints¶
from compas_slicer.print_organization import InterpolationPrintOrganizer
print_organizer = InterpolationPrintOrganizer(slicer, config, DATA_PATH)
print_organizer.create_printpoints()
For curved slicing, printpoints include:
- Variable layer heights (thicker on steep areas)
- Surface normals for tool orientation
- Up-vectors for robotic end-effector
8. Smooth Tool Orientations¶
from compas_slicer.print_organization import (
smooth_printpoints_up_vectors,
smooth_printpoints_layer_heights,
)
# Smooth up-vectors to avoid jerky robot motion
smooth_printpoints_up_vectors(print_organizer, strength=0.5, iterations=10)
# Smooth layer heights for consistent extrusion
smooth_printpoints_layer_heights(print_organizer, strength=0.5, iterations=5)
Why smooth orientations?
Raw orientations can change abruptly between points, causing:
- Jerky robot motion
- Vibrations in the print
- Potential collisions
Smoothing creates gradual transitions while maintaining overall direction.
9. Variable Velocity by Layer Height¶
from compas_slicer.print_organization import set_linear_velocity_by_range
set_linear_velocity_by_range(
print_organizer,
param_func=lambda ppt: ppt.layer_height,
parameter_range=[avg_layer_height * 0.5, avg_layer_height * 2.0],
velocity_range=[150, 70], # Fast for thin, slow for thick
bound_remapping=False
)
This sets velocity inversely proportional to layer height:
| Layer Height | Velocity | Reason |
|---|---|---|
| 1.0 mm (thin) | 150 mm/s | Less material, can go fast |
| 4.0 mm (thick) | 70 mm/s | More material, need time to deposit |
10. Safety and Export¶
from compas_slicer.print_organization import set_extruder_toggle, add_safety_printpoints
set_extruder_toggle(print_organizer, slicer)
add_safety_printpoints(print_organizer, z_hop=10.0)
# Export
printpoints_data = print_organizer.output_printpoints_dict()
utils.save_to_json(printpoints_data, OUTPUT_PATH, 'out_printpoints.json')
Complete Code¶
Running the Example¶
With visualization:
Input Data¶
The example uses a Y-shaped branching structure:
| File | Description |
|---|---|
mesh.obj |
Triangulated mesh |
boundaryLOW.json |
Vertex indices of lower boundary |
boundaryHIGH.json |
Vertex indices of upper boundary |
Output Files¶
| File | Description |
|---|---|
gradient_norm.json |
Gradient magnitude field |
gradient.json |
Gradient vector field |
minima.json |
Local minimum vertices |
maxima.json |
Local maximum vertices |
saddles.json |
Saddle point vertices |
curved_slicer.json |
Slicer output data |
out_printpoints.json |
Final printpoints |
Mathematical Background¶
The interpolation is based on geodesic distance fields:
For each vertex \(v\):
- \(d_{low}(v)\) = geodesic distance to LOW boundary
- \(d_{high}(v)\) = geodesic distance to HIGH boundary
The interpolated field at parameter \(t \in [0, 1]\):
The zero-level set of \(f_t\) gives one contour. Varying \(t\) generates all contours.
Key Takeaways¶
- Boundaries define the slicing direction: Choose them carefully
- Mesh quality matters: Use clean, regular triangulation
- Critical points handle branching: Essential for Y-shapes, trees
- Smooth everything: Orientations, heights, velocities
- Variable velocity: Thick layers need slower speeds
References¶
This technique is based on research from ETH Zurich:
- Mitropoulou, I., et al. "Nonplanar 3D Printing of Bifurcating Forms" (3D Printing and Additive Manufacturing, 2022)
- Mitropoulou, I., et al. "Print Paths Key-framing" (SCF 2020)
Next Steps¶
- Scalar Field Slicing - Custom slicing patterns
- Slicing Algorithms - Theory deep dive
- Attribute Transfer - Custom per-point data