Orca 2.5 per extruder layer height - #802
Open
LixNix wants to merge 40 commits into
Open
Conversation
Printing with different nozzle sizes is now possible and each extruder can now print at a different semi fixed / semi adaptive layer height depending on user choice.
Includes:
- Fix Filament for Features (OrcaSlicer#13607)
- Fix + Support 'Default' filament option, index 0 (OrcaSlicer#13887)
- Filament for features for SEMM (OrcaSlicer#13937)
- Divided filament for features (OrcaSlicer#14042)
- Port of LixNix/OrcaSlicer-multi-nozzle-size-printing commit 944da13
Port of the mainline OrcaSlicer feature chain to this fork (based on Orca ~2.3.4): - Fix Filament for Features (OrcaSlicer#13607) - Fix + Support 'Default' filament option, index 0 (OrcaSlicer#13887) - Filament for features for SEMM (OrcaSlicer#13937) - Divided filament for features (OrcaSlicer#14042) The coarse per-feature filament selectors are split into six independent ones: outer_wall_filament_id, inner_wall_filament_id, sparse_infill_filament_id, internal_solid_filament_id, top_surface_filament_id and bottom_surface_filament_id. All default to 0 ("Default" = use the active object/part filament) instead of the old 1-based scheme where "1" doubled as the default. Backward compatibility: PrintConfigDef::handle_legacy() renames the old keys (wall_filament, sparse_infill_filament, solid_infill_filament and their pre-Orca aliases) on any config load - presets, 3mf project files and imported G-code alike - and maps the legacy default value 1 to 0. Fork-specific integrations kept working: the mixed-filament manager's per-layer filament resolution (LayerTools resolve_mixed, pointillism and grouped-manual-pattern splitting in GCode::process_layer, effective filament remapping in LayerRegion/Fill), Local-Z clipped collections, SSWCP/Flutter filament slot collection, and the object settings tab resolver all route the new six selectors through the same paths the three old ones used. Mixed perimeter collections are now split into outer/inner sub-collections when the two wall filaments differ.
Port of LixNix/OrcaSlicer-multi-nozzle-size-printing commit 944da13 "Add per extruder layer height" onto this fork. New printer option extruder_layer_height (per extruder): the preferred layer height of each extruder, for printers whose extruders carry different nozzle sizes. Object parts whose walls are assigned to an extruder preferring an integer multiple N of the object layer height print only on every Nth layer with N-times-thicker extrusions wherever geometry allows (region slices are combined at the slicing step); sparse infill assigned to such an extruder is combined up to that height instead. New print options extruder_layer_height_mode (consistent/adaptive) and extruder_layer_height_tolerance control the run combining, and support_nozzle_diameter restricts support, raft and interface printing to filaments of one nozzle diameter. Includes: per-tool line widths on the wipe tower (WipeTower2), tool ordering that skips combined-away layers and prefers nozzle-matching filaments for flushing, per-feature-filament flow resolution (bottom surfaces / external bridges / gap fill print with their own filament's nozzle width), support flow and layer height limits honoring the support nozzle restriction, and extensive Print::validate() checks. Adaptations to this fork: no BBL filament-map rerouting (a filament index is the extruder index on classic multi-tool printers), the single-StringObjectException warning channel of this fork's Print::validate(), mixed-filament (Local-Z / pointillism / grouped patterns) integration kept working - resolved effective filaments feed the new flow lookups and the wipe tower's Local-Z planning gained the same per-tool widths. All new behavior is inert while the new options stay at their defaults (0). Tests: tests/fff_print/test_multi_nozzle_layer_height.cpp (ported to Catch2 v2 and this fork's APIs).
…ment logic Follow-up fixes from an adversarial review of the two ports: - GCode::process_layer's fork-local dispatch lambdas (configured_filament_id_1based / configured_extruder_id) now mirror ToolOrdering's classification: the role of a fill collection is derived from its first non-gap-fill entity, pure gap fill dispatches to the outer wall filament, and external bridges print with the bottom surface filament. Without this, gap fill and external bridges were grouped under a different extruder than ToolOrdering scheduled and than their flow widths were computed for (inert at default settings, wrong once the per-feature filaments diverge). - combine_infill() and its Print::validate() mirror pick the combining filament at 100% sparse density from the sparse infill filament, matching this fork's print-time dispatch (internal_solid_infill_uses_sparse_filament), instead of mainline's internal solid filament. - Per-extruder layer height combining ignores Snapmaker mixed (virtual) filament ids: mixed rows resolve to a different physical extruder per layer, so regions whose walls use them never combine and no preferred height is derived from an out-of-range id. - ToolOrdering::collect_extruders expands grouped manual-pattern mixed filaments for the inner wall exactly like the outer wall, so the wipe tower planner sees every extruder the grouped perimeter split will emit when the two wall filaments differ.
…d per-nozzle sidebar
Make the per-extruder preferred layer heights ("extruder_layer_height")
actually take effect on mixed-nozzle printers and give them a first-class
UI, so each feature of a part prints with the layer height of the
extruder assigned to it wherever physics allows.
Engine (libslic3r):
- A part follows one layer pitch, defined by its wall filaments'
explicit preferences, or by the agreement of its other pitch-bound
feature filaments (top/bottom/internal solid) when no wall filament
carries one. Runs of N base layers combine and extrude once at N
times the object layer height; region tops/bottoms, overhangs and the
first layer keep the finer base layers.
- When the rest of the part vetoes the walls' pitch, the walls still
combine on their own (walls-only pitch); disagreeing explicit wall
preferences meet at the lower height instead of falling back.
- Full-density top surfaces absorb the internal solid layers below them
and extrude once at their filament's preferred height.
- Sparse infill combines separately as before; at 100% density the
combined solid interior is internal solid infill printed with the
internal solid filament (PrintRegion::extruder()).
- Only a nozzle that physically cannot extrude a pitch vetoes it;
min/max layer heights are soft profile limits that warn instead.
- Mixed (virtual) filaments rotate physical extruders per layer and
never combine.
- Tool ordering and G-code dispatch classify wall extrusions per
entity, so overhang-first inner loops, split mixed perimeters and
wipe overrides route to the correct filament.
Validation (Print::validate()):
- Strict errors for wall preferences the printer cannot honor
(below the object layer height, not an integer multiple, above the
nozzle bore) and for line widths below the combined heights.
- Warnings when preferences are ignored, unhonorable, below an
extruder's minimum or above its maximum, when disagreeing wall
preferences meet at the lower height, and when a fine nozzle forces a
part back to the object layer height.
- Per-extruder layer heights are rejected with spiral vase, interface
shells and variable layer height.
GUI:
- The Prepare sidebar shows one tab per nozzle with a Diameter combo
(sets only that nozzle's diameter instead of switching the whole
printer preset) and a Preferred layer height combo listing the valid
multiples of the object layer height for that extruder.
- Changing a nozzle's diameter adopts the layer height limits of the
printer's profile for the new size when one exists and notifies the
user; otherwise it warns to review the limits manually. The tabs
follow the extruder count and refresh on config changes.
- The preview attributes extrusions to the per-feature filaments.
Tests: fff_print [MultiNozzleLayerHeight] scenarios cover region
combining, walls-only pitch, top-surface absorption, bore vetoes,
disagreeing wall preferences and validation errors.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EniRUwSktuaiWwd11GH7FP
Let the outer and inner walls print at their own layer heights when
they are assigned to filaments with different preferred layer heights
("Filament for Features" wall selectors + per-extruder layer heights).
Splitting is automatic whenever both wall filaments carry explicit
conforming preferred heights and one is a whole multiple of the other
(e.g. inner walls 0.24 mm, outer wall 0.48 mm over a 0.12 mm object
layer height): the finer class prints every time its height is reached
and the coarser class extrudes once per multiple at the full height, so
the two classes' tops stay flush. Either wall class may be the coarse
one, and the finer pitch may equal the object layer height itself.
Where geometry prevents a coarse run (shape drift, caps, first layer),
both classes fall back to printing together at the lower height.
When the two preferred heights do NOT divide evenly, the new "Adjust
wall layer height" option (split_wall_adjust, default off, in the
Filament for Features group) adjusts the wall-only layer height of one
wall filament to the nearest divisor or multiple of the other so the
walls can still split; landing on the other filament's height merges
the walls there instead. Two selections appear while the toggle is on:
which wall filament is adjusted (split_wall_adjust_filament) and
whether its height may only decrease or increase
(split_wall_adjust_direction). Unlike the preferred heights themselves,
adjusted heights are hard-bounded by the adjusted filament's min/max
layer heights and nozzle bore; without a legal candidate in the chosen
direction the walls keep merging at the lower height with the existing
conflict warning. The adjustment applies only to that filament's walls,
its other features keep the preferred height, and validation reports
the adjusted height once as an info message.
Implementation. The decision logic lives in one place,
PrintObject::wall_effective_multipliers() - the raw conforming
multipliers of the two wall classes plus the optional adjustment -
consumed by wall_layer_height_multiplier() (min-merge = the fine
cadence), wall_split_pitches() (unequal + divisible = split) and
Print::validate(). The fine cadence is the existing walls-only run
machinery unchanged. A second marking pass in
apply_extruder_layer_heights() groups whole fine runs into coarse runs
(same shape tolerance and rest-on-object tests, applied across the
whole span) and marks them on the LayerRegions
(wall_split_count/height). LayerRegion::make_perimeters() drops the
coarse class from the fine pass inside committed coarse runs -
classified with perimeter_entity_uses_outer_wall_filament(), the same
predicate the G-code dispatch uses, so cadence and filament routing
agree by construction - and on a coarse-run top regenerates the coarse
class once at the full height with the layer below the run as overhang
reference; both generator passes share one parameterized routine. Thin
/ gap fills and the fill boundaries stay with the fine pass. validate()
detects adjusted classes by comparing effective against raw
multipliers, keeps the min-merge conflict warning silent when a split
or adjustment resolved the disagreement, checks per-class heights
against line widths and max layer heights, and does not count an
adjusted class's own preference as unhonored. Mixed (virtual)
filament ids never combine and never split; the engine code stays
textually aligned with the mainline-Orca fork except for the mixed
guards and the direct filament-to-extruder mapping.
Tests: scenarios for the automatic divisible split (including the
fine-pitch-equals-object-layer-height case), the non-divisible fallback,
and the three adjustment outcomes (decrease to a divisor -> split,
increase onto the other wall's height -> merge there, no legal
candidate -> unchanged fallback with the conflict warning).
Add a third "Thick layer regions" choice next to Consistent and Adaptive: Fixed. Regions assigned to a thicker-pitch extruder always print at the extruder layer height: the run walk skips the shape drift tolerance and the rest-on-below overhang fallback, so curved boundaries become steps of the run shape instead of dropping to finer layers. Only geometry that cannot span a whole run keeps finer heights: the first layer, region tops, lateral jumps, and columns whose surviving intersection is too thin to carry a bead of the region's nozzle. The thick layer tolerance field grays out in fixed mode, as it no longer participates. The step faces such combined groups expose are classified correctly: a group prints only its layers' common shape, and the combined-away layers' geometry outside it becomes the group's step. Surface detection compared against Layer::lslices, which carry the uncombined shape, so those step faces counted as covered - no top skin or solid backing formed and sparse infill showed through on stepped slopes, and bridges over such steps went undetected. Each combined-away layer now remembers its unprinted remainder; detection classifies the faces it exposes as top / bottom on the printed layers around it, and a combined group's own bridges check the layer below the whole group. Covered by pyramid scenarios comparing fixed against consistent mode and a stepped-cube regression test for the step top surfaces.
Fixed mode is the intended everyday behavior for per-extruder layer heights, so ship it as the default; the tolerance default rises to 80% so consistent and adaptive combine generously when selected (fixed ignores it). The test suite pins the previous consistent / 10% baseline explicitly, as its scenarios are written against it.
Geometry starting inside a combined group could count as supported and print as sparse infill over air, depending on where the group phase fell. Track the groups' unprinted remainders (run top included) and use them in the unsupported-bottom check.
Refactor the feature code for review: shared filament-to-extruder mapping helpers, deduplicated filters and lower-layer walks, folded duplicate blocks. No behavior change. At a filament boundary a region's per-layer geometry can lose its support to a neighboring region's combined group: the support prints later (at the run top) or never (dropped remainders). Classify bottoms over any region's unprinted remainder as unsupported bridges; drop pieces that would extrude into thin air before the covering pass exists so the column resumes - bridging, anchored - at the pass; where a piece's only anchor is the pass committing at its own layer, fill the object volume with the pass (welded into its shape) and resume fully supported on top of it.
…height Internal solid infill was not covered by any combining pass: whole-region, walls-only and split-wall combining skip it, combine_top_surfaces() only absorbs it under a full-density top, and combine_infill() reaches it only at 100% density. Interior and shell-backing solid always fell back to the object layer height - silently below the assigned extruder's minimum layer height when a coarse nozzle prints it (0.12 mm courses on a 0.8 mm nozzle whose minimum is 0.16 mm). Add a combining pass for the leftover internal solid: groups anchor top-down at the top of each solid column (shell bands sit wherever their surface is, not on a fixed grid) and extrude once at the solid filament's preferred height, leaving VOID surfaces behind like the other passes. Leftover bands re-group over ever shorter runs down to the filament's minimum layer height, so odd-length bands don't strand a below-minimum layer. Only the single-layer seat an internal bridge rests on - tied to its own layer - and the bottom surfaces keep the object layer height, which Print::validate() warns about. Print::validate() mirrors the pass: the internal solid pitch feeds the per-feature height checks (line width, max/min layer height, unhonored preferences) like the top-surface pitch does. Reported-by: jiyang1018 (https://github.com/jiyang1018)
…eight With an explicit preferred layer height, sparse infill combined on a fixed window grid: windows align to the global layer stack, not to where an infill band starts at a shell boundary, so band-edge layers fell out of every window - and out of the min-height re-group pass, which used the same grid - and printed at the object layer height, below the infill extruder's minimum layer height when one is set. Extract the anchored run machinery of combine_internal_solid_infill() into a shared combine_surface_runs() and use it for the preference-driven infill path too. The full-height sparse runs stay anchored on one global grid phase, so a contiguous field extrudes on the same layers everywhere (independently anchored areas would combine phase-shifted wherever their band tops differ, leaving a permanent one-course step along the seam). Band edges re-group over ever shorter runs down to the extruder's min layer height, with one-layer lookaheads at both band ends splitting off pairs instead of stranding a below-minimum single layer. Internal solid keeps free band-top anchors - its short shell bands sit wherever their surface is and would mostly miss a fixed grid. The plain infill_combination option keeps the stock window grid.
Enabling support on a printer whose extruders have differing nozzle sizes opens a dialog asking which nozzle size prints the support and which loaded filament types serve as the raft/base and the interface. The choices land in the support nozzle diameter option and two new print options, "Support/raft base material" and "Support/raft interface material", shown in Support -> Filament for Supports; the legacy base/interface filament selectors keep working behind a "Show legacy filament selection" toggle so older projects stay valid. The material options act as exclusion filters, mirroring the support nozzle diameter restriction: extruders loaded with another material type are not used for the role, a "default" (0) support filament still resolves per layer (a passing filament already printing the layer is preferred, with a deterministic fallback otherwise), and an explicitly selected support filament stays authoritative. Validation rejects a material no loaded filament matches and an explicit selection that conflicts with the material.
Add translations to every shipped language for the UI strings the multi-nozzle work introduced: the per-extruder layer height options and their warnings and validation messages, the split-wall options, the support nozzle diameter restriction, the support material options, the mixed-nozzle support dialog and the legacy filament selection toggle. Translated with AI so there might be incorrect translation.
Show the support/raft base and interface material options for every printer, not only mixed-nozzle ones, so multi-filament setups with a single nozzle size can use them too; they stay visible like the other support rows and merely disable while neither support nor a skirt needs them. The legacy base/interface filament selectors are hidden unless the "Show legacy filament selection" toggle is on. Ticking the toggle takes effect immediately, and opening a 3mf project that assigns either selector - globally, per object, per part or per height range - switches the toggle on, so explicit selections from older projects stay visible and editable. Nothing else changes the toggle. Also fix a crash to desktop after visiting the Support page: the toggle checkbox is cached to keep its state in sync, but the page's controls are destroyed when another page activates, so the cached pointer dangled; it now clears on wxEVT_DESTROY.
Translation for added features and improved support material selection.
Resolves conflicts between the multi-nozzle feature-filament migration (legacy wall/infill filament keys replaced by *_filament_id selectors with 0 = Default) and upstream's temp-mixing detail/flow-ratio-zero plate gating: - Plater.cpp: keep upstream's structure (FilamentTempMixingDetail overload, used_slots_0_based) with this branch's selector-override slot collection; port the same collection logic into upstream's new collect_used_filament_slots_on_plate and check_flow_ratio_zero so cold-plate and flow-ratio checks see the migrated selector keys. - Snapmaker.json: take upstream's newer profile version 02.02.55.02. - zh_CN/zh_TW po: union of both sides' new entries (msgfmt-clean). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Combine the mainline-synchronized Snapmaker Orca base with the per-extruder layer height and multi-nozzle-size printing features: per-tool preferred layer heights, nozzle-aware tool ordering and wipe tower widths, support filament restrictions, and the associated UI and test coverage. AI assistance (Anthropic Claude) was used for porting and conflict resolution.
check_volumes_outside_state() dereferenced its ObjectFilamentResults argument unconditionally, but several callers use the default null argument (e.g. plate validation triggered from the canvas context-menu path). Route null callers to a local results object. Also guard the per-extruder printable-area lookup in the unprintable-error text builder: not every printer profile defines extruder_printable_area.
- Flush-volume auto-calculation: iterate physical filaments only (the flush matrix and minimum flush volumes are sized by the physical count) and clamp all matrix accesses. - Plate filament-to-extruder mapping: bounds-check the filament map and physical extruder map lookups; return -1 for out-of-range ids. - Plate list import: stop when the plate limit is reached instead of indexing with a failed create result. - Printer preset switch: tolerate an unknown target preset or missing extruder-variant option; default to not transferring variants. - Object list: clear the real UI-to-3D volume map entry (the loop-by- value clear was a no-op), and bounds-check remapped volume indices before dereferencing.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Per-extruder layer heights and multi-nozzle-size printing
Adds independent nozzle sizes and per-extruder layer heights for the Snapmaker U1, on top of the 2.5 mainline sync already merged via #801.
Multi-nozzle-size printing
Per-extruder preferred layer heights
Old PR for 2.3.5 just for reference: #580
More infos available in old PR and on Lark in private chat.