Commit Graph
287 Commits
Author SHA1 Message Date
Clifford GarwoodandClaude Opus 5.5 e8cd4be675 Merge upstream, including deterministic painted multi-material slicing
Seven commits: painted multi-material segmentation made deterministic, missing
includes added across src/slic3r/GUI, U1 high-flow nozzle variants, Windows
ARM64 build and HTTPS fixes, and a rule added to the wxWidgets agent skill.

Three conflicts, all include lists: upstream's include pass and ours each added
to the same lists in GCodeViewer.hpp, PartPlate.hpp and PartPlate.cpp. Resolved
as the union of both. Three includes both sides had added at different places,
which git kept twice, are kept once: Color.hpp and <set> in PartPlate.cpp,
<sstream> in Plater.cpp.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 08:22:47 -04:00
HanifKoh 1a5f91d727 Add Missing Includes Across src/slic3r/GUI (#16048)
* Add Missing Includes Across src/slic3r/GUI

Every GUI source and header now directly includes the headers declaring what it uses, rather than relying on the precompiled header or transitive includes. Generated with clang-tidy misc-include-cleaner, plus one hand edit making CalibrationPanel.hpp self-contained.

* Drop the OS-Specific Includes Added Outside Their Platform Guards

GLib, GTK, D-Bus and POSIX headers are only used inside platform #if blocks, which already include them. Added unconditionally at the top of the file they broke the Windows build.

* Add the clang-tidy Configuration That Generated These Includes

Only misc-include-cleaner's missing-include check, with the headers it must never suggest: per-platform, internal and OS-specific ones that would break other platforms or are not meant to be included directly.

* Match Windows Paths in the clang-tidy Ignore List

Header paths use backslashes on Windows, so every / in a pattern is now [/\\]. The Windows SDK headers are ignored alongside the other OS-specific ones, and the list is one pattern per line. Suggested by @raistlin7447 from a Windows clang-cl run.
2026-10-02 14:56:56 +08:00
Clifford GarwoodandClaude Opus 5 a3eccdc8b7 Resolve the IMEX mode the same way everywhere it is read
A plate can carry its own parallel mode, or leave it on Primary and inherit the
process preset's. set_imex_mode() erases the key on Primary precisely so the
preset's value survives the config merge, and the slicer honors it: Print::validate()
and the G-code path both read the merged object config, so such a plate slices, and
emits, in the preset's mode.

Three places that describe that plate did not resolve it the same way. They read
the plate's own value and stopped, so on a plate left at Primary:

  the multi-material conflict badge stayed dark on a plate validate() will refuse,
  which is the one invariant the comment above it claims to keep;
  the bed-temperature and filament-type warnings never ran, so the job went out in
  copy or mirror with a mismatched bed and no notice;
  the plate tooltip reported Primary for a plate about to print in another mode.

The geometric badge beside the first of those already resolved correctly, because
it goes through the zone layout, so one badge fired while its neighbour stayed
dark on the same plate.

The fallback had been written out by hand four times. Three are now collapsed onto
one accessor, get_effective_imex_mode(); the fourth is in libslic3r, which cannot
call a GUI method and resolves the two modes itself from arguments. The remaining
callers of the raw get_imex_mode() want the raw value and keep it: the accessor
itself, the zone layout call that passes both modes separately, the mode menu and
the left-click cycle, which act on what the plate stores, and the reset that looks
for plates whose own mode was removed.

The tooltip is the one place the two readings meet. It headlines the stored mode,
because it sits on the button whose menu and click act on that value, and names
the inherited mode after it when they differ -- so one control no longer says
three different things while still telling the user what will actually print.

Reachability, honestly: nothing in the UI writes the process preset's copy today,
so this needs a hand-edited preset, a vendor process profile or a project that
carries one. It is latent rather than live -- and it stops being latent the moment
a process-level mode selector exists. Note the key lives in the process preset, so
one value there would govern every Primary plate in every project using it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-30 20:54:17 -04:00
Clifford GarwoodandClaude Opus 5 bd8b7ced6a Colour the carriage markers by filament where the preview does
Multi-material previews now open in Filament view, where toolpaths are coloured
by the filament printing them, and a marker carrying a fixed palette colour can
sit on toolpaths of that same colour. The plan already resolves which physical
head each carriage is, so in that view every carriage - the primary marker
included - takes its head's filament colour, from the accessor the ghosts
restamp themselves with, and marker, ghost and toolpath agree on what is loaded
where. The primary marker is the one every preview draws, so it goes back to its
own colour when no parallel mode is active.

Every other view keeps the Okabe-Ito palette, which is what identifies the
carriages when colour means something else, and so does a head whose filament
does not resolve. Each head is resolved once per frame and shared by its marker
and its toolhead box, the way the ghosts already hoist the map they all read.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-28 17:31:27 -04:00
Clifford Garwood 299093844f Merge upstream main: multi-variant printers, X-Ray view, preview and startup work
The two conflicts are both places where upstream landed on top of IDEX/IQEX
code. In GCode.cpp the relocated file header block meets the IMEX placeholder
block, and the placeholders are set first: file_start_gcode is processed through
the placeholder parser now, which throws on a name it does not know, so a script
naming {imex_mode} would abort the export if the header ran first. In
test_multifilament.cpp both sides appended a case at the end of the file.
2026-09-28 17:12:53 -04:00
anjisandyw4z c30c9beb09 Fix clipped tall models in G-code preview for gcode files (#15360)
Fix clipped tall models in G-code preview

Co-authored-by: yw4z <ywsyildiz@gmail.com>
2026-09-28 14:58:24 +03:00
Rodrigo Faselli 468b0bb94d Merge branch 'main' into main 2026-09-24 09:15:04 -03:00
HanifKoh 12670a6e40 Answer the Preview's Per-Frame Lookups From Cached Sums and Draw Segments From an Index Buffer (#15833)
* Answer the Preview's Per-Frame Time Query From a Cached Sum

The G-code preview's cost is linear in the number of toolpath vertices, and on a
tall multi-filament print the wipe tower dominates that count: it emits a roughly
constant 160-180 moves on every layer whatever the object is, measured at 57-61%
of all moves on a three-filament print.

Four places scanned or allocated across the whole vertex array. None of them
needed to.

get_estimated_time_at re-accumulated the estimated time from vertex 0 on every
call, and its caller is the tool marker tooltip, which ImGui re-renders every
frame while the properties panel is unfolded. It now starts from a running sum
kept at each layer's first vertex, built at load in vertex order, and adds only
that layer's vertices: the same additions in the same order, so the float result
is unchanged, at a cost of one float per layer and time mode rather than per
vertex. At the 351k vertices of a 636-layer test print the call scanned the whole
print (238us); it now scans one layer.

update_view_full_range walked from vertex 0 to find where the layer range starts,
on every slider tick. It now starts at the first vertex of that layer. The index
is derived from the vertices rather than from Layers::Item::range, because
Layers::update folds a vertex whose layer_id arrives out of order into whichever
bucket is open, which makes that range the wrong answer in general; the index
costs four bytes per layer, not per vertex.

update_colors_texture allocated one float per vertex of the whole print on every
slider tick. It now reuses a buffer.

render_legend fetched the layer Zs and the per-layer times from inside loops over
the custom G-code items, and built whole vectors only to test them for emptiness.
The times are hoisted, the Zs are built lazily so a print with no colour change
does not pay for them at all, and the emptiness tests use the existing counters.

No rendering behaviour changes.

* Draw the Preview's Toolpath Segments From an Index Buffer

The preview's frame cost is dominated by one call: a single instanced draw of
every visible toolpath segment. On a tall multi-filament print the wipe tower
supplies most of those segments, which is why the preview of a large tower is
slow and why shrinking the layer range speeds it up again.

That draw is not fill bound. Shrinking the model to about a fortieth of its
screen area moved the frame from 419 ms to 401 ms, so the cost is per segment,
not per pixel, and it is paid in the vertex shader: five texelFetch calls plus
several cross/normalize per invocation.

Each segment is a box of eight corners, but it was submitted with
glDrawArraysInstanced over a 24 entry array, so every corner was transformed
once per triangle that touches it and the shader ran 24 times per segment. The
same 24 entries are now an element buffer over the eight distinct corners, which
lets the post-transform cache reuse them and drops the shader to 8 runs per
segment. The triangles, their winding and the vertex_id each corner receives are
unchanged.

Measured over 100 frames on the 636-layer, 351k-vertex three-filament fixture,
the segment draw goes from 381 ms to 322 ms per frame. That is a software
rasterizer, where triangle setup dominates and understates the win; the drop in
shader invocations is the transferable part.

Verified by loading the same project in this build and in a build of the parent
commit and comparing the canvas across three states - the default view, a
rotated camera, and a reduced layer range: pixel identical in all three. The
rotated case matters because the shader picks its corner offsets from the camera
direction. The only pixels that differ anywhere on screen are in the G-code text
panel, which prints a per-process object id that varies between any two runs.
2026-09-23 12:06:02 +08:00
Ian Bassi e71497738f Configurable default G-code preview view type (#15769) 2026-09-22 20:50:59 -03:00
Ian BassiandRodrigo Faselli d820303a3f Improve preview colors (#15809)
Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
2026-09-22 20:50:18 -03:00
Clifford GarwoodandClaude Opus 5 1f1d13e3d9 Describe the estimate's actual blind spots in the tower comments
The estimate no longer reports a tower for a single filament whose flush matrix
purges, so the comments that justify calling prime_tower_is_printed() instead of
reading a depth now cite what still holds: it reads neither enable_prime_tower
nor print_sequence. GCodeViewer's comments name render_scene(), the function
that replaced the render() they still pointed at, and the pass contract mentions
the toolhead boxes it draws.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-21 17:10:23 -04:00
raistlin7447 068dc4346f fix(gcode-viewer): port the IDEX/IQEX rendering onto the render_scene/render_overlay split
The merge of upstream main (dc021c6ef6) resolved the GCodeViewer.cpp conflict
from #15674 by keeping both sides. That left a stray `}` in
SequentialView::render_overlay, so the file no longer compiles. It also kept
the IMEX carriage update and the toolhead-box GL draw in the ImGui overlay
pass, which #15674 no longer uses for 3D geometry.

The carriage update and the toolhead boxes now run in render_scene next to the
primary marker, and render_marker draws the secondary markers. render_overlay
goes back to what upstream has, without the duplicated marker-position block
and the unused bottom_margin the merge left behind.

The same upstream change renamed PartPlate::show_tooltip to set_hover_tooltip;
the two IMEX call sites follow it.
2026-09-21 08:22:07 -05:00
Rodrigo Faselli dc021c6ef6 Merge branch 'main' into main 2026-09-21 09:47:39 -03:00
Kris AustinandRodrigo Faselli 213c6569ab cut the GPU load of moving the mouse over the 3D viewport (#15674)
Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
2026-09-18 20:19:37 -03:00
Clifford GarwoodandClaude Opus 5 84c13d4913 Merge upstream main: wipe tower port, per-filament bounds checks, CLI mixed rules
Four files overlapped, and each resolution favours upstream where the two sides
had done the same work:

WipeTower's M104/M109 tool qualifier. Both sides bounds-checked the physical
extruder map lookup; upstream omits the T qualifier when the map cannot answer,
where this branch fell back to the logical index and so named a carriage that may
not be the one printing. Upstream's behaviour is what this branch documents
elsewhere, so its version is taken and the local helper is dropped.

get_extruders' mixed-slot switch. Upstream added the same concept to the CLI
overload as expand_mixed_slots, so the GUI overloads' parameter is renamed to
match rather than carrying two names for one idea.

GLCanvas3D's sequential-clearance branch gains upstream's
update_compacted_wipe_tower_clearance for the by-layer case.

The printer_agent re-sync in TabPrinter::reload_config was upstream's and their
preset-undo fix removed it, so it goes; the IMEX modes grid re-sync beside it
stays, since it spans three options and is not a Field.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-18 01:50:53 -04:00
Ian Bassi c833ccdf6f Update localizations and improve strings (#15739) 2026-09-17 14:27:53 -03:00
Clifford Garwood b17414b160 Merge remote-tracking branch 'upstream/main' into integration/upstream-main-sync 2026-09-13 02:21:43 -04:00
Kris Austin 0365304ae0 fix: G-code preview drops comment text and cuts non-ASCII lines short (#15448) 2026-09-06 15:42:57 -03:00
Clifford GarwoodandClaude Opus 5 6c67fcfe07 Read IMEX geometry defaults from print_config_def instead of literals
Five sites carried a hardcoded fallback for imex_tools_per_gantry that had
to match the value registered in print_config_def, with nothing enforcing
the agreement, and several explanatory comments miscounted the sites they
described or cited stale line numbers.

Add imex_cfg_int/_float/_bool/_enum<T> to IMEXHelpers, which return the
value registered for the key when it is absent from the config, so the
registration is the single source and there is nothing left to keep in
sync. Route every read of the IMEX geometry keys through them: 32 call
sites across IMEXZones, PartPlate, GCodeViewer and Tab. The only direct
lookup left is the bail in PartPlate::imex_multicolor_block_reason, which
must not default because it reports a routing conflict and a defaulted
grid would produce a false warning.

imex_cfg_enum uses dynamic_cast on both halves rather than the type()
comparison the others use: every ConfigOptionEnum<T> reports coEnum, so a
type() check cannot tell one enum type from another and would cast a
ConfigOptionEnum<OtherEnum> to the requested T. The ConfigOptionPercent :
ConfigOptionFloat inheritance that rules dynamic_cast out for the float
accessor has no analogue for enums.

Correct the comments that prompted this: the cache-key input list in
PartPlate named five inputs for a nine-part key, the ImexMarkerKey note
in GCodeViewer called imex_tool_layout an input only the preview reads
when the plate keys it too, and four file:line citations pointed at the
wrong lines. Values are unchanged at every converted site; cache key
strings keep their existing representation.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 20:34:04 -04:00
Clifford GarwoodandClaude Opus 5 c09ce3a0d1 Merge upstream main: CLI argument parsing, GUI string fixes, nozzle type undo tracking, warning cleanups
No IMEX code upstream, so nothing in this merge touches the feature. All 21 overlapping
files auto-resolved; verified every upstream addition is present in the merged tree.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 00:17:36 -04:00
Clifford GarwoodandClaude Opus 5 1e608fe24c Have the preview consume the shared zone layout instead of rebuilding it
Closes review comment 17.

GCodeViewer had its own copy of the flip_x/flip_y corner mapping, the active
column/row sets, the physical-to-zone index mapping and the zone pitch -- the same
derivation as the plate's, with nothing keeping the two in step. It now calls
compute_imex_zone_layout() and consumes head_zone_centers. The mirror is expressed
as a reflection about the midpoint of the two zone centres rather than about a
zone-relative strip width, which is algebraically identical for equal-sized zones
and needs no pitch, and the toolhead-box face is chosen by comparing zone centres
instead of physical columns.

The July report of a math error in the visualizer for non-primary heads was this
drift: the sec_box_offset_y else-branch hardcoded -imex_box_wy, which happened to
equal the primary's offset on the rear-* layouts and pointed the wrong way on the
front-* ones. Structurally unreachable now.

Verifying the two sides matched turned up two config defaults that disagreed, both
fixed in their own commits: imex_nozzle_clearance_x/y (the viewer's 30.0f matched
PrintConfig, the zone code's 0.0 did not, and the strip loops are gated on it) and
imex_tools_per_gantry (the library's 2 matched, both GUI paths used 1).

Consuming the shared function meant resolving it per frame, and the sequential-view
marker flag is sticky, so one drag of the slider made every subsequent frame parse
five strings and allocate a dozen containers from inputs that never change. The
resolve now sits behind a cache key mirroring PartPlate::build_imex_cache_key(),
plus the two inputs only the preview reads -- the bed extents and the tool layout.
An idle frame compares scalars and allocates nothing. The toolhead-box mesh, which
was being re-uploaded to the GPU every frame for the same reason, is rebuilt only
when the clearances change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 00:56:55 -04:00
Kris AustinandRodrigo Faselli 1749c293a6 build: clear 237 warnings - unused lambda captures (#15417)
* build: enable /Zc:lambda for MSVC

MSVC keeps its legacy lambda processor under /std:c++17, which rejects
reading a constexpr constant inside a lambda that does not capture it
(C3493). No other compiler requires that capture, and clang reports it as
an unused one, so the two cannot both be satisfied without the flag.

/Zc:lambda selects the conforming lambda parser that clang and GCC
already use. It is implied by /std:c++20 and /permissive-, so it is only
needed while we are on C++17. clang-cl is conforming already and does not
take the flag.

It requires VS2019 16.8, so build_release_vs.bat now says 16.8+.

* build: clear 237 unused lambda capture warnings

236 captures across 81 files, 142 of them `this`. Removing an unused
capture changes no behavior; clang does not report a capture whose type
has a non-trivial destructor, so nothing held only to extend an object's
lifetime is in this set.

Nine of them are the second half of the warning, "is not required to be
captured for this use", where the capture is a const or constexpr value
the body does read. Those depend on the /Zc:lambda change in the previous
commit. One of them, in FillRectilinear.cpp, had been worked around with
an #ifndef __APPLE__ guard around the capture list, which is now gone.

GUI_ObjectTableSettings.cpp captured its reset button only to read it
inside #ifdef __WXOSX_MAC__. That branch now takes the button from the
event it is already handling.

* build: fail configure on MSVC older than 19.28 instead of dropping /Zc:lambda

cl.exe answers an unrecognized /Zc: sub-option with warning D9002 and keeps
going, so on VS2019 before 16.8 the flag is silently ignored and the build
instead dies with C3493 in FillRectilinear.cpp, nowhere near the cause.

* fix: delete three locals that are now unused

Their only remaining use was the lambda capture this branch removed. The
Clang builds set -Wno-unused-variable, so the build never flagged them.

---------

Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
2026-09-02 07:38:05 -03:00
Clifford GarwoodandClaude Opus 5 ba07716ddb Merge upstream main: color mixing feature and related fixes
Brings in 54 upstream commits, the bulk of them the BambuStudio-ported color
mixing / mixed filament subsystem (#15347) plus its follow-ups, along with the
Assimp-backed colored OBJ import, warning-policy build changes, and assorted
profile and localization updates.

Two conflicts, both "each side added at the same point", resolved by keeping
both:

- Print::validate() -- our IMEX multi-color block and upstream's new gradient
  mixed filament warning were inserted at the same spot after the empty
  extruders check. They test unrelated conditions, so both are kept, each with
  its own closing brace.
- tests/libslic3r/test_3mf.cpp -- our three IMEX per-plate round-trip scenarios
  and upstream's mixed-filament round-trip scenario both append to the end of
  the file, and each side added one include. All four scenarios and both
  includes are kept.

Everything else merged cleanly, including GCode.cpp, ToolOrdering.cpp,
PartPlate.cpp and PrintConfig.cpp. Upstream left the is_extruder_used block
untouched, so the IMEX supplement still applies, and estimate_wipe_tower_polygon
is unchanged, so the prime tower hull work is unaffected.

Not addressed here, and worth its own change: a mixed filament is a virtual slot
that no nozzle carries, while physical_extruder_map routes logical slots to
physical heads. Print::extruders() lists mixed slots under their own id whereas
tool_ordering.all_extruders() lists them post-expansion, so the IMEX pem lookups
have no defined answer for a mixed slot. Upstream's own guards reject a mixed
filament where a physical slot is required; IMEX likely wants the same.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 13:07:26 -04:00
Ian Bassi ea376e858c Show move length in Previewr panel (#15326)
Adds a new "Length" row to the sequential marker position popup in GCodeViewer and updates row capacity accordingly.
For arc commands (G2/G3) that are split into multiple vertices, it now sums segment distances across vertices with the same gcode_id so the displayed value reflects the full move length instead of a single chord.
2026-08-23 12:18:22 -03:00
Clifford Garwood bbcd2e2a80 Merge upstream main: cyclic print ordering, brim overlap fix, perimeter and support fixes 2026-07-31 01:42:48 -04:00
2d4b431d5f Improve dimmed layers (#15001)
Co-authored-by: yw4z <yw4z@outlook.com>
Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
2026-07-29 16:27:15 -03:00
Rodrigo Faselli fccd9d52ce Merge branch 'main' into main 2026-07-25 19:19:07 -03:00
Ian Bassi d6cb667b89 Localization refactor pt 2 (#14907) 2026-07-24 09:41:24 -03:00
Ian Bassi 4e4e867502 Dim lower layers (#14705) 2026-07-20 18:14:01 -03:00
Clifford Garwood 58b4a68a10 Merge upstream main: Python plugins, Bambu device rework, macOS fixes
Resolved 4 conflicts:
- PrintConfig.hpp / Preset.cpp: upstream restored calib_flowrate_topinfill_special_order
  (removed in the prior merge, now re-added and re-registered); kept it alongside our
  IMEX options (imex_parallel_mode, imex_head_filament_map).
- Plater.cpp: kept our <sstream> include plus upstream's <optional> and plugin includes.
- tests/fff_print/test_gcodewriter.cpp: both sides appended disjoint scenarios after a
  shared base. Reconstructed as upstream's full file (base + its 3 toolchange/H2C
  scenarios + the boost/filesystem include its helper needs) followed by our 10
  set_pressure_advance / set_temperature scenarios. 22 scenarios total, no duplicates.
2026-07-19 18:33:58 -04:00
Alexandre Folle de MenezesandIan Bassi 979e56f1d0 Standardize the Unicode chars for unit strings (#14631)
Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
2026-07-16 17:37:09 -03:00
Clifford GarwoodandClaude Opus 4.8 90e8ff7cee fix(imex): align the preview marker grid with the plate zone grid
Follow-up to the cross-gantry mirror axis change, from an adversarial review of it.

GCodeViewer builds its own copy of the zone grid to place the sequential-preview
carriage markers, and it must reproduce PartPlate::calc_imex_zones exactly or the
markers drift away from the ghosts they are meant to track. It did not, in two ways,
because sizing the grid and placing a cell answer different questions:

  - Sizing: calc_imex_zones counts every Copy/Mirror tool's OWN column, including the
    non-representatives of an aggregated (Span) gantry -- they still donate a column.
    GCodeViewer only ever saw the representative, so on an aggregated gantry it could
    count fewer columns than the plate and lay its markers out against wider strips.

  - Placement: calc_imex_zones PINS an aggregated cell to the primary's column, because
    that row-strip spans the full bed and has no column of its own. GCodeViewer used the
    representative's own column, which put the marker a strip away from the ghost
    whenever the representative was not column-paired with the primary.

Track the two sets separately: grid_tool_ids sizes the grid from own columns, eff_col_of
pins only aggregated tools when placing. Out-of-grid tool indices are deliberately left
unfiltered -- calc_imex_zones drops them while calc_imex_ghosts keeps them, so no policy
here can agree with both, and a comment says so rather than pretending otherwise.

Also:
  - The mirror-axis rule lived in three copies (two PartPlate lambdas plus an inline
    re-derivation here). Hoist it to imex_mirror_axis_for() so the ghosts and the markers
    cannot drift apart, and unit-test it, including degenerate tools_per_gantry.
  - Drop imex_head_transform's mirror_axis default. A defaulted axis silently hands a
    forgetful caller the X reflection, which is wrong for every cross-gantry tool and
    fails silently -- exactly how a stale test kept certifying the old rule.
  - Replace that stale test: it asserted a diagonal mirror "flips X only", the rule this
    work overturned, and stayed green only because of the default.
  - A secondary sharing the primary's gantry now takes the primary's Y box facing. The
    box shows the side a tool could be hit from, and two tools on one beam can only be
    hit by the same other gantry. No-op on the rear-* layouts, where the hardcoded value
    already matched; on front-* layouts it pointed the box away from the only tools that
    could reach it.
  - Correct two comments that described the aggregated X-frame substitution as a
    reflection plane. It is not one: it exists to zero gantry_offset.x, and removing it
    would push aggregated ghosts a column off their strip.

Verified: 385/385 tests; CLI slice of the IMEX regression project is byte-identical to
the previous commit's G-code apart from the timestamp, confirming this is
visualization-only and cannot affect sliced output.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 10:01:09 -04:00
Clifford Garwood f850912123 fix(imex): mirror across the gantry-row axis on two-gantry printers
A Mirror tool reflects across the boundary it shares with the primary's zone, and
which boundary that is depends on where the tool sits:

  - Same gantry: the tools are side by side along X, so the shared boundary is
    vertical and the reflection negates X. This is what single-gantry IDEX does,
    and it was the only case the code modelled.
  - Different gantry: the zones are stacked along Y (front strip vs rear strip),
    so the shared boundary is horizontal and the reflection negates Y. The part
    that comes off gantry 1 is a Y-reflection of the tool directly behind it.

imex_head_transform() hardcoded diag(-1, 1, 1) for every mirror, as its own TODO
acknowledged. Lift the axis to a caller-supplied ImexMirrorAxis; PartPlate picks it
from the tool's gantry row. Both reflections keep det = -1, so a mirrored part stays
a true mirror image rather than a 180-degree rotation, which would print the
primary's part merely turned around.

The correct axis removes two workarounds. Both ghost paths special-cased aggregated
mirrors to "drop the X reflection, translate 1:1 and bake the flip into the mesh"
because reflecting X pushed the ghost off-bed as the primary was dragged. With a Y
reflection the X translation is already zero for aggregated tools, so that falls out
for free and the special cases are deleted.

Preview markers follow the same rule, which also fixes two placement bugs:

  - Mirrors reflected across a Copy tool's zone edge, falling back to the primary's
    column when a row had no Copy. In iq-mirror (0:P,1:C,2:M,3:M) the front row has
    no Copy, so t2 and t3 both fell back and computed the identical X — both drawn
    on top of each other in t3's zone. A mirror now reflects within its own zone.
  - The toolhead footprint box flipped to the far side of the nozzle for any mirror
    right of the primary. That only holds for an X-axis mirror, which reverses the
    carriage's orientation; a cross-gantry mirror keeps the X orientation of the
    tool behind it, so its box stays on the same side.

Tests cover the cross-gantry and diagonal cases, that the axis is caller-supplied
rather than inferred from the offset vector, and that both axes are reflections
(det = -1) rather than rotations.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 00:57:31 -04:00
Clifford Garwood fdb0e20f36 Merge upstream main (Bambu H2C/A2L multi-nozzle support) into IMEX branch
Conflicts were all co-located additions rather than design collisions:
- GCode.cpp: adopt upstream's toolchange(filament_id, nozzle_id) signature and
  per-variant set_config_index() while keeping the IMEX bare-T<n> suppression;
  rebase the second-layer temperature loop's non-IMEX branch onto upstream's
  get_filament_config_index() resolution.
- Preset.cpp / PresetBundle.cpp / PrintConfig.cpp: keep both sides' option-list
  and enum-map entries.
- GLCanvas3D.cpp: upstream's printable_heights argument plus the IMEX ghost pass.
- PartPlate.cpp: keep <set> (still used).
- test_gcodewriter.cpp / test_3mf.cpp: keep both sides' test cases.
2026-07-13 16:22:22 -04:00
Clifford Garwood 0ba09780d1 Merge upstream main into IMEX parallel printing branch
Resolves two conflicts:
- GLCanvas3D.hpp: keep both the IMEX ghost render declarations and
  upstream's _render_wireframe_overlay().
- test_gcodewriter.cpp: both sides appended test cases to the same
  region; keep upstream's origin/machine-limit tests alongside the
  pressure-advance and temperature scenarios.
2026-07-13 13:17:45 -04:00
Rodrigo Faselli 79aa0bd4a2 Merge branch 'main' into feature/h2c_support_clean 2026-07-12 22:00:40 -03:00
yw4z e1c0ea0cc4 UI fixes & improvements (#14186)
* titlebar menus

* sidebar separators

* Update BonjourDialog.cpp

* Update StepMeshDialog.cpp

* Z contouring icon

* Update AmsMappingPopup.cpp

* Update AmsMappingPopup.cpp

* sync ams dialog

* Update calib_dlg.cpp

* sync popups

* Update AmsMappingPopup.cpp

* operation section on gizmos

* Update PresetComboBoxes.cpp

* Update GCodeViewer.cpp

* Update GCodeViewer.cpp

* Update AboutDialog.cpp

* disable realistic view option on gcode preview

* Update Preferences.cpp

* fix nozzle text clipping on linux
2026-07-11 20:24:03 +03:00
SoftFever 28b7127150 feat(gui): multi-nozzle UI for H2C/A2L
Multi-nozzle sync widget, AMS rack-nozzle mapping popup, calibration rework, send-dialog nozzle mapping and extruder-count UI. Includes the fix to persist the AMS sync badge on filament cards (H2C/A2L and direct-sync printers).
2026-07-09 01:16:26 +08:00
Ian Bassi 12b63ebe36 Localization context (verb noun adjective adverb) (#14646)
* Torre de purga capitalization

* Russian navigation back

* Inches

* verb noun adjective adverb

* Posterior

* Fix atras for camera view

* Camera view
2026-07-08 09:16:00 -03:00
Clifford GarwoodandClaude Opus 4.8 c5d0cdbcff Merge upstream/main into iXex PR branch (#13086)
Catches the iXex/IDEX parallel-printing branch up to upstream main
(102 commits). Two content conflicts resolved:

- src/libslic3r/Preset.cpp: s_Preset_printer_options — kept upstream's
  new "use_3mf" key and our iMEX printer-capability/mode keys.
- tests/fff_print/test_gcodewriter.cpp: upstream revived the disabled
  suite (#14196), dropping the obsolete [.]-tagged lift() test and its
  config_lift_unlift.ini; kept their set_speed + z_hop tests and appended
  our 10 per-firmware set_pressure_advance/set_temperature scenarios.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 23:29:35 -04:00
Ian Bassi 514ab02525 Localizations refactor (#14254) 2026-06-18 09:13:51 -03:00
Allyn Malventano b0c1887f40 fix: persist user-selected preview view mode after first load (#13625)
fix: apply smart preview defaults per extruder count session

- Track last extruder count (1=single, 2+=multi) instead of boolean flag
- Apply appropriate default (ColorPrint/FeatureType) when count changes
- User selections persist within same extruder count
- Symmetric behavior: both single and multi actively apply defaults
- Delete duplicate dead code block (uncommented TODO scaffolding)

Behavior:
- First slice (any type) → appropriate default
- User changes view → persists on re-slice
- Switch single→single or multi→multi → persists
- Switch single↔multi → appropriate default applies
2026-06-16 22:15:00 +08:00
SoftFever 1e1d9cbaf8 Fix crash on printer switch from stale filament/extruder indices (#14103)
Switching to a printer with fewer filaments (e.g. H2D -> X2D) threw
std::out_of_range in check_filament_printable. Clear stale per-volume
extruder config on count shrink and bound-check filament indices at the
read sites.
2026-06-08 18:57:52 +08:00
Clifford Garwood d0c6e8305d fix(imex): hide secondary carriages in g-code preview when firmware-managed-zones is on
The firmware-managed-zones writer offset centers the slice at bed origin,
so prim_pos and the preview toolpaths sit in a shifted frame relative to
the plate-local bed bounds the secondary-marker zone math uses. Computing
secondaries against unshifted bed bounds puts them off the build plate.

Skip the secondary-marker / toolhead-box computation when the
firmware-managed-zones option is enabled. The firmware physically fans the
centered toolpath out into the zones at print time, so the honest preview
is the single centered toolpath with no secondaries.

Reported by @Felix14-v2 on PR #13086.
2026-06-08 01:24:35 -04:00
Clifford Garwood 4a6342d646 fix(imex): correct secondary carriage placement in g-code preview
- Layout enum read via opt<ConfigOptionEnum<ImexToolLayout>>() did a
  dynamic_cast that fails for the ConfigOptionEnumGeneric type enums load
  as from presets, silently defaulting to FrontLeft. This desynced flip_y
  from PartPlate::calc_imex_zones, computing secondaries against the wrong
  primary zone and rendering them off the bed. Use option<>() (type-checked
  static_cast), matching how PartPlate reads the same key.

- Span modes now collapse each non-primary gantry to one active marker via
  group_imex_active_tools_by_gantry() (the same aggregation PartPlate uses
  for zones), since only one tool prints per zone at a time. Previously every
  Copy/Mirror tool got its own marker.

- Aggregated mirror markers reflect across the bed centerline rather than a
  column edge; the aggregated strip spans full-X, so edge reflection would
  push the marker off the bed.
2026-05-29 09:31:15 -04:00
Clifford Garwood 9b3c1b87f4 Sync upstream main
Includes nozzle-diameter guards for printers without nozzle info (#13255,
now landed upstream) and manual-calibration nozzle mismatch fix (#13882).

# Conflicts:
#	src/slic3r/GUI/GCodeViewer.cpp
#	src/slic3r/GUI/GLCanvas3D.hpp
#	src/slic3r/GUI/Tab.cpp
2026-05-29 01:49:42 -04:00
Ian BassiandSteve Scargall c5855db578 Line Type preview: Display distances and amount values (#13681)
* feat(viewer): Display travel distance and move count in G-code summary

This commit introduces a new feature that enhances the G-code viewer by displaying the total travel distance and the total number of travel moves in the 'Line Type' summary.

This provides users with more detailed statistics about their prints, helping them to better understand the printer's behavior and identify opportunities to optimize travel moves for faster print times.

This commit also fixes a critical bug in the G-code processor where the travel distance was being calculated incorrectly. The distance variable was not being updated for non-extruding travel moves, leading to inaccurate statistics. The calculation has been corrected to ensure it is performed for all relevant move types, resulting in accurate travel distance reporting.

* Subfix segments

kilo mega giga tera peta exa

* Add missing values

* Grams to Kilos and tons

* add distance

* Fix tool view

* Record and display seam distances

Track seam-related distances in print statistics and show them in the GCode viewer. Added total_seam_gap_distance and total_seam_scarf_distance to PrintEstimatedStatistics (with initialization). In GCode::extrude_loop the code now computes seam gap and scarf distances and accumulates them for external perimeters. GCodeViewer uses the summed seam distance when the Seams option is selected in the legend.

* Fix travel / wipe distances

* Update GCode.cpp

* Filament changes estimated time

---------

Co-authored-by: Steve Scargall <37674041+sscargal@users.noreply.github.com>
2026-05-21 13:49:43 +08:00
Clifford Garwood 5fd0f3c5b7 Merge remote-tracking branch 'upstream/main' into feedback 2026-05-17 20:21:23 -04:00
Kiss Lorand 26e6216d91 Fix stale keyboard refresh and auto-resize flicker of speed profile window in Preview tab (#12707)
* Fix speed popup refresh and sizing in Preview

Fix two UX issues in the Preview actual speed profile popup.

Keyboard-driven horizontal slider changes could leave the popup visually stale
until a later input event, which made keyboard navigation feel delayed.
Also, the speed-profile popup could briefly render at an incorrect height
before resizing on the next frame.

This change makes keyboard slider updates explicitly request a follow-up frame,
so refresh no longer depends on incidental mouse activity.

It also removes auto-resize from ToolPositionTableWnd and sizes the popup
before begin() each frame. The popup height is computed from plot height,
table rows, and window paddings, then clamped to at least the height of the
adjacent ToolPosition window.

The popup width is now computed from translated header text and padding terms,
with a 16:9-derived minimum for a more stable appearance. The width is cached
for the session and recalculated when UI scale changes.

Result:
- keyboard navigation updates reliably
- popup no longer shows the transient wrong-height first draw
- sizing is more stable across scale and localization

* Fix ToolPosition window also + other fixes

* Fix compile error when ENABLE_ACTUAL_SPEED_DEBUG disabled

IMGUI_DEFINE_MATH_OPERATORS is needed even if ENABLE_ACTUAL_SPEED_DEBUG is not enabled

* Guard seam fallback vertex index in GCodeViewer
2026-05-15 00:48:23 +08:00
Clifford Garwood ab63789076 Merge remote-tracking branch 'upstream/main' into feedback
# Conflicts:
#	src/libslic3r/GCodeWriter.cpp
#	src/libslic3r/PrintApply.cpp
#	src/slic3r/GUI/Tab.cpp
#	tests/libslic3r/test_config.cpp
2026-05-13 00:57:36 -04:00