Commit Graph
92 Commits
Author SHA1 Message Date
Clifford Garwood bbcd2e2a80 Merge upstream main: cyclic print ordering, brim overlap fix, perimeter and support fixes 2026-07-31 01:42:48 -04:00
Maksym PyrozhokandIan Bassi ef7bfeda9c Cyclic ordering (#13578)
Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
2026-07-27 19:52:29 -03:00
Kris Austin 306d4b73ef test: stop littering the working directory with debug files (#14785) 2026-07-26 12:53:56 -03:00
Rodrigo Faselli fccd9d52ce Merge branch 'main' into main 2026-07-25 19:19:07 -03:00
Kris Austin 11fdb472d6 fix: hide the extruder switch on single-variant printers (#14930) 2026-07-25 18:46:55 -03:00
SoftFever 9a72dda79a Fix plugin configuration not taking effect, and improve the plugin config UI (#14944)
# Description

Changing a slicing plugin's configuration had no effect on the sliced
result until you forced a re-slice some other way; it now applies
immediately. Print, printer and filament presets also keep their plugin
configuration separately, so configuring a plugin on one no longer wipes
out what you set on another.

A plugin's custom configuration page gets the same round of improvements
in both the Plugins dialog and the per-preset dialog: it follows the
app's light/dark theme, keeps its state while you edit instead of
resetting under the cursor, and can tell whether it is being edited
globally or for a preset, so "Restore defaults" can be labeled for what
it will actually do. The two bundled examples show this off — Twistify
now ships a custom configuration UI, and Inspector is themed, groups

# Screenshots/Recordings/Graphs



https://github.com/user-attachments/assets/02ca062a-5143-49a3-abe0-a2a040b3a928



## Tests

<!--
> Please describe the tests that you have conducted to verify the
changes made in this PR.
-->

<!--
> A guide for users on how to download the artifacts from this PR.
-->

[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
2026-07-25 20:00:56 +08:00
Kris Austin b8cfa32a41 fix: multi-material slicing crash with line width 0 (#14455) 2026-07-22 16:49:37 -03:00
Kris Austin dfb93e0332 fix: PA pattern calibration over-retracts with absolute E distances (#14473) 2026-07-21 12:42:09 -03:00
Clifford Garwood 58b4a68a10 Merge upstream main: Python plugins, Bambu device rework, macOS fixes
Resolved 4 conflicts:
- PrintConfig.hpp / Preset.cpp: upstream restored calib_flowrate_topinfill_special_order
  (removed in the prior merge, now re-added and re-registered); kept it alongside our
  IMEX options (imex_parallel_mode, imex_head_filament_map).
- Plater.cpp: kept our <sstream> include plus upstream's <optional> and plugin includes.
- tests/fff_print/test_gcodewriter.cpp: both sides appended disjoint scenarios after a
  shared base. Reconstructed as upstream's full file (base + its 3 toolchange/H2C
  scenarios + the boost/filesystem include its helper needs) followed by our 10
  set_pressure_advance / set_temperature scenarios. 22 scenarios total, no duplicates.
2026-07-19 18:33:58 -04:00
SoftFever 8ee6095e08 fix ci test errors 2026-07-19 12:07:58 +08:00
SoftFever 8cb0cb7693 Merge branch 'main' into feat/plugin-feature
Resolve five conflicts, all of which needed both sides rather than a pick:

- BackgroundSlicingProcess: ours was a pure tabs->spaces reformat of base, so
  keep main's per-filament volume/nozzle map read-back (its only change here).
- GUI_App: main's #12506 else-if attached to an `if` this branch deleted;
  re-expressed onto the same-agent early-return path (the agent factory caches
  per id, so pointer equality is the same predicate).
- MainFrame: both sides relocated Sync Presets independently; keep main's
  push_notification plus the branch's Plugins menu items.
- Tab: the "TODO: Orca: Support hybrid" blocks were unchanged base, not a branch
  decision; take main's enabled Hybrid to match the already auto-merged siblings.
- test_config: union of both sides' cases (6 plugin + 9 multi-nozzle).
2026-07-15 17:18:46 +08:00
Clifford GarwoodandClaude Opus 4.8 90e8ff7cee fix(imex): align the preview marker grid with the plate zone grid
Follow-up to the cross-gantry mirror axis change, from an adversarial review of it.

GCodeViewer builds its own copy of the zone grid to place the sequential-preview
carriage markers, and it must reproduce PartPlate::calc_imex_zones exactly or the
markers drift away from the ghosts they are meant to track. It did not, in two ways,
because sizing the grid and placing a cell answer different questions:

  - Sizing: calc_imex_zones counts every Copy/Mirror tool's OWN column, including the
    non-representatives of an aggregated (Span) gantry -- they still donate a column.
    GCodeViewer only ever saw the representative, so on an aggregated gantry it could
    count fewer columns than the plate and lay its markers out against wider strips.

  - Placement: calc_imex_zones PINS an aggregated cell to the primary's column, because
    that row-strip spans the full bed and has no column of its own. GCodeViewer used the
    representative's own column, which put the marker a strip away from the ghost
    whenever the representative was not column-paired with the primary.

Track the two sets separately: grid_tool_ids sizes the grid from own columns, eff_col_of
pins only aggregated tools when placing. Out-of-grid tool indices are deliberately left
unfiltered -- calc_imex_zones drops them while calc_imex_ghosts keeps them, so no policy
here can agree with both, and a comment says so rather than pretending otherwise.

Also:
  - The mirror-axis rule lived in three copies (two PartPlate lambdas plus an inline
    re-derivation here). Hoist it to imex_mirror_axis_for() so the ghosts and the markers
    cannot drift apart, and unit-test it, including degenerate tools_per_gantry.
  - Drop imex_head_transform's mirror_axis default. A defaulted axis silently hands a
    forgetful caller the X reflection, which is wrong for every cross-gantry tool and
    fails silently -- exactly how a stale test kept certifying the old rule.
  - Replace that stale test: it asserted a diagonal mirror "flips X only", the rule this
    work overturned, and stayed green only because of the default.
  - A secondary sharing the primary's gantry now takes the primary's Y box facing. The
    box shows the side a tool could be hit from, and two tools on one beam can only be
    hit by the same other gantry. No-op on the rear-* layouts, where the hardcoded value
    already matched; on front-* layouts it pointed the box away from the only tools that
    could reach it.
  - Correct two comments that described the aggregated X-frame substitution as a
    reflection plane. It is not one: it exists to zero gantry_offset.x, and removing it
    would push aggregated ghosts a column off their strip.

Verified: 385/385 tests; CLI slice of the IMEX regression project is byte-identical to
the previous commit's G-code apart from the timestamp, confirming this is
visualization-only and cannot affect sliced output.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 10:01:09 -04:00
Clifford GarwoodandClaude Opus 4.8 0cb1788b23 fix(tests): use ASCII hyphens in IMEX test names so Windows can run them
catch_discover_tests registers each Catch2 test with ctest by name, and ctest then
re-invokes the binary passing that name back as a -# filter. The IMEX test names
contain em dashes (U+2014). They survive discovery, but on Windows the round trip
through the console codepage mangles them, so the filter matches nothing:

    Filters: "imex_head_transform G-- copy mode is pure translation"
    No test cases matched  ->  No tests ran  ->  exit != 0  ->  ctest: Failed

All 85 IMEX tests were reported as failures on both Windows x64 and arm64 without a
single one of them ever executing. Linux and macOS are UTF-8 end to end and were
unaffected, which is why this went unnoticed since the names were introduced in
77c32a2e15.

These were the only non-ASCII test names in the whole tests/ tree. Renaming them to
plain hyphens costs nothing and keeps the suite portable.

Test names only -- no assertion, no logic, no comment is touched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 10:00:50 -04:00
Clifford Garwood f850912123 fix(imex): mirror across the gantry-row axis on two-gantry printers
A Mirror tool reflects across the boundary it shares with the primary's zone, and
which boundary that is depends on where the tool sits:

  - Same gantry: the tools are side by side along X, so the shared boundary is
    vertical and the reflection negates X. This is what single-gantry IDEX does,
    and it was the only case the code modelled.
  - Different gantry: the zones are stacked along Y (front strip vs rear strip),
    so the shared boundary is horizontal and the reflection negates Y. The part
    that comes off gantry 1 is a Y-reflection of the tool directly behind it.

imex_head_transform() hardcoded diag(-1, 1, 1) for every mirror, as its own TODO
acknowledged. Lift the axis to a caller-supplied ImexMirrorAxis; PartPlate picks it
from the tool's gantry row. Both reflections keep det = -1, so a mirrored part stays
a true mirror image rather than a 180-degree rotation, which would print the
primary's part merely turned around.

The correct axis removes two workarounds. Both ghost paths special-cased aggregated
mirrors to "drop the X reflection, translate 1:1 and bake the flip into the mesh"
because reflecting X pushed the ghost off-bed as the primary was dragged. With a Y
reflection the X translation is already zero for aggregated tools, so that falls out
for free and the special cases are deleted.

Preview markers follow the same rule, which also fixes two placement bugs:

  - Mirrors reflected across a Copy tool's zone edge, falling back to the primary's
    column when a row had no Copy. In iq-mirror (0:P,1:C,2:M,3:M) the front row has
    no Copy, so t2 and t3 both fell back and computed the identical X — both drawn
    on top of each other in t3's zone. A mirror now reflects within its own zone.
  - The toolhead footprint box flipped to the far side of the nozzle for any mirror
    right of the primary. That only holds for an X-axis mirror, which reverses the
    carriage's orientation; a cross-gantry mirror keeps the X orientation of the
    tool behind it, so its box stays on the same side.

Tests cover the cross-gantry and diagonal cases, that the axis is caller-supplied
rather than inferred from the offset vector, and that both axes are reflections
(det = -1) rather than rotations.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 00:57:31 -04:00
Clifford Garwood fdb0e20f36 Merge upstream main (Bambu H2C/A2L multi-nozzle support) into IMEX branch
Conflicts were all co-located additions rather than design collisions:
- GCode.cpp: adopt upstream's toolchange(filament_id, nozzle_id) signature and
  per-variant set_config_index() while keeping the IMEX bare-T<n> suppression;
  rebase the second-layer temperature loop's non-IMEX branch onto upstream's
  get_filament_config_index() resolution.
- Preset.cpp / PresetBundle.cpp / PrintConfig.cpp: keep both sides' option-list
  and enum-map entries.
- GLCanvas3D.cpp: upstream's printable_heights argument plus the IMEX ghost pass.
- PartPlate.cpp: keep <set> (still used).
- test_gcodewriter.cpp / test_3mf.cpp: keep both sides' test cases.
2026-07-13 16:22:22 -04:00
Clifford Garwood 0ba09780d1 Merge upstream main into IMEX parallel printing branch
Resolves two conflicts:
- GLCanvas3D.hpp: keep both the IMEX ghost render declarations and
  upstream's _render_wireframe_overlay().
- test_gcodewriter.cpp: both sides appended test cases to the same
  region; keep upstream's origin/machine-limit tests alongside the
  pressure-advance and temperature scenarios.
2026-07-13 13:17:45 -04:00
SoftFever 582017235c feat(engine): resolve per-variant config columns for raw filament reads on the export path
When a per-layer nozzle grouping migrates a filament across nozzle
variants, the write-back turns two groups of config arrays from
filament-indexed into column-indexed: the per-variant filament options
(one column per variant a filament uses) and the merged extruder
retract overrides (resized to the column count by apply_override).
Export-path readers that still indexed them with the raw filament id
read a neighbor's column for every filament ordered after a migrating
one: toolchange/standby temperatures (M104/M109), retraction lengths
and feedrates, wipe distance, z-hop types, air-filtration keys, and -
through the Extruder's cached flow term - the extrusion E of every
move.

Now every such read resolves its column through the existing
layer-aware resolver (get_filament_config_index ->
Print::get_filament_config_indx), which returns the raw filament id
whenever no per-layer grouping result is published, so static prints
are byte-inert by construction. The Extruder itself has no layer
knowledge, so it gains an injected config column (set_config_index,
default = filament id) that the generator refreshes at the only two
resolution-changing events - layer change and writer toolchange - and
that re-syncs the cached e_per_mm3 flow term. Old-filament reads
resolve at the current layer, which is safe because the per-layer maps
are gap-filled carry-forward. Whole-array placeholder copies
(toolchange temperature overrides) are rebuilt in filament order,
mirroring the existing per-variant placeholder remap. The resolvers
move to the public section so non-friend helpers (ooze prevention) can
resolve too.

Documented, deliberately unchanged: the wipe tower's per-filament
parameter rows (no layer dimension; tower x per-layer grouping is a
follow-up), travel_slope's physical-extruder read, estimator pre-heat
bookkeeping temps, and index-0 header diagnostics.

Verification: new Extruder column-injection scenario (defaults, column
follow + flow-cache rescale, filament-indexed reads unaffected, reset
semantics) and a migrating write-back case proving the column shift for
filaments ordered after a migrator and the resolver tracking it (11 +
14 assertions); suites green (libslic3r 48998/169, fff_print 655/61);
20/20 pinned-slice byte gate bit-identical (incl. sequential repro x2
deterministic).
2026-07-12 14:30:39 +08:00
SoftFever abbd420f2a feat(engine): write per-variant filament values back for selector regroups
When the per-layer filament selector (enable_filament_dynamic_map)
migrates a filament across nozzle variants (e.g. Standard -> High Flow),
the config write-back only stored the derived extruder map; every
per-variant filament value (retraction, nozzle temperature, flow,
flush...) kept the numbers resolved from the pre-slice static mapping.

Now both dynamic write-back sites (the by-layer branch and the
sequential stitch) branch on the result's dynamic support. Migrating
results run a mixed-filament expansion that regathers every
filament_options_with_variant key from the pristine per-variant
superset, giving a migrating filament one config slot per (extruder
type x nozzle volume type) it lands on - filament_self_index,
filament_extruder_variant, and all value arrays grow in lockstep - and
recompute the retract overrides with per-slot machine indices so a nil
slot falls back to its own variant's machine value. Non-migrating
dynamic results take the merged three-map write-back so re-applies
reproduce from the written maps. Unrouted filaments resolve from the
result's own default map, so slot resolution never depends on
filament_map round-tripping through the plate config.

Print::apply reproduces the identical expansion from the persisted
group result (shared dedupe helper, expansion function, and slot
indices on both sides): the expanded keys sit in the psWipeTower /
psGCodeExport invalidate lists, so without the reproduction every
re-apply after a selector slice would diff non-empty and permanently
invalidate. cal_non_support_filaments now resolves the extruder per
layer from the published result for dynamic groupings.

filament_map_2 keeps its apply-time static derivation; nothing on the
dynamic path reads it (the per-slot machine indices key the override
merge), and per-(extruder x volume-type) machine limits in the g-code
processor remain a documented follow-up.

Every change is gated behind is_dynamic_group_reorder() or a persisted
result with dynamic support; no profile sets the flag, so the static
fleet's instruction stream is unchanged (20/20 pinned-slice byte gate
identical, incl. the sequential repro sliced twice, deterministic).

Tests: expansion unit coverage (migrating slots, unrouted fallback via
the default map, mis-sized volume map ignored, nullable retract keys in
lockstep, slot machine index layout), an end-to-end stub-driven
write-back asserting expanded slots, per-layer config-index resolution,
the override merge incl. the nil-slot variant fallback, and re-apply
stability, plus a real selector slice staying valid across re-apply.
Suites green (libslic3r 48987/168, fff_print 633/60).
2026-07-12 11:15:58 +08:00
SoftFever c8db06b1d4 feat(engine): stitch per-object selector plans for sequential prints
Sequential (by-object) prints were incoherent with the per-layer filament
selector (enable_filament_dynamic_map): the by-object branch published a
static grouping while each per-object ToolOrdering independently ran the
dynamic planner from an empty nozzle status and wrote its own map to the
config (one write per object, last object wins). The exported toolchange
sequences then disagreed with the published result that drives the
per-layer maps, placeholders, and selector emission.

Now the by-object branch, when the selector is enabled, plans each unique
object once — threading the physical nozzle occupancy and the previous
object's last filament into the next plan — stitches the per-object
per-layer nozzle maps into one print-wide result (gap-filled by the new
normalize_nozzle_map_per_layer so any layer index resolves a filament's
nozzle consistently), publishes it, and writes the derived extruder map
back once. The plans are cached on the Print and g-code export consumes
the cache: the ToolOrdering seed changes the plan input (dontcare
assignment, first-layer reorder), so a fresh export-time construction
could re-plan differently from the published stitch. The per-object
dynamic write-back is gated off for sequential prints.

Every change is gated behind is_dynamic_group_reorder(); no profile sets
the flag, so the static fleet's instruction stream is unchanged (20/20
pinned-slice byte gate identical, incl. the by-object repro sliced twice).

Tests: normalize unit coverage (carry-forward, back-fill, ragged input),
stitched-blocks selector detection, and an end-to-end by-object selector
slice (apply -> process -> export) asserting the published stitched
result, one cached plan per object, the config write-back, and a clean
export. Suites green (libslic3r 48958/165, fff_print 633/60).
2026-07-12 03:24:35 +08:00
SoftFever c65b0540a3 Merge remote-tracking branch 'origin/main' into feature/h2c_support_clean
# Conflicts:
#	src/libslic3r/PrintApply.cpp
#	src/libslic3r/PrintConfig.cpp
#	src/libslic3r/PrintConfig.hpp
#	tests/libslic3r/test_config.cpp
2026-07-11 23:10:54 +08:00
93d5b65852 Fix per-obj multi-variant options handling (#14700)
* ENH: config: add logic to apply params to object/region config with multi-extruder

JIRA: no-jira
Change-Id: Ieab98cd8d031e5ca82a3aad2d0b89d8ae4a794f1
(cherry picked from commit 3179fd416e68ca8bc2d746f859508d07db18fe5b)

* FIX: X1C switch to H2D lose Highflow parameter

Jira: STUDIO-15272
Change-Id: Id8cf5d93a49d5542ac82f9554974b458e15c1193
(cherry picked from commit 15d9f072ff658a3beb4f916d978dfea12c2d9f16)

* Fix mishandling of `stride` param and add unit test for it

* Fix modified multi-variant per-obj option highlight

* Fix issue that per-obj FloatsOrPercents options are marked as dirty incorrectly when lost focus

---------

Co-authored-by: lane.wei <lane.wei@bambulab.com>
Co-authored-by: weiting.ji <weiting.ji@bambulab.com>
2026-07-11 22:21:22 +08:00
SoftFever dacf26b06f fix(engine): keep engine-derived filament_map_2 out of the apply diff
Print::apply rebuilds m_config.filament_map_2 to the real per-filament slot
map on every apply, while the incoming full config only ever carries the
ConfigDef default. The resulting phantom one-key print_diff hit the
invalidator's catch-all branch and killed every print-level step on each
apply, so on multi-extruder printers a fresh slice result was invalidated
the moment the GUI re-applied after slicing completed.

Dropping the key from print_diff loses no information: it is never a user
input, and the rebuild derives it from filament_map, filament_volume_map
and the variant slots, each of which is diffed and invalidation-listed on
its own.

Regression test: re-applying an unchanged config after process() must not
invalidate psSlicingFinished (fails with APPLY_STATUS_INVALIDATED without
the fix). Suites green (libslic3r 48891/154, fff_print 631/59); 19-fixture
byte gate identical incl. the Hybrid repro project, determinism x2.
2026-07-11 22:11:35 +08:00
SoftFever 21b46044d0 feat(gcode): resolve per-filament variant slots per layer
- the g-code writer tracks the current layer id and resolves
  FILAMENT_CONFIG/NOZZLE_CONFIG (plus every non-macro variant lookup,
  toolchange placeholder scalars, and the change-filament flush
  overrides) through Print's per-filament, per-layer config-index
  resolvers instead of the filament->extruder collapse
- update_layer_related_config refreshes the per-layer
  extruder/volume/nozzle maps in the writer config;
  update_placeholder_parser_with_variant_params remaps the
  filament-variant arrays into filament-id space for custom g-code
  (Orca's flush placeholder computation moves inside it)
- the engine's concrete per-filament volume assignment now merges into
  the config write-back (the temporary hold from the producer commit
  is lifted together with these consumers), and the background process
  reads the computed volume map back to the plate
- append_full_config dumps the resolved filament_map_2 slots
- update_used_filament_values gains a bounds guard
- tests: per-filament Hybrid slot resolution + null-result fallback

Result: on a Hybrid extruder, each filament's features slice with its
assigned sub-nozzle's variant values (speeds, volumetric limits,
retraction). Verified on a 4-filament H2C Hybrid project: outer walls
split into three feedrate populations (30/50/200 mm/s), toolpath
geometry byte-identical, deterministic across repeated slices. All 18
non-Hybrid reference fixtures stay byte-identical except the
filament_map_2 header value now showing the real slot. Auto grouping
ties (multiple zero-flush perfect matchings) may pick a different
filament-to-nozzle isolation than other slicers; verified co-optimal.
2026-07-11 15:00:33 +08:00
SoftFever 03a704df7c feat(engine): wire the per-filament volume-map producer pipeline
- Print::update_filament_maps_to_config takes filament/volume/nozzle
  maps, backfills an empty volume map from extruder types, rebuilds
  filament_map_2, re-expands the per-filament variant arrays, and
  recomputes retract overrides keyed by resolved slots
- grouping writes its result back in every non-sequential mode;
  manual multi-nozzle grouping validates the user mapping and raises a
  translatable error on deviation; the engine's concrete volume
  assignment is deliberately not merged yet (per-filament arrays are
  already consumed by filament id, so materializing High Flow now
  would change motion before the layer-aware resolvers land)
- Print::apply treats the three map keys as engine outputs in auto
  modes (erased from the diff and adopted), compares them against used
  filaments in manual mode, and keeps the pre-expansion snapshot in
  sync with the late normalization pass so rebuilt headers reflect the
  sliced state instead of resurrecting stale values
- volume/nozzle maps and extruder_nozzle_stats join the invalidation
  group of filament_map (wipe tower + skirt/brim)
- PresetBundle composes full configs with an optional per-filament
  volume map (plate map, else defaults derived from each extruder's
  flow type); project config keeps the map sized across filament
  count changes
- PartPlate stores per-plate volume/nozzle maps; Plater injects them
  at every slice-composition site (incl. g-code reload and wipe-tower
  estimation); BackgroundSlicingProcess reads engine results back to
  the plate in auto modes
- per-filament map trust guards relaxed to size-match everywhere now
  that every producer sizes the map; single-filament explicit flow
  assignments are honored
- tests: grouping volume maps stay concrete, merge semantics of
  update_used_filament_values, single-filament override honoring

Motion g-code is byte-identical fleet-wide including Hybrid projects
(19-fixture gate + repro determinism double-slice). Header deltas:
the map keys now dump real values, and stale pre-normalization values
(e.g. enable_prime_tower on single-used-filament prints) no longer
leak into the config block.
2026-07-11 04:38:31 +08:00
SoftFever 18b9279c26 feat(print): add per-filament config-index resolvers and filament_map_2
- Print::get_nozzle_config_index / get_filament_config_indx resolve a
  filament's variant slot per layer from the nozzle group result, with
  hashed index caches; when no group result is published (sequential
  prints), they fall back to the static filament->extruder mapping so
  behavior is unchanged
- filament_map_2 caches each filament's resolved print-variant slot;
  rebuilt in Print::apply after the filament_map diff handling and in
  the filament-map write-back
- filament retract overrides now key by slot indices: apply_override
  fallback indexing flips to 0-based, Print::apply passes
  filament_map/extruder indices, the write-back passes filament_map_2
  (identical resolution while slots equal extruders)
- filament_volume_map/filament_nozzle_map/filament_map_2/
  filament_self_index become PrintConfig static members (required for
  member access); grouping input guards tightened so their registered
  1-element defaults are never mistaken for real per-filament maps
  (single-filament manual mode keeps the mix-marker fallback)
- update_filament_self_index_cache refreshed at every full-config
  assignment
- tests: 0-based apply_override fallback, get_config_index_base
  hit/miss/mixed-type cases

The resolvers are not consumed by the g-code writer yet. Non-Hybrid
g-code is unchanged except the config header, which now serializes the
three new static keys (defaults until the per-filament producer lands);
verified by the 19-fixture byte gate: 3 added header lines per fixture,
zero motion changes.
2026-07-11 02:29:40 +08:00
SoftFever 13cd8d4864 feat(config): keep per-(extruder x volume-type) slots in variant expansion
- get_extruder_nozzle_volume_count derives per-extruder volume-type slot
  lists from extruder_nozzle_stats (absent stats = one slot per extruder)
- update_values_to_printer_extruders learns the slot layout: when any
  extruder mixes volume types, option arrays keep one slot per
  (extruder x volume type), extruder-ascending then volume-ascending;
  single-slot resolution takes the filament's volume type on mixed
  extruders
- update_values_to_printer_extruders_for_multiple_filaments applies a
  per-filament nozzle_volume_type override from filament_volume_map
  (when sized to the filament count) and remaps filament_self_index
  through the same pipeline as every other filament key
- get_config_index_base + is_auto_filament_map_mode helpers (consumers
  land with the per-filament config-index resolvers)
- callers updated: PresetBundle composition paths, PrintApply (counts
  hoisted above the extruder_applied guard), Print write-back
- new tests: slot counting, Hybrid slot expansion incl. stride 2,
  per-filament override, non-Hybrid degeneracy

Non-Hybrid printers keep their variant layout and values (proven by a
19-fixture byte gate; the only header delta is filament_self_index now
flowing through the same variant pipeline as its sibling filament
keys). Hybrid slices grow the config-block variant arrays to one entry
per sub-nozzle volume type; motion g-code is unchanged until the
g-code writer consumes the new slots.
2026-07-11 00:51:10 +08:00
SoftFever 19352215da feat(plugin)!: merge G-code post-processing into the slicing pipeline as psGCodePostProcess
G-code post-processing is now a step of the slicing-pipeline plugin rather than a
separate capability type. One capability class can transform slices at the geometry
seams AND edit the final G-code, behind a single picker/option.

- Add SlicingPipelineStepPlugin::psGCodePostProcess (bound as
  orca.slicing.Step.psGCodePostProcess). Unlike the geometry steps it fires from the
  GUI export path in PostProcessor.cpp, not from Print::process(): ctx.print/ctx.object
  are None and the plugin edits the file at ctx.gcode_path in place. It may run more
  than once per slice (file export and/or upload) and its output is not shown in the
  preview.
- Extend SlicingPipelineContext with gcode_path/host/output_name and a C++-only
  full_config; config_value() falls back to it when there is no live Print.
- PostProcessor.cpp dispatches SlicingPipelinePluginCapability at psGCodePostProcess,
  driven by the existing slicing_pipeline_plugin option.
- The exported G-code lives outside data_dir(), so the plugin audit sandbox would
  block the write; the trampoline's audit setup grants ctx.gcode_path's folder as a
  scoped allowed root, gated on a non-empty gcode_path so the geometry-step hooks gain
  no extra filesystem access.

BREAKING CHANGE: the separate G-code post-processing capability type is removed.
- orca.gcode.GCodePluginCapabilityBase and orca.PluginType.PostProcessing are gone;
  post-processing plugins migrate to orca.slicing.SlicingPipelineCapabilityBase +
  Step.psGCodePostProcess (and gain ctx.params / ctx.config_value()).
- The post_process_plugin config option is removed; use slicing_pipeline_plugin.
  Presets carrying the old key degrade to the standard unknown-key warning.
- Manifest type = "post-processing" now maps to Unknown (advisory only; the loader
  dispatches on the C++ get_type()).

Also repairs two latent build breaks the branch carried: stale Step enum value usages
in test_slicing_pipeline_hook.cpp and a reference to the removed
ConfigOptionDef::PluginType::None in Tab::on_value_change (now is_plugin_backed()).
Adds the orca_gcode_stamp sample plugin and a psGCodePostProcess binding test.
2026-07-10 19:57:35 +08:00
SoftFever b2eeed2622 feat(slicing): enforce filament flow-type restrictions in auto grouping
Filament grouping already consumed per-filament forbidden nozzle volume
types, but every call site passed an empty map, so a variant-restricted
filament (e.g. one limited to "Direct Drive TPU High Flow") could be
auto-grouped onto an incompatible nozzle flow type on multi-variant
printers.

- add convert_to_nvt_type() to parse extruder variant strings
- add Print::get_filament_unprintable_flow(): forbidden volume types =
  printer extruder variants minus the filament's declared variants;
  filaments declaring no variants stay unrestricted
- feed the map into grouping at the by-object path (Print.cpp) and all
  six mapping/planning sites in reorder_extruders_for_minimum_flush_volume
- unit-test the string parser

Non-restricted configurations produce an empty map, so existing
printers' grouping and g-code are unchanged.
2026-07-10 15:10:46 +08:00
SoftFever 00816968a2 Merge branch 'main' into feature/h2c_support_clean 2026-07-09 22:17:20 +08:00
SoftFever 26a0caff96 save exact (not standard-rounded) nozzle diameter in 3mf metadata 2026-07-09 13:13:29 +08:00
Kris Austin 2194037d16 test: cover floor/ceil and the built-in function boundary in the placeholder parser (#14667)
round() already had unit coverage; floor() and ceil() had none. Add the missing
positive cases for both signs, plus round()'s half-away-from-zero tie-break, and
one negative case asserting that a name outside the grammar's built-in function
set is treated as an undefined variable and throws, rather than being passed
through to a math library.
2026-07-09 09:57:06 +08:00
SoftFever 9810397546 test+i18n: multi-nozzle filament-group goldens and ported strings
Filament-group golden harness (config_a subset) and .3mf multi-nozzle round-trip tests, plus i18n msgids for the ported H2C/A2L strings.
2026-07-09 01:16:26 +08:00
Kris Austin dec67345be fix: prevent out-of-bounds crash in Arachne beading interpolation (#14656)
* fix: prevent out-of-bounds crash in Arachne beading interpolation

SkeletalTrapezoidation::interpolate() derives an inset index from `left` but
uses it to index the merged beading, which follows the thicker of left/right.
When the thicker side has fewer insets, the index runs past the end and the
slicer crashes during "Generating walls".

Skip the adjustment when the index is out of range, as the adjacent guards
already do. interpolate() uses no instance state, so make it static and add a
regression test that exercises it directly.

Fixes #14584
2026-07-08 20:58:39 +08:00
Kris Austin 4188ed2e00 feat: add regex_replace() string transform to placeholder templates (#14650)
feat: add regex_replace() string transform to filename templates

The filename template language could test strings (=~, !~, one_of) but
never rewrite one, so there was no supported way to reshape a placeholder
value, such as dropping a file extension from {first_object_name}.

Add regex_replace(subject, /pattern/, replacement), reusing the existing
regex-literal syntax and boost::regex engine. Every placeholder keeps
returning its exact value and the template does the transform explicitly:

  {regex_replace(first_object_name, /\.[^.]*$/, "")}   strip any extension

The replacement may reference capture groups ($1, $2, ...). It is one
grammar function mirroring digits(), with the name registered as a keyword
so it is not parsed as a variable.
2026-07-08 08:58:51 +08:00
SoftFever f81a24abfb feat(plugin): expose the slicing print-graph as raw orca.host classes + Twistify sample
Adds PluginHostSlicing, which registers the print-graph data model (Print,
PrintObject, Layer, LayerRegion, Surface, ExPolygon, extrusions, ...) into the
orca.host submodule in the same raw-class style as PluginHostApi's Model/Preset
graph, with shared helpers in PluginBindingUtils. SlicingPipelinePluginCapability
is trimmed to the capability surface (the standalone SlicingNumpy helper is folded
away). Adds the Twistify example plugin next to Inset and broadens the binding,
hook, and plugin-install tests.
2026-07-08 00:05:28 +08:00
raistlin7447 2860353b9f fix: multi-user slicing crash on shared temp dir (#14607)
On Linux every account shares /tmp, but slicing builds temp paths there under
fixed, app-owned names via temporary_dir() (model backups, STEP import,
part-skip). The first user to slice creates and owns those dirs, so the next
user cannot write under them and slicing crashes with "No such file or
directory".

Tag the app temp root with the user id at startup (<temp>/orcaslicer_<uid>)
so every temporary_dir() consumer is isolated at once. The id stays at the
top level of the world-writable system temp so each user's dir is created
directly there; a shared parent dir would be owned by whichever user made it
first. The root is pre-created because STEP import writes into it directly.
Windows keeps the plain temp dir since it is already per-user.

Fixes #10108. Same root cause as #5969.
2026-07-06 09:18:04 +08:00
Ian Chua 3668b01721 Merge branch 'main' into feat/plugin-feature 2026-07-03 13:29:40 +08:00
Noisyfox 464a81d585 Support accessing coFloatsOrPercents values in gcode template (#14526)
* Support accessing `coFloatsOrPercents` values in gcode template (OrcaSlicer/OrcaSlicer#14522)

* Vector option values are separated by comma

* Fix wrong cast used for checking nullability
2026-07-02 23:51:20 +08:00
Ian Chua ecddf3d18f feat: Python Plugins 2026-07-02 17:49:36 +08:00
SoftFever f516f47c8e add profile validator checks 2026-07-01 21:55:19 +08:00
SoftFever e39be23d6d fix compatible_prints/printers on load instead 2026-07-01 15:25:43 +08:00
Clifford Garwood 17584e2ffb Merge remote-tracking branch 'upstream/main' 2026-06-30 01:55:14 -04:00
raistlin7447 f13f67959c fix: PA pattern calibration crash with first layer line width 0 (#14447)
CalibPressureAdvancePattern::line_width_first_layer() returned the raw
initial_layer_line_width, so a value of 0 (which means "use the default")
fed 0 into the Flow spacing math and threw FlowErrorNegativeSpacing,
crashing the whole app when slicing a PA pattern calibration.

Mirror the guard already present in the sibling line_width(): when the
configured width is non-positive, fall back to auto_extrusion_width.

Add a libslic3r regression test covering the width resolution.

Fixes #13188
2026-06-29 09:22:23 +08:00
Clifford GarwoodandClaude Opus 4.8 4677c79e18 Merge upstream/main into iXex PR branch (#13086)
Catches the iXex/IDEX branch up to upstream main (44 commits). One
content conflict resolved:

- src/libslic3r/Print.hpp: kept upstream's default-initialized
  m_origin {0,0,0} alongside our m_imex_slice_offset member.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 12:02:29 -04:00
SoftFever 14e572db5f Add nozzle flow variant & make some options multi-variant (#13712)
# Description

Port BBS per-extruder config variants toggle, thanks Bambu!

Also fix invalid num error when resetting multi-variant filament
overrides.

TODOs:

- [x] Make more configs multi-variant (such as speeds)
- [x] Add flow variant (normal/high flow) combo box
- [x] Add botton to sync config to other variant


# Screenshots/Recordings/Graphs

<img width="846" height="1494" alt="image"
src="https://github.com/user-attachments/assets/47abf053-f5b8-4c86-ac0b-c0323ed8b242"
/>

<img width="1478" height="1189" alt="707420e5be01c30af3e6ede1f3dc9b67"
src="https://github.com/user-attachments/assets/11268712-da65-42ab-9cb8-3cede7790a5c"
/>

<img width="1478" height="1189" alt="81d5d3877f4d5bf9dccd44d7f0b30aa7"
src="https://github.com/user-attachments/assets/cda5ea51-07c3-48fc-bfbb-1428dca14e26"
/>


## Tests

<!--
> Please describe the tests that you have conducted to verify the
changes made in this PR.
-->

<!--
> A guide for users on how to download the artifacts from this PR.
-->

[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
Fix #10336
2026-06-28 22:30:40 +08:00
Ian Chua 0e8288de36 fix: old preset names should resolved renamed_from field (#14429)
* fix: old preset names should resolved renamed_from field

* chore: remove misleading comments

* Merge branch 'main' into fix/consider_renamed_from

* normalize_inherits

* improve find_preset2 performace
2026-06-27 22:15:43 +08:00
SoftFever 8cb2e4e01e profiles: deterministic setting_id from vendor/type/name (#14432)
* profiles: enforce globally-unique, per-vendor-namespaced setting_id

Many non-Bambu vendors copied Bambu's generic setting_ids (GFSA04 alone
appeared in 1557 files), so setting_id was not globally unique. This
namespaces every vendor's ids and reserves Bambu/OrcaFilamentLibrary space.

- Reserve "G*" (Bambu) and "O*" (OrcaFilamentLibrary) id spaces.
- Assign each other vendor a 2-char prefix (first+last letter, collision
  resolved) and renumber every instantiated preset to <PREFIX><NNNN>.
- Strip setting_id from base profiles (instantiation:false) per Bambu's
  convention; assign one to instantiated presets that lacked it.
- Remove the pre-existing misspelled "settings_id" key (91 files).
- filament_id is left untouched (it is a per-material id).
- Add one-time migration script scripts/assign_vendor_setting_ids.py with a
  persisted registry resources/profiles/vendor_prefixes.json. Re-runs freeze
  existing ids; only new vendors/profiles get new ids.
- Bump version in each changed vendor index file.
- Extend scripts/orca_extra_profile_check.py with a CI guard: global
  uniqueness, in-namespace, no base setting_id, no gaps, no settings_id typo.

7425 profile files changed across 61 vendors; 0 cross-vendor collisions;
validator clean; migration idempotent. BBL and OrcaFilamentLibrary id spaces
untouched.

* profiles: add setting_id authoring guide for new vendors / profiles

* profiles: drop in-repo README; setting_id guide now lives in the wiki

* profiles: derive setting_id deterministically from vendor/type/name

* bump profile version
2026-06-27 20:11:25 +08:00
Clifford GarwoodandClaude Opus 4.8 c5d0cdbcff Merge upstream/main into iXex PR branch (#13086)
Catches the iXex/IDEX parallel-printing branch up to upstream main
(102 commits). Two content conflicts resolved:

- src/libslic3r/Preset.cpp: s_Preset_printer_options — kept upstream's
  new "use_3mf" key and our iMEX printer-capability/mode keys.
- tests/fff_print/test_gcodewriter.cpp: upstream revived the disabled
  suite (#14196), dropping the obsolete [.]-tagged lift() test and its
  config_lift_unlift.ini; kept their set_speed + z_hop tests and appended
  our 10 per-firmware set_pressure_advance/set_temperature scenarios.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 23:29:35 -04:00
Noisyfox cdc336d80c Merge remote-tracking branch 'upstream/main' into dev/extruder-toggle-wip 2026-06-25 12:46:35 +08:00
SoftFever da074043f2 Fix fuzzy skin artifacting regression (#14376) (#14382)
Fix fuzzy skin artifacting (#14376)
2026-06-25 10:52:19 +08:00