Files
OrcaSlicer/doc/MixedFilament.md
SoftFever 9c8caf121e init work to integrate OrcaSlicer-FullSpectrum fork
Integrations up to commit b3c41fda4180a2946812726218d0a849be0aedb6.

  - libslic3r: vendor FilamentMixer; MixedFilamentManager (auto-gen, resolve,
    serialize; manual-pattern / gradient / pointillism); 19 new PrintConfig
    keys; PresetBundle owns the canonical manager with 3MF + AppConfig
    roundtrip and AMS-safe strip+restore; Print owns the slicing-time copy
    with PrintApply auto-regen on color change; TriangleSelector::
    shift_states_above + filament-id remap; inset_idx propagation through
    ExtrusionPath/Loop/MultiPath copy/assign.
  - Slicing: virtual filament IDs in painted regions (same-physical channels
    collapse when mixed_filament_region_collapse is on); ByObject
    collect_filament_data expands mixed slots; pair-cadence + whole-object +
    3+component Local-Z plan generators; LocalZOrderOptimizer utility.
  - GCode + ToolOrdering: LayerTools resolves virtual IDs through wall /
    infill / sparse / solid queries; SameLayerPointillisme in process_layer
    (uniform-segment + grouped per-perimeter-index splitters); WipeTower2
    Local-Z reservation + sub-layer G-code emission; per-layer infill
    filament override.
  - PartPlate: get_extruders* expand virtual slots into physical components;
    CLI path rebuilds a local manager from full_config.
  - GUI: five widget files extracted from FS Plater.cpp (~5000 LOC) —
    MixedMixPreview, MixedGradientSelector + WeightsDialog,
    MixedFilamentColorMapPanel, MixedFilamentColorMatchDialog (ΔE₀₀ recipe
    search), MixedFilamentConfigPanel; Sidebar Mixed Filaments panel
    (drag-reorder, enable/delete, Add Gradient/Pattern/Color); Tab exposure
    of mixed-filament / dithering / per-layer infill-override settings +
    ConfigManipulation visibility and slot-validation rules;
    BBLMixedFilamentBroken / BBLSingleExtruderMixedFilamentRisk
    notifications + slice gate; WipeTowerDialog edits physical P×P
    sub-matrix; bounds-safe extruder_id guards in 3DScene / GLCanvas3D /
    GLGizmoMmuSegmentation; change_filament merge guard and
    on_filaments_delete is_mixed_before_delete propagation.
  - Tests: 4 Catch2 tests for 3MF roundtrip (auto/custom persistence,
    PresetBundle string path, total_filaments stability); full-pipeline
    slice E2E deferred — TODO in file.

  Co-authored-by: Rad <radugheorghiu96@gmail.com>
  Co-authored-by: Justin Hayes <justinh@rahb.ca>
  Co-authored-by: Calogero Guagenti <calogeroguagenti@gmail.com>
  Co-authored-by: xSil3nt <ahmedshazin21@gmail.com>
  Co-authored-by: ratdoux <62392831+ratdoux@users.noreply.github.com>

Co-authored-by: Rad <radugheorghiu96@gmail.com>
Co-authored-by: Justin Hayes <justinh@rahb.ca>
Co-authored-by: Calogero Guagenti <calogeroguagenti@gmail.com>
Co-authored-by: xSil3nt <ahmedshazin21@gmail.com>
Co-authored-by: ratdoux <62392831+ratdoux@users.noreply.github.com>
2026-05-02 12:32:38 +08:00

7.7 KiB

Mixed Filament

Ported from OrcaSlicer-FullSpectrum with contributions from Rad, Justin Hayes, Calogero Guagenti, xSil3nt, and ratdoux.


User Guide

What It Does

Mixed Filament lets a single virtual filament slot alternate between two physical filaments across layers (or within a layer in Pointillisme mode), producing blended or gradient-like colours on single-extruder printers.

Enabling

  1. Load a multi-colour (multi-extruder) profile with at least 2 filaments.
  2. The Mixed Filaments panel appears automatically in the right-hand sidebar when 2 or more filaments are configured.
  3. Each auto-generated row represents one pair of physical filaments. Toggle a row to enable it; the total filament count grows to include the virtual slot.

Sidebar Workflow

  • Add — creates a custom row for the same pair or a different ratio.
  • Edit — opens MixedFilamentConfigPanel to adjust ratio, pattern, surface offset (bias), and distribution mode.
  • Delete — marks the row deleted; existing painted geometry retains its virtual filament ID until you re-slice or repaint.

Painting with a virtual filament ID behaves the same as painting with a physical one — use the Multi-Material Painting gizmo and select the virtual slot from the colour palette.

Color Match Dialog

Open via the colour swatch on a mixed row. The dialog (MixedFilamentColorMatchDialog) shows a live preview strip of the blended result and lets you adjust ratio until the preview matches your target colour. The preview accounts for surface-offset bias when enabled.

Anti-Banding Options

Setting What it does
mixed_filament_advanced_dithering Uses an ordered dither pattern instead of simple A-then-B runs. Reduces stripe visibility on some hue pairs. More experimental than the default.
dithering_local_z_mode Splits each blended layer into two sub-layers whose heights are proportional to the mix ratio (e.g. 66/33 at 0.12 mm → 0.08 mm + 0.04 mm). Produces the smoothest colour gradients.
dithering_local_z_whole_objects Extends Local-Z splitting beyond painted masks to cover the entire object cross-section. Useful when mixed walls surround a painted zone.
dithering_local_z_direct_multicolor For rows with 3 or more physical components, allocates Local-Z sub-layers directly across all components with carry-over error correction instead of collapsing to pair cadence. More toolchanges; less banding.

Gotchas

  • Single-extruder warning — Mixed Filament requires a physical toolchange between the two components. On a true single-nozzle printer this means a manual filament swap. Verify your printer profile supports T0/T1 before using mixed slots in a production print.
  • Variable-layer interaction — If Variable Layer Height is enabled, Local-Z sub-layer heights are recomputed per interval. The mix ratio is preserved but the absolute sub-layer heights change with the variable height. Review the layer preview after applying variable layers.
  • Custom sequence disabled — OrcaSlicer's "custom toolchange sequence" is suppressed when mixed filaments are active (PlateSettingsDialog). The virtual-to-physical resolution must control toolchange order; a user-defined sequence would break it.
  • Stable IDs — each mixed row carries a stable_id (64-bit). If you reorder or delete rows and then load an older project, the ID remap in PresetBundle::update_mixed_filament_id_remap translates painted geometry to the correct new virtual slot. Do not rely on the 1-based filament index as a stable identifier.

Developer Guide

Core Data Structures

src/libslic3r/MixedFilament.hpp   — MixedFilament struct, MixedFilamentManager
src/libslic3r/MixedFilament.cpp   — serialization, resolve(), auto_generate()
src/libslic3r/LocalZOrderOptimizer.hpp — bucket-ordering helpers for Local-Z

The key scalar fields on MixedFilament:

  • component_a, component_b — 1-based physical filament indices.
  • ratio_a, ratio_b — layer-alternation cadence numerators.
  • mix_b_percent — nominal colour mix (used for Local-Z height computation and the Color Match preview; does not change the cadence).
  • stable_id — monotonically increasing 64-bit ID assigned at construction. Never reused. Survives serialization round-trips.
  • distribution_mode — selects between Simple, SameLayerPointillisme, and GroupedManual.

Seam: Adding New Distribution Modes

MixedFilamentManager::resolve() in MixedFilament.cpp is the single dispatch point that maps (virtual_filament_id, num_physical, layer_index) to a physical extruder. The current switch covers Simple and SameLayerPointillisme. A new mode is added by:

  1. Adding a value to the MixedFilament::DistributionMode enum in MixedFilament.hpp.
  2. Adding a case to MixedFilamentManager::resolve() in MixedFilament.cpp.
  3. Serializing the new mode token in serialize_custom_entries / load_custom_entries (format is a semicolon-delimited row string; see existing tokens for the convention).

G-code emission (src/libslic3r/GCode/) reads only the physical ID returned by resolve(), so new modes are automatically emitted without further changes.

Seam: New Toolchange-Cost Heuristics

LocalZOrderOptimizer (src/libslic3r/LocalZOrderOptimizer.hpp) exposes:

  • order_bucket_extruders(bucket, current, preferred_last) — reorders a single-layer bucket to minimise toolchanges given the current active extruder.
  • order_pass_group(group, current_extruder) — greedy walk across a set of buckets (one per Local-Z sub-layer) to minimise total transitions.

To add a new heuristic (e.g. cost-based look-ahead), replace or wrap order_pass_group. The caller in PrintObjectSlice.cpp passes the result directly into the sub-layer plan, so the heuristic is fully decoupled from the plan builder.

Seam: New Picker Shapes in the Color Map Panel

MixedFilamentColorMapPanel (src/slic3r/GUI/MixedFilamentColorMapPanel.hpp) renders a 2-D colour map using a set of geometry "types" (currently strip and gradient). Each type is a small self-contained rendering path keyed by an enum value. New shapes are added by:

  1. Adding an enum value to MixedFilamentColorMapPanel::GeometryType.
  2. Implementing the corresponding Paint* helper (follow PaintStrip as a template).
  3. Wiring the new type into the switch in OnPaint.

Persistence (3MF)

The entire mixed-filament state is stored as a single string key mixed_filament_definitions in the project config block (section [presets] in the 3MF metadata).

Round-trip path:

MixedFilamentManager::serialize_custom_entries()
  called by PresetBundle::sync_mixed_filaments_to_config()
  written by bbs_3mf: store_bbs_3mf → config.set("presets", "mixed_filament_definitions", ...)

load_bbs_3mf → config.get("presets", "mixed_filament_definitions")
  stored in project_config["mixed_filament_definitions"]
  read by PresetBundle::sync_mixed_filaments_from_config()
    → mixed_filaments.auto_generate(colours)
    → mixed_filaments.load_custom_entries(defs, colours)

Auto-generated rows are not written to the definitions string; they are rebuilt from the filament colour list. Only custom == true rows are stored.

See tests/fff_print/test_mixed_filament_e2e.cpp for regression tests covering this path.

ID Remap

When filaments are added, removed, or reordered, virtual IDs shift. PresetBundle::update_mixed_filament_id_remap(old_mixed, old_count, new_count) produces a remap vector where remap[old_virtual_id] = new_virtual_id. Painted triangle mesh face data uses these IDs; the remap is applied in TriangleSelectorMixed after any filament list change.