Commit Graph
374 Commits
Author SHA1 Message Date
SoftFever 2b1499a087 clean up comments 2026-08-23 22:43:41 +08:00
SoftFever fcdfcae427 Port mixed filament engine fixes from BambuStudio 2026-08-23 22:11:49 +08:00
Ian Bassi 8fea099d99 BBL Port Color Mix Base 2026-08-23 22:11:48 +08:00
Clifford GarwoodandClaude Opus 5 8edf6b9d4f fix(imex): rotate the prime-tower hull before checking it against carriage zones
imex_wipe_tower_hull() took the axis-aligned box estimate_wipe_tower_polygon()
returns and compared it to the IMEX collision zones as-is. The real tower is
rotated about its anchor corner before placement -- first_layer_wipe_tower_corners
builds the box in tower-local coordinates, rotates about the local origin, then
translates by wipe_tower_x/y -- so a rotated tower's true footprint fell outside
the hull and the placement check passed on a tower that intrudes into a
carriage's reserved space.

The gap was documented in place and previously harmless, because
wipe_tower_rotation_angle was read from the project config after it had been
moved to the print preset, so the setting did nothing. Upstream repaired that
read ("Fix prime tower rotation angle setting not working"), which makes the
angle reachable and the stale hull wrong.

Rotation is applied to the hull alone. estimate_wipe_tower_polygon() still
returns an unrotated box and still leaves ArrangePolygon::rotation unset, since
the arranger consumes that field separately.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-23 01:09:38 -04:00
Clifford GarwoodandClaude Opus 5 a5b2600b2c fix(imex): re-register the IMEX mode icon's bed raycaster after rebuilding it
SceneRaycasterItem keeps the MeshRaycaster it was registered with as a raw
pointer, while PickingModel::reset() destroys it through a unique_ptr. Rebuilding
an icon therefore invalidates any registration still referring to it.

refresh_imex_icon(), reached only from Plater::on_config_change when is_imex or
the bed shape changes, rebuilt the IMEX mode icon without touching the
SceneRaycaster. The stale entry survived, and the next picking pass dereferenced
freed memory inside AABBMesh::intersect_ray.

Swap that one registration in place, matching how calc_vertex_for_plate_name()
handles the name-edit icon. Only the mode icon is registered for picking -- the
warning badge beside it is a plain GLModel -- so a single id is affected and the
blanket remove/re-register reload_scene() performs is not needed here.

Crashes were delayed and looked unrelated to the config change, because bed
raycasters are only tested when the camera looks down (SceneRaycaster::hit). The
reported dump landed on File > New Project, whose render ran a picking pass with
a registration that had gone stale earlier.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-22 12:37:28 -04:00
Clifford GarwoodandClaude Fable 5 8a5bc75e7b fix(imex): key the resolved tool roster and fw-managed flag in the zone/ghost cache
Review follow-up. Removing pem from the ghost cache key also removed its
accidental role as the key's only printer-identity signal: the resolved
active-tools string (roster + primary) and imex_firmware_managed_zones both
shape the baked zone/ghost set but were never keyed directly, so a printer
swap between presets with matching mode names and topology could leave a
stale ghost set. Key all three in build_imex_cache_key, which also hardens
the zone cache against the same pre-existing gap.

Also from review: the tooltip swatch reuses the pem/map its label already
hoisted (one resolution, not two); the bake constructs ghosts with no color
at all, making update_imex_ghost_colors the sole color author; the headless
!m_plater guard is documented as the wxGetApp sentinel it is.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 16:54:48 -04:00
Clifford GarwoodandClaude Fable 5 28c0c681fe refactor(imex): make render the sole author of ghost RGB; scope cache key to mesh inputs
Follow-up to the live ghost-color fix, addressing review findings: with the
render loop restamping ghost colors every frame, the bake-time resolution in
calc_imex_ghosts was dead code (its RGB was displayed for zero frames), and
the pem + head-filament-map entries in the ghost cache key had become
color-only inputs that forced a full mesh re-bake — including a visible
hitch on every ghost-picker selection — for what is now a pure recolor.

- calc_imex_ghosts bakes an alpha-only placeholder; IMEX_GHOST_ALPHA is
  hoisted to file scope as the single opacity authority (no more reading
  alpha back out of the field the restamp overwrites).
- build_imex_ghost_cache_key drops pem and the head-filament map; the
  forced invalidations in set/reset_imex_head_filament_map go with them.
  Picker selections now recolor live with no rebuild.
- The restamp moves into PartPlate::update_imex_ghost_colors(), beside the
  transform refresh, so PartPlate owns its volumes' colors and the canvas
  calls one hook. Plate-level inputs (pem, override map) are hoisted once
  per frame via a new get_imex_head_filament_color overload that the
  single-head form delegates to, keeping tooltip parity by construction.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 16:39:14 -04:00
SoftFever 030e5f469e Merge branch 'main' into cad-mainline 2026-08-13 15:22:08 +08:00
Clifford Garwood 520cf3a415 Merge upstream main: prime tower on non-rectangular beds, toolchange temperature wait, ironing speed override, preset dialog QOL
# Conflicts:
#	src/libslic3r/GCode.cpp
#	src/slic3r/GUI/Tab.cpp
2026-08-06 17:07:02 -04:00
SoftFever 32a4e0fb37 Size the prime tower from the actual flush volumes
Prime towers reserved depth from the prime volume alone, ignoring the flush
matrix: rib-wall towers in both the engine and the preview, and rectangle and
cone towers in the preview, which never carried the flush-aware estimate the
engine already used. The preview also read the print preset, which does not
carry the printer- and filament-scope keys the estimate needs and so silently
fell back to defaults. On multi-nozzle printers the flush matrix, which holds
one block per nozzle, was additionally read as a single block. The tower could
come out too small for the purge it has to hold.

The flush-based estimate also skipped the height-based minimum depth that the
prime-volume one applies, so low-flush prints could estimate a tower shallower
than the one that actually gets built.
2026-08-06 17:58:41 +08:00
SoftFever 7c73739e1a Keep the prime tower and its approach travel on non-rectangular beds
The placement clamps and the tower-approach router both stood in the bed's
bounding box for the bed itself, so on a delta or hexagonal bed the prime tower
could be parked in a corner that does not exist and the nozzle could be routed
across it. Both now test the real printable outline, slicing reports a tower
that does not fit instead of printing it off the bed, and a tower parked near an
edge is routed along the clamped side rather than falling back to a straight
line across the tower.

Also fixes the placement validation rotating the tower hull by degrees read as
radians about the plate origin, and never rotating the generated tower footprint
at all.
2026-08-06 15:48:40 +08:00
Clifford GarwoodandClaude Opus 5 5e38877ba1 refactor(imex): use IMEX consistently in user-facing strings
Labels, tooltips, menu items and dialog text mixed "IDEX/IQEX" with "IMEX"
for the same feature. IMEX is now the user-facing name throughout: IDEX and
IQEX are hardware categories, IMEX is the feature spanning them. The
hardware terms remain only where they help a user tell whether the feature
applies to their printer.

Comments and log messages keep IDEX/IQEX, where the specific carriage
topology is the more precise term.

Also corrects the imex_parallel_mode tooltip, which pointed at a
"Printer -> IDEX/IQEX tab" that does not exist; the options live under
Printer -> Multimaterial -> IMEX Configuration.

None of these strings appear in any .po or the .pot, so no translation is
affected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-03 08:26:38 -04:00
Clifford GarwoodandClaude Opus 5 3852f4be61 fix(imex): block slicing when the prime tower overlaps a parallel-printing zone
IMEX placement validation only walked model instances. The prime tower is
not a ModelObject, so it could sit in a secondary zone or a carriage
collision strip and slice with no warning -- on mirror mode, a carriage
crash. Span (paired-gantry multicolor) is what made towers reachable in
parallel modes, so this is a gap in that feature, not inherited breakage.

The check now returns a cause instead of a bool so the message can name the
offender, and the tower and per-instance paths share one predicate,
imex_hull_violates_zones(), moved to libslic3r and covered by tests.
Overlap is area-based: a hull flush against a zone boundary is legal, only
a crossing violates. The tests pin that in both directions, since switching
to a touch-based test would silently block placements that work today.

The tower footprint comes from the same estimate the scene draws, so
validation matches what the user sees and drags. Three config reads there
are load-bearing in non-obvious ways and are commented at the point of use.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-03 08:26:38 -04:00
Clifford Garwood 3315b88012 Merge upstream main: rib wall prime tower, wipe tower sync, multi-extruder config fixes, plugin resolution fixes 2026-07-31 15:09:53 -04:00
SoftFever 1696d5ca39 Use the squared rib tower size in arrange estimates
estimate_wipe_tower_polygon reserved the arrange footprint and clamped the
tower X position with the raw prime_tower_width, under-reserving space
whenever the rib wall squares the tower to a different width.
2026-07-27 03:08:47 +08:00
Rodrigo Faselli fccd9d52ce Merge branch 'main' into main 2026-07-25 19:19:07 -03:00
Kiss Lorand fc5aec425e Fix stale plate membership during plate grid rearrangement (#14850) 2026-07-25 18:08:12 -03:00
Tommaso BianchiandClaude Opus 5 8c6df84acc Merge upstream/main into cad-mainline (530 commits)
Catches the fork up from 449a4cf9fc (2026-06-28) to d6cb667b89 (2026-07-24).
Upstream touched 2326 files; 17 of them overlap the 164 this branch touches.

16 of the 17 auto-merged, including all three CMakeLists.txt, the build_all.yml
CI workflow, and every GUI file. The CAD core never conflicts: CadDocument,
SketchEngine, SketchSolver, McpControl and test_caddocument are files this fork
adds, so upstream does not touch them.

The one conflict, tests/libslic3r/test_3mf.cpp, was purely additive in all three
hunks and is resolved as a union: our test pinning that store_bbs_3mf embeds the
CAD recipe as Metadata/SnapOrca_cad.bin, upstream's multi-nozzle plate-metadata
round-trip tests, and both sets of includes. All three were verified present
after resolution rather than assumed.

NOT BUILD-VERIFIED, for a reason that predates this merge and is not caused by
it: this fork cannot be configured on nativedev at all. Its CMakeLists has
required Eigen3 5.0.1 since before the merge (line 592 pre-merge), while the
only deps image on the machine is snaporca-deps, built for snaporca's
find_package(Eigen3 3.3). CMake fails at configure, so nothing compiles.

That means this fork's Catch2 suite has never run. Every "suite green" figure
recorded for M1-M8 was snaporca's suite; the ports were verified by patch-apply
plus the CAD sources being byte-identical to snaporca's. Building an orca_cad
deps image with Eigen 5.0.1 is what would finally close that gap.

Pre-merge state is preserved at branch cad-mainline-pre-upstream-2026-07-25
(30d54f0074).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 12:28:33 +02:00
packerlschupfer 705c82688e PartPlate::check_outside: guard m_plater null deref (fix CLI rotate SIGSEGV) (#14415) 2026-07-22 09:50:32 -03:00
Clifford GarwoodandClaude Opus 4.8 f74b8177af fix(imex): repaint plate-selector icon when a plate's slice-ready state flips
The big upstream merge replaced the old per-idle unconditional plate-selector toolbar
refresh with a dirty flag (Plater::mark_plate_toolbar_image_dirty), which the
geometry-change sites already set. PartPlate::update_slice_result_valid_state flips a
plate's slice-ready / IMEX blocked-plate ("naughty plate") state without any geometry
change and did not set the flag, so after the merge nothing repaints that plate's
thumbnail or warning badge — the old per-idle refresh used to mask it. Mark the toolbar
image dirty when the state actually changes, matching the established pattern.

Cosmetic; no effect on slicing. Surfaced by the merge review of 58b4a68a10.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-19 19:10:08 -04: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
Noisyfoxandjun.zhang 5e7ad1ad70 Fix OpenGL framebuffer object support on macOS (#14769)
* Fix OpenGL framebuffer object support on macOS
this enables the outline on macOS

* Revert "Fix OpenGL framebuffer object support on macOS"

This reverts commit 9872708954e785a86209613bbab7cc770d636b7a.

* Enable ARB framebuffer if OpenGL >= 3.0 (OrcaSlicer/OrcaSlicer#14770)

* FIX: plater toolbar image update

jira: STUDIO-12457

Change-Id: Ia855b4bd9bec884d52202f5d8f5cfad9efce0f2f
(cherry picked from commit 474691813b497654ef701fe7073992db366e81a9)

* FIX: thumbinal regen

jira: STUDIO-16732

Change-Id: I4cc4e76b36caf1a41793c1f84ee5e88a44ebdd42
(cherry picked from commit 53f33b7db260005e477842d6459b1b28aa8aa0d8)

* FIX: force update thumbinal once dirty

jira: STUDIO-13041

Change-Id: I770743b53c51351cb3b8a7133140974c75c05364
(cherry picked from commit 96198a12712f21d2b4521b6e3d1caff50bed21ff)

* FIX:thumbnail update

jira: STUDIO-13041 / STUDIO-13304

Change-Id: I9dec25f12454fc8485195cf5dc3feb421b6347c4
(cherry picked from commit cf778dc90f805d3f46e28f8f1d1d5814d3fb4d51)

---------

Co-authored-by: jun.zhang <jun.zhang@bambulab.com>
2026-07-16 11:02:55 +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 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 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
SoftFever e717dea94b Merge branch 'main' into feature/h2c_support_clean 2026-07-11 22:19:59 +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 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 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
Tommaso BianchiandClaude Opus 4.8 0f4060c0a9 Orca-Cad: port SnapOrca Design (parametric CAD tab) onto mainline OrcaSlicer
Grafts the sketch-first CAD environment from snaporca-cad onto the mainline
OrcaSlicer/OrcaSlicer base (vs snaporca's Snapmaker/OrcaSlicer base):
- 133 new files: CadDocument/SketchEngine/GeometryEngine/SketchConstraints/
  SketchSolver/SketchInference/ThreadStandards + vendored libslvs solver;
  DesignPanel/DesignCanvas/DesignSketchTool/SketchInlineEditor GUI; GLGizmo
  Primitive/Sketch; 75 design icons; Catch2 tests.
- Integration hooks ported to mainline's diverged versions: Design tab in
  MainFrame, embedded design viewport + sketch overlay + per-canvas chrome
  suppression in GLCanvas3D/PartPlate, gizmo registration, Plater accessors,
  CMake wiring (libslvs subdir, CAD sources, OCCT ModelingAlgorithms=ON).

Structural integration complete; build verification pending.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BVzKmX6Y1aEteit1HTXG4Q
2026-06-28 12:40:38 +02: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
yw4z b06305f990 Plate toolbar improvements and fixes (slice button, keep view angle on slice, review button for failed plates, percentage and improve failed state) (#14292)
* Update Plater.cpp

* init

* Revert "Update Plater.cpp"

This reverts commit ae515bc7a4.

* fix empty plate showing as failed

* keep zooming on slice

* update

* add hover effect for active plate

* fix condition and match code

* Update GLCanvas3D.cpp

* fix button colors

* add hover effect to border of plate stats button

* fix scrollbar disappear while clicking plate buttons

* Update GLCanvas3D.cpp

* fix slicing percent for new plates

* update

* Update GLCanvas3D.cpp

* update plate states on config change

* fix non functional scrollbar
2026-06-25 22:31:33 +08:00
SoftFever 1e1d9cbaf8 Fix crash on printer switch from stale filament/extruder indices (#14103)
Switching to a printer with fewer filaments (e.g. H2D -> X2D) threw
std::out_of_range in check_filament_printable. Clear stale per-volume
extruder config on count shrink and bound-check filament indices at the
read sites.
2026-06-08 18:57:52 +08:00
Clifford Garwood e502175030 Sync upstream main
124 upstream commits including CrealityPrint integration (added include
in Plater.cpp alongside the existing IMEXHelpers include), profile fixes
and version bumps (#14084, #14085, Polymaker), CI artifact publishing,
test refactors (arachne walls test added; test_3mf/test_config/
test_gcodewriter content moved/removed upstream — IMEX test file
preserved as it is feedback-only), translations.
2026-06-08 01:26:33 -04:00
Ian Bassiandigiannakas ea35b9ce68 Divided filament for features (#14042)
* Implement legacy key handling to address possible profile issues following semantic change of per feature filaments

* Reimport base

* Fix inner wall

* Profiles update

---------

Co-authored-by: igiannakas <ioannis@my-spot.co.uk>
2026-06-05 20:45:43 +08:00
Ioannis Giannakas 9c6a3b1054 Fix crash when deleting filaments after a profile switch (#14055) 2026-06-05 14:27:20 +03:00
Clifford Garwood ed6c608dc2 Sync upstream main
Pulls latest upstream fixes (Default filament option #13887, SEMM
multi-extruder crash #13896, linux wxstring crash #13925, Polymaker
profiles, et al.).
2026-05-29 09:50:18 -04:00
Ian BassiandLixNix 3db37d004a Fix + Support 'Default' filament option (index 0) (#13887)
* Support 'Default' filament option (index 0)

Treat filament index 0 as the new "Default" (use active object/part filament) instead of using 1. Update config defaults and tooltips for wall/sparse/solid infill filament options (min/default -> 0, tooltip explains "Default"). Adjust normalization and propagation logic to respect explicit feature overrides and only apply base extruder when feature values are zero; only copy sparse->solid infill when sparse > 0. Introduce FeatureFilamentOverrideMask and clamp_feature_filament_to_valid to resolve and clamp feature filaments. Update UI lists and selection behavior to expose a "Default" entry and handle zero-based indices in PartPlate and Plater.

* enable_filament_for_features option

Co-Authored-By: LixNix <105106115+lixnix@users.noreply.github.com>

* \n

* Allow wipe_tower_filament to equal nozzle count

Relax the assertion in Print::extruders to permit wipe_tower_filament == config().nozzle_diameter.size(). The configuration value is 1-based and the code subtracts 1 when pushing the extruder index, so equality should be valid and selecting the last nozzle should not trigger an assertion.

* Revert "Allow wipe_tower_filament to equal nozzle count"

This reverts commit 2c976574327a8bcdc74a1b296bf1aaff7752a94e.

* Revert "enable_filament_for_features option"

This reverts commit 01c13baeddb8e26793f752deab788ee4d086975b.

* Migrate legacy feature filament defaults

Add migration logic to convert legacy feature filament selections from 1 to 0 for older 3mf files. Introduces a local migrate_legacy_feature_filament_defaults lambda in src/OrcaSlicer.cpp and src/slic3r/GUI/Plater.cpp that scans keys (wall_filament, sparse_infill_filament, solid_infill_filament, support_filament, support_interface_filament) on configs/objects/volumes, updates values, counts conversions and logs the result. Also adds a Semver check for "2.4.0-dev" in OrcaSlicer to trigger the migration for files older than that version. This preserves expected default filament selections when loading older project files.

* Update OrcaSlicer.cpp

* Extract migration helper to ConfigMigrations

Centralize legacy feature-filament default migration by moving the duplicated lambda into ConfigMigrations::migrate_legacy_feature_filament_defaults (src/libslic3r/Config.cpp) and declaring it in Config.hpp. Update OrcaSlicer.cpp and slic3r/GUI/Plater.cpp to call the new function instead of inline lambdas. The helper converts specific feature filament keys (wall_filament, sparse_infill_filament, solid_infill_filament, support_filament, support_interface_filament) from int 1 to 0 and returns the count of conversions to avoid duplicated migration logic.

* Remove DynamicFilamentList1Based and consolidate lists

Delete the specialized DynamicFilamentList1Based struct and its global instance. Update Choice registrations to use the single dynamic_filament_list for wall, sparse_infill and solid_infill filaments, and remove the extra update call for the removed instance. This consolidates filament choice handling and removes duplicated logic in Plater.cpp.

* move it

* fix objects

* Update Config.hpp

* Update profiles
2026-05-29 10:54:26 +08:00
Clifford Garwood ab63789076 Merge remote-tracking branch 'upstream/main' into feedback
# Conflicts:
#	src/libslic3r/GCodeWriter.cpp
#	src/libslic3r/PrintApply.cpp
#	src/slic3r/GUI/Tab.cpp
#	tests/libslic3r/test_config.cpp
2026-05-13 00:57:36 -04:00
Clifford GarwoodandClaude Opus 4.7 48c603faa5 feat(imex): firmware-managed zones — center slice + ghost suppression
Adds the `imex_firmware_managed_zones` printer-config key (default off) for
IDEX/IQEX printers whose firmware applies its own copy/mirror offsets in
non-primary modes (e.g. RepRapFirmware IDEX duplication mode, Flashforge
Creator Pro 2/3 Pro). For these printers the slicer needs to emit a single
centered slice at bed origin and let the firmware fan toolheads out from there;
the previous slicer-managed iMEX rendering would draw a print at the primary
zone's world position (off-bed for the firmware-fan-out paradigm).

When the flag is on and the active mode is non-primary, the slicer subtracts
the primary zone's plate-local center from the gcode emission frame. The
writer offset is augmented but the gcode-processor offset stays at plate_origin
so the gcode-preview visualizer renders the centered slice at the bed center
rather than at the prepare-view zone placement. translate_to_print_space is
augmented too so first_layer_print_min/max placeholders (consumed by user
start_gcode like Felix's M118 header) reflect the centered frame.

Slice handoff lives in PartPlate::refresh_imex_slice_offset, called from both
update_slice_context (plate switch) and Plater::priv::update_background_process
(every-slice path — reslice() goes through here with switch_print=false so the
plate-switch hook alone wouldn't fire on mode toggle).

calc_imex_ghosts early-returns in firmware-managed mode: the existing
imex_head_transform math places ghosts at primary_zone_center + gantry_offset
(slicer-managed semantics), which renders off-bed when the toolpath is being
emitted in a centered frame. Proper firmware-managed ghost rendering (showing
where copies/mirrors will actually print after firmware fan-out) is deferred.

When the flag is off, all the new code paths reduce to no-ops byte-identical to
prior behavior. Layer 1 unit tests in test_imex_helpers cover every gating path
of compute_imex_slice_offset; full ctest suite passes (247/247).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-13 00:55:45 -04:00
glowstab 9956ad5b48 X2D Support (#13388)
# Description

Adresses #13294 

- Adds the X2D printer definition, machine presets, process presets,
filament presets, BBL profile index entries, CLI config entries,
filament blacklist updates, and printer/load/calibration/cover assets.
- Updates dual-nozzle handling to use configured toolhead labels and
match Bambu X2D hotend placeholders.
- Adds X2D-specific wipe tower cooling placeholder support and 3MF
filament/nozzle change sequence metadata import/export plumbing.

# Note

I own a P2S and an X2D. That's all. I frankly have no idea if my changes
cause regression on other printers, and have no capability to test. I
know that for my X2D, which runs an AMS, .2mm nozzles, SuperTack, and in
LAN mode, this has been working without issue.

# Screenshots/Recordings/Graphs

<img width="606" height="380" alt="Dual nozzle control"
src="https://github.com/user-attachments/assets/0d1c1063-4621-4097-b97c-d739557bf18c"
/>

*Dual nozzle control*

<img width="726" height="260" alt="image"
src="https://github.com/user-attachments/assets/270355b7-ca67-4ca3-ad19-582b8f11411b"
/>

*Multi nozzle filament override*

<img width="416" height="202" alt="X2D Machine config and dual nozzle
support"
src="https://github.com/user-attachments/assets/6a5c07b2-0d20-4819-8f42-d60731313249"
/>

*X2D Machine config and dual nozzle support*

<img width="397" height="142" alt="Filament for Supports test prints"
src="https://github.com/user-attachments/assets/3c7546bd-0e27-4d56-89b7-d9ca18c976f9"
/>

*Filament for Supports has been used in over 20 hours of test prints*

<img width="210" height="263" alt="Left vs Right filament distinction"
src="https://github.com/user-attachments/assets/03322268-b669-4f14-8d77-c4d96843d219"
/>

*Left vs Right filament distinction*

<img width="557" height="327" alt="Custom filament mapping"
src="https://github.com/user-attachments/assets/c1c4396f-7359-474e-80bd-78fec22f9c82"
/>

*Custom filament mapping*

<img width="556" height="314" alt="Auto map"
src="https://github.com/user-attachments/assets/d83e3217-edce-4340-886e-043962003a30"
/>

*Auto map*

<img width="689" height="664" alt="LAN mode send print with X2D preview
and no errors"
src="https://github.com/user-attachments/assets/76009bbf-31d3-4a6c-979c-8643b487c824"
/>

*LAN mode send print with X2D preview and no errors, dual nozzle
selection*


## Tests

- 20 hours of dual-nozzle printing.
- 100% CTest tests passed
- Validated 208 changed JSON files.

<!--
> 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 #13294
2026-05-10 02:21:13 +08:00
Noisyfoxandlane.wei 8c7cc6972f FIX: context: fix the .gcode.3mf not shown issue (#13360)
filament_self_index difference caused

JIRA: no-jira

Change-Id: Ia70fe8a8095b8067fe13bbfc9e5c39d9f3ee05d3
(cherry picked from commit 3a55a3a1eef94f577376cd697c3b72cb46967672)

Co-authored-by: lane.wei <lane.wei@bambulab.com>
2026-05-09 13:15:17 +08:00
Clifford GarwoodandClaude Opus 4.7 6c7bd484db fix(imex): guard IMEX zone refresh against headless CLI slicing
`check_outside` (placement check) calls `ensure_imex_zones()` to make sure
zone geometry is current before deciding if an instance is in-bounds. In
the GUI path that's fine, but the CLI / headless 3MF-load path also reaches
this through `PartPlateList::load_from_3mf_structure -> reload_all_objects
-> add_instance -> check_outside`, and CLI mode has no GUI_App initialized.

`build_imex_cache_key` and `calc_imex_zones` both dereference
`wxGetApp().preset_bundle` — without a GUI_App, `wxGetApp()` returns memory
that segfaults on member access, killing the slicer with SIGSEGV before
any G-code is produced.

Latent since 461c69c83e (Apr 9), surfaced now that upstream's main carries
the headless regression-test CI step (#13353) that exercises CLI slicing
on every PR build.

Fix: short-circuit `ensure_imex_zones()` when `m_plater` is null (already
the GUI/CLI marker used by `calc_imex_ghosts`). Also tighten the existing
`build_imex_cache_key` null check to consult `m_plater` first as defence
in depth, so the function stays safe if reached from another headless
caller.

Verified locally against the upstream regression suite — klipper /
p1s_multicolor / toolchanger_4_color all slice cleanly within the
20% baseline tolerance.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-01 08:14:07 -04:00
Clifford GarwoodandClaude Opus 4.7 1d6fab7a44 fix(imex): pivot Span ghost X-flip on mesh bbox center
The aggregated-mirror ghost was X-flipping about the mesh's local origin,
which shifted the ghost sideways for models whose local origin sits at a
corner (calibration cubes, calicat, most STL imports anchored at the
min corner). Visible as a constant left-X offset between the primary's
position and the ghost's position.

Pivot on `mo->raw_mesh_bounding_box().center()` instead, applied through
the instance transform so rotated objects flip about the rotated bbox
center too. Same correction applied to the live-drag update path.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-01 01:27:47 -04:00
Clifford GarwoodandClaude Opus 4.7 4966d0fae8 feat(imex): Span tile state for paired-gantry multicolor
Introduce ImexRole::Span as a 5th tile cycle state that declares "this
tool is the multicolor partner of Primary on the same gantry." Encoded
as the `S` role suffix in `imex_mode_active_tools` (e.g. `0:P,1:S,2:M,3:M`).
Disambiguates paired-gantry mc-mirror from 4-independent-copies — both
share the same active_tools shape sans the marker.

Span drives:
- Multicolor block rule: now requires Span on primary's gantry to allow
  multi-color slicing in a parallel mode. Replaces the prior "≥2 tools
  on primary's gantry" check; pre-existing 4-tool multicolor configs
  need T1 flipped to Span.
- Ghost aggregation: one ghost per non-primary gantry when Span is
  present, using the column-paired representative. Aggregated-mirror
  drag tracks primary 1:1 in X (gantries don't share an X rail) with
  X-flip baked into mesh-local frame so geometry still reads as mirrored.
- Zone aggregation: one full-X row strip per non-primary gantry instead
  of per-tool quadrants.
- UI: 5th button in IMEXModesCtrl. Cycle Off→P→C→M→S→Off, only offered
  on multi-gantry printers and only on tiles sharing primary's gantry row.

Single source of pairing truth: group_imex_active_tools_by_gantry in
IMEXHelpers, consumed by ghost factory and zone calculator.

Also fixes the carriage collision strip's X-boundary check, which lacked
the row constraint its Y-boundary counterpart already had — paired-gantry
mc-mirror was drawing a spurious right-edge strip from T3 sitting
diagonally from primary.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-01 01:16:02 -04:00
Clifford GarwoodandClaude Opus 4.7 66891899e9 refactor(imex): drop unused Split mode-type infrastructure
The mode-type tag (imex_mode_types config + imex_mode_type_for helper +
Split sentinel) was added in 0bb1cef as scaffolding for the Split rendering
work that landed in 4370cca and then got reverted in d9be71b. With Phase 2-4
gone, this scaffolding is now unused dead code — and the design we settled
on instead is to leave topology entirely implicit (parsed from active_tools_str)
rather than carrying a per-mode type tag the user would otherwise have to
manage explicitly.

The multi-color slicing block + bare T<n> suppression that were the actual
substance of the safeguards work stay in place:
- imex_multicolor_block_reason still allows multi-color exactly when 2+ tools
  are active on the primary's gantry — Felix's hypothetical IQEX paired-gantry
  case works through this path, no new mode type required.
- Slicer-side: bare T<n> stays suppressed at print-start in IMEX parallel modes;
  mid-print T<n> emits naturally for the legitimate IQEX 4-tool-active scenario.

Removed:
- ConfigOptionStrings imex_mode_types (PrintConfig.hpp/cpp + Preset.cpp key list)
- imex_mode_type_for helper + kImexModeType{Primary,Copy,Mirror,Split} sentinels
- mode_type parameter on imex_multicolor_block_reason and the Split short-circuit
- mode_type plumbing in Print::validate and PartPlate::has_imex_multimaterial_conflict
- Three unit tests for imex_mode_type_for + two Split-specific multicolor block tests

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-28 00:48:39 -04:00
Clifford Garwood d9be71b1ca Revert "feat(imex): Split mode rendering + modes editor Type column"
This reverts commit 4370cca4c9.
2026-04-28 00:20:31 -04:00