Commit Graph

29599 Commits

Author SHA1 Message Date
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
780b2f1ebe fix(engine): publish the nozzle group result for sequential prints
The Print-level LayeredNozzleGroupResult had a single producer, the
by-layer branch of ToolOrdering, which is gated to non-sequential prints.
The by-object branch in Print::process computed a grouping only in auto
map modes and never stored it, so a sequential slice exported with a null
group result: the per-nozzle placeholder tables came up empty and any
start g-code indexing nozzle_diameter_at_nozzle_id[] aborted with
"Indexing an empty vector variable". A prior by-layer slice masked the
bug by leaving its (never cleared) result on the Print.

Now the by-object branch runs get_recommended_filament_maps in every
static map mode (in manual modes the result mirrors the user's
assignment, deviations throw as in by-layer) and publishes it
print-wide. The config write-back stays gated to auto modes: in manual
modes it would only re-store the pre-slice values.

Regression test: a two-object by-object print must publish a non-null
group result and resolve nozzle_diameter_at_nozzle_id[] in start g-code
(both fail without the fix). Suites green (libslic3r 48929/162,
fff_print 633/60); 18-fixture byte gate identical; the by-object repro
project goes from the export error to valid g-code, determinism x2.
2026-07-12 00:46:15 +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
Noisyfox
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
e717dea94b Merge branch 'main' into feature/h2c_support_clean 2026-07-11 22:19:59 +08:00
pedrosland
22e121f4e4 Fix unreadable overlay elements in 3D view on XWayland/Intel (#11557)
Fix unreadable overlay elements in 3D view on XWayland/Intel - closes #10287
2026-07-11 22:19:12 +08:00
SoftFever
f5418415ac fix(gui): stop clipping the nozzle-count edit button on flow changes
HoverLabel's constructor used SetSizerAndFit, which records the
count-hidden width as the panel's explicit minimum size. An explicit
minimum outranks best size in sizer allocation, so once the "(N)" count
was shown, any ancestor Layout() - e.g. switching the extruder flow type
to Standard or High Flow - shrank the title row back to the stale width
and clipped the trailing edit button. Hybrid only appeared correct while
nothing had re-laid the row since its own Fit().

Use plain SetSizer and, on every title/count change, invalidate the
cached best size and re-lay both the row and its parent so the sizer
always allocates the current content width.
2026-07-11 22:11:47 +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
Kris Austin
4b7182b048 fix: guard against stale instance ids in PartPlate instance scans (#14523)
Deleting an object's instance leaves a stale (obj_id, instance_id) pair in
PartPlate::obj_to_instance_set until it is pruned. object_list_changed() runs
during the delete path and calls has_printable_instances(), which guarded only
obj_id and then indexed object->instances[instance_id] on the now-shorter
vector, dereferencing a garbage ModelInstance* and crashing with SIGSEGV. The
reported fault address (0x12a) matches a member read on that bad pointer.

Reuse the existing valid_instance() helper, which bounds-checks both obj_id and
instance_id, at every obj_to_instance_set scan that was missing the instance-id
check: has_printable_instances(), printable_instance_size(),
is_all_instances_unprintable(), get_extruders_under_cli(),
duplicate_all_instance() and set_pos_and_size() (the last had no bounds check at
all). valid_instance() is made const so the const CLI scan can call it.

Fixes #14159
2026-07-11 17:41:22 +08:00
SoftFever
75c8d0775a feat(gui): per-filament nozzle volume selection and persistence
- FilamentMapDialog's manual page understands volume types: a mixed
  (Hybrid) extruder shows separate Standard / High Flow drop zones
  with live sub-nozzle counts, a validation timer with an inline
  error + "set nozzle count" suggestion, and composes a per-filament
  volume map on OK (persisted to the plate or globally)
- switching an extruder's Flow type rewrites the affected plate map
  entries; plate maps stay sized across filament add/delete/count
  changes (values keep their filament, no index shift)
- CLI: manual mapping on multi-nozzle printers synthesizes the volume
  map from extruder flow types when absent; nozzle-manual mode
  requires explicit maps; computed maps land on the plate so exported
  projects carry filament_volume_maps
- filament_nozzle_map joins the project options (selection seeding +
  filament-count resizing)
- pot entries for the new dialog strings

All 19 reference fixtures byte-identical; slicing an exported Hybrid
project reproduces its g-code byte-for-byte with the volume map
round-tripped through model_settings.config.
2026-07-11 16:37:50 +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
7c8d086d9e feat(gui): show per-extruder nozzle count with inline editor
The sidebar extruder cards get an interactive title row — "<name> ( <count> )"
with an edit button — showing the extruder's physical nozzle count on
multi-nozzle printers (hidden elsewhere). Clicking it opens the existing
"Set nozzle count" dialog, which now also handles a Hybrid extruder by
offering both Standard and High Flow counts (an empty mix is rejected) and
shows a hotend thumbnail.

Because `extruder_nozzle_stats` is session-only (saved presets never carry
it, so preset switches rebuild the edited config without it), the stats are
re-baselined whenever they are missing: each extruder starts with
extruder_max_nozzle_count nozzles of its selected volume type. Switching an
extruder's flow type carries its total count over to the new type, except
when the stats came from a device sync — the machine-reported per-type
breakdown must survive a manual flow switch.

The badge refreshes on preset load, flow-type change, manual edit, device
sync, and project load. Single-extruder cards keep the title row but never
enable editing.
2026-07-10 19:47:21 +08:00
SoftFever
c528d3a0bc feat(gui): enable Hybrid nozzle flow for multi-sub-nozzle extruders
An extruder with more than one physical sub-nozzle can hold a mix of
Standard and High Flow nozzles. The Flow dropdown now offers Hybrid for
such extruders (extruder_max_nozzle_count > 1, nil-guarded); grouping
already expands a Hybrid extruder into per-volume nozzle groups from
extruder_nozzle_stats.

- sidebar Flow combo offers Hybrid only for multi-sub-nozzle extruders
- preset lookup treats Hybrid as Standard (presets define no Hybrid
  variant); variant strings are never fabricated for it
- printer tab splits a Hybrid extruder into Standard + High Flow rows,
  with matching selection-index arithmetic and sync-enable rules
- syncing from a printer whose extruder holds mixed nozzle flows now
  selects Hybrid instead of collapsing to the dominant flow type
- send-to-printer flow check: a nozzle-rack extruder validates its
  nozzle inventory (mounted + rack) against every needed flow instead
  of comparing only the mounted nozzle; mounted-flow lookup is now
  per-extruder, fixing an index shift when a nozzle reports no flow
- Hybrid is session-only in app config (stored as Standard), so a
  fresh session starts from concrete flow types

Printers whose extruders have a single sub-nozzle (including all
dual-extruder machines without a rack) see no new option and identical
check behavior.
2026-07-10 18:02:26 +08:00
SoftFever
83d2d4179d feat(profiles): enable cooling filter on H2D, H2D Pro, and H2S
Declare support_cooling_filter=1 on the three profiles (0.2/0.6/0.8
variants inherit from 0.4) and insert the cooling-filter conditional
into the H2D and H2S machine start g-code, inside the low-chamber-temp
airduct branch:

  {if(cooling_filter_enabled)} M145.2 P0 F0 {else} M145.2 P0 F1 {endif}

H2D Pro intentionally gets no g-code edit: its duct firmware takes the
filter mode over the device channel only, so the flag merely enables
the toggle.

Impact on existing users at default settings: H2D/H2S start g-code gains
exactly one line (M145.2 P0 F1, filter off) in the cool-chamber branch;
nothing is removed or reordered. The existing support_air_filtration=1
overrides are deliberately kept so exhaust-fan behavior for ABS-class
filaments is unchanged, even though the machine-tab row is hidden while
the cooling-filter toggle is shown.
2026-07-10 16:49:22 +08:00
SoftFever
826e36fb1e feat(printer): add cooling filter support flag and expose its toggle
cooling_filter_enabled existed as a config option but was shown nowhere,
and there was no capability flag to gate it. The cooling filter and air
filtration are alternative accessories sharing the same duct, so a
printer declares one or the other.

- new hidden printer capability flag support_cooling_filter
- "Use cooling filter" toggle in the Accessory group, shown only when
  the printer supports it; the air-filtration toggle hides in that case
  (no vendor restriction: third-party printers keep air filtration)
- explicit defaults (0) in the common machine base
- H2C declares support_cooling_filter=1 instead of support_air_filtration;
  its start-gcode already carries the cooling-filter conditional, so the
  toggle is functional. On H2C this drops the two exhaust-fan lines that
  air filtration emitted for ABS-class filaments, matching the printer's
  actual duct accessory; H2C is new on this branch so no existing user
  output changes.

Printers without the flag keep exactly the previous accessory UI and
g-code.
2026-07-10 16:38:46 +08:00
SoftFever
b12aad6a8c feat(orient): auto-orient the largest overhang toward the cooling fan
New printer option fan_direction (undefine/left/right/both, default
undefine) declares which side the auxiliary part-cooling airflow comes
from. When set and the printer has an auxiliary fan, auto-orient adds a
yaw rotation so the dominant overhang area faces the airflow, and newly
added primitive shapes are pre-oriented the same way (except the Cube,
whose axis-aligned bounding box the pressure-advance pattern calibration
depends on).

- FanDirection enum + fan_direction printer option (Accessory group,
  enabled only with auxiliary_fan)
- orient engine: weighted overhang areas per candidate, yaw-direction
  search, vertical rotation applied on top of the primary orientation;
  the cooling weights are taken from the candidate actually chosen,
  including the flat-bottom tie-break
- orient_for_cooling() for primitive placement
- set fan_direction=left on H2C/H2D/H2D Pro/X1/X1E/P1S 0.4 profiles
  (X1C/H2S/P2S/X2D/Qidi X-Max 4 already carried the key, which now
  takes effect)

With fan_direction unset or no auxiliary fan the vertical rotation stays
identity and auto-orient results are unchanged; slicing and g-code are
never affected.
2026-07-10 16:21:48 +08:00
SoftFever
93a81179a1 feat(gui): add purge mode selector dialog
The engine already implements prime_volume_mode (Default/Saving/Fast)
but nothing in the UI could set it, leaving prime-saving unreachable on
multi-sub-nozzle extruders and fast purge unreachable on printers that
support it.

- new PurgeModeDialog with selectable Standard/Fast or
  Standard/Prime Saving cards depending on printer capability
- "Purge mode" sidebar button next to Flushing volumes; opens the
  dialog and stores the choice in the project config
- printer preset-load gating: button shown only when the printer has
  multiple sub-nozzles per extruder or sets support_fast_purge_mode;
  stale project values the printer cannot honor reset to Default
- enable fast purge on A2L 0.4 (support_fast_purge_mode), explicit
  default 0 in the common machine base
- new dialog strings added to OrcaSlicer.pot

Printers without these capabilities never show the button and their
projects keep prime_volume_mode at Default, so slicing output is
unchanged.
2026-07-10 15:41:57 +08:00
nvs1_cinci
e56cdd707f fix: use bambu's network plugin (#14688)
Fixes a regression where Timelapse and SD Card media failed to load on Bambu devices. 

During the dual-cloud-agent refactor, NetworkAgent was updated to require an explicit provider argument. The call site in MediaFilePanel::fetchUrl() was missing this argument, causing it to silently fall back to the default Orca stub instead of routing to the Bambu network plugin. 

This explicitly passes wxGetApp().get_printer_cloud_provider() to get_camera_url to correctly route the request and restore functionality.
2026-07-10 15:23:30 +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
9e8ac03476 test(filament_group): don't assert max_group_size cap for MatchMode
The FilamentGroup property/golden harness checked a per-extruder
max_group_size cap unconditionally in check_constraints. That cap is an
invariant of the flush-partition solvers only (calc_group_by_enum /
calc_group_by_kmedoids, reached via calc_filament_group_for_flush), which
partition filaments subject to each extruder's capacity.

MatchMode (calc_filament_group_for_match) does not partition by capacity:
it maps every filament to the extruder holding the nearest-color loaded
AMS filament, and its solver capacity is the used-filament count, not
max_group_size (FilamentGroup.cpp:1067). So a legitimate MatchMode result
can place more than max_group_size filaments on one extruder.

The prop_a/b/c_mode_match specs run MatchMode, and their scenarios are
generated with std::uniform_int_distribution / std::shuffle, which are
implementation-defined. For a fixed mt19937 seed, libc++ (macOS), libstdc++
(Linux) and MSVC (Windows) draw different scenarios, so the CI failure only
surfaced on Linux/Windows while macOS passed. Verified locally: 248/600
config-A MatchMode seeds exceed the cap under libc++ — it is reachable
everywhere; seed 90400 just isn't an exceeding draw on macOS.

Gate section 3 on FGMode != MatchMode. No test case is removed or skipped:
all 57 FlushMode specs still assert the cap, MatchMode still asserts the
unprintable-filament/volume correctness constraints (which it honors), and
MatchMode grouping regressions are still caught by the golden score gate at
3% tolerance. Test-only change; slicing behavior and g-code are unaffected.
2026-07-10 02:17:52 +08:00
SoftFever
b58dcf5978 test(libnest2d): fix use-after-free crash in NfpPlacer lifetime tests
NfpPlacer stores std::reference_wrapper to the items it packs and re-reads
them from finalAlign() in its destructor (via clearItems()). Two placer
tests declared the placer before the items in the same scope, so the items
were destroyed first and the destructor dereferenced dangling references.

On macOS this is a deterministic SIGSEGV: libmalloc poisons the freed block
on free, so the item's point vector reads back as ~null (deref at 0x8). On
Linux/glibc the freed bytes usually survive, which is why it slipped through
upstream CI (introduced by #14267).

Declare the items before the placer so they outlive it, matching the pattern
the sibling 'packs many items' and 'obstacle' tests already use. Test-only;
the library lifetime contract (items must outlive the placer) is unchanged
and honored in production via _Nester in Arrange.cpp.
2026-07-10 01:55:00 +08:00
Robert J Audas
79d51b31b5 Disable prime tower width for rib walls (#14625)
* Disable prime tower width for rib walls

Only enable the prime tower Width field when the selected wall type uses the standard tower shape. Rib towers use the rib-specific settings instead, so leaving Width editable suggests it changes rib geometry when it does not.

Fixes #14537

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Simplify prime tower width toggle

Use the existing rib-wall helper when disabling the prime tower width control for rib wall towers.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-07-09 22:48:17 +08:00
SoftFever
00816968a2 Merge branch 'main' into feature/h2c_support_clean 2026-07-09 22:17:20 +08:00
SoftFever
05ed2e4dfa Fixed a rare crash on app startup caused by ENOTSUP (errno 45) on Mac (#14686)
fix: prevent startup crash when preset-sync directory scan hits a transient FS error

On startup the user-preset sync thread scans the preset folder for orphaned
.info files (scan_orphaned_info_files). It iterated the directory with a
throwing boost::filesystem::directory_iterator while running on a background
thread that has no exception guard. On macOS, readdir() can intermittently
fail with ENOTSUP (errno 45); boost then throws filesystem_error, which --
uncaught on the sync thread -- calls std::terminate and aborts the whole
application on startup.

- Iterate with the error_code-based directory_iterator so a transient read
  failure is logged and skipped instead of thrown. The orphan scan is
  best-effort and re-runs on the next sync, so skipping a cycle is harmless.
  This mirrors the existing pattern in has_json_presets() and the plugin scan.
- Wrap the entire sync-thread body in try/catch as defense-in-depth, so no
  future uncaught exception on that otherwise-unguarded thread can abort the
  app.
2026-07-09 21:57:59 +08:00
SoftFever
844c374798 update colors 2026-07-09 19:42:21 +08:00
SoftFever
f688a3e741 update the profile 2026-07-09 19:01:19 +08:00
SoftFever
0d31325df0 feat: Filament Track Switch (H2-series O2L-FTS) support
The Filament Track Switch (H2-series accessory, product code O2L-FTS) feeds
every AMS to both extruders through a two-track switch. Port full support
across the device layer, project config, and GUI.

Device / config:
- Model the switch-aware AMS binding (the set of extruders an AMS can feed
  and which input track A/B feeds it), switch readiness, the O2L-FTS firmware
  module, and the fun2 capability bit for checking a slice against installed
  hardware.
- Register has_filament_switcher and enable_filament_dynamic_map as project
  config that persists with the project and restores from a saved 3mf, and
  force both back to false on every project/printer/CLI load path. Live
  device sync is the only thing that sets them true.

GUI:
- Sidebar sync activates the switch from live device state, attributes each
  AMS to the extruder its input track feeds, shows a floating status icon
  (ready / not-calibrated), and surfaces a one-time tip / not-calibrated
  warning.
- Send dialog gains a non-blocking slice-vs-hardware mismatch warning and a
  blocking error when a slice needs dynamic nozzle mapping but the switch is
  missing or not set up.
- AMS load/unload guards, AMS-view routing glyph + un-calibrated banner +
  hidden external-spool road, and mapping-popup external-spool lockout.
- Filament pickers collapse the per-extruder split into a single deduplicated
  "AMS filaments" group with a smart-assign toggle when the switch is ready.
- Firmware-upgrade panel lists the O2L-FTS accessory and its version.
- Device-provided filament-change steps (ams.cfs) drive the change-step
  display when firmware sends them, including the three switch steps.
- "Load current filament" asks which extruder to feed via a
  FeedDirectionDialog when the switch is calibrated.

Inert without the accessory: every path is gated on the switch being
installed (MQTT aux bit 29, default off) or ready, both project flags default
false, and the per-extruder AMS attribution is byte-identical, so AMS state,
the send/load UI, and sliced g-code are unchanged for every printer that does
not report a Filament Track Switch.
2026-07-09 18:57:05 +08:00
Kris Austin
6fda82476d fix: out-of-bounds read computing tool-ordering max layer height (#14665)
* fix: out-of-bounds read computing tool-ordering max layer height

calc_max_layer_height() loops over the extruder count (nozzle_diameter)
but indexes max_layer_height with the same counter, reading past the end
when that array is shorter. Silent on release builds, aborts under a
bounds-checked STL (_GLIBCXX_ASSERTIONS).

Read via get_at(), which falls back to the first entry when the index is
out of range, as Slicing.cpp already does for this option.

Add a fff_print regression test slicing a two-extruder printer with a
single-entry max_layer_height.

* docs: clarify how max_layer_height ends up short in the regression test

Normalization sizes it to the filament count under single_extruder_multi_material,
not "a mismatch a profile can ship" as the earlier comment guessed.
2026-07-09 15:47:57 +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
Kris Austin
7d17400443 fix: dangling static lambda crashes support G-code export on repeated slices (#14677)
GCode::extrude_support declared its per-path speed helper as a function-local
static lambda that captures `this` by reference. The closure is built once, on
the first extrude_support call, and reused for the rest of the process, so a
second G-code export in the same process runs the helper against a `this` from
the first export's stack frame, which has already returned.

The stale `this` flows through NOZZLE_CONFIG(...) -> cur_extruder_index() ->
GCodeWriter::filament(), reading a garbage current-extruder id and indexing
with it. It is silent whenever the reused stack still holds a usable pointer,
and an order-dependent SIGSEGV otherwise; AddressSanitizer reports it as a
stack-use-after-return in GCodeWriter::filament(). It is the only static
capturing lambda in libslic3r.

Drop static so the closure is rebuilt each call against the live frame. Add an
fff_print regression test that slices a support object twice in one process; it
fails without the fix (stack-use-after-return under ASan) and passes with it.
2026-07-09 08:56:03 +08:00
π²
bc6ffcfb31 Anisotropic surfaces + Separated Infills (remake) (#11682)
Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
2026-07-08 15:40:19 -03:00
Ian Bassi
378843a4da Remove unused variable (#14670) 2026-07-08 15:15:50 -03:00
Ian Bassi
20a66fa99b Top Surface Expansion (#14296)
Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
2026-07-08 14:20:40 -03:00
Ian Bassi
da149ee75a Add toolchange ordering option (Standard/Cyclic). (#13582) 2026-07-08 14:18:41 -03: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
SoftFever
28b7127150 feat(gui): multi-nozzle UI for H2C/A2L
Multi-nozzle sync widget, AMS rack-nozzle mapping popup, calibration rework, send-dialog nozzle mapping and extruder-count UI. Includes the fix to persist the AMS sync badge on filament cards (H2C/A2L and direct-sync printers).
2026-07-09 01:16:26 +08:00
SoftFever
a098b483f6 feat(device): H2C/A2L device layer
Nozzle rack data model and device-tab panel, multi-nozzle sync, per-nozzle filament blacklist, and print-dispatch nozzle mapping (DevNozzleMappingCtrl V0/V1).
2026-07-09 01:16:25 +08:00
SoftFever
15412cd812 feat(profiles): Bambu Lab H2C and A2L profiles and resources
Machine models and presets, process and filament presets, plus bed models, covers, previews and device images for the H2C and A2L.
2026-07-09 01:16:25 +08:00
SoftFever
237ef41b06 feat(libslic3r): multi-nozzle slicing engine for H2C/A2L
Port BambuStudio's dual-nozzle slicing core: H2C-era config keys, filament-to-nozzle grouping with per-layer dynamic regrouping, filament/nozzle/hotend gcode placeholder vocabulary, multi-nozzle wipe tower pre-heat/pre-cool, the two-pass pre-cooling injector, and corexy farthest-point timelapse.
2026-07-09 01:16:25 +08:00
Kris Austin
781ecdc2c1 ci: run unit tests on Windows and macOS (#14443)
* ci: run unit tests on Windows and macOS

The Unit Tests CI job only ran on Linux, so platform-specific bugs
invisible to a Linux build could land undetected (e.g. the MSVC-only
NfpPlacer crash fixed in #14267). The full non-[NotWorking] suite already
builds and passes on every shipped arch, so this wires them into CI.

A reusable unit_tests.yml, called once per built arch, downloads that
arch's test artifact and runs ctest. Each build leg builds the test
executables and uploads them; a single publish_test_results job on Linux
aggregates the JUnit results into one check.

Coverage: Linux x86_64 + aarch64, Windows x64 + arm64, macOS arm64.
macOS x86_64 is deferred (cross-built on arm64, needs Rosetta).

- build_release_vs.bat: "tests" token enables BUILD_TESTS
- build_release_macos.sh: -T builds and runs tests; ORCA_TESTS_BUILD_ONLY
  builds them without running (used by CI)
- scripts/run_unit_tests.sh: parameterized test dir and build config

Addresses #11273.

* ci: bump actions/checkout to v7 in reusable unit_tests workflow

Match the actions/checkout v6->v7 bump (#14517) that upstream applied to
the inline test job this reusable workflow replaces.
2026-07-08 22:00:35 +08:00
Kris Austin
b58025575d fix: isolate calibration temp paths per user (#14619)
* fix: isolate calibration temp paths per user

The calibration temp files under <temp>/calib were file-scope statics
initialized before set_temporary_dir() runs at startup, so they kept
using the shared system temp root and missed the per-user isolation
added in #14607. On Linux every account shares /tmp, so the first user
to calibrate owns /tmp/calib and later users fail to write there, the
same cross-user collision #14607 fixed for model backups, STEP import,
and part skip.

Build the paths lazily from temporary_dir() instead, through a
calib_temp_dir() accessor and a calib_temp_file() join helper. The base
becomes <temp>/orcaslicer_<uid>/calib on Linux and is unchanged on
Windows, where the temp dir is already per user.

Also make StoreParams::path a std::string rather than a non-owning
const char*. The calibration code had to keep a std::string alive
solely to feed that pointer, and the field was uninitialized by
default; owning the string removes the lifetime hazard for all three
callers and makes the entry guard a reliable empty() check. Confined to
the 3mf project exporter (three callers, two internal reads); no
on-disk, format, or ABI impact.

Follows up on #14607 per @Noisyfox's review suggestion.
2026-07-08 21:14:05 +08:00
Mister Anderson
b37fa41742 dedupe-issues.yml remove unsupported claude_args option (#14627)
* dedupe-issues.yml remove unsupported claude_args option

claude_args: is causing an error. Only calling for "--model" anyway, so replaced with model: option.

* Merge branch 'main' into MisterAnderson91-dedupe-issues-yml-update
2026-07-08 21:12:08 +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
anjis
cf86f20ae1 Improve filament change time estimation for CC & CC2 printers and add purge length statistics. (#14663)
* Improve filament change time estimation for CC & CC2 printers and add purge length statistics.

* fix(gcode): route elegoo M6211 handling
2026-07-08 20:48:32 +08:00
anjis
cab62070b5 Fix garbled Chinese filenames when importing ZIP files. (#14633)
* Fix garbled Chinese filenames when importing ZIP files.

* fix: avoid repeated ZIP path CRC calculation
2026-07-08 20:24:55 +08:00
Ian Bassi
12b63ebe36 Localization context (verb noun adjective adverb) (#14646)
* Torre de purga capitalization

* Russian navigation back

* Inches

* verb noun adjective adverb

* Posterior

* Fix atras for camera view

* Camera view
2026-07-08 09:16:00 -03:00