Slicer Project Compiler#

The SlicerProjectCompiler turns OpenVCAD designs that carry scalar slicer attributes (for example INFILL_DENSITY, FUZZY_SKIN_POINT_DISTANCE, or TEMPERATURE) into a PrusaSlicer-style 3MF project (.3mf). You import that file into PrusaSlicer (or another slicer that understands the same project layout), slice, and print on filament / FFF hardware.

This page explains how the compiler works (by method, not line-by-line code), walks through examples, and documents the Python API. Renders show the OpenVCAD fields; PrusaSlicer screenshots show the sliced out.

Prerequisites#

What this compiler is for#

Use Slicer Project when you want spatially varying slicer settings derived from continuous fields on your model, exported as a single project file for PrusaSlicer, instead of voxel PNG stacks for inkjet workflows. For slice images, see the Material Inkjet and Color Inkjet guides.

Opening your project in PrusaSlicer#

The compiler only writes the .3mf. It does not launch a slicer.

  1. Run your Python script (or build the compiler in code) so compile() finishes and the .3mf path exists.

  2. Open PrusaSlicer, use File → Import, and select the generated .3mf (or drag it into the window).

  3. Inspect sub-volumes and per-volume settings in the Plater / Models UI, then Slice now and review the G-code preview (layer view, color by feature, etc.).

Printer and filament choices inside PrusaSlicer are up to you; the .3mf carries model geometry and embedded project/config produced by the compiler.

How it works#

At a high level, compile() does the following:

  1. Discover attributes on your tree and classify each supported name using an internal registry. There are two families:

    • Settings mesh: attributes that map to PrusaSlicer per-volume metadata (for example INFILL_DENSITYfill_density, FUZZY_SKIN_POINT_DISTANCEfuzzy_skin_point_dist). The compiler does not need separate .ini profiles for this mode.

    • Virtual extrusion: attributes that must be driven through G-code (today this path is built around TEMPERATURE with a FLOW_RATE companion). This mode requires printer_profile_path and filament_profile_path pointing to PrusaSlicer .ini files so the compiler can inject toolchange-style G-code derived from templates. Unsupported attribute names are printed to the console and ignored.

  2. Sample the prepared tree on a voxel grid over the bounding box using your voxel_size. For each voxel inside the solid, the compiler reads double-valued attributes it cares about.

  3. Estimate ranges for each segmented attribute over the solid, then split each range into num_regions sub-ranges (default 10). Each sub-range becomes a region of the solid where attribute values are similar.

  4. Build geometry for those regions (segmented meshes), attach Prusa-compatible metadata (settings mesh) and/or extruder / tool assignments (virtual extrusion), and package everything into a .3mf with the expected config attachments.

  5. Progress and cancellation follow the usual CompilerBase behavior.

The important idea: continuous fields in OpenVCAD become piecewise-constant regions in the slicer project—each region carries one representative setting (derived from the sub-range), not a unique value per voxel inside Prusa’s mesh-metadata model.

Pipeline overview#

Classify supported attributes → sample on a grid → segment by attribute ranges → emit meshes + metadata and/or virtual extruderswrite .3mf.

Reference: Python API#

Constructor#

SlicerProjectCompiler(root, voxel_size, output_file_path, num_regions=10, printer_profile_path="", filament_profile_path="")

Argument

Meaning

root

Root Node.

voxel_size

Vec3 sampling spacing (mm); finer values better resolve thin regions at higher cost.

output_file_path

Full path to the output .3mf file.

num_regions

Number of sub-ranges per segmented attribute (default 10).

printer_profile_path

PrusaSlicer printer .ini; required when virtual extrusion attributes (e.g. TEMPERATURE) are present.

filament_profile_path

PrusaSlicer filament .ini; required with virtual extrusion.

Methods (including CompilerBase)#

Method

Role

compile()

Build the .3mf.

supported_attributes()

Discovery list: every attribute name the registry knows (settings mesh + virtual extrusion + companions). The tree does not need all of them; only present attributes participate.

cancel()

Request cooperative cancellation.

set_progress_callback(fn)

fn(progress) with progress in [0, 1].

For autodoc signatures, see pyvcad_compilers (SlicerProjectCompiler, CompilerBase).

Examples and figures#

The scripts below live under examples/compilers/slicer_project/ and write .3mf files under output/ next to each script. Run them from the repository with your venv, then import the .3mf into PrusaSlicer as described above.

(a) Fuzzy skin gradient on a cylinder#

Design (FUZZY_SKIN_POINT_DISTANCE ramp along Z): matches the idea of examples/applications/slicer_settings_mesh/fuzzy_skin_cylinder.py. Script: 01_fuzzy_skin_cylinder.pyoutput/fuzzy_skin_cylinder.3mf.

"""
Slicer project - fuzzy skin gradient (cylinder)
==============================================

Upright cylinder with **FUZZY_SKIN_POINT_DISTANCE** ramping along Z, compiled
to a PrusaSlicer-style **.3mf** for per-volume fuzzy skin settings.

Companion to: docs/source/guides/compilers/slicer-project.md
See also: examples/applications/slicer_settings_mesh/fuzzy_skin_cylinder.py
"""
import os

import pyvcad as pv
import pyvcad_compilers as pvc
import pyvcad_rendering as viz

cylinder_height_mm = 100.0
cylinder_radius_mm = 15.0
edge_buffer_mm = 10.0

half_h = 0.5 * cylinder_height_mm
span = 2.0 * (half_h - edge_buffer_mm)
fuzzy_expr = (
    f"0.4 * clamp((z + {half_h} - {edge_buffer_mm}) / ({span}), 0, 1)"
)

cylinder = pv.Cylinder(pv.Vec3(0, 0, 0), cylinder_radius_mm, cylinder_height_mm)
cylinder.set_attribute(
    pv.DefaultAttributes.FUZZY_SKIN_POINT_DISTANCE,
    pv.FloatAttribute(fuzzy_expr),
)

root = cylinder
viz.Render(root)

_here = os.path.dirname(os.path.abspath(__file__))
out_dir = os.path.join(_here, "output")
os.makedirs(out_dir, exist_ok=True)
out_3mf = os.path.join(out_dir, "fuzzy_skin_cylinder.3mf")

regions = 12
compiler = pvc.SlicerProjectCompiler(
    root,
    pv.Vec3(0.25, 0.25, 0.25),
    out_3mf,
    regions,
)
compiler.compile()
print("Wrote", out_3mf)

OpenVCAD preview (field used for segmentation):

OpenVCAD (fuzzy skin distance)

OpenVCAD render of fuzzy skin point distance on a cylinder

PrusaSlicer:

Slicer Preview (left: model preview | right: g-code fuzzy skin texture preview)

PrusaSlicer G-code preview showing fuzzy skin

Models / sub-volumes (fuzzy skin overrides)

PrusaSlicer models list with per-volume fuzzy skin

(b) Infill density gradient on a bar#

Design (INFILL_DENSITY ramp along X): same idea as examples/applications/slicer_settings_mesh/infill_density_bar.py. Script: 02_infill_density_bar.pyoutput/infill_density_bar.3mf.

"""
Slicer project - infill density gradient (bar)
============================================

Rectangular bar with **INFILL_DENSITY** ramping along X, compiled to a **.3mf**
with per-volume **fill_density** metadata for PrusaSlicer.

Companion to: docs/source/guides/compilers/slicer-project.md
See also: examples/applications/slicer_settings_mesh/infill_density_bar.py
"""
import os

import pyvcad as pv
import pyvcad_compilers as pvc
import pyvcad_rendering as viz

bar_size_x = 100.0
bar_size_y = 25.0
bar_size_z = 25.0
edge_buffer_mm = 10.0

half_x = 0.5 * bar_size_x
span = 2.0 * (half_x - edge_buffer_mm)
infill_expr = (
    f"5 + 75 * clamp((x + {half_x} - {edge_buffer_mm}) / ({span}), 0, 1)"
)

bar = pv.RectPrism(pv.Vec3(0, 0, 0), pv.Vec3(bar_size_x, bar_size_y, bar_size_z))
bar.set_attribute(pv.DefaultAttributes.INFILL_DENSITY, pv.FloatAttribute(infill_expr))

root = bar
viz.Render(root)

_here = os.path.dirname(os.path.abspath(__file__))
out_dir = os.path.join(_here, "output")
os.makedirs(out_dir, exist_ok=True)
out_3mf = os.path.join(out_dir, "infill_density_bar.3mf")

regions = 12
compiler = pvc.SlicerProjectCompiler(
    root,
    pv.Vec3(0.25, 0.25, 0.25),
    out_3mf,
    regions,
)
compiler.compile()
print("Wrote", out_3mf)

OpenVCAD preview:

OpenVCAD (infill density)

OpenVCAD render of infill density gradient on a bar

PrusaSlicer G-code preview (cutaway to show internal infill):

G-code preview (infill gradient, cutaway)

PrusaSlicer G-code preview with infill density gradient

(c) Temperature + flow (virtual extrusion)#

Similar in spirit to examples/applications/foaming_filaments/compensation_demo/compensation_pla_demo.py: TEMPERATURE field plus FLOW_RATE from a lookup table via AttributeModifier. Script: 03_temperature_compensation_demo.pyoutput/temperature_compensation_demo.3mf, using printer/filament .ini files from examples/applications/foaming_filaments/profiles/.

"""
Slicer project - temperature + flow (virtual extrusion)
=======================================================

**TEMPERATURE** field with **FLOW_RATE** from a lookup (**AttributeModifier**),
similar to the foaming PLA compensation demo. Requires printer and filament
**.ini** profiles for virtual-extrusion G-code injection.

Companion to: docs/source/guides/compilers/slicer-project.md
See also: examples/applications/foaming_filaments/compensation_demo/compensation_pla_demo.py
"""
import os
import sys

import pyvcad as pv
import pyvcad_compilers as pvc
import pyvcad_rendering as viz

_here = os.path.dirname(os.path.abspath(__file__))
_profiles = os.path.normpath(os.path.join(_here, "..", "..", "applications", "foaming_filaments", "profiles"))
sys.path.insert(0, _profiles)
from flow_compensation_data import foaming_pla_flow_compensation

temperature_attr = pv.FloatAttribute("max(min((x/3+236),256),216)")

rect_prism = pv.RectPrism(pv.Vec3(0, 0, 0), pv.Vec3(200, 10, 10))
rect_prism.set_attribute(pv.DefaultAttributes.TEMPERATURE, temperature_attr)

flow_entries = [pv.LookupTableEntry(kv[0], kv[0], kv[1]) for kv in foaming_pla_flow_compensation]
mod_func = pv.LookupTableConverter(
    [pv.DefaultAttributes.TEMPERATURE],
    [pv.DefaultAttributes.FLOW_RATE],
    flow_entries,
    pv.InterpolationMode.LINEAR,
)
mod = pv.AttributeModifier(mod_func, rect_prism)

root = mod
viz.Render(root)

out_dir = os.path.join(_here, "output")
os.makedirs(out_dir, exist_ok=True)
out_3mf = os.path.join(out_dir, "temperature_compensation_demo.3mf")

printer_profile_path = os.path.join(_profiles, "prusa_xl_multitool.ini")
filament_profile_path = os.path.join(_profiles, "ColorFabb_LW_PLA.ini")
regions = 10

compiler = pvc.SlicerProjectCompiler(
    root,
    pv.Vec3(0.25, 0.25, 0.25),
    out_3mf,
    regions,
    printer_profile_path,
    filament_profile_path,
)
compiler.compile()
print("Wrote", out_3mf)

OpenVCAD preview (temperature):

OpenVCAD (temperature)

OpenVCAD render of temperature field on a bar

PrusaSlicer G-code preview (color or tooltip by temperature):

G-code preview (temperature)

PrusaSlicer G-code preview colored by temperature

Limitations#

  • Piecewise regions: each segment gets one representative slicer value, not a unique value per voxel in the slicer metadata.

  • Unsupported attributes on the tree are skipped (with a console message); if no supported attributes are found, compile() fails.

  • Virtual extrusion depends on bundled G-code templates and .ini inputs that must match your workflow.

  • Slicer compatibility is aimed at PrusaSlicer-style 3MF projects; other slicers may require export or conversion.

Further examples#

  • examples/compilers/slicer_project/ — scripts paired with this guide (0103).

  • examples/applications/slicer_settings_mesh/ — additional settings-mesh demos.

  • examples/applications/foaming_filaments/compensation_demo/temperature / flow compensation patterns and profile assets.