29633 Commits

Author SHA1 Message Date
Kris Austin
3c0338f462 fix: wipe tower height crash triggered by H2C/A2L support (#14758)
The new "Prime-tower visits..." test from #14685 (H2C/A2L support) throws
"Coordinate outside allowed range" at random on CI, on both Windows arm64 and
Linux x86_64. It's an uninitialized read of WipeTowerData::height.

#10780 (H2D/H2S) added a second wipe tower path, generate_new(), that fills in
depth, bbx, brim_width and rib_offset but not height. The older generate() sets
height, and clear() never did, so on the generate_new path it stays garbage.

first_layer_wipe_tower_corners() passes height to get_wipe_tower_cone_base() as
R = tan(cone_angle/2) * height. The stray bytes are usually zero, so R is zero
and the slice is fine, which is why it passes most runs on every platform. When
they aren't zero the cone radius runs past ClipperLib's limit and the slice
throws. Nothing selects for it, so it just flakes around.

#14685's test is the first to exercise this path, so that's when it started
showing up.

Initializing height in clear() fixes it, same as the m_origin fix in #13712. The
BBL generate_new path has no stabilization cone, so height = 0 is right.
nightly-builds
2026-07-14 11:22:25 +08:00
SoftFever
25216998b3 Add Bambu H2C and A2L machine support (#14685)
# Description

This PR adds full support for Bambu H2C and A2L printers.

All features should work, including pre-heating and cooling.

The Filament Track Switch feature has been ported but hasn't been tested
yet. Feedback is welcome.


# Screenshots/Recordings/Graphs


https://github.com/user-attachments/assets/417f0e32-1967-4536-b732-3c8dc8d4dd4a

<img width="1369" height="999" alt="image"
src="https://github.com/user-attachments/assets/3c3c12ee-69c7-44ef-90f1-badc2a7e803a"
/>

<img width="848" height="580" alt="image"
src="https://github.com/user-attachments/assets/28186a99-979d-4059-b40e-798162c30ce2"
/>

<img width="892" height="551" alt="image"
src="https://github.com/user-attachments/assets/22823921-f22d-4c4d-9b89-43070120d8a6"
/>


## Tests


- [x] Multi-color/material test printing on H2C
- [x] Test printing on A2L
- [x] Test printing on P1S
- [x] Test printing on other tool changers to confirm no regressions
~~- [ ] Optional: test with newer plugin. Note: existing v02.03.00.62
plugin works without any issues~~ Will address as separate task

[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
2026-07-14 02:55:08 +08:00
SoftFever
74813b401f Merge remote-tracking branch 'origin/main' into feature/h2c_support_clean
# Conflicts:
#	localization/i18n/fr/OrcaSlicer_fr.po
#	localization/i18n/list.txt
2026-07-14 02:54:20 +08:00
SoftFever
df1e114b72 resolve current_filament_id/current_nozzle_id in layer_change_gcode - enhanced 2026-07-14 02:12:09 +08:00
SoftFever
56f672ecbd resolve current_filament_id/current_nozzle_id in layer_change_gcode (fixes X2D) 2026-07-14 01:26:34 +08:00
SoftFever
9aa90fa6a8 update BBL H2D and X2D profiles 2026-07-14 00:52:54 +08:00
foXaCe
ec96ca17b8 i18n(fr): align walls with the glossary and complete the catalog (#14623) 2026-07-13 13:52:33 -03:00
Alexandre Folle de Menezes
174f043f67 Changes in pt-BR to conform to the translation Glossary (#14629) 2026-07-13 13:17:57 -03:00
Alexandre Folle de Menezes
3395d87240 pt-BR translation fixes after added context (#14679)
Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
2026-07-13 13:17:15 -03:00
Felix14_v2
32216dc931 Update OrcaSlicer_ru.po (#14745) 2026-07-13 12:35:30 -03:00
SoftFever
2268544a68 fix test error on Windows ARM64 2026-07-13 23:26:16 +08:00
foXaCe
c41e685a69 i18n: wrap hardcoded GUI strings (undo/redo labels, dialogs, notifications) + FR (#14729) 2026-07-13 12:19:33 -03:00
SoftFever
35ffe49b17 Fix sending print jobs to Bambu H2C printer 2026-07-13 18:13:15 +08:00
packerlschupfer
88262ee504 CLI: guard 4 null derefs when loading a 3mf without preset ids (#14580)
CLI: guard 4 null derefs when loading a 3mf with no preset ids

At OrcaSlicer.cpp:1700-1704 the post-load block reads printer_settings_id,
print_settings_id, filament_settings_id, and nozzle_diameter from the
config that the 3mf carries. If the 3mf is a BBL/BBS-flavored 3mf but
was produced by a non-GUI writer (e.g. CLI --export-3mf without a
loaded preset) any of those keys can be absent, and config.option<T>(...)
returns nullptr — the ->value / ->values deref then SIGSEGVs.

Wrap each optional lookup in an if-let. printer_model, printer_extruder_variant,
and print_extruder_variant already pass create_if_missing=true and are safe.

Repro (BEFORE this patch):
  orca-slicer --export-3mf out.3mf in.stl   # produces preset-less 3mf
  orca-slicer --info out.3mf                # SIGSEGV at :1700
  bt: __cxx11::basic_string::_M_assign
    -> Slic3r::CLI::run @ OrcaSlicer.cpp:1700

AFTER: --info out.3mf returns exit 0 with the mesh summary.

The same failure mode affected --inspect-mesh, --inspect-paint, and
every other action that has to walk the loaded model's config; --slice
would only survive because it always injects a printer via
--load-settings.
2026-07-13 15:10:33 +08:00
bobomb
4eb9a85a49 Fix filament sync targeting wrong printer after switching printer presets (#12506) (#14587)
For non-BBL host printers (Moonraker/Klipper, Qidi, Snapmaker, Creality), switch_printer_agent() only re-selected the machine when the agent type changed. Switching between two printer presets that use the same agent left the selected machine and the agent's cached device_info pointing at the previously active preset's host, so filament sync kept hitting the old printer.

Re-select the machine when the agent type is unchanged but the target host differs, so the selected machine and device_info always follow the active printer preset.

Co-authored-by: Noisyfox <timemanager.rick@gmail.com>
2026-07-13 15:08:53 +08:00
Walter Almada
5a53d2eb88 Fix use-after-free crash on exit during GLCanvas3D teardown (#14588)
Plater's pImpl (unique_ptr<priv> p) is destroyed before the wxWindow base
destructor runs DestroyChildren(), so child GLCanvas3D windows are torn down
after p is gone. GLCanvas3D::~GLCanvas3D() -> reset_volumes() then dereferences
the freed p through two paths:
  - Selection::clear() -> plater()->canvas3D() -> p->get_current_canvas3D()
  - _set_warning_notification() -> plater()->get_notification_manager()

Guard both with the existing wxGetApp().is_closing() flag; both are UI-only
side effects that are no-ops during shutdown, so normal-use behavior is
unchanged.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 14:54:42 +08:00
Noisyfox
72005959d4 Fix issue that extruder 2 tab in printer settings uses the wrong nozzle variant id after loading 3mf (#14572) 2026-07-13 14:42:21 +08:00
Noisyfox
113d488e6f Fix text rendering issue when using MacType (#14696)
Fix text rendering issue when using MacType, with the original harmony os sans installed globally (OrcaSlicer/OrcaSlicer#14566)

The new fonts are generated using fonttools that only add gasp table to the original font without touching other parts
2026-07-13 14:40:54 +08:00
Noisyfox
64a321528d Fix issue that unable to open 3mf again once the 3mf action dialog is cancelled (#14715) 2026-07-13 14:40:24 +08:00
Noisyfox
6a254c35d4 Make printer profile swithing faster (#14437)
* Fix issue that switching printer profile is slow.
`wxGetApp().get_tab(preset_type)->select_preset(preset_name);` is called twice when switching printer profiles. Only one needed.

* Avoid unnecessary type conversion & function call during printer profile switching:
- Don't call `config->opt_string("printer_model")` repeatedly
- Use ref when possible during iterating
- Avoid unnecessary `wxString` to `std::string` conversion
2026-07-13 09:52:18 +08:00
Rodrigo Faselli
79aa0bd4a2 Merge branch 'main' into feature/h2c_support_clean 2026-07-12 22:00:40 -03:00
Rodrigo Faselli
78692aa08f Interactive shadows (#14702)
Co-authored-by: Ian Bassi <12130714+ianalexis@users.noreply.github.com>
Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
2026-07-12 21:44:30 -03:00
SoftFever
30aa7d9557 feat(gui): warn when the hotend rack inventory falls short of the sliced plate
Follow-up to the rack-aware pre-print checks: after the blocking checks
pass, warn (without disabling Send) when the plate needs more matching
hotends than the rack printer currently holds - suggesting rack setup,
a nozzle info refresh, or a re-slice to avoid filament waste - or when
the only matches rely on unreliable nozzle information.

Text-only warning rows; this message board has no refresh or
don't-show-again buttons.
2026-07-13 00:55:52 +08:00
SoftFever
2fecfc291f fix(gui): rack-aware pre-print nozzle checks in the send dialog
A print sliced for a nozzle that sits in the hotend rack (but is not
mounted) was blocked by the send dialog's mounted-nozzle diameter check,
even though the printer fetches the required nozzle itself (#14685).

Consolidate the three mounted-nozzle gates (_is_nozzle_data_valid,
is_nozzle_type_match, _is_same_nozzle_diameters) into a single
CheckErrorExtruderNozzleWithSlicing fed by s_get_slicing_extuder_nozzles,
which collects the plate's per-extruder nozzle requirements (hybrid
extruders contribute one entry per used sub-nozzle flow). The rack
extruder validates against its whole inventory (mounted + rack) with
guidance to calibrate the rack, refresh nozzle info, or re-slice, and
blocks when toolhead + rack are full (no free slot to stow a nozzle).
Other extruders keep the validity/flow/diameter checks against the
mounted nozzle.

Also add CheckErrorRackStatus, which holds Send while the printer is
still reading the rack hotend information, and judge material hardness
for the rack extruder per dispatch-mapped nozzle as a non-blocking
caution (mounted nozzles keep the blocking gate).
2026-07-13 00:55:16 +08:00
Rodrigo Faselli
b1e64dc69c Decouple Smooth Normals from Realistic View (#14724) 2026-07-12 13:30:55 -03:00
Kiss Lorand
3ae7bd04fa Fix access violation during travel generation before initial tool selection (#14725) 2026-07-12 13:30:29 -03:00
Ian Bassi
7e2047aa30 Rename align_infill_direction_to_model (#14734) 2026-07-12 12:29:54 -03:00
Ian Bassi
c4ea86041b Separated Infills enhancements (#14674)
Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
2026-07-12 11:45:00 -03:00
Ian Bassi
bfbf591613 Wiki links (#14731) 2026-07-12 11:16:29 -03:00
SoftFever
e98750e713 fix(engine): real-change-only toolchange ordinals and a single M1020 toolchange command
A dual-nozzle H2C print with support filament hangs at its first nozzle
switch. The emitted file shows the change-filament block's M620 O ordinal
jumping from O1 straight to O230, plus a duplicate "M1020 S<n>" toolchange
command right after every change block. Two causes, fixed together because
they interlock (the ordinal check keys off the same toolchange detection
that suppresses the duplicate):

- append_tcr incremented m_toolchange_count once per prime-tower visit
  (roughly once per layer), while the change-filament template only emits
  its M620 O{toolchange_count + 1} line on real filament changes. With 229
  change-less sparse tower layers below the first support layer, the first
  real change reported ordinal 230. The counter now advances only when the
  expanded change block really contains a toolchange command, and the
  placeholder exposes the upcoming change's ordinal (count + 1). The
  set_extruder path already counted per real change and is unchanged.

- toolchange_prefix() returned "M1020 S" for BBL printers, so the
  custom_gcode_changes_tool() dedup could never match the stock profiles'
  line-leading "T[next_filament_id] ..." commands and the writer's own
  toolchange was appended after every change block on dual-extruder
  machines. The prefix is now the plain "T" (the manual-filament-change tag
  branch stays first), and the M1020 form moved into GCodeWriter::toolchange()
  as an explicit branch that also carries the nozzle:
  "M1020 S<filament> H<nozzle>". The nozzle parameter is signed on purpose:
  the null-safe nozzle lookup legitimately yields -1, matching the stock
  templates' own H-1 convention.

The prefix change also lets the CoolingBuffer recognize the change blocks'
T commands as tool boundaries on BBL printers (its per-filament attribution
previously keyed off the duplicate M1020, or nothing at all on
single-extruder models); its existing out-of-range guard ignores
T1000-class machine commands.

Verification: full suites green (libslic3r 48998 assertions / 169 cases;
fff_print 692 / 65 including three new scenarios - writer emission per
printer kind, dedup + ordinal progression on sequential prints, and a
prime-tower regression scenario verified to fail against the old per-visit
counting). Byte gate: 18 of 20 fixtures bit-identical; the sequential repro
differs by exactly its 3 removed duplicate M1020 lines, deterministic
across two runs. Reslicing the field project that exposed the hang yields
M620 O1 followed by a gapless O2..O59 and zero duplicate M1020 lines.

Co-authored-by: songwei.li <songwei.li@bambulab.com>
2026-07-12 21:30:12 +08:00
SoftFever
3ebd68d959 feat(engine): per-nozzle-variant machine limits in the time estimator
The time estimator's speed/acceleration limits were indexed by time
mode only, reading slot 0 of the per-(extruder x volume-type) arrays
the multi-extruder profiles already carry (H2C 0.4: 8 entries, H2D
0.4: 10). Every move was therefore modelled with the first machine
slot's limits regardless of which nozzle variant was printing -
estimation fidelity only, since emitted feedrates/accelerations are
decided on the slicing side.

Now the estimator resolves the machine slot of the nozzle currently
mounted in the active extruder: the nozzle grouping context is handed
to the processor BEFORE the streaming replay (new member + setter -
deliberately separate from the post-stream result-field handover that
gates the richer change-time model, whose timing is unchanged), the
occupancy recorder is populated on every filament change (bookkeeping
decoupled from the gated time model; recorder writes have no time
effect), and get_machine_config_idx maps (volume type x extruder type
x extruder) to the slot via the printer's variant layout, newly
carried on the processor result. The feedrate/acceleration getters
gain a slot parameter indexing [slot*2 + mode]; jerk and the
print/travel/retract accelerations stay mode-only. Reloaded sliced
projects re-estimate with the result's saved grouping context;
imported bare g-code degrades to slot 0 - the historical read.

M201/M203 write the parsed value into EVERY slot's mode entry (a
firmware envelope change is global), which keeps per-slot reads in
lockstep with the mode-only reads they replace: the fleet emits
envelope lines before any motion, so estimates - hence the estimated
time header, M73 lines, and every other byte - are unchanged (20/20
pinned-slice byte gate bit-identical, incl. the sequential repro
sliced twice). Fidelity improves where envelope emission is off or a
migrating per-layer plan moves filaments across variants.

Tests: a stub-driven processor case proving the slot follows the
active nozzle through the exact production path (T..H.. commands,
fallback recorder bookkeeping, 4x time ratio on the slow variant),
that emitted M201/M203 reach every slot, and that a missing context
degrades to slot 0. Suites green (libslic3r 48998/169, fff_print
667/62).
2026-07-12 14:48:45 +08: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
yw4z
e1c0ea0cc4 UI fixes & improvements (#14186)
* titlebar menus

* sidebar separators

* Update BonjourDialog.cpp

* Update StepMeshDialog.cpp

* Z contouring icon

* Update AmsMappingPopup.cpp

* Update AmsMappingPopup.cpp

* sync ams dialog

* Update calib_dlg.cpp

* sync popups

* Update AmsMappingPopup.cpp

* operation section on gizmos

* Update PresetComboBoxes.cpp

* Update GCodeViewer.cpp

* Update GCodeViewer.cpp

* Update AboutDialog.cpp

* disable realistic view option on gcode preview

* Update Preferences.cpp

* fix nozzle text clipping on linux
2026-07-11 20:24:03 +03: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