Scalar Field Slicing¶
This example demonstrates non-planar slicing using a custom scalar field - enabling you to print along arbitrary surfaces rather than horizontal planes.
What You'll Learn¶
- Creating a scalar field from geometric distance
- Assigning scalar values to mesh vertices
- Slicing with
ScalarFieldSlicer - Printing on top of existing geometry
Why Scalar Field Slicing?¶
Traditional planar slicing creates horizontal layers. But what if you want to:
- Print on a curved base: Add features on top of an existing surface
- Follow terrain: Print conformal layers over a landscape
- Custom layer patterns: Define exactly where each layer goes
Scalar field slicing generates isocurves (contours) from any per-vertex scalar field, not just Z-height.
Planar slicing: Scalar field slicing:
_____ _____
|_____| Z=3 / \ u=3
|_____| Z=2 / \ u=2
|_____| Z=1 ( ) u=1
|_____| Z=0 \_______/ u=0 (base surface)
The Pipeline¶
flowchart TD
A[Load Mesh to Slice] --> B[Load Base Surface]
B --> C[Compute Distance Field]
C --> D[Assign to Vertices]
D --> E[ScalarFieldSlicer]
E --> F[Extract Isocurves]
F --> G[Create PrintPoints]
Step-by-Step Walkthrough¶
1. Load Meshes¶
We need two meshes:
- Mesh to slice: The geometry you want to print
- Base surface: The surface to print on top of
from pathlib import Path
from compas.datastructures import Mesh
mesh = Mesh.from_obj(DATA_PATH / 'geom_to_slice.obj') # Geometry to slice
base = Mesh.from_obj(DATA_PATH / 'custom_base.obj') # Base surface
2. Compute Distance Field¶
For each vertex of the mesh, compute its distance to the base surface:
from compas.geometry import distance_point_point
import compas_slicer.utilities as utils
# Get all vertex coordinates
pts = [mesh.vertex_coordinates(v_key, axes='xyz') for v_key in mesh.vertices()]
# Project points onto base surface
_, projected_pts = utils.pull_pts_to_mesh_faces(base, pts)
# Compute distance from each point to its projection
u = [distance_point_point(pt, proj_pt) for pt, proj_pt in zip(pts, projected_pts)]
This creates a scalar field u where:
u = 0at points on the base surfaceu > 0at points above the base- The value increases with distance from the base
3. Assign Scalar Field to Mesh¶
Store the scalar values as vertex attributes:
mesh.update_default_vertex_attributes({'scalar_field': 0.0})
for i, (v_key, data) in enumerate(mesh.vertices(data=True)):
data['scalar_field'] = u[i]
4. Slice with Scalar Field¶
from compas_slicer.slicers import ScalarFieldSlicer
slicer = ScalarFieldSlicer(mesh, u, no_of_isocurves=50)
slicer.slice_model()
The slicer:
- Sorts vertices by scalar value
- Determines the range
[u_min, u_max] - Creates
no_of_isocurvesevenly spaced threshold values - Extracts the zero-crossing contour for each threshold
Parameters:
| Parameter | Description |
|---|---|
mesh |
The mesh to slice |
u |
List of scalar values (one per vertex) |
no_of_isocurves |
Number of contours to extract |
5. Simplify and Create PrintPoints¶
from compas_slicer.post_processing import simplify_paths_rdp
from compas_slicer.print_organization import ScalarFieldPrintOrganizer
simplify_paths_rdp(slicer, threshold=0.3)
print_organizer = ScalarFieldPrintOrganizer(slicer, DATA_PATH=DATA_PATH)
print_organizer.create_printpoints()
6. Export Results¶
printpoints_data = print_organizer.output_printpoints_dict()
utils.save_to_json(printpoints_data, OUTPUT_PATH, 'out_printpoints.json')
How It Works¶
Isocurve Extraction¶
An isocurve is a contour where the scalar field equals a specific value. For a threshold \(t\):
The algorithm finds edges where the scalar field crosses the threshold:
The crossing point is found by linear interpolation:
Choosing Number of Isocurves¶
The number of isocurves determines layer height:
More isocurves = thinner layers = smoother surface but longer print time.
Custom Scalar Fields¶
You can use any scalar field, not just distance:
Height-Based Field¶
This is equivalent to planar slicing.
Radial Field¶
from compas.geometry import Point, distance_point_point
center = Point(0, 0, 0)
u = [distance_point_point(mesh.vertex_coordinates(v), center)
for v in mesh.vertices()]
Creates concentric circular layers (spiral vase mode).
Geodesic Field¶
# Using igl for geodesic distance from boundary vertices
import igl
distances = igl.exact_geodesic(V, F, boundary_vertices)
Creates layers that follow surface curvature.
Complete Code¶
Running the Example¶
With visualization:
Output Files¶
| File | Description |
|---|---|
distance_field.json |
Scalar values for visualization |
isocontours.json |
Slicer output data |
out_printpoints.json |
Final printpoints |
Use Cases¶
Printing on Curved Surfaces¶
Print text or patterns on top of a curved object:
- Load the base object
- Create the feature geometry offset above the surface
- Use distance-to-surface as scalar field
- Layers conform to the base curvature
Conformal Printing¶
Print a thin shell that follows terrain:
- Load terrain mesh as base
- Create offset shell above terrain
- Scalar field = distance from terrain
- Each layer follows the terrain contours
Variable Layer Height¶
Combine with other slicing methods:
- Planar slice the main body
- Scalar field slice the curved top
- Merge the toolpaths
Key Takeaways¶
- Scalar field defines layers: Any per-vertex value can drive slicing
- Distance field is common: Distance from base surface creates conformal layers
- Isocurves are contours: Zero-crossings at each threshold value
- Number of curves = resolution: More isocurves = finer layers
Next Steps¶
- Attribute Transfer - Transfer mesh properties to toolpaths
- Slicing Algorithms - Deep dive into theory
- Curved Slicing - Geodesic interpolation approach