8.1 KiB
U1 Local Z Dithering Design Draft
Status: Draft for later implementation Last updated: 2026-02-10 Owner: Snapmaker Orca engineering
1. Problem Statement
Current dithering support can alternate component filaments in Z, but layer height is still resolved globally per object layer. This means:
dithering_z_step_sizecan only change full layers (or full Z bands), not only painted XY zones.- The requested behavior (
---===) is not achievable today:- non-painted area keeps base height (example:
0.12) - painted mixed area is subdivided (example:
0.06 + 0.06) in the same nominal Z interval
- non-painted area keeps base height (example:
2. Current Architecture Constraints
Key code paths:
- Layer heights are created before segmentation:
src/libslic3r/PrintObjectSlice.cpp(update_layer_height_profile,generate_object_layers)
- Mixed painting segmentation is applied after layers already exist:
src/libslic3r/PrintObjectSlice.cpp(apply_mm_segmentation)src/libslic3r/MultiMaterialSegmentation.cpp
Layerhas oneheight,slice_z,print_zfor the entire layer:src/libslic3r/Layer.hpp
Conclusion: existing pipeline assumes one global Z step per layer. Local per-XY sublayering needs a new planning model.
3. Goals
- Support local Z subdivision for mixed-painted zones only.
- Keep base regions at user base layer height whenever possible.
- Preserve current mixed filament alternation logic (A/B cadence).
- Keep existing behavior when local mode is disabled.
- Avoid regressions in non-mixed prints.
4. Non-Goals (Phase 1)
- Full non-planar slicing.
- Arbitrary local adaptive mesh refinement outside mixed-painted zones.
- Rewriting all perimeter/infill algorithms from scratch.
5. Proposed Feature Model
Add a new mode on top of current dithering:
dithering_local_z_mode(bool, defaultfalse)- Existing
dithering_z_step_sizeremains the micro step for painted zones. - Existing
dithering_step_painted_zones_onlyremains as compatibility switch for current global mode.
When dithering_local_z_mode = true:
- For each base Z interval
[z0, z1]:- if no mixed paint intersects interval: print normally at base layer height.
- if mixed paint intersects interval: split interval into sublayers at
dithering_z_step_size. - mixed-painted XY polygons are printed on each sublayer with alternating components.
- non-mixed XY polygons are printed as a base-height pass in that interval (not duplicated every sublayer).
6. High-Level Architecture
Introduce a two-level planning pipeline:
- Base Layer Plan (existing):
- Build base object layers as today.
- Local Z Expansion Plan (new):
- For base layers that intersect mixed-painted areas, build
SubLayerPlanentries.
- For base layers that intersect mixed-painted areas, build
- Toolpath Assignment Plan (new):
- Route painted polygons to sublayers.
- Route non-painted polygons to one base-height pass within same interval.
- G-code Scheduler (extended):
- Emit sublayer passes in Z order while respecting extrusion height per pass.
7. New Data Structures (Draft)
Add new planning structs (names tentative):
struct LocalZInterval {
double z_lo;
double z_hi;
double base_height; // e.g. 0.12
double sublayer_height; // e.g. 0.06
bool has_mixed_paint;
};
struct SubLayerPlan {
double z_lo;
double z_hi;
double print_z;
double flow_height;
// per extruder painted masks for this sublayer
std::vector<ExPolygons> painted_masks_by_extruder;
// polygons printed as normal/non-mixed in this pass
ExPolygons base_masks;
};
Attach std::vector<SubLayerPlan> to PrintObject (or a dedicated planner cache) without immediately replacing Layer.
8. Algorithm Draft
8.1 Build Local Z Intervals
- Start from base layer intervals from
generate_object_layers. - For each base interval, query whether mixed-painted states are present in that Z range.
- If not present, interval remains single-pass.
- If present, split into
N = ceil(base_height / z_step)subintervals.
8.2 Build Painted Masks Per SubLayer
- Re-run or adapt
multi_material_segmentation_by_paintingto produce painted masks at sublayer Z samples (not only base layer Z samples). - For each sublayer:
- derive active mixed pair (A/B) based on cadence index.
- assign painted XY polygons to active physical extruder for that sublayer.
8.3 Preserve Base Regions at Base Height
- For non-painted polygons inside a locally-split base interval:
- emit once with
flow_height = base_heightin a designated pass (typically final sublayer pass of interval). - do not emit these polygons on intermediate sublayers.
- emit once with
8.4 Boundary Handling
- At boundaries between painted and non-painted masks:
- enforce overlap/tolerance compensation to avoid cracks.
- clip with robust polygon booleans and min-area filtering.
- Add seam strategy notes for transitions (TBD in implementation).
9. Required Code Areas
Primary:
src/libslic3r/PrintObjectSlice.cpp(new local-Z planning stage, call ordering)src/libslic3r/MultiMaterialSegmentation.cpp(sublayer mask generation)src/libslic3r/PrintObject.cpp(cache invalidation + storage for local-Z plans)src/libslic3r/GCode/*(scheduler/tool ordering to emit sublayer passes correctly)
Likely:
src/libslic3r/Layer*(if promoting local-z plan into first-class layer abstraction)src/libslic3r/PrintApply.cpp(config propagation and reset handling)src/slic3r/GUI/Tab.cppandsrc/libslic3r/PrintConfig.*(new option + tooltips)
10. Compatibility and Migration
- Keep existing
dithering_z_step_sizebehavior whendithering_local_z_mode=false. - Hide/disable local-Z checkbox unless mixed virtual filament is enabled.
- Project files without new key must load as current behavior.
- Fallback: if local-Z planner fails, auto-fallback to current global mode with warning.
11. Validation Plan
Unit/logic tests
- Interval splitting:
- no mixed paint -> no split
- mixed paint -> expected number of sublayers
- Cadence:
- A/B alternation across sublayers matches configured ratio/step
- Config changes:
- step size changes between slices must invalidate planner cache
Integration tests
- Painted stripe through object:
- verify non-painted region keeps base pass count
- verify painted region gets subdivided passes
- Multi-object plate with only one mixed object.
- Support + mixed paint interaction.
G-code assertions
- In affected intervals:
- expected
Zmove cadence in mixed zones - base region extrusion appears once per base interval
- expected
- In unaffected intervals:
- unchanged base layer Z cadence
12. Risks
- High complexity around perimeter/fill continuity at mask boundaries.
- Performance impact from finer slicing and extra polygon clipping.
- Increased memory use for per-sublayer painted masks.
- Potential regressions in wipe tower/tool ordering and support synchronization.
13. Rollout Plan
Phase A - Planner skeleton (no G-code changes yet):
- Build and debug
LocalZIntervalandSubLayerPlan. - Add debug export (SVG/JSON) for visual verification.
Phase B - Limited emission path:
- Enable local-Z only for perimeter walls on painted regions.
- Keep infill/base regions on current behavior for early validation.
Phase C - Full emission:
- Perimeters + infill + top/bottom in local-Z path.
- Boundary compensation and seam cleanup.
Phase D - Stabilization:
- Performance tuning, cache policy, presets/profile updates.
- Expand regression suite and add fixture models.
14. Open Questions
- Should non-painted regions in split intervals be printed on first or last sublayer pass?
- Do we allow base-height extrusion in an interval where painted sublayers already deposited material nearby, or enforce sublayer-only flow there?
- How should top/bottom skin logic behave when only part of a layer is sublayered?
- Should local-Z mode require a minimum nozzle/step ratio gate for print safety?
15. Recommended Next Step
Prototype only Phase A:
- Build
LocalZInterval/SubLayerPlanand dump debug artifacts. - No toolpath emission changes yet.
- Use this to validate that painted masks and interval splitting are stable before committing to full pipeline rewrite.