(guide-compilers-slicer-project)= # Slicer Project Compiler ```{include} ../_guide-sidebar-compiler-membership.md ``` 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 - OpenVCAD with **`pyvcad`** and **`pyvcad_compilers`** installed. - Familiarity with **scalar attributes** on geometry (see {ref}`guide-getting-started`, especially **Lesson 4: Attributes**, and the [Functional Grading Guide](../gradients.md)). ## 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](material-inkjet.md) and [Color Inkjet](color-inkjet.md) guides. (opening-your-project-in-prusaslicer)= ## 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_DENSITY`** → **`fill_density`**, **`FUZZY_SKIN_POINT_DISTANCE`** → **`fuzzy_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 extruders** → **write** **`.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](#opening-your-project-in-prusaslicer). ### (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.py`** → **`output/fuzzy_skin_cylinder.3mf`**. ```python """ 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.py`** → **`output/infill_density_bar.3mf`**. ```python """ 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.py`** → **`output/temperature_compensation_demo.3mf`**, using printer/filament **`.ini`** files from **`examples/applications/foaming_filaments/profiles/`**. ```python """ 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 (`01` … `03`). - **`examples/applications/slicer_settings_mesh/`** — additional **settings-mesh** demos. - **`examples/applications/foaming_filaments/compensation_demo/`** — **temperature** / **flow** compensation patterns and profile assets.