Commit Graph
1370 Commits
Author SHA1 Message Date
Ian BassiandRodrigo Faselli b81d761605 Refresh plate tab after bed type change (#16344)
Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
2026-10-10 21:06:29 -03:00
HanifKoh de5f2cb3b9 Expose App, GL, Printer and Project Facts to Plugins (#16324)
* Expose App, GL, Printer and Project Facts to Plugins

Plugins that help users file bug reports could only guess these from log lines: the app config is
deny-listed, and the project path, a project export and the device list had no API.

orca.host gains app_info(), gl_info() and selected_printer(), and Plater gains project_path() and
export_3mf_copy(path). The copy export leaves the project's file name, saved state and model
unchanged, does not write the signed-in account as the designer, and raises the audit event of
open(path, "w") so the plugin gets the same permissions as for writing the file itself.

* Remove app_language in Favour of app_info

app_info() returns the same language. app_language() was added after the last release, so no released build has it.
2026-10-11 04:35:37 +08:00
SoftFever aa14598e32 iXex: Parallel Printing Support for IDEX and IQEX Printers (#13086)
## Summary

This PR adds first-class parallel printing support to OrcaSlicer for
printers with multiple independent X-axis carriages — the **IDEX** and
**IQEX** hardware families. The feature is named **IMEX** (independent
multi-extruder) internally, which captures the supported topology space
more accurately than either acronym alone, and is exposed in the UI
under the user-facing label **IDEX/IQEX Configuration**. It is designed
to be printer-agnostic and firmware-flexible, with Klipper,
RepRapFirmware, and Marlin all supported for per-tool Pressure Advance
and per-layer temperature management. MMU/AFC setups where multiple
logical filament slots share one physical extruder are supported via a
`physical_extruder_map` profile option. Printers whose firmware handles
copy/mirror placement internally — RepRapFirmware IDEX duplication mode
on Flashforge Creator Pro 2 / Creator 3 Pro is the canonical example —
are supported through a firmware-managed-zones flag that emits a
centered single-half slice for the firmware to fan out.

The implementation spans printer configuration, process settings UI, bed
visualization, per-plate mode selection, ghost-object rendering with
per-plate filament overrides, placement validation, pre-slice conflict
warnings, G-code generation (with per-tool PA, per-layer temperature
management, and optional center-origin slice frame), and layer preview
animation. It supports five distinct topology paradigms:

- **1 gantry × 2–4 tools** — classic IDEX with 2 tools (BCN3D Sigma,
Snapmaker J1, Tenlog Hands-2 style — two independent X carriages on a
shared Y gantry); multi-extruder on a shared rail when extended to 3 or
4.
- **2 gantries × 1 tool each** — two fully-independent XY systems
sharing a bed (Vivedino Xplorer style). Both Y rails independent —
effectively "half an IQEX."
- **2×2 grid, independent quadrants** — IQEX running four separate
prints in parallel.
- **2×2 grid, paired-gantry multicolor** — IQEX where cross-gantry tools
share color responsibilities via the **Span** tile state.
- **Firmware-managed center-origin** — slicer emits a centered
single-half slice; firmware decides where to physically fan it out.
Overlays on any of the above hardware topologies; canonical example is
Flashforge IDEX with RepRapFirmware.

---

## Feature Walkthrough

### Printer Configuration

Printer preset options that declare IMEX capability and geometry:

| Option | Type | Description |
|--------|------|-------------|
| `is_imex` | bool | Marks this printer as IMEX-capable |
| `imex_firmware_managed_zones` | bool | Emit centered single-half
slice; firmware handles copy/mirror placement (default `false`) |
| `imex_gantry_count` | int | Number of independent Y-axis gantries
(rows) |
| `imex_tools_per_gantry` | int (1–4) | Toolheads per gantry along X
(columns) |
| `imex_nozzle_clearance_x` / `imex_nozzle_clearance_y` | float |
Nozzle-to-collision-edge distance in mm (literal, not halved) |
| `imex_tool_layout` | string | Physical orientation: front-left /
front-right / rear-left / rear-right |
| `imex_mode_names` | string[] | Names of user-defined parallel modes;
first entry is the reserved `primary` sentinel |
| `imex_mode_active_tools` | string[] | Tool role assignments per mode
(e.g. `0:P,1:C,2:M,3:M`, or `0:P,1:S,2:M,3:M` with Span) |
| `imex_mode_gcodes` | string[] | Firmware macro to activate per mode |
| `physical_extruder_map` | string[] | 0-indexed map from logical
filament slots to physical extruders (for MMU/AFC — see below) |

Tool roles per mode: **Primary** (P), **Copy** (C), **Mirror** (M),
**Span** (S), **Inactive**.

### Parallel Mode Editor

[Kooha-2026-05-14-13-45-35.webm](https://github.com/user-attachments/assets/af7650e6-d636-478b-9398-3f02ec662f03)

The **IDEX/IQEX Configuration** and **IDEX/IQEX Parallel Modes**
sections live in the printer preset's **Multimaterial** page (visible
only when `is_imex = true`). The mode editor is a visual grid:

- Each row defines one named parallel mode. The first row is a reserved,
non-deletable **Primary** row stored as the sentinel `primary` in
`imex_mode_names`.
- The Primary tool is always T0; **Tool 0 Position** chooses which
physical corner it occupies, and its tile is read-only. The other tool
buttons cycle Inactive → Copy → Mirror → Span → Inactive, color-coded,
with Span offered only where it applies.
- **Span** is only offered when `imex_gantry_count >= 2` AND the tile is
on the primary's gantry row — it declares "this tool is the multi-color
partner of Primary on the same gantry," distinct from a Copy/Mirror role
on a non-primary gantry.
- A G-code field per mode holds the firmware macro to activate that mode
(e.g. `IMEX_COPY` for Klipper). A placeholder-browser button per row
opens the `EditGCodeDialog` for quick insertion.
- Modes can be added and removed (removed via a dedicated
`imex_remove.svg` ScalableButton).
- Deleting the active mode resets affected plates to Primary.
- Tool assignments are preserved across `imex_gantry_count` changes:
going from IQEX (4 carriages) → IDEX (2 carriages) → IQEX restores all
previously assigned roles, and the visible grid anchors to the gantry
row containing the Primary assignment.

<!-- TODO: replace this placeholder with a re-recorded
parallel-mode-editor video
showing the Span tile state, the remove button, and the
placeholder-browser button.
Old URL (out of date):
https://github.com/user-attachments/assets/cabf1f70-206d-4f84-9c2e-594b38b83951
-->

### Span Tile State — Paired-Gantry Multicolor
<img width="2833" height="1300" alt="2026-05-14-135412_grim"
src="https://github.com/user-attachments/assets/52203133-7380-459a-973f-0502a1edc3c2"
/>

For IQEX printers (`imex_gantry_count >= 2`), the **Span (S)** tile role
enables paired-gantry multicolor mirror mode. The same active-tools
string `0:P,1:C,2:M,3:M` is ambiguous between two distinct hardware jobs
— four independent quadrants vs. paired-gantry multicolor mirror — so
the topology has to be declared explicitly rather than inferred. Span on
the primary's gantry row is the declaration.

Behavior driven by Span presence:

- **Multicolor block rule**: requires Span on primary's gantry to allow
multi-color slicing in a parallel mode. Without Span, multi-color
slicing in a parallel mode is blocked with an actionable error message.
- **Ghost aggregation**: one aggregated ghost per non-primary gantry
when Span is present, using the column-paired representative tool.
Mixed-role gantries fall back to per-tool.
- **Zone aggregation**: one row-strip zone per non-primary gantry when
Span is present (cell at primary's column collapses col-sep;
`make_boxes` expands to full-X strip).
- **Aggregated mirror drag**: ghost translates 1:1 with primary in X
(copy-style), with X-flip baked into the mesh-local frame so geometry
still reads as mirrored. Gantries don't share an X rail, so reflecting
motion serves no collision purpose.
- **Carriage collision strip audit**: X-boundary checks require `zr ==
pri_row_k` (matching the existing Y-boundary `c == pri_col` constraint).
Prevents spurious strips on primary's right edge in paired-gantry
mc-mirror, where T3 sits diagonally and can't actually collide with
primary's carriage.

Single source of truth:
`IMEXHelpers::group_imex_active_tools_by_gantry(active_tools_str,
tools_per_gantry)`. Ghost factory and zone calculator both consume it,
so pairing logic lives in one place.

### Per-Plate Mode Selection

Each build plate has an IMEX mode icon in its toolbar (normal, hover,
dark, and dark-hover SVG variants). The mode can be set independently
per plate:

- **Left-click** cycles through all non-sentinel modes in order.
- **Right-click** opens a popup menu listing all modes as radio items
for direct selection.
- Mode changes are recorded in the undo/redo snapshot system.
- The selected mode is persisted in the 3MF project file per plate
(`imex_parallel_mode` key).


https://github.com/user-attachments/assets/5ab497e3-9c2f-476a-ac67-ef34a592395b

When a parallel mode is active alongside multi-material objects on the
same plate, a warning badge (`obj_warning.svg`) overlays the plate icon
— see **Pre-Slice Warning System** below.

### Bed Visualization + Auto-Arrange Constraints

When an IMEX mode is active, the build plate renders the carriage grid:

<img width="2761" height="1447" alt="IMEX bed visualization with active
primary zone and dimmed secondary zones"
src="https://github.com/user-attachments/assets/3ee64b7b-a3ed-40aa-ad07-fa2f1cb74edf"
/>

- The **active (primary) zone** is full brightness.
- **Inactive zones** are dimmed with a color-coded overlay (blue for
copy, orange for mirror).
- **Zone dividers** are rendered as lines across the bed.
- The grid is a full 2D layout: `imex_tools_per_gantry` columns ×
`imex_gantry_count` rows. Zone sizing is based on the **active** tool
count only — inactive tools donate their bed share to active neighbors.
- Span-present configs render as a single row-strip zone per non-primary
gantry rather than per-tool quadrants, reflecting that the firmware will
paint the multi-color across the entire strip.
- Colors are drawn from the Okabe-Ito palette for colorblind
accessibility, with a deuteranopia/protanopia-safe alternate theme.
- **Auto-arrange** is constrained to the primary zone when a parallel
mode is active — `ArrangeJob::process()` replaces full-bed `bedpts` with
the primary zone corners via `PartPlate::imex_primary_zone()`. Collision
strips are additionally registered as hard obstacles through
`m_unselected` (the working NFP placer input, not the dead
`excluded_regions` field), so placement cannot drop parts into the
danger strips.

### Placement Validation


https://github.com/user-attachments/assets/5cb5a94a-981e-4fe9-898e-7e2f891b4f40

Objects placed outside the primary zone block slicing:

- `has_imex_placement_violations()` in `PartPlate` checks each object's
convex hull against the primary zone boundaries and collision strips.
- Violations inject into the existing `update_background_process`
validation pathway — the Slice button is disabled and an error
notification is shown.
- Mirror tools additionally generate X-axis collision strips (copy tools
move in the same direction and cannot collide). Y-direction strips are
scoped to same-column tools to avoid false positives from diagonal
mirror pairs.
- Strip width is taken **literally** from `imex_nozzle_clearance_x` (the
measurement is nozzle-to-collision-edge distance, not carriage
half-width).
- Multi-color block path: a pre-slice rule blocks multi-color slicing in
parallel modes that can't physically support it (e.g. a "fake IMEX" mode
where all tools sit on a single gantry without a Span partner). The rule
is centralized in `imex_multicolor_block_reason()` with unit-test
coverage of every gating case.

### Ghost Object Rendering + Per-Plate Filament Picker

When a parallel mode is active, the slicer renders colored, transparent
**ghost copies** of primary-head instances on the plate — one per
secondary active head, transformed under its Copy/Mirror role (or
aggregated per gantry when Span is present).

- Ghosts track the primary through drag/rotate/scale/mirror and
invalidate on mode, filament-map, or `physical_extruder_map` changes.
- **Left-click** on a ghost opens the `IMEXFilamentPickerPopover` for
that ghost's head — a compact `BitmapComboBox` that writes directly to
the per-plate `imex_head_filament_map` (MMU lane override).
- Mirror ghost geometry is a **true reflection about the zone-boundary
plane** (`x = primary_zone_center.x + gantry_offset.x/2`), so the ghost
stays anchored in the target zone as the primary moves and drag reflects
correctly (primary +X → ghost −X, Y tracks 1:1).
- Hover tooltip: `"Tn → filament N"` with a color swatch. When no
filament resolves to a head, the tooltip surfaces an actionable message
directing the user to extend the extruder count in the Machine tab.
- Ghost rendering + picking are scoped to the active plate — non-active
plates don't draw stale ghosts during arrange/preview transitions, and
click-picking never falls through to a non-current plate's ghost
geometry.
- Per-plate `imex_head_filament_map` round-trips through the 3MF project
file.

### Pre-Slice Warning System
<img width="1786" height="911" alt="2026-05-14-135714_grim"
src="https://github.com/user-attachments/assets/16532d47-2da5-4472-ab9c-4b8b00303bae"
/>

Before a plate slices, IMEX parallel-mode plates are checked for three
classes of conflict:

1. **Multi-material on secondary tools** — when the mode has Copy/Mirror
tools and the plate has multiple filaments active; plate icon gets a
warning badge.
2. **Bed temperature mismatch** — any two carriages configured >5 °C
apart.
3. **Filament type incompatibility** — filaments from different type
families on active carriages.

A dismissible Yes/No `RichMessageDialog` fires once per user action from
both `on_action_slice_plate` and `on_action_slice_all`. The dialog has a
**"Don't show again"** checkbox that persists to `app_config` as
`imex_pre_slice_warnings=false`. A re-enable toggle lives in **Printer
Settings → Multimaterial → IDEX/IQEX Configuration** so suppressed
warnings can be restored.

### Firmware-Managed Zones — Center-Origin Slice
<img width="701" height="197" alt="2026-05-14-135842_grim"
src="https://github.com/user-attachments/assets/3e2dfaab-fb4f-42b1-bf9d-c88dc0c95110"
/>

The `imex_firmware_managed_zones` printer-config option (default
`false`) supports IDEX/IQEX printers whose firmware applies its own
copy/mirror offsets in non-primary modes. Canonical examples:
**RepRapFirmware IDEX duplication mode** on **Flashforge Creator Pro 2 /
Creator 3 Pro**. These printers expect a centered single-half slice at
bed origin and fan toolheads out from there — the slicer-managed
paradigm of placing toolpaths at zone-relative positions produces gcode
the firmware can't reconcile, since it'd double-apply offsets.

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:

- **Writer offset** is augmented by the primary zone center so emitted
gcode is centered at bed origin.
- **Processor offset** stays at plate_origin only, so the gcode-preview
visualizer renders the centered toolpath at bed center rather than at
the prepare-view zone placement. The user sees what'll physically print
after firmware fan-out.
- **`translate_to_print_space()`** is augmented for frame coherence so
`first_layer_print_min/max` placeholders consumed by user start_gcode
(e.g. Flashforge's M118 "max delta from zero" header) reflect the
centered frame.

Slice-handoff runs per-slice: `PartPlate::refresh_imex_slice_offset()`
is called from `Plater::priv::update_background_process` after
`Print::apply()`, so reslicing with mode toggled but no plate change
picks up the updated offset.

When the flag is off, all related code paths reduce to no-ops
byte-identical to standard slicer-managed behavior. The two stock-code
touches at `Print.cpp:2553-2554` (gcode_offset composition) and
`Print.cpp:2823-2829` (translate_to_print_space) are explicit additive
shifts that collapse to identity when the offset is `Vec2d::Zero()`.

### G-code Injection

The selected parallel mode's G-code is written into the output file
immediately before `machine_start_gcode`:

- Looks up the plate's active mode name in `imex_mode_names` and writes
the corresponding `imex_mode_gcodes` entry.
- Processed through `placeholder_parser_process()` first, so
Klipper-style variable substitution works **and** any `{global}`
declarations flow forward into `machine_start_gcode`.
- Primary mode's G-code field is emitted too.
- Guarded against headless CLI slicing — `ensure_imex_zones()`
short-circuits when `m_plater` is null so the CLI path (used by
upstream's regression-test CI step) doesn't segfault on
`wxGetApp().preset_bundle` dereference.

### Placeholder Parser Integration
<img width="535" height="393" alt="2026-05-14-140227_grim"
src="https://github.com/user-attachments/assets/6d059c77-38f6-4418-bfe9-3bf963e8c836"
/>

Three placeholders register under **Slicing State** and are settable
from anywhere downstream:

| Placeholder | Type | Description |
|---|---|---|
| `imex_mode` | string | The active mode name |
| `imex_mode_index` | int | Index into `imex_mode_names` |
| `imex_mode_gcode` | string | The resolved mode G-code (post-parser) |

### Per-Tool Pressure Advance — Firmware-Agnostic

`set_pressure_advance()` takes an optional tool index (default `-1`,
preserving existing behavior for all non-IMEX call sites). Per firmware:

- **Klipper**: `EXTRUDER=extruder[N]` when `tool >= 0`, bare command
otherwise
- **RepRapFirmware**: `M572 D<N>` when `tool >= 0`, `M572 D0` otherwise
(preserves the pre-IMEX output; a bare `M572` applies to whatever tool
is selected and errors when there is none)
- **Marlin 2**: `M900 K<X> T<N>` when `tool >= 0`, bare `M900` otherwise
- **Marlin Legacy / fallback**: `M900 K<X>` always
- **Repetier**: `M233 X<X> Y<X>` (X is quadratic, Y is linear; same
value applied to both)

`m_imex_parallel_mode` is set once per export from the active plate
mode. PA tool-qualification is gated on this being a **non-primary**
parallel mode — primary-mode prints emit ordinary tool-change PA exactly
like any non-IMEX printer. Secondary active tools in parallel modes
receive explicit per-tool PA at print start since they never go through
a tool-change sequence.

### Per-Layer Temperature Management

In IMEX parallel modes, all active tools (primary + secondaries) get
temperature commands in `layer_change_gcode`:

- Layer 1 temperatures only emit on the first layer; subsequent layers
use normal layer-change temperatures.
- IMEX temperature handling is consolidated into the second-layer
transition.
- Layer-change temperature commands use `M104 T<N>` with
`physical_extruder_map` translation when applicable.

### MMU / AFC Support via `physical_extruder_map`

For printers where multiple logical filament slots share one physical
extruder (MMU, AFC, toolchangers), the `physical_extruder_map` profile
option translates tool slot indices to physical extruder qualifiers
before G-code emission.

- **Fallback**: on IMEX printers, `Print::apply()` uses the profile's
map only when it has one entry per extruder (the `nozzle_diameter`
count). Anything else, including the single-entry default, is replaced
by the identity map `0..n-1`.
- **Used by**: IMEX PA emission, layer-change temperature commands
(`M104 T`, `EXTRUDER=`, `M572 D`), ghost color resolution, ghost cache
key, tooltip lookup, click gate.
- **Profile authoring example** for a 7-slot printer with a 4-lane MMU
on extruder 0 and three independent direct drives on extruders 1/2/3:
  ```json
  "physical_extruder_map": ["0","0","0","0","1","2","3"]
  ```
  Non-MMU printers need no action — the identity fallback handles them.
- A centralized helper in `IMEXHelpers`,
`effective_physical_extruder_map(explicit_pem, nozzle_count)` (with a
`PresetBundle` overload that prefers the project's map over the
printer's), routes all PA/temp/ghost-color lookups through a single code
path.
- **No UI for editing the map** in this PR — non-trivial MMU/AFC layouts
require profile-authoring (hand-edit the printer JSON) or an updated
printer profile shipped by a vendor. A future enhancement would expose a
per-slot extruder picker in the Multimaterial section.

### Layer Preview Multi-Carriage Animation

The sequential preview (scrubber) animates all active carriages
simultaneously:

- One toolhead marker (colored cube) per active carriage, in addition to
the primary.
- Secondary marker colors: cyan (T1), yellow (T2), magenta (T3).
- **Copy** tools: marker placed at the same relative position within
their bed zone as the primary is in the primary zone.
- **Mirror** tools: reflect across the target zone's facing edge
(left-of-copy reflects across copy zone's left edge; right-of-copy
across the right edge). Y position is always zone-relative copy (all
tools on a row share a physical Y rail).
- Carriage footprint boxes use per-carriage `box_offset_x/y` so the
nozzle marker sits at the physically correct edge of the footprint —
zone-based X by default, collision-side edge for Mirror; gantry-behind Y
for back-row primaries, flipped for front-row primaries with a back-row
secondary.
- The filament usage legend notes the active carriage count and mode
name (e.g. `IMEX: ×2 (copy_mode)`).
- **View menu toggle**: `View → Show IDEX/IQEX Toolhead` (visible only
on the Preview tab, only when the active printer is IDEX/IQEX)
hides/shows the per-carriage toolhead representation during preview
playback. State persists to `app_config` as `show_imex_toolhead_boxes`.
Useful when scrubbing through dense toolpaths and the boxes get in the
way of seeing the underlying geometry:


[Kooha-2026-05-14-14-04-09.webm](https://github.com/user-attachments/assets/9869cd4d-3b60-4a6b-8dca-7ee1fc573ac1)

Copy mode:


https://github.com/user-attachments/assets/0774d285-ba36-4b86-a594-fb6572c3aede

Mirror mode:


https://github.com/user-attachments/assets/51528926-94dc-4d90-9b1a-1b42d9044be7

---

## Known Limitations

**Per-layer G-code collision detection**
The placement-time zone check catches gross violations (object placed in
wrong zone) but does not verify that toolpaths on any given layer
maintain adequate X separation between adjacent carriages. A per-layer
check via `ConflictChecker` was designed but deferred. Without it, a
print that passes placement validation could still crash carriages if
the primary object's toolpaths reach too close to a zone boundary.

**Brim avoidance of IMEX zones**
Standard `bed_exclude_area` exclusion zones are already respected by
brim generation. IMEX collision strips are not — the brim generator has
no visibility into them. The correct fix (feeding computed strip
polygons from `PartPlate` through the `Print` object to `Brim.cpp`) was
designed but deferred. In practice, users should leave adequate
clearance between printed objects and zone boundaries to account for
brim width.

**`extruder_printable_area` integration**
IMEX zones are not clipped against per-extruder printable polygons, and
there is no violation check for placing an object outside the
intersection of its active extruder's printable area and the IMEX
primary zone. Deferred pending clarification on the tool→extruder index
mapping.

**Ghost rendering in firmware-managed mode**
When `imex_firmware_managed_zones` is on, ghost rendering is suppressed
entirely. The existing `imex_head_transform` math is slicer-managed
semantics (places ghosts at `primary_zone_center + gantry_offset`) and
produces wrong positions when the toolpath is being emitted in a
centered frame. Proper firmware-managed ghost rendering — showing where
copies/mirrors will physically print after firmware fan-out — needs new
transforms designed around firmware-frame positions rather than a
coordinate-flip of the slicer-managed ones. Deferred to a follow-up.

**Filament-accurate multi-region ghost color**
For paired-gantry multicolor mirror (Span mode), the aggregated ghost
currently uses the representative tool's filament color as a single
solid swatch. A future enhancement would render the ghost split into
per-source-tool regions, each colored by the secondary tool that mirrors
it. Slicer-side correctness is already shipped — the G-code emits the
right T-codes; the ghost is a visual aid only.

**Ghost path overlays**
Secondary carriage toolpaths are not rendered in the layer preview. The
secondary markers animate correctly, but the paths they would trace are
not drawn. Adding ghost path rendering would require duplicating and
offsetting the toolpath geometry per secondary carriage, which is a
significant addition to the libvgcode rendering pipeline.

**No per-mode slicing**
All carriages in a mode execute the same sliced toolpaths (transformed
per zone). There is no support for slicing different objects for each
carriage independently within one mode.

**Global default IMEX mode**
There is no job-level IMEX default mode. Each plate's mode must be set
individually (default is always Primary). A future improvement would add
a global default in the sidebar (following the same pattern as bed type
and nozzle diameter), with per-plate overrides.

**Dynamic GL-rendered mode icons**
The per-plate icon currently uses static SVGs. A GL-rendered
carriage-grid icon that visually represents the mode's tool layout would
be a nicer UX but is deferred.

**Slice-all thumbnail icon refresh**
On the "Slice All" path, plates flagged as IMEX-violated do not refresh
their toolbar icon. Root cause and fix identified, not yet applied.

**Start-G-code filament placeholder**
The resolved per-head filament map is not yet exposed as a
`PlaceholderParser` vector. Exposing it would let MMU firmware macros
pre-load lanes before print start.

**No UI for `physical_extruder_map` authoring**
The MMU/AFC slot-to-physical-extruder map is currently profile-only —
there's no in-app dropdown or editor for it. Users with non-trivial
MMU/AFC setups must hand-edit the printer-preset JSON (or rely on a
vendor-supplied profile). A future enhancement would expose a per-slot
extruder picker in the Multimaterial section so users can declare the
mapping without touching JSON.

**Bundled IQEX printer profile**
A full IQEX printer profile with cover image, bed mesh, and matched
process/filament profiles is not bundled with this PR. Users must
currently author their own printer preset. Deferred to a follow-up
profile-only PR.

**Mode lifecycle gap**
Per-plate mode is stored as a string (the mode name). If a mode is
renamed or deleted from the printer preset after a project is saved, the
plate's saved mode name will not resolve and will silently fall back to
Primary on next load. A warning on load would be a useful addition.

**Firmware-managed prepare→preview frame jump**
In firmware-managed-zones mode, the prepare view shows the part at its
placed-in-zone position while the preview view shows the centered slice.
This is intentional — the views truthfully represent the prepare frame
vs. the post-firmware-fan-out frame — but the visual jump can be
confusing on first use.

---

## Firmware Assumptions

- The implementation assumes the firmware handles all carriage
synchronization and offset math. OrcaSlicer only injects the
mode-activation macro before machine start and is entirely dependent on
user configuration.
- Per-tool Pressure Advance is firmware-aware (Klipper / RRF / Marlin 2
/ Marlin Legacy / Repetier). Layer-change temperature commands use `M104
T<N>` with `physical_extruder_map` translation.
- Tested against Klipper on a real IQEX printer. RepRapFirmware gcode
emission is exercised via profile-driven slice tests (Flashforge Creator
Pro 2 profile); Marlin and RRF paths have not been validated on real
hardware print runs.
- Mid-print mode switching is explicitly not supported. The mode is
locked at print start.
- Firmware-managed-zones mode requires the printer's firmware to
translate centered slice coordinates into physical toolhead positions;
the slicer does not attempt to model the firmware's offset logic.

---

## Files Changed (High-Level)

| File | Change |
|------|--------|
| `src/libslic3r/PrintConfig.{cpp,hpp}` | IMEX config option definitions
+ declarations, `IMEXMode` enum, `physical_extruder_map`,
`imex_head_filament_map` (plate option), `imex_firmware_managed_zones` |
| `src/libslic3r/Preset.cpp` | IMEX keys registered in printer and
process preset option lists |
| `src/libslic3r/PrintApply.cpp` | `physical_extruder_map` identity
fallback on IMEX printers |
| `src/libslic3r/Print.{cpp,hpp}` | IMEX slice-offset field + accessors;
`translate_to_print_space` augmentation for frame coherence |
| `src/libslic3r/GCode.{cpp,hpp}` | Mode G-code injection, per-tool PA
emission (firmware-agnostic), per-layer temperatures for all active
tools, `m_imex_parallel_mode` state, `set_gcode_offset_with_imex_shift`
writer/processor split |
| `src/libslic3r/GCodeWriter.{cpp,hpp}` |
`set_pressure_advance(tool_index = -1)` per-firmware implementation |
| `src/libslic3r/IMEXHelpers.{cpp,hpp}` | `imex_head_transform`
(Primary/Copy/Mirror), `parse_imex_active_tools`,
`imex_primary_tool_for_mode`, `effective_physical_extruder_map`,
per-head filament resolution, `group_imex_active_tools_by_gantry` (Span
pairing), `compute_imex_slice_offset` (firmware-managed) |
| `src/libslic3r/Format/bbs_3mf.cpp` | Per-plate IMEX mode and
`imex_head_filament_map` serialization |
| `src/slic3r/GUI/Tab.{cpp,hpp}` | `IMEXModesCtrl` in Multimaterial
page, tool assignment persistence across gantry count changes, `primary`
sentinel handling, Span tile state, firmware-managed-zones checkbox,
null guard in `clear_pages()` |
| `src/slic3r/GUI/PartPlate.{cpp,hpp}` | Zone visualization (Span
row-strip aggregation), placement violation detection, per-plate mode
icon, ghost volume rebuild on mode/map/object mutation, warning-badge
overlay, pre-slice warning collection, `refresh_imex_slice_offset()`,
ghost suppression in firmware-managed mode, headless-CLI guard |
| `src/slic3r/GUI/Plater.cpp` | Validation pathway injection, per-plate
mode popup, ghost click handling, pre-slice warning dialog with "Don't
show again", IMEX multimaterial conflict routing, per-slice IMEX offset
refresh in `update_background_process` |
| `src/slic3r/GUI/GLCanvas3D.cpp` | Ghost rendering with per-head
filament color and translucent blending, picking via volume composite
id, active-plate scoping |
| `src/slic3r/GUI/GCodeViewer.cpp` | Multi-carriage marker animation,
Mirror math fix, carriage footprint box offsets, legend annotation |
| `src/slic3r/GUI/IMEXFilamentPickerPopover.{cpp,hpp}` | Ghost-click
filament picker |
| `src/slic3r/GUI/Jobs/ArrangeJob.cpp` | Auto-arrange constrained to
IMEX primary zone + collision strip exclusion via `m_unselected` |
| `src/slic3r/GUI/OG_CustomCtrl.cpp` | Empty `option_set` guard for
widget-only lines |
| `resources/images/plate_imex_mode*.svg`, `imex_remove.svg` | Per-plate
mode icons (light/dark/hover variants), remove button |
| `tests/libslic3r/test_imex_helpers.cpp` | Coverage for IMEX head
transforms, `effective_physical_extruder_map`,
`imex_multicolor_block_reason` gating,
`group_imex_active_tools_by_gantry` Span pairing,
`compute_imex_slice_offset` |

---

## Testing Notes

Validated on an IQEX printer (4 carriages, 2×2 grid, Klipper firmware)
and against the Flashforge Creator Pro 2 profile (2-tool IDEX, RRF
flavor, center-origin bed) with the following configurations:

- **Primary only** — baseline, no regression vs. standard
single-extruder workflow
- **Copy mode** (T0 Primary, T1 Copy, same row) — carriage markers
animate in sync offset by strip width; ghost tracks drag/rotate/scale
- **Mirror mode** (T0 Primary, T1 Mirror, same row) — T1 marker reflects
T0 across zone-boundary plane; ghost drag reflects X correctly while Y
tracks 1:1
- **Cross-row copy + mirror** (T0 Primary row 0, T2 Copy row 1, T3
Mirror row 1) — T2 follows T0's zone-relative position; T3 mirrors T2's
X; Y shared per row
- **4-tool copy mode** (T0 Primary, T1/T2/T3 Copy) — zone sizing
correct, all four markers + ghosts render in sync
- **Paired-gantry multicolor (mc-mirror, Span)** — `0:P,1:S,2:M,3:M`:
ghost aggregates to one per non-primary gantry, X-flipped, drag tracks
1:1 with primary; collision strip audit doesn't fire spurious strips on
primary's right edge
- **Firmware-managed copy mode** (Flashforge Creator Pro 2) — emitted
gcode coordinates are centered at bed origin (X = −cube_half_width …
+cube_half_width), `first_layer_print_min/max` placeholders evaluate
symmetrically, M118 header produces correct "max delta from zero" values
for Flashforge's existing template, ghosts suppressed
- **Firmware-managed flag toggle** — flipping the checkbox off restores
slicer-managed iMEX behavior (toolpath at zone position)
byte-identically; flipping on restores center-origin slice
- **Delete active mode** — no crash; plate resets to Primary
- **Placement outside primary zone** — slicing blocked with error
notification
- **Multi-material conflict warning** — plate icon gets warning badge;
Yes/No dialog fires on slice; "Don't show again" checkbox persists
- **Multi-color block rule** — `0:P,1:C` on a single gantry blocks at
slice time with actionable error message
- **Ghost filament picker** — left-click on ghost opens picker;
selection writes to `imex_head_filament_map` and round-trips through 3MF
save/load
- **Per-plate mode selection** — left-click cycles modes, right-click
shows popup, undo/redo correctly reverts mode changes, mode persists
through project save/load
- **Gantry count change round-trip** — reducing from IQEX (4 carriages)
to IDEX (2 carriages) and back restores all previously assigned tool
roles
- **MMU/AFC `physical_extruder_map`** — explicit map routes PA and
temperature commands to correct physical extruder qualifiers;
auto-derive fallback preserves 1:1 behavior for non-MMU printers
- **Headless CLI slicing** — `orca-slicer --slice project.3mf` does not
segfault on IMEX-enabled printers
- **Dark mode** — all four plate-icon variants render correctly
- **Pre-slice warnings suppress + restore** — `app_config` flag flips on
checkbox; re-enable toggle in Multimaterial config restores dialog
- **Stock non-IMEX printer regression check** — slicing a single-head
Voron Trident 350 profile produces byte-identical gcode vs. baseline;
the firmware-managed flag and Span tile state both no-op when not
applicable
- **Full unit test suite** — 247/247 passing, including Layer 1 coverage
for `compute_imex_slice_offset`, `group_imex_active_tools_by_gantry`
Span pairing, and `imex_multicolor_block_reason` gating
2026-10-10 20:34:56 +08:00
Kris Austin cf5f7b778d Show OK instead of Yes on notice dialogs (#16332)
Several warning and info dialogs passed wxYES as their only button, so
the only choice was "Yes" even though nothing is asked. Use wxOK.

None of the callers act on wxID_YES. They ignore the result, except
Field.cpp, which only checks that it's nonzero, and every button id is.
wxYES_DEFAULT is 0, so dropping it next to wxOK changes nothing.

Also drop the empty `if (ShowModal() == wxID_YES) {}` in
Plater::priv::load_files.
2026-10-10 15:30:17 +03:00
SoftFever b14ed3e0c1 Multi nozzle UI Fixes / Improvements (#16075)
# New multi nozzle icon for guidance

new icon has tooltip for guidance
<img width="407" height="77" alt="Screenshot-20261005181353"
src="https://github.com/user-attachments/assets/f27bbf79-7570-4f30-ab3c-52fa972c9fa3"
/>

**Before**
<img width="414" height="228" alt="Screenshot-20261002144627"
src="https://github.com/user-attachments/assets/7941891a-5a27-478b-a63f-49cf386e430f"
/>

**After - Moved icon to pre label position**
• reduces possibility of wrapping of labels
• Aligns labels and improves readability

<img width="399" height="239" alt="Screenshot-20261005181511"
src="https://github.com/user-attachments/assets/93f6b555-bbec-496a-b708-8c12c3b3185b"
/>

# Fix > Modified state not applied on tabs
**Before - modified colors not applied to control**
<img width="751" height="157" alt="Screenshot-20261001004828"
src="https://github.com/user-attachments/assets/0c149fce-45e8-498f-8daa-7b54803399e9"
/>

**After**
<img width="752" height="150" alt="Screenshot-20261005181614"
src="https://github.com/user-attachments/assets/5a930bb2-6487-4987-b58d-850de6ae6779"
/>

Applied color as border because changing text color was not readable
<img width="159" height="71" alt="Screenshot-20261002152522"
src="https://github.com/user-attachments/assets/f694fc8d-c9f6-46d7-9e06-7762003c9f20"
/>

and applying as background color was too eye catchy
<img width="184" height="80" alt="Screenshot-20261002152533"
src="https://github.com/user-attachments/assets/2b67c1dd-8977-46ea-88fc-ce57bfad55bd"
/>

# Usable tabs on Printer settings
• Fix > Using long string makes "Motion ability" and "Notes" tab
unreachable
• Matches T1,T2.. terminology with nozzle section
• Makes https://github.com/OrcaSlicer/OrcaSlicer/pull/14737 available to
use now
 
**Before**
<img width="751" height="126" alt="Screenshot-20261002152643"
src="https://github.com/user-attachments/assets/0241d3c4-acc1-491e-ab85-fa60385c5e85"
/>

**After**
<img width="748" height="129" alt="Screenshot-20261002152628"
src="https://github.com/user-attachments/assets/c475d6c8-4f0f-47df-aa04-9d894b1c358d"
/>

No issue with 8 items
<img width="748" height="124" alt="Screenshot-20261002153018"
src="https://github.com/user-attachments/assets/9a8c4e12-2cdc-4508-b82b-134476589d80"
/>

# Dynamic Padding
• buttons uses dynamic horizontal padding now depends on item count.
reduces padding size to reduce scrolling

**Before**
<img width="416" height="91" alt="Screenshot-20261002153309"
src="https://github.com/user-attachments/assets/977b22db-e686-4967-95be-13772d77cefd"
/>

**After** 
<img width="403" height="79" alt="Screenshot-20261005181728"
src="https://github.com/user-attachments/assets/9aac0d17-0729-408f-9237-62b17236488f"
/>

**Before**
<img width="745" height="153" alt="Screenshot-20261002153659"
src="https://github.com/user-attachments/assets/06951759-419a-40fb-8fc2-d93f5b04d6ea"
/>

**After** 
<img width="748" height="149" alt="Screenshot-20261005181833"
src="https://github.com/user-attachments/assets/9ccec344-1dd0-4b03-ac12-20d298f413c8"
/>

**After** 
<img width="756" height="178" alt="Screenshot-20261009144637"
src="https://github.com/user-attachments/assets/1343c39e-5cbd-4f9e-b501-df3797579961"
/>
<img width="761" height="198" alt="Screenshot-20261009144459"
src="https://github.com/user-attachments/assets/efb1ef75-a602-4d3a-a17f-9354ed633357"
/>
<img width="758" height="196" alt="Screenshot-20261009144510"
src="https://github.com/user-attachments/assets/df1d88c5-1b6f-4748-a6bd-a96659a83fe0"
/>
<img width="764" height="190" alt="Screenshot-20261009154210"
src="https://github.com/user-attachments/assets/b876ed71-5fa0-47e4-83c8-a33100e0a229"
/>

# Keeping compact look & Making it more readable
• Removed ":" from labels to gain space as much as possible and
increased font size a bit but UI didnt allowed bigger font

• Matched font sizes of nozzle section while keeping height of items.
Previous font was too small. removed "mm" text to gain more space but
added it in tooltip
**Before-After** 
<img width="800" height="240" alt="Screenshot-20261002154132"
src="https://github.com/user-attachments/assets/bc4ddcb1-2248-4fc7-9492-ab4fb68cb659"
/>

• Matched font sizes for BBL nozzle selectors
**Before-After** 
<img width="806" height="200" alt="Screenshot-20261002154513"
src="https://github.com/user-attachments/assets/b3d38317-8e4d-4c1e-a490-ce4959ed700a"
/>

<img width="794" height="156" alt="Screenshot-20261002154649"
src="https://github.com/user-attachments/assets/00917584-8e1a-44b0-8c0b-79cec30059bf"
/>

# Layout and Styling fixes
• Matched background color of non active buttons with (Global/Objects)
switch
**Before**
<img width="405" height="159" alt="Screenshot-20261002154947"
src="https://github.com/user-attachments/assets/8a8adcc8-431a-48be-aabd-a981335b6ab9"
/>

**After** 
<img width="407" height="159" alt="Screenshot-20261005182106"
src="https://github.com/user-attachments/assets/7f837be3-ce85-49e6-909a-05350b3d8b30"
/>

# Other effected dialogs
<img width="541" height="195" alt="Screenshot-20261002165003"
src="https://github.com/user-attachments/assets/109abbc3-d7c0-4384-9b85-d943e2027963"
/>
<img width="547" height="308" alt="Screenshot-20261002165307"
src="https://github.com/user-attachments/assets/951d9a8d-56f9-4659-aebe-77f236e8c838"
/>


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

[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
2026-10-10 20:27:37 +08:00
Clifford GarwoodandClaude Opus 5.5 8b5280098f Merge upstream main: center of mass markers, adaptive TPMS, texture bake color mixing, post-processing preview fix, GLEW/OpenCSG removal
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 01:44:42 -04:00
SoftFever e4142db820 Let texture bakes create a chosen number of mixed colours (#16320)
# Description

Follow-up to #16242, which turned texture colour mixes into mixed
filament slots but created a slot for every possible mix, even when only
previewing. A new Mixed colors setting (default 8) caps how many mixed
filaments a bake adds. They are picked from the texture's own colours,
and only the ones the bake actually paints with are created. Previews no
longer touch the project's filaments, both previews show a mix in the
colour its slot will have, and the bake paints each mix with the slot it
got, which went wrong whenever the project already held mixed slots.

It also improves the colour preview: the green paint highlight no longer
covers the colours, and the colours a bake writes stay visible in the
gizmo.

Only texture displacement changes. Models without a colour layer behave
as before, and there is no change to project or profile formats.

# Screenshots/Recordings/Graphs

**Before**



https://github.com/user-attachments/assets/554a0d0d-cd9f-4095-840e-ca764dde6d52

**After**



https://github.com/user-attachments/assets/7fe10431-8c3f-4759-bd3f-78b4f7a12fc7



## Tests

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

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

[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
2026-10-10 12:00:39 +08:00
0f3e8fbf27 Add center of mass markers to Prepare and Preview (#16291)
Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
Co-authored-by: Kris Austin <kris.austin@gmail.com>
2026-10-09 16:48:48 -03:00
SoftFever 3755618146 Keep nozzle row label fonts after a DPI change 2026-10-10 02:32:18 +08:00
SoftFever ebaff19754 Merge branch 'main' into extruder-variants-ui-2 2026-10-10 00:42:48 +08:00
SoftFever b8665b69b0 Merge branch 'main' into feature/texture-color-number 2026-10-09 22:58:10 +08:00
SoftFever 0473da4ef8 Let texture bakes create a chosen number of mixed colours
The new Mixed colors setting caps how many mixed filaments a bake adds. They are picked from the
texture's colours, and only the ones the bake paints with are created. Previewing no longer creates
filament slots, both previews show a mix in its slot's colour, and the bake paints each mix with the
slot it actually got.
2026-10-09 22:23:07 +08:00
Clifford GarwoodandClaude Opus 5.5 f970a607ae Merge upstream main: belt printing, texture displacement color mixing, 3MF component cycle checks, undo blocked during background jobs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 07:44:12 -04:00
ExPikaPaka 6ac7ae24a3 Block undo and redo while a background job runs (#16015)
* Block undo and redo while a background job runs

A job is queued against the model as it stands and hands its result back
when it finishes, so undoing underneath it leaves that result landing on
geometry it was never computed for. Undo and redo now wait for the job
and say so, and can_undo()/can_redo() report the same, so the toolbar
and the menu items stay in step.

* Say what to do about the running operation, not just that it blocks

Review feedback: "Stop it first" is the only way out the old text offered,
and stopping is rarely what the user wants. Waiting for the operation to
finish works just as well, so the notification now names both.
2026-10-09 19:26:37 +08:00
ExPikaPaka 62b829d1cb Texture displacement: mix filament colours in the slicer (#16242) 2026-10-09 17:52:35 +08:00
SoftFever a5d21b9922 Merge branch 'main' into belt-printer 2026-10-09 13:28:39 +08:00
HanifKoh 9a9285a894 Log Instead of Showing Info and Substitution Dialogs When Opening a BambuStudio Project (#16153)
Every BambuStudio project opened with a "BambuStudio Project" info dialog
(or, from BambuStudio 2.8.2, one saying the file is newer than the
compatible version and to update the software), followed by the
configuration-substitution dialogs for the project and its embedded
presets. None of them asks anything and all of them fire for every
BambuStudio file.

For BambuStudio projects (untagged files newer than 2.3.2, the existing
test) log the version with the unrecognized settings, and each replaced
value, instead. The geometry-only, invalid-values and G-code safety
dialogs stay, and other 3MFs are unchanged.
2026-10-09 11:33:53 +08:00
SoftFever 5072f6b614 Merge branch 'main' into belt-printer 2026-10-08 23:03:56 +08:00
Rodrigo Faselli d09850c528 Merge branch 'main' into main 2026-10-08 10:45:49 -03:00
SoftFever bf59db1053 Merge branch 'main' into extruder-variants-ui-2 2026-10-08 20:31:57 +08:00
LixNix 7d44b60ae4 H2D X2D Multi Nozzle Size Printing Support (#14125) 2026-10-08 20:28:32 +08:00
harrierpigeonandClaude Fable 5.1 cfad587c2d Belt: follow-ups from a second review pass
Guard the layer count and the per-object layer collection against an
object that is left without a layer to print on a belt (the counting
loop stepped before begin() and front() was taken of an empty vector).
Check the belt temperature tower's embossed model before the current
project is replaced, not after.  Only invalidate the support step of
objects that own a belt brim when an object is added or removed.  The
empty-layers test now counts an extrusion only where material is laid
down along a move.  The BeltBrim.cpp SEQUENCING note says exactly which
layers are read, and the machine-frame scale is 1/|sin|.

Remove more code that nothing calls: the kinematics inverse
(to_logical, apply_axis_remap_inverse, to_build_volume and the state
kept for them), the world_coordinates(), is_active() and
belt_brim_areas_by_layer() accessors, the PrintConfig overload of
physical_tilt() and the DynamicPrintConfig overload of
compute_belt_height_and_floor().  Comments in GCode.hpp,
BeltSliceStrategy.hpp/.cpp and PrintObjectSlice.cpp that described the
retired pre-slice remap and plane-evaluator still did; the purge-tower
width tooltip named the wrong switch.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-07 19:08:59 -05:00
harrierpigeonandClaude Fable 5.1 e78f437ce4 Belt temperature tower: refuse a range without a model
The calibration fell back to the 230-190 tower when no embossed model
existed for the requested range, so the printed numbers did not match
the temperatures.  Show an error naming the range and stop instead.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-07 19:08:58 -05:00
Joseph Robertson e39d6cc089 Merge branch 'main' into belt-printer 2026-10-07 14:14:20 -05:00
SoftFever 27418cea0f Merge branch 'main' into extruder-variants-ui-2
# Conflicts:
#	src/slic3r/GUI/Tab.cpp
2026-10-07 19:40:01 +08:00
SoftFever 7fdfea6efa Merge branch 'main' into main 2026-10-07 19:31:24 +08:00
HanifKoh 6639a32b0c Remove using namespace std from json_diff.hpp (#16222)
The directive sat at global scope in a header that DeviceManager.hpp
includes, so most of the GUI compiled with all of std in the global
namespace. 42 files had come to rely on it, mostly for string, vector
and unordered_map, four of them for the ""sv and ""ms literals.

Those sites are qualified. GCodeViewer.cpp spelled the type as
std::vector<::string>, which only resolved through the directive. The
files that use the ""sv and ""ms literals get a file-scope
"using namespace std::string_view_literals;" or
"using namespace std::chrono_literals;", as other sources already do.
2026-10-07 14:58:49 +08:00
harrierpigeonandClaude Fable 5.1 e34f4aa0e5 Belt printer: bring back the raw G-code view in the canvas view menu
The "Show raw G-code (belt only)" toggle, retired in #16236, returns as
an item of the Preview canvas view menu (with its B shortcut), and only
there: no legend checkbox.  Unlit, the preview shows the designed,
upright view; lit, the raw machine-frame G-code, which is what to look
at when checking the machine frame transforms.  The toggle is view
only; exported G-code is the same either way.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-07 01:43:56 -05:00
HanifKoh f02292f163 Stop Exporting Names Through Usings in GUI and Voronoi Headers (#16223)
Each of these headers put a using or namespace alias at global or
namespace scope, which every includer inherited:

- BBLTopbar.hpp: "using namespace Slic3r::GUI;" at global scope, reached
  through MainFrame.hpp. Seven source files used GUI names unqualified
  outside the namespace because of it, one of them as "::RadioBox".
- IMSlider.hpp and TickCode.hpp: "using namespace CustomGCode;" inside
  Slic3r.
- ProjectTask.hpp, Jobs/PrintJob.hpp and ConfigWizard_private.hpp:
  "namespace fs = boost::filesystem;". PresetBundle.cpp and GUI_App.cpp
  had no alias of their own.
- VoronoiUtils.hpp: "using VD = Slic3r::Geometry::VoronoiDiagram;" at
  global scope.

The headers now spell the names out. Source files that used them get
the qualifier, or a using of their own where there are many uses.
2026-10-07 14:22:45 +08:00
HanifKoh 23546e71ce Remove Unused Usings and the Includes Kept Only for Them (#16220)
151 using-directives, using-declarations, type aliases and namespace
aliases in source and test files that nothing refers to: the name is
never used, it duplicates a using already in scope, or the code sits
inside the namespace it names. Each one was removed on its own and the
file still compiled, both as it is and with every header-level using
taken away, so none of them was only redundant because a header leaks
the same name.

With the using gone, 28 #include lines and one forward declaration had
no other reference left in their file (boost/optional.hpp without any
optional, property_tree headers without any ptree) and go with it.

No header is touched.
2026-10-07 14:22:32 +08:00
harrierpigeonandClaude Fable 5.1 dda58b07cd Belt printer: supports reach the belt under a leading overhang
The slicing frame of a belt object started at its lowest vertex, but the
belt under the leading end of an overhang lies below that, by the
overhang's length times the tilt's shear.  Every support generator works
in layers at z >= 0, so none of them could reach it: normal supports
stopped at the object's own lowest layer, and the two tree generators
each carried a stack of hacks to extend themselves below it (a post-hoc
copy of the lowest base area in TreeSupport, "virtual belt raft layers"
in TreeSupport3D/TreeModelVolumes), sized from the pre-rotation bbox
and capped at global z = 0, which is only right for the trailing half
of the belt.

Start the frame at the lowest belt-floor point under the footprint
instead, less a 10 mm margin along the belt for the base of a support
column (BeltSliceStrategy::apply_preslice_transforms and
BeltTransformPipeline::compute_belt_height_and_floor agree on it).  The
layers between it and the first vertex come out empty, which belt
slicing already tolerates, and the generators need no extension at all:

- normal supports: the generator anchors its layer grid at the frame
  origin, so run it in the object frame and shift the global belt Z
  offset onto the result afterwards, as organic supports already did.
  With the offset on the object layers a top contact at negative z
  turned the intermediate-layer count negative and the generator
  allocated layers until the kernel killed it (any overhang in the
  leading half of the belt).  Drop the first-layer flange expansion on a
  belt: the first support layer is the leading tip of the support, not
  a flange, and inflating it put lines in the air ahead of the belt.
- classic tree: a node now keeps dropping until its whole circle is in
  the belt, so the branch tapers to a tip on the belt instead of
  stopping, a radius above it, when its centre crosses.
- organic: the belt is no longer a support blocker.  A blocker is a
  collision, and a branch descending onto one slides off it, down the
  tilted belt and ahead of the part; the belt is where branches end,
  which the per-layer m_belt_floor clipping already does.

The belt brim is generated after the parallel support step instead of
inside it: belt_brim_obstacles() reads every object's layers and support
layers, which another object's support step rebuilds (and, now, shifts)
at the same time.  This is the race behind the Windows arm64 segfault
in "Belt brim of each object precedes its perimeters on its own
filament".

Also: the belt tilt axis moves to Developer mode as its own row (a
shared line is shown by its first option's mode), first_layer_plane
band thickness, belt_support_floor_mode, belt_preslice_global and
gcode_back_transform are retired and presumed on, the gravity arrow is
a plain line along the up direction, and the "Show raw G-code (belt
only)" preview toggle is gone.

Regression test: "Belt supports reach the belt under a leading
overhang" slices a cube with a fin whose underside is parallel to the
layers, 20 mm ahead of the cube and up to 41 mm of slicing Z above the
belt, for normal, organic and classic tree supports, and checks that
the lowest support layer sits on the belt beneath its own lines.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-06 22:14:25 -05:00
Ioannis Giannakas 6561c4d35b Merge branch 'main' into main 2026-10-06 08:01:59 +01:00
harrierpigeonandClaude Fable 5.1 9dbd0307f8 Add the includes clang-tidy's include cleaner asks for on the belt files
The clang-tidy job on #14394 fails on 129 misc-include-cleaner findings:
the belt sources and tests use std::, Eigen, Point/PrintConfig and
BeltBrim symbols without including the header that provides them, which
only compiled because the precompiled header supplied it. Every include
the job names is added, in each file's existing include style ("../" in
the GCode/ and Support/ subdirectories, quoted libslic3r/ paths in the
GUI and tests). No code changes.

Verified with scripts/clang_tidy_diff.py -p build-tidy --base eb5b9a77b9
(SLIC3R_PCH=OFF compile database, clang-tidy 22.1.8): no findings left.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L6Kg5igmmMU2YLoK6HrsWV
2026-10-05 20:43:18 -05:00
harrierpigeon a7c1595738 Merge upstream/main into belt-printer
Brings belt-printer up to main eb5b9a77b9. One conflict: main translates
the pressure advance test name (#16142) on the line after belt's guard that
keeps PA Line and PA Pattern off belt printers; both are kept.
2026-10-05 13:40:41 -05:00
Ian BassiandAlexandre Folle de Menezes eb5b9a77b9 Update Translations + String improvements (#16142)
Co-authored-by: Alexandre Folle de Menezes <afmenez@gmail.com>
2026-10-05 14:43:19 -03:00
yw4zandNoisyfox 1496906939 UI Fixes / Improvements (#15074)
* plates-toolbar-scrollbar-size

* update

* filament grouping dialog

* mixed filament list

* Update StepMeshDialog.cpp

* moves plot scrollbar

* fix position of popups

* printer agent combo box width

* match multiline text control background

* add dots to configure button

* also correct label color for multiline input

* side tools connecting text color

* recenter dialog text color

* preferences experimental features

* upgrade panel hyperlink color + right margin

* fix position of + sign on printer selector

* speed control popup

* bbl fan control window

* AMS materials setting

* monitor > ams section

* match background color of multiline text editor on project page

* Fix SwitchButton colors

* bbl camera popup

* BBL > Send print dialog

* Revert "BBL > Send print dialog"

This reverts commit 128e145897.

* Revert "bbl fan control window"

This reverts commit 4a0db62790.

* Revert "bbl camera popup"

This reverts commit e2301e3237.

* Revert "monitor > ams section"

This reverts commit 66e6894eb1.

* Revert "fix position of + sign on printer selector"

This reverts commit 1523ba24c0.

* Revert "upgrade panel hyperlink color + right margin"

This reverts commit 541f2514c5.

* Revert "side tools connecting text color"

This reverts commit a9ab074247.

* Revert "recenter dialog text color"

This reverts commit f4670d32b2.

* Revert "AMS materials setting"

This reverts commit b12827f9b5.

* Revert "Fix SwitchButton colors"

This reverts commit 2d4f1d1fe9.

* match object list background color

* match compare dialog wxDataViewCtrl background color

* fix centering of iconized buttons on linux

* fix compare dialog background color not applied on linux

* edit gcode dialog components background color

* Update Plater.cpp

* fix dev button font size

* Fix scaling issue on SwitchButton while using 150%

* transfer or discard changes dialog wiki label

* "Transfer or Discard changes" / "Unsaved Changes" dialog header color

* object table colors & header spacing

* progress dialog

* fix progressbar look on linux

* fix build

* fix build

* fix centering of iconized icon again

* fix header background color

* revert bbl fan control

* add includes

---------

Co-authored-by: Noisyfox <timemanager.rick@gmail.com>
2026-10-05 19:06:24 +03:00
Rodrigo Faselli 881f2e0c12 Merge branch 'main' into main 2026-10-05 12:17:33 -03:00
yw4z 19cc65f43a Merge branch 'main' into extruder-variants-ui-2 2026-10-05 18:05:41 +03:00
SoftFever 257c589331 Merge branch 'main' into claude/inspiring-knuth-7cp6pk-upstream 2026-10-05 18:29:35 +08:00
Hanif Koh 325ecdda10 Merge Main into Belt Printer 2026-10-05 17:27:52 +08:00
HanifKoh 63d5fa23b6 Remove the OBJ Color Dialog That Texture Import Replaced (#16122)
* Remove the OBJ Color Dialog That Texture Import Replaced

* Remove references to ObjColorDialog
2026-10-05 14:39:10 +08:00
HanifKoh c44f2d6324 Remove the Unreachable MakerWorld Publish Dialog (#16121) 2026-10-05 14:12:00 +08:00
SoftFever d999a0bac9 Merge branch 'main' into pr/tommasobbianchi/16019 2026-10-05 10:58:44 +08:00
harrierpigeon bbb94724ba Merge upstream/main into belt-printer
Brings belt-printer up to main 4b4a261787. Resolutions:

- G-code header (#15897, #15915): main moved the header, config and
  thumbnail block later in _do_export; write_belt_header() moves with it,
  still after the thumbnails and outside the BTT_TFT gate.
- _extrude: first-layer acceleration keeps the per-path first-layer plane
  test with main's cached nozzle index (#16028); main's set_speed out-param
  form (#16108) everywhere else.
- GCodeWriter (#16108): the arc-to-polyline fallback for machine mappings
  that cannot express G2/G3 now runs in the out-param extrude_arc_to_xy,
  which is the overload GCode calls, and appends to the caller's string.
- GCodeProcessorResult: the belt fields join main's forwarding assign.
- Clipper2 (#15969): belt arrange helpers take Slic3r::Point; the tree
  support join types lose their ClipperLib qualifier.
- CLI arrange (#15837): belt printers still reserve no wipe tower.
- Wipe tower options (#15841): the two new sparse-layer toggles are hidden
  for belt printers like the rest of the tower options.
- Keyboard shortcuts (#15706): main's registry replaces the old key switch;
  the belt view toggle is re-registered in the next commit.
- Print::process: the belt purge-plan undo runs before main's SliceStarted
  event.
- scripts/filament_id_snapshot.json: deleted on main (a77209af8f).
- Includes and appended tests: union of both sides.
2026-10-04 19:20:33 -05:00
TheLegendTubaGuy 73a4ff9b16 Fix adding filaments with incomplete mixed metadata (#15728) 2026-10-04 13:03:42 -03:00
harrierpigeon 6a0d07664f Belt purge tower: one prism per plate
ensure_belt_purge_tower only ever looked at the current plate and kept a single
prism, so the other plates had no tower and switching plates moved the one
prism around. Every plate is now planned on its own: a prism that lies on no
plate is stale, a plate whose prism matches its recorded inputs is left alone,
the rest are deleted and recreated, highest index first.
2026-10-03 18:59:01 -05:00
harrierpigeon 82c462ab2a Prepare view: drop the dormant tilted-bed rendering
Plater::set_bed_shape read the belt keys from the plater's own config, which
never carries them, so the branch that tilted the bed model, drew the slicing
arrow and plane and switched the build volume to belt mode never ran. The
Prepare view shows the bed as the slicing pipeline treats it, flat; the
gravity arrow from build_plate_tilt stays, as does the preview's belt view,
which takes its angle from the G-code header.
2026-10-03 13:12:41 -05:00
Rodrigo Faselli 88eb219868 Merge branch 'main' into main 2026-10-03 14:41:20 -03:00
SoftFever d47fb804c3 Make the Design tab sidebar dockable and collapsible like Prepare's
The sidebar can move to either side, float, be resized, and collapse with
the canvas button or Shift+Tab. Its layout is remembered separately from
Prepare's, starts where Prepare's sidebar is, and View > Reset Window
Layout resets both tabs.
2026-10-03 19:05:53 +08:00
Claude 815716a4b5 Merge branch 'main' into claude/inspiring-knuth-7cp6pk-upstream
Conflicts were only in include lists (CadDocument.cpp, SketchEngine.cpp);
both sides' includes are kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QK4VgguuCAk2hZLWgcjJb9
2026-10-03 08:12:47 +00:00