Commit Graph
280 Commits
Author SHA1 Message Date
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
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
Clifford GarwoodandClaude Opus 4.7 4370cca4c9 feat(imex): Split mode rendering + modes editor Type column
Completes the Split-type IMEX mode plumbing started in 0bb1cef. Phase 1
landed the config option, helper, validator, and plater badge updates;
this commit lands the visualization side and the editor surface so users
can actually create and use Split modes.

Zone aggregation (PartPlate::calc_imex_zones)
- When mode_type == "split" and there's row separation between primary
  and secondaries, collapse copy_cells and mirror_cells per-gantry
  (each cell gets primary's column) and force has_col_sep = false.
  make_boxes then takes its full-X-row branch and renders one zone
  covering each non-primary gantry's full Y band, instead of per-tool
  quadrants.

Ghost aggregation (PartPlate::calc_imex_ghosts)
- Pre-scan the active mode's tool list to pick a canonical head per
  non-primary gantry (the tool whose physical column matches primary's;
  fallback to first-seen). For Split modes, the per-head emission loop
  skips non-canonical tools so each non-primary gantry gets exactly one
  aggregate ghost rendered at the canonical's mirrored position.
- update_imex_ghost_transforms unaffected — it iterates the already-
  filtered ghost set.

Modes editor Type column (IMEXModesCtrl in Tab.cpp)
- Each non-primary mode row gains a wxChoice dropdown selecting Copy /
  Mirror / Split. Primary row gets a static "Primary" label.
- Header row picks up a "Type" column header with sized spacer that
  aligns with the dropdown.
- get_mode_data() now returns a fourth tuple element (types vector).
  load_from_config() reads imex_mode_types via imex_mode_type_for so
  legacy presets without the new option still infer types from mode
  names. on_change writes imex_mode_types back to config.
- matches_config(), snapshot_row(), row_differs_from_parent(), and
  reset_row_to_parent() all extended to track type alongside name/
  tools/gcode — per-row reset arrows reflect type-only changes, and
  reset restores the parent's type via imex_mode_type_for fallback.
- Type-choice dropdown fires notify() on change so the dirty/save flow
  catches it like any other row edit.

End-to-end: switching a row to Split type in the modes editor, then
selecting that mode on a plate, allows multicolor slicing (the gantry-
pair check is bypassed in imex_multicolor_block_reason) and the bed
visualization shows a single aggregate zone + one ghost per non-primary
gantry instead of per-tool clutter.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-27 23:17:24 -04:00
Clifford GarwoodandClaude Opus 4.7 0bb1cef78e feat(imex): polish UI per PR feedback + add parallel-mode safeguards
Felix14-v2 PR review feedback (https://github.com/OrcaSlicer/OrcaSlicer/pull/13086#issuecomment-4323696312)
plus the slice-time validation work that follows from his bug list.

UI polish:
- Capitalize "Primary" in display (right-click mode menu, plate tooltip).
  Sentinel stays lowercase for wire compatibility.
- Pre-slice-warnings checkbox now uses Orca's ::CheckBox so it matches the
  green toggle style of the rest of the IDEX/IQEX configuration page.
- DPI-scaled the IMEXModesCtrl (modes editor) sub-panels, button grid,
  text wraps, gcode textarea, and the ghost-tooltip swatch (imgui.scaled).
  Legend swatches sized to body-text height for visual balance.
- Primary mode tool buttons in the modes editor are now disabled (read-
  only): cycling roles on the IMEX-off mode is a no-op and confusing.
- Modes editor sub-panels now explicitly inherit the app's window-default
  dark colour so chromeless ScalableButtons don't render with a visible
  light box around their icons on GTK dark themes.
- Per-mode-line reset arrows in the modes editor: each row gets a small
  reset bitmap that snaps that row's name+tools+gcode triplet back to the
  saved preset's value. matches_config() guard on the page-level reload
  prevents the textbox-being-typed-into from being destroyed mid-keystroke.
- New View menu item "Show IDEX/IQEX Toolhead" — toggles the per-carriage
  footprint boxes during G-code preview playback. Gated to Preview tab +
  IMEX printer; backed by app_config so it persists.

Coordinated config migrations:
- imex_tool_layout and imex_viz_theme migrate from coString to coEnum
  (ImexToolLayout / ImexVizTheme). Existing wire format preserved so
  saved presets and 3MFs deserialize unchanged. Side-benefit: both now
  pick up standard Field rendering and so finally show reset arrows.

Slice-time safeguards (the validation half):
- imex_suppresses_bare_toolchange(parallel_mode, count): suppresses the
  slicer's bare T<n> at print-start in any IMEX parallel mode (the user's
  imex_mode_gcode + machine_start_gcode owns tool activation there). Mid-
  print T<n> emits normally — Print::validate blocks the configurations
  where mid-print T<n> wouldn't make sense. Applied to both code paths
  inside GCode::set_extruder (the long multi-extruder path AND the
  single-extruder path that fires when multiple_extruders=false).
- imex_multicolor_block_reason(): hard-stop validator returning a user-
  facing reason string when the active IMEX configuration can't physically
  support multi-color. Catches IDEX (1 tool/gantry), 2-tool-active IQEX
  (no within-gantry swap topology), and any MMU/AFC lane sharing among
  used filaments. Wired into Print::validate as a slice blocker, and into
  PartPlate::has_imex_multimaterial_conflict so the plater badge agrees
  with the slice block (no more false positives where the badge warns but
  the slice goes through).
- New imex_mode_types config option (parallel array to imex_mode_names)
  and imex_mode_type_for() helper. Mode-type tag drives behaviour: zones,
  ghosts, and validation interpret modes differently per type. Initial
  types: "primary", "copy", "mirror", "split". Split modes are explicitly
  designed for paired-gantry IQEX multi-color and bypass the gantry-pair
  check in imex_multicolor_block_reason (MMU sharing still blocks them).

Test coverage:
- New unit tests cover imex_suppresses_bare_toolchange (4 cases),
  imex_multicolor_block_reason (8 cases including IDEX, IQEX 2-/4-tool-
  active, MMU sharing, missing primary, Split type), and imex_mode_type_for
  (3 cases including legacy fallback). Total: 186 IMEX assertions across
  71 test cases, all passing.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-27 22:51:05 -04:00
Clifford GarwoodandClaude Opus 4.7 7dcb74e504 docs(imex): document inputs of build_imex_cache_key
The IMEX zone cache key drives ghost rebuild and zone-overlay
invalidation. Adding a printer config option that affects zone geometry,
ghost transforms, or collision strips without extending the key produces
a silent staleness bug: the cache thinks the zones are still valid and
ghost meshes / overlays don't refresh after the new option changes.

Document what currently feeds the key and pin the precision convention
(*10 scale on float values for 0.1 mm resolution) so future contributors
know the contract and where to extend it.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 00:32:42 -04:00
Clifford GarwoodandClaude Opus 4.7 085f5ccec8 refactor(imex): replace "primary" magic string with kImexPrimaryMode constant
Across 6 files the literal "primary" string was the sentinel for "no IMEX
parallel mode active" — used for short-circuiting in serialization, ghost
visualization, zone calc, popup-menu list construction, the IMEXModesCtrl
non-deletable first row, and several layer-of-export checks. A typo in
any one would silently bypass the guard.

Define kImexPrimaryMode in IMEXHelpers.hpp with a docstring describing
what equality with it means semantically, and route every call site
through it. No behavior change.

Touched: bbs_3mf.cpp, GCode.cpp, PartPlate.cpp, Plater.cpp, Tab.cpp.
The bbs_3mf and Tab files now include IMEXHelpers.hpp; the other three
already did.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 00:32:22 -04:00
Clifford GarwoodandClaude Opus 4.7 efa9d65cb4 fix(imex): code-review nits — null check, cache-key precision, lambda capture
Four quick-fix items surfaced by pre-PR self-review.

GCodeViewer.cpp:1607
  Null-check get_curr_plate() before dereferencing. Other call sites in
  the file already guard; this was the only unguarded one in the IMEX
  layer-preview path. In practice m_plate_list always has a plate, but
  the inconsistency is easy to fix and removes the only ungated deref.

PartPlate.cpp:build_imex_cache_key
  Cache key for IMEX zone geometry truncated nozzle_clearance_x/y to int
  before stringifying — a config change from 30.0 to 30.5 would not
  invalidate the cache. Match the *10 precision pattern already used for
  imex_carriage_margin so 0.1 mm steps invalidate correctly.

Plater.cpp:select_plate_by_hover_id (right-click popup)
  Two issues:
  1. Lambda captured `modes` by reference. PopupMenu() is synchronous
     today so the reference outlived the menu's event handling, but the
     pattern is fragile — anyone refactoring to async Popup() would
     silently dangle. Capture by value.
  2. Used wxID_HIGHEST + i for menu item IDs — standard wx anti-pattern
     because it can collide with other handlers listening in that range.
     Allocate per-item IDs via wxNewId() and look up the chosen mode by
     finding the event ID in a parallel vector. The lookup becomes O(N)
     instead of O(1) but N is small (mode count) and this is clicker
     latency, not a hot path.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 00:07:35 -04:00
Clifford GarwoodandClaude Opus 4.7 9a87b96ded refactor(imex): Mirror reflects about zone-boundary plane, not primary origin
Replace imex_head_transform's fifth argument (Vec3d primary_origin) with a
Vec2d primary_zone_center and rewrite the Mirror branch as a true reflection
about the plane x = primary_zone_center.x + gantry_offset.x/2. Previous math
flipped about the primary's current origin, which:
  * let the ghost drift out of the target zone as the primary moved, and
  * made mirrored drag motion track 1:1 with the primary instead of reflecting.

The new transform places the ghost at the mirrored position within the target
zone (matching where the mirror tool actually prints) and reflects drag so
primary +X → ghost -X while Y tracks 1:1 — i.e. the ghost stays a true
mirror while the user drags. Off-row Mirror targets (e.g. T3 on a 2x2) still
reflect across the same X-plane as on-row peers.

PartPlate::calc_imex_ghosts and update_imex_ghost_transforms now feed primary_off
(the primary head's zone center) instead of an instance-space Vec3d.

Mirror tests rewritten against the new geometric contract: ghost origin at the
reflected position, primary drag deltas reflected across the zone-boundary plane.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 22:51:56 -04:00
Clifford GarwoodandClaude Opus 4.7 6a3de6a28f feat(imex): effective_physical_extruder_map helper + diagnostic no-filament tooltip
Centralize the project→printer→printer_extruder_id fallback for the physical
extruder map. PrintApply, PartPlate (ghost color + cache key), Plater (tooltip
+ click gate) all previously open-coded the three-step lookup, and each handled
the "pem unset, derive from pei" case slightly differently — an IDEX printer
without an explicit pem could paint an UNPRINTABLE_COLOR ghost even though the
slicer would have derived a valid mapping.

- IMEXHelpers: add effective_physical_extruder_map(explicit_pem, pei) and a
  PresetBundle overload that wraps the project→printer precedence.
- PrintApply: use the helper in place of the inline pei→pem normalization.
- PartPlate / Plater: call the PresetBundle overload at every ghost-color,
  ghost-cache-key, tooltip, and click-gate site.
- Plater::format_imex_ghost_tooltip: when no filament resolves to a head,
  surface an actionable message directing the user to extend the extruder
  count in the Machine tab, instead of the generic "(no filament routed)".
- IMEXFilamentPickerPopover: hold m_pem by value so callers can pass a
  stack-local derived pem without lifetime worries.
- Tests: 5 new cases covering explicit-wins, default-pem fallback, null
  inputs, and the IDEX ghost-color regression that motivated this.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 22:51:31 -04:00
Clifford GarwoodandClaude Opus 4.7 b2128cc330 refactor(imex): route three inline tool-state parsers through parse_imex_active_tools
PartPlate::calc_imex_zones, GCodeViewer::render, and Plater::collect_imex_warnings
each hand-rolled their own "phys:P/C/M" tokenizer with subtly different error
handling. Replace the three inline loops with parse_imex_active_tools +
imex_primary_tool_for_mode so the Primary/Copy/Mirror classification agrees
across zones, the G-code viewer legend, and slice warnings.

No behavior change: the shared helpers preserve the 1=Primary / 2=Copy /
3=Mirror encoding already consumed downstream and continue to accept the
legacy bare-index form as Primary.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 22:49:08 -04:00
Clifford Garwood 77c32a2e15 feat(imex): plate ghost renderer + per-plate filament map
Replaces the plater-icon popover with colored transparent ghost copies
of primary-head instances on the plate, one per secondary active head
under its Copy/Mirror role transform. Left-click on a ghost opens a
compact filament picker popover for the ghost's head (MMU lane override).
Ghosts track the primary through drag/rotate/scale/mirror and invalidate
on mode or pem changes.

Key pieces:
  IMEXHelpers -- imex_head_transform (Primary/Copy/Mirror), shared role
    parser, per-head filament resolution with X-axis Mirror anchor.
  PartPlate -- ghost state, volume rebuild on mode/map/object mutation,
    primary_origin plumbed for Mirror reflection across the primary-row
    gantry plane.
  GLCanvas3D -- ghost rendering with per-head filament color and
    translucent blending; picking routed via volume composite id.
  Plater -- ghost click + tooltip; plater icon left-click always cycles.
  IMEXFilamentPickerPopover -- BitmapComboBox row for one secondary head,
    writes imex_head_filament_map on selection.
  bbs_3mf -- round-trip the per-plate imex_head_filament_map option.
  PrintConfig -- add imex_head_filament_map as a plate option.

MMU/AFC routing for parallel modes relies on the printer profile's
physical_extruder_map (see prior commit for authoring format). Primary-
row heads and their per-plate filament overrides are resolved through
that map, so PA and temperature emission address the correct physical
extruder when multiple logical slots share one carriage.

Tests: IMEXHelpers coverage for Primary/Copy/Mirror transforms
including a 2x2 off-row regression guard for the X-axis reflection fix.
2026-04-21 14:56:22 -04:00
Clifford Garwood 481cd7d8e7 Merge remote-tracking branch 'upstream/main' into feedback 2026-04-21 14:56:08 -04:00
Clifford GarwoodandClaude Sonnet 4.6 ad0b42fd37 feat: iMEX temperature handling and pre-slice warnings
Temperature emission (GCode.cpp, PrintConfig.cpp, ConfigManipulation.cpp):
- Set temperatures for all active tools in layer_change_gcode for iMEX
  parallel modes (primary + secondary carriages)
- Fix filament temperature commands so Layer 1 temperatures only emit on
  the first layer; subsequent layers use normal layer-change temperatures
- Remove stray temperature commands that fired outside the intended context
- Consolidate iMEX temperature handling into the second-layer transition;
  clean up emission logic and naming throughout

Pre-slice warning dialogs (PartPlate.cpp/hpp, Plater.cpp):
- Collect per-plate IMEX warnings before slicing: multi-material conflict,
  bed temperature mismatch between carriages (>5 °C delta), and filament
  type incompatibility
- Show a dismissible Yes/No dialog from both "Slice Plate" and "Slice All"
  actions; No returns to 3D view, Yes proceeds to slice
- Refresh plate thumbnails after the panel switch so previously-generated
  thumbnails are not left black

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 17:45:11 -04:00
SoftFever 3e4af2c723 WIP: Add native Wayland support for Linux (#13197)
* Add runtime display backend detection for Wayland support

Add LinuxDisplayBackend utility to detect X11 vs Wayland at runtime
using GDK_IS_X11_DISPLAY / GDK_IS_WAYLAND_DISPLAY macros. This is
the foundation for removing the forced GDK_BACKEND=x11 and enabling
native Wayland support.

- New files: LinuxDisplayBackend.hpp/.cpp with get_linux_display_backend(),
  is_running_on_wayland(), and is_running_on_x11()
- Propagate wxHAVE_GDK_X11 / wxHAVE_GDK_WAYLAND from FindGTK3.cmake
  as compile definitions to libslic3r_gui
- No-op on non-Linux platforms (returns Unknown / false)

* Fix Phase 1 code quality: pragma once, source ordering, static cache

* Make X11 initialization conditional for Wayland support

Remove the unconditional GDK_BACKEND=x11 force that blocked native
Wayland. Replace with conditional logic:

- EGL safety fallback: re-force X11 only when wxUSE_GLCANVAS_EGL is
  off and WAYLAND_DISPLAY is set, with a warning log
- XInitThreads() only called when DISPLAY is set (X11 in use)
- __GLX_VENDOR_LIBRARY_NAME only set when DISPLAY is present (GLX-specific)
- WEBKIT_DISABLE_COMPOSITING_MODE only set under XWayland (both
  DISPLAY and WAYLAND_DISPLAY present)
- Guard X11/Xlib.h include with __has_include for robustness
- Restore display validation to accept either DISPLAY or WAYLAND_DISPLAY

This is Phase 2 of the Wayland support plan.

* Fix Phase 2: safer EGL macro check, add clarifying comments

* Add GLAD2 library and replace GLEW linkage in build system

Set up GLAD2 as a static library to replace GLEW for OpenGL loading.
GLAD2 supports both GLX and EGL, which is required for Wayland support.

- Create src/glad/ with pre-generated GLAD2 sources (GL 4.6 compat)
- Add src/glad/CMakeLists.txt building glad as a static library
- Wire glad into src/CMakeLists.txt before libvgcode
- Modify libvgcode to use shared glad for GL path (keeps local copy
  only for GLES2/Emscripten) to avoid duplicate symbol conflicts
- Replace GLEW::GLEW with glad in libslic3r_gui link libraries

Note: GLEW is kept in deps for OpenCSG. Code migration from GL/glew.h
to glad/gl.h headers will follow in Phase 3B+3C.

* Fix Phase 3A+3D: libvgcode GLAD include, dead files, dlopen dep, OpenGL link var

* Migrate from GLEW to GLAD: replace headers and API calls across codebase

Replace all #include <GL/glew.h> with <glad/gl.h> across 49 source files.
Migrate GLEW API calls to GLAD equivalents:
- glewInit/glewExperimental -> gladLoaderLoadGL()
- GLEW_EXT_* / GLEW_ARB_* extension checks -> GLAD_GL_EXT_* / GLAD_GL_ARB_*
- Remove GLEW-specific EGL/GLX mismatch #error guards (not needed with GLAD)
- Replace unavailable EXT symbols with core GL equivalents in
  GLCanvas3D.cpp (GL_MAX_SAMPLES, glRenderbufferStorageMultisample,
  glBlitFramebuffer, GL_READ/DRAW_FRAMEBUFFER)
- Update log messages from glewInit to gladLoadGL

* Fix Phase 3B+3C: remove GLEW find, clean EXT symbols, update attribution

- Remove find_package(GLEW) block from root CMakeLists.txt since GLEW
  is no longer linked by any main application code
- Remove "glew" from SLIC3R_STATIC option description
- Replace all remaining EXT framebuffer symbols with core equivalents
  in render_thumbnail_framebuffer_ext and _rectangular_selection_picking_pass
- Update AboutDialog credits from GLEW to GLAD

* Enable EGL in wxWidgets and add runtime GLX/EGL selection for Wayland

- Set wxUSE_GLCANVAS_EGL=ON in wxWidgets build and Flatpak manifest
- Add PreferGLX() call on X11 sessions for driver compatibility
- Remove Phase 2 safety fallback (EGL is now always compiled in)
- Guard SwapBuffers against hidden canvases to prevent Wayland stalls

* Fix Phase 4: move PreferGLX to app startup, fix FPS counter guard

Move wxGLCanvas::PreferGLX() from OpenGLManager::create_wxglcanvas()
(static initializer) to GUI_App::on_init_inner() before any wxGLCanvas
is constructed. This prevents a race where SkipPartCanvas could trigger
wxGLBackend::Init() before the GLX preference is set. The new location
also adds explicit is_running_on_wayland() detection with a warning for
unknown backends.

Move increment_fps_counter() inside the IsShownOnScreen() guard so FPS
is only counted when a frame is actually swapped.

* Update GLFW from 3.3.7 to 3.4 for runtime Wayland/X11 backend selection

Replace the compile-time GLFW_USE_WAYLAND flag (which locked to a single
backend) with GLFW 3.4's GLFW_BUILD_WAYLAND + GLFW_BUILD_X11 flags that
build both backends and auto-select at runtime based on the available
display server. This enables the CLI thumbnail renderer to work on both
Wayland and X11 sessions without separate builds.

* wayland: Fix UI call sites that rely on global screen coordinates

On Wayland, wxGetMousePosition() returns (0,0) and SetPosition() is a
no-op for top-level windows. Fix the highest-impact call sites:

- GLCanvas3D: Use cached m_mouse.position from event handlers instead
  of wxGetMousePosition() + ScreenToClient() in get_local_mouse_position()
- Plater: Use event-relative coords via ClientToScreen(e.GetPosition())
  instead of wxGetMousePosition() in 3 leave-window handlers
- BBLTopbar: Use event.GetPosition() and FindToolByPosition() directly
  in mouse handlers instead of wxGetMousePosition()/FindToolByCurrentPosition()
- Search: Use focus-based dismiss logic on Wayland instead of
  wxGetMousePosition()-based rect checks in SearchDialog and
  SearchObjectDialog
- GUI_App: Skip SetPosition() in window_pos_restore() on Wayland where
  it is a no-op; still restore size and maximize state
- Button: Position tooltip relative to button widget via ClientToScreen
  instead of wxGetMousePosition()

* Fix SearchDialog Wayland dismiss: guard against search_line focus

* flatpak: Add Wayland socket permission for native Wayland support

* spec

* Fix crash on Wayland when wxWidgets lacks EGL support

Restore the safety fallback that forces GDK_BACKEND=x11 when wxWidgets
was not built with wxUSE_GLCANVAS_EGL=ON. Without this, the GLX backend
tries to access a non-existent X11 display on native Wayland, crashing
in wxGLCanvas::IsDisplaySupported() with SIGSEGV at offset 0xe4.

Also add a defense-in-depth guard in detect_multisample() that skips
the IsDisplaySupported call entirely on Wayland without EGL.

Root cause: deps/wxWidgets must be rebuilt after enabling EGL. The
compile-time check in OrcaSlicer.cpp detects the mismatch and falls
back safely.

* Fix EGL detection: use wxHAS_EGL instead of wxUSE_GLCANVAS_EGL

wxUSE_GLCANVAS_EGL is a CMake build option, NOT a C++ preprocessor
macro. The actual macro defined in wxWidgets setup.h is wxHAS_EGL.
All compile-time EGL checks were using the wrong macro, causing
the safety fallback to always trigger even with a properly built
EGL-enabled wxWidgets.

* Fix GL function pointers invalidated on Wayland/EGL

gladLoaderLoadGL() dlopen's libGL.so.1 to resolve GL function pointers
via dlsym, then immediately dlclose's the handle. On X11/GLX this is
fine because the GLX context keeps libGL.so mapped. On Wayland/EGL,
nothing else holds libGL.so open, so dlclose unmaps it and all function
pointers become dangling — causing SIGSEGV on the first GL call.

Fix: on Wayland, use gladLoadGL(eglGetProcAddress) which resolves
function pointers through the EGL loader without opening/closing
libGL.so.

* fix crash on start and various rendering issues

* fix crash on close

* small refactor

* move GPU selection to desktop file

* clean up a bit

* clean up more

* fix appimage error
2026-04-13 19:45:39 +08:00
Clifford GarwoodandClaude Sonnet 4.6 73b5b750c6 feat: iMEX multi-material warning badge, conflict dialog, and mode row UI
- Add warning badge (obj_warning.svg overlay) to the iMEX plate icon when
  a parallel mode is active alongside multi-material objects on the same plate
- Add has_imex_multimaterial_conflict() using get_extruders(true) so only
  filaments actually used on the plate are checked
- Move multi-material caution dialog from reslice() into on_action_slice_plate /
  on_action_slice_all so it fires exactly once per user action and does not
  disrupt GL thumbnail generation during Slice All
- Fix is_imex missing from p->config init key list so on_config_change()
  diff detection correctly triggers refresh_imex_icons()
- Defer imex_changed handling until after set_bed_shape() so m_shape is current
- Replace plain remove button with ScalableButton (imex_remove.svg) in mode rows
- Add EditGCodeDialog launch button per mode row for placeholder browsing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 00:10:03 -04:00
Clifford GarwoodandClaude Sonnet 4.6 9a87363044 feat: 4-tool support, primary row, zone sizing, and stability fixes
IMEXModesCtrl:
- Primary mode is now a non-deletable first row stored as sentinel
  "primary" in imex_mode_names; older configs load cleanly
- New rows default T0 → Primary when no tool assignment is stored
- Filter "primary" from plater popup/cycle list to prevent double entry
- imex_tools_per_gantry cap raised 2 → 4

Zone sizing:
- Zone width/height now based on active tool count only; inactive tools
  donate their bed share to active neighbors (fixes 4-tool layout)
- Active col/row maps (col_to_zone/row_to_zone) applied consistently
  across zone fills, collision strips, and primary zone box

GCodeViewer animation:
- Mirror position formula fixed: left-of-copy reflects across copy
  zone's left edge; right-of-copy reflects across right edge
  (T3 was rendering on top of T1)
- strip_width/row_strip_height use active counts, matching PartPlate

Stability:
- Early return in calc_imex_zones() when tool_states is empty; prevents
  OOB crash on new printer with stale process-preset mode name
- is_imex toggle in on_config_change calls refresh_imex_icons() so the
  plate mode icon appears without requiring a new project

GCode:
- Remove "primary" guard so Primary mode gcode field is emitted at
  start of print

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 19:08:57 -04:00
Clifford GarwoodandClaude Sonnet 4.6 461c69c83e refactor: rename iXex → IMEX throughout; use IDEX/IQEX as user-facing label
- All config keys: ixex_* → imex_* (is_imex, imex_gantry_count, etc.)
- All C++ identifiers: IXexModesCtrl → IMEXModesCtrl, m_ixex_* → m_imex_*, etc.
- 3MF serialization key: ixex_parallel_mode → imex_parallel_mode
- UI strings: iXex → IDEX/IQEX
- SVG icons: plate_ixex_mode*.svg → plate_imex_mode*.svg
- Remove unused ixex_mode.svg

Breaking change for existing printer configs — acceptable pre-merge.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 19:08:57 -04:00
Clifford Garwood 2934b18d2a Merge remote-tracking branch 'upstream/main' 2026-04-09 00:22:06 -04:00
goofoo afc3756843 fix+feat: Snapmaker U1 — 5 bug fixes and 0.6mm / mixed-nozzle profiles (#12244 #12390 #12652 #12073 #12797 #11424) (#12824) 2026-04-08 12:08:35 +08:00
Clifford GarwoodandClaude Sonnet 4.6 5fa6480f61 feat: per-plate iXex mode selection with undo, dark mode icons, and menu fixes
- Add per-plate iXex mode icon to the plate toolbar (normal, hover, dark, dark-hover SVG variants)
- Left-click cycles through available modes; right-click shows a popup menu with all modes as radio items
- Mode changes are recorded in the undo/redo snapshot system
- Fix double context menu: suppress EVT_GLCANVAS_PLATE_RIGHT_CLICK and EVT_GLCANVAS_RIGHT_CLICK when the iXex icon popup was already shown
- Remove ixex_parallel_mode combo from Print Settings > Other > Special mode (superseded by per-plate icon)
- Remove dead code: refresh_ixex_mode_combo(), m_ixex_mode_combo member, related Tab reload hook
- iXex mode persisted in 3MF project files via existing plate metadata serialization

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 23:25:38 -04:00
Clifford f25a433ec7 Merge branch 'OrcaSlicer:main' into main 2026-04-07 22:50:22 -04:00
Vovodroid e1d6cb1764 Remove "auto" wall direction option (#6193)
Remove "auto" wall direction
2026-04-07 22:33:32 +08:00
Clifford GarwoodandClaude Sonnet 4.6 167211c85c refactor: rename ixex_carriage_width → ixex_nozzle_clearance; fix strip math and carriage box visualization
Config key rename (breaking for saved profiles — call out in PR):
  ixex_carriage_width_x/y → ixex_nozzle_clearance_x/y
  Labels updated to "Nozzle Clearance X/Y" with consistent tooltips
  describing the measurement as nozzle-to-collision-side-edge distance.

Strip math fix:
  Previously halved the clearance value (× 0.5) under the assumption
  the nozzle was centered in the carriage. The measurement is now the
  literal nozzle-to-edge distance, so the × 0.5 factor is removed.
  The collision strip width now equals the configured value directly.

Carriage box visualization (GCodeViewer):
  - Add per-carriage box_offset_x/y so the nozzle marker sits at the
    physically correct edge of the footprint box rather than centered.
  - X: zone-based by default (nozzle at inner edge facing bed center).
    Copy secondaries inherit the primary's X orientation (same movement
    direction). Mirror secondaries use the collision-side edge.
  - Y: always row-based regardless of copy/mirror mode. Gantry is always
    behind the nozzle (high-Y); front-row primaries with a back-row
    secondary override to place nozzle at the low-Y edge.
  - Fix stale mesh bug: GLModel::init_from() is a no-op when already
    initialized. Call reset() before init_from() so mesh rebuilds
    correctly when nozzle clearance values change in config.
  - Remove m_ixex_toolhead_box_dims (was the now-unnecessary cache key).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 14:57:57 -04:00
Clifford GarwoodandClaude Sonnet 4.6 b9f7148080 fix: constrain auto-arrange to iXex primary zone when parallel mode is active
Adds std::optional<BoundingBoxf> m_ixex_primary_zone_box to PartPlate,
populated by calc_ixex_zones() alongside the existing secondary/collision
zone geometry. The new ixex_primary_zone() getter calls ensure_ixex_zones()
so callers always get fresh data. ArrangeJob::process() replaces the
full-bed bedpts with the primary zone corners when the getter returns a
value, so auto-arrange no longer drops objects into the bed center.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 14:57:56 -04:00
Clifford GarwoodandClaude Sonnet 4.6 a3cac6c139 fix: remove iXex zone outline borders to eliminate aliasing flicker
The thin rectangular outlines around copy/mirror zones caused visible
aliasing and appeared to flash during interaction. Removed the border
GLModel, its build code in calc_ixex_zones(), the render block, and
the unused border field from IXexTheme.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 14:57:40 -04:00
Clifford GarwoodandClaude Sonnet 4.6 a03d2cd566 fix: restore info log level in two commented-out PartPlate log lines
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 14:57:39 -04:00
Clifford GarwoodandClaude Sonnet 4.6 e786fabb6a fix: restore info log levels in PartPlate accidentally raised to warning
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 14:57:39 -04:00
Clifford Garwood 1e56b65146 feat: Add iXex parallel printing support for IDEX and IQEX printers
Introduces first-class parallel printing (copy/mirror modes) for printers
with multiple independent X-axis carriages. Branded iXex (independent X
extruder), targeting Klipper firmware with a firmware-agnostic design.

- PrintConfig: new printer options declaring iXex capability and geometry
  (is_ixex, ixex_gantry_count, ixex_tools_per_gantry, carriage dims,
  tool layout, and per-mode name/role/gcode arrays)
- Preset: iXex keys registered in printer and process preset option lists
- Tab: IXexModesCtrl visual grid editor in Printer preset tab; mode
  dropdown in Process → Others tab; clear_pages() nulls iXex pointers to
  prevent dangling-pointer crash on preset save
- PartPlate: 2D zone visualization (active/dimmed/dividers) and
  placement-violation detection (has_ixex_placement_violations) that
  blocks slicing when objects fall outside the primary zone
- Plater: violation detection wired into update_background_process so
  the Slice button is disabled with an error notification on violation
- GCode: mode-activation G-code injected before machine_start_gcode
- GCodeViewer: multi-carriage toolhead markers in sequential preview,
  filament legend annotated with active carriage count and mode name
2026-04-06 14:57:39 -04:00
SoftFever 62ffbfd33d revist fixes for wipe tower pos issues 2026-03-18 14:42:40 +08:00
SoftFever c8070b67d5 Revert "Fix wipe tower placed outside bed boundary on first slice (#12777)"
This reverts commit 494601eea5, reversing
changes made to d28e964e04.
2026-03-17 19:14:50 +08:00
SoftFever ad0cce4c38 Fix wipe tower placed outside bed boundary on first slice
The wipe tower config position (wipe_tower_x/y) could be outside the
plate boundary (e.g. default y=250 on a 200mm printer). No constraint
was applied at slice time, so the tower was generated out-of-bounds.
2026-03-15 17:38:44 +08:00
Niccolo 40bf2157e3 Fix CLI segfault (SIGSEGV) when using --info, --slice, or --export-3mf (#12719)
In CLI mode, PartPlateList is constructed with a NULL plater pointer
(OrcaSlicer.cpp:3612). When set_shapes() calls PartPlate::set_shape(),
it unconditionally executes render data preparation code that
dereferences the null plater through calls like generate_print_polygon()
→ wxGetApp().plater(), causing a segmentation fault (exit code 139).

This adds a null check on m_plater in PartPlate::set_shape() to skip
the render-only code block that generates logo triangles, print/exclude
polygons, gridlines, icon vertices, and plate name textures.

These rendering operations are not needed in CLI mode and this change
has no impact on GUI mode where m_plater is always valid.
2026-03-12 19:36:45 +08:00
Sabriel-Koh ca85d8ade3 Disable filament grouping button logic when when not Bambu H2D (#12693)
# Description
When using the Bambu H2D, users can use Filament Grouping, a button found next to the bed preview. When not on a H2D profile, the button is hidden. However, it remained usable, and clicking an empty space below all the buttons causes the dialog to appear.

This PR fixes that by adding a check before the button is registered.

Addresses point 9 (focusing on the 2nd bullet) in #12684 

## Tests
Tried to click on the empty area right below the column of buttons (see the below images)
Made sure to have the H2D profile loaded as well, and checked that the dialog still opens for that printer.

H2D active
<img width="1635" height="949" alt="image" src="https://github.com/user-attachments/assets/da6fb03b-e3c8-4504-ac60-b96ecc6347dc" />
Area to click for the invisible button
<img width="935" height="788" alt="image" src="https://github.com/user-attachments/assets/1f001bcf-0224-4922-b5bf-47a95820e536" />
2026-03-09 18:24:24 +08:00
yw4z 095b5116ac Add option for hiding / showing gridlines (#10545)
Might be useful who want to use custom grid line system with textures. or a clean look

### PROBLEM / SCENARIOS
• Custom made textures overlapping with grid system. so it might be confusing for user if want to use specific markings
• User may found its a distracting item and wants a clean look

### SOLUTION
added to view menu
<img width="477" height="434" alt="Screenshot-20251230192707" src="https://github.com/user-attachments/assets/e298d9b2-5f8f-4e33-af22-ea7c84c9b5b8" />

added to canvas menu
<img width="278" height="297" alt="Screenshot-20251230192717" src="https://github.com/user-attachments/assets/a9952408-a361-4e64-ad9a-72e2480c74cf" />

Clean look without gridlines
<img width="1669" height="1157" alt="Screenshot-20250827144140" src="https://github.com/user-attachments/assets/9229f330-9543-4d39-a8fc-01deb9b61336" />

<img width="1669" height="1157" alt="Screenshot-20250827144212" src="https://github.com/user-attachments/assets/ab3848fb-74d7-4618-8bc7-0cdff10a3804" />


Few Examples with custom made textures / markings without gridlines
<img width="1669" height="1157" alt="Screenshot-20250827140008" src="https://github.com/user-attachments/assets/5d7b17ab-e97a-489c-9e4d-99157a37b6b7" />

<img width="1669" height="1157" alt="Screenshot-20250827141904" src="https://github.com/user-attachments/assets/a7e0f360-a85a-489d-9bc2-39286984643a" />
2026-03-01 00:19:04 +08:00
OcraftyoneandSoftFever abefb0e698 Fix thumbnail rendering in CLI (#12129)
* Fix thumbnail rendering in CLI

* Remove OSMesa context flag

* Add stringified error message for glfwInit and glewInit

* micro refactor

---------

Co-authored-by: SoftFever <softfeverever@gmail.com>
2026-02-10 13:08:22 +08:00
Andrew Sun e4d354b673 Merge remote-tracking branch 'upstream/main' into libvgcode
# Conflicts:
#	src/libslic3r/GCode/GCodeProcessor.cpp
2025-11-18 20:22:57 -05:00
Andrew Sun 7814605906 Merge remote-tracking branch 'upstream/main' into libvgcode
# Conflicts:
#	src/libslic3r/GCode/GCodeProcessor.cpp
#	src/libslic3r/GCode/GCodeProcessor.hpp
#	src/slic3r/CMakeLists.txt
#	src/slic3r/GUI/GCodeViewer.cpp
#	src/slic3r/GUI/GCodeViewer.hpp
#	src/slic3r/GUI/GLCanvas3D.cpp
#	src/slic3r/GUI/GLCanvas3D.hpp
#	src/slic3r/GUI/GUI_Preview.cpp
2025-11-15 11:11:20 -05:00
Noisyfox ffddfb0559 Merge branch 'main' into dev/p2s-pr 2025-11-01 20:29:55 +08:00
yw4z 2b02a19404 Fix plate icons & filament grouping (#11222)
* init

* Update PartPlate.cpp
2025-11-01 20:20:25 +08:00
Noisyfox 93c4838efe Merge branch 'main' into dev/p2s-pr 2025-10-31 16:54:45 +08:00
Noisyfox 05533a7fd3 Fix black bed texture if svg file (#11166)
* Fix blank bed texture if svg file

* Refresh scene once the texture compression is completed
2025-10-31 16:53:08 +08:00
weizhen.xie 9f7ff97be6 ENH: add initial layer time to plate_x.json and slice_info.config
Jira: STUDIO-14504
Change-Id: I32460f1c19cce7c729806e35370e8b4b57aa069e
(cherry picked from commit 04a487d210134c28245c1f0f7d0986266a1c66f2)
2025-10-28 15:30:05 +08:00
jiangkai.zhao 3a8269f9d4 ENH:Frontend limits rib width to less than half the tower side length
jira: STUDIO-14681
Change-Id: Id303ae07cacf36059d6de8570fa5ddf7802829ee
(cherry picked from commit 919a57eef5d05066c4a804448cad69bdd3102456)
2025-10-28 15:29:55 +08:00
jiangkai.zhao b9cd05dc86 FIX:When a smaller negative number is entered for the primer_tower_extra_rib_length, the program crashes.
jira: STUDIO-14677
Change-Id: I67477535ce2e88a2bded23959195ba9b95abf75a
(cherry picked from commit 21e01c87b678bc4f639293363591142fc9884f2d)
2025-10-28 15:29:55 +08:00
zhou.xu be88fc5a84 ENH:modify svg
jira: STUDIO-13916
Change-Id: I1248bd0c367f49b6c3b6a9d8a3e5226cd7fac7e0
(cherry picked from commit 77278dc34175875e15f802e8e6023c6087e3a6c9)
2025-10-28 15:29:53 +08:00
zhou.xu 11345d6b54 NEW:modify svg name
jira: STUDIO-13919
Change-Id: I8a7ae0df8c4203c8f00a0c0da83e98c1ec7be605
(cherry picked from commit 926809b06fe15e486f24c671af576d0a4dd13c37)
2025-10-28 15:29:53 +08:00