mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-14 11:21:18 +00:00
5c8a287b5ea9221fe0ad6bcb24389aa575670fb2
14127
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
5c8a287b5e |
Keep User Filament Presets on Printers Their Parent Listed in 2.4.2 (#16367)
* Keep User Filament Presets on Printers Their Parent Listed in 2.4.2
User presets store only their diff, so they inherit the parent's
compatible_printers. Profile changes since 2.4.2 renamed and narrowed many
system filament presets, so user presets made against them disappear from
printers they were made for and the saved selection silently falls back to
another filament at startup.
orca_profile_tool.py legacy-compat writes, per current system filament
preset, the printers it covered in an older release and no longer lists,
kept only where a preset with the same filament_id still covers the printer
and they share an extruder variant. The loader gives these printers to user
presets that inherit their parent's list, without writing any file; saving
a preset detached, cloned or with its own list drops them.
* Ask Before Migrating User Filament Presets to Their Legacy Printers
Instead of giving user presets their parent's 2.4.2 printers in memory,
detect the user presets that inherit their parent's compatible_printers
and are missing printers from the legacy map, and offer to migrate them
at startup or from Help > Migrate Filament Presets.
Migrating first backs up the user folder to
filament_preset_migration_<datetime>_backup.zip in the configuration
folder, and migrates nothing if that fails. It then writes the printers
into each preset's compatible_printers, patching only that key so the
preset keeps the parent name it was saved with and older releases can
still resolve it. Cloud-synced presets are marked for upload.
No asks again at the next launch; "Don't remind me again" stops the
startup prompt.
* Key the legacy filament preset map by the parent's 2.4.2 name
* Check the legacy filament preset map in CI
* Limit the 2.4.2 filament preset migration to presets that need it
Only presets that still name their parent as 2.4.2 did and have no printer
list of their own are offered, and a preset changed after it was found is
left alone. The startup prompt appears once, after upgrading from 2.4.2 or
earlier, as a standard dialog that keeps "Don't remind me again"; Help >
Migrate Filament Presets is unchanged. Migrating no longer overwrites an
unsaved printer-list edit or marks the project modified.
* Ask on Every Start and Take the User Presets Lock Once When Migrating Filament Presets
The startup prompt no longer depends on the version the previous run
recorded: whoever still has user filament presets that lost printers is
asked, including users already on a 2.5.0 nightly or alpha. Answering No
asks again at the next start; "Don't remind me again" stops it.
Migration takes the user presets lock once, around the backup and every
file write, instead of once per preset. A lock another instance holds is
reported once ("another OrcaSlicer instance is saving presets") with
nothing written, rather than as one failed preset after another once the
lock's cool-down kicks in. Detection records each preset's file and the
list it will get, so the writes need no collection lock while the user
presets lock is held, the order InstanceLock requires; the loaded presets
are updated afterwards.
* Run the Legacy Compatible Printers Map Check in check_profile.sh and .ps1
CI's "Check profiles" job checks resources/info/legacy_compatible_printers.json
against the v2.4.2 profiles, and its failure message points at these scripts
as the local reproduction, so they run the same check: legacy_compat, right
after profile_tool. The v2.4.2 tree comes from git archive of the tag, is
cached in the work dir like the other downloads, re-extracted with --refresh,
and fetched from upstream first when the checkout lacks the tag.
* Back Up Before the User Presets Lock and Merge Migrated Printers Into the Live List
The lock guard is meant for a few file operations, and zipping a large user
folder is not one, so the backup is taken before it and the guard covers the
writes only; each write re-reads its file, so a save that slipped in before
is still respected.
The in-memory update adds the lost printers to the list the preset holds at
that moment rather than replacing it with the one found at detection, and
leaves a pending cloud sync state alone: a download waiting to be saved kept
its newer list and its "save" flag, which "update" would have cancelled.
The lock message no longer blames another instance alone, since the lock is
also refused for a while after a miss and when the lock file cannot be
opened.
---------
Co-authored-by: SoftFever <softfeverever@gmail.com>
|
||
|
|
a338f6ca7d | Fix instance Z synchronization (#15465) | ||
|
|
e821665e4b |
Add orca-translations Claude skill and PO tool (#16436)
* Add orca-translations Claude skill and PO tool
Add a .claude skill that guides translation passes over the localization/i18n gettext catalogs. It covers the workflow: run gettext, settle new glossary terms, translate in batches, verify, and report.
Includes translation-rules.md, which condenses the placeholder, line-break and glossary rules plus the guidance on review notes.
Also adds scripts/po_tool.py (standard library only). It lists pending entries with glossary hints, validates and applies translations, adding '# AI Translated' and '# Needs human review' comments, and rewrites only the entries it changes. It can also check catalogs, look up existing translations, suggest glossary candidates and update the wiki glossary TSV.
* Let po_tool apply translations by entry id
pending now gives each entry a short id, so a translation file can name
entries by id instead of repeating the English msgid. apply accepts either.
A "review": "" item clears an earlier review note when overwriting an
entry, and check gives each msgfmt run its own temporary output file so
parallel checks do not collide.
* Fill all empty translations
Regenerate the template with run_gettext --full and translate every empty
entry in the 22 catalogs (7,089 entries) with the orca-translations skill,
following the localization glossary, including the new belt printer,
IDEX/IQEX and plugin terms. Every new entry is marked "# AI Translated";
122 also carry "# Needs human review" with what to check.
* Show entry ids in po_tool lookup and search translations
lookup now prints each entry's id, so an entry found there can be
rewritten with apply --overwrite without repeating the English, and
--msgstr searches the translations instead of the msgids, which finds
every entry that still uses a term being changed.
SKILL.md describes how to change an established term: check the
catalog's usage first, update the glossary cell, then rewrite only the
entries where the term has that meaning.
* Fix mistranslated terms and align catalogs with the glossary
Correct labels that the glossary had copied, and the entries that used
them:
- ca: Raft was rendered as the Brim term; it is now "Rai". Exhaust fan,
Monotonic line, Unretract, Slice, Lateral Honeycomb and PA Tower
labels fixed.
- cs: Lightning infill is "Blesk", not "Osvětlení" (illumination).
- sv: Retraction is "Retraktion", not "Reduktion"; Unretract,
Lagerhöjd, Delkylningsfläkt, Utsugsfläkt, Aligned Rectilinear and the
Preset label fixed.
- de: Brim is "Rand" and Skirt is "Umrandung", as most entries already
had them.
- lt: brim entries used the Skirt word "apvadas"; they now use "kraštas".
- es: Sparse infill is "relleno de baja densidad", the catalog majority.
- eu: nozzle wipe is "garbiketa", not purge.
- th: the project plate is "เพลต", distinct from the bed.
- uk: Quarter Cubic, Latin "PA" in PA Tower, and Belt purge tower.
- pl: Cross Hatch, Tri-hexagon, Volumetric speed and Raft labels.
- zh_TW: Brim stays "Brim", as the existing labels have it.
- tr: "Prime Tower" read "Prime Minister Tower".
- ko: the plugin "Capabilities" label read "performance".
- Build Volume labels fixed in tr, de, cs and eu.
All 242 rewritten entries are marked "# AI Translated".
* Make more UI text translatable and fix pluralization and context
Audit the sources for text that translators could not translate
correctly and fix it.
Mark text that reached users in English only: IDEX/IQEX role names,
plugin permission prompts and errors, 3MF/preset loading errors, G-code
placeholder parser errors, calibration and device messages, notification
texts, file dialog filters, and the config validation messages.
Replace sentences built from translated fragments, such as the prime
tower warnings, "left"/"right" nozzle names, and "Filament " + number,
with single msgids that have placeholders, so translators can reorder
and inflect them.
Use plural forms for texts with a count ("%1% mixed filaments",
"%d objects", "%1% triangles") instead of "(s)" and mismatched verbs.
Fix the swapped singular/plural pair in "The following presets inherit
this preset".
Add translation contexts where one English word has several meanings
(Subscribed/Local, Standard, Pause, View, Step, Model, Camera, Flow,
Filter, Others, Prepare, the Design tools). Show "Size" instead of
"Build Volume" in the Cut tool, which displays the object size.
Fix code that compared translated text with English (the Edit G-code
dialog group, the plugin registration failure check), and add the
bundled web pages' missing string-table entries. Use the single
character for degrees Celsius, say "G-code", and describe the head
filament map without internal jargon.
Install the libslic3r translate callback before the presets load, so
their load errors are translated, and list the 28 source files that had
marked strings but were never extracted into localization/i18n/list.txt.
* Suggest earlier translations and list fuzzy entries in po_tool pending
pending now attaches the closest earlier translation of a similar
English text (from obsolete or translated entries), so a reworded or
split string keeps its wording, and it lists fuzzy entries by default,
since users never see them. A fuzzy entry also carries its stale
translation as previous_translation.
* Fix wording of the non-boolean operands error
The logical operation error said "operators" where it means the
operands the operation was applied to.
* Translate the newly translatable strings in all languages
Regenerate the template after the translatability changes and fill every
new or changed entry in the 22 catalogs (about 12,800 entries): the
strings that became translatable, the plural forms, the entries split by
context, and the previously fuzzy singular entries that became plurals.
Every new entry is marked "# AI Translated", and about 360 also carry a
"# Needs human review" note saying what to check. Terms follow the
localization glossary. Earlier translations of reworded strings were
reused where the meaning was unchanged. Existing entries were not
changed, and obsolete entries are kept.
* Document the source translatability audit in the orca-translations skill
Add a reference describing how strings reach translators, what to look
for (untranslated text, spliced sentences, missing plurals, ambiguous
words), what not to translate, where a context cannot be carried, and
what to do after the edits, plus a scanner that lists untranslated
user-facing string literals.
* Document how to write translatable strings in AGENTS.md
Tell developers to keep each message complete in one msgid: use
placeholders instead of concatenating translated fragments, plural forms
for counts, and contexts for ambiguous words, and to list new source
files in localization/i18n/list.txt. Add a matching code review item.
* Translate the strings added since the rebase
Regenerate the template against the current main and translate the 26
new entries per language: the Fix Model commands and messages, and the
texture displacement image adjustment panel. Each new entry is marked
"# AI Translated"; existing entries are unchanged.
* tidy
|
||
|
|
f546551b3f | Use a model's own UV channel for texture displacement, and keep its colours through a bake (#16394) | ||
|
|
39c77325ce |
Carry existing colour across a bake instead of reindexing it (#16392)
* Carry existing colour across a bake instead of reindexing it Baking, painting colour, then baking again left colour debris on surfaces nothing had painted and destroyed what the earlier bake had laid down. TextureDisplacementBakeJob::finalize() deserialized the volume's pre-bake mmu_segmentation_facets into a TriangleSelector built on the post-bake mesh, on the assumption that the bake is topology-preserving. That holds for the classic path, which moves vertices and leaves the topology alone, but not for the one-run pipeline, which rebuilds the mesh. TriangleSelector::deserialize() only checks that the stored triangle ids are in range, and a bake almost always grows the triangle count, so every stale id passed that check and landed on an unrelated face. Measured on a 26 mm slab baked twice at 0.19 mm with a 750 k budget: 7580 triangles ended up carrying paint neither bake produced, as far as 31 mm from any colour that was actually laid down, while the first pass's colour was lost where the second bake left the surface alone. TriangleSelector::remap_painting() already transfers paint between meshes by position, and ModelVolume::restore_painting() uses it for the same reason, so the bake job now uses it too. It runs in process(), on the worker thread: it builds an AABB tree over both meshes and took around a second on the case above, which would have frozen the UI in finalize(). The bake's own colour is still applied last, so it wins where it painted and the rest is kept, which is what makes successive bakes accumulate rather than overwrite. If the volume is painted while the bake is running, a timestamp check falls back to the live mask so that stroke is not dropped. The out_triangle comment in TextureColorRequest claimed the bake is topology-preserving, which is what justified the original indexing; it now says which mesh the colours belong to. The regression test bakes twice, checks both results are closed, and checks the first bake's colour survives the second at the positions where it was laid down and nowhere else. * Carry the paint across with transfer_facet_paint The branch used TriangleSelector::remap_painting(), which pairs a source paint triangle with a target triangle overlapping it to within TriangleCursor::tolerance. That is right after a remesh, which leaves the surface where it was, and wrong after a bake, which has moved it by the relief depth - most of the paint finds no partner. transfer_facet_paint() asks the other question: every triangle of the new mesh takes the state painted at its foot point on the old one, which is exactly where it came from however deep the relief. It also does all four paint channels rather than colour alone, and it runs where the volume is read, so the snapshot and the timestamp that guarded against the user painting while the bake ran are no longer needed: the annotation is read at commit time and is by definition current. The function and its call site are byte for byte what the model-UV branch carries, so the two merge in any order and in either order produce one mechanism rather than two fighting over mmu_segmentation_facets. The regression test is unchanged except for the call it makes. --------- Co-authored-by: SoftFever <103989404+SoftFever@users.noreply.github.com> |
||
|
|
af29e4d553 | Image adjustment panel for texture displacement layers (#16391) | ||
|
|
11493a2236 | Merge branch 'main' into fix-texture-uv-orientation | ||
|
|
01499e536e |
fix(gui): stop the UV editor pane from hijacking the shared GL context
The texture-displacement gizmo pushes pane state to the UV editor from the preview rebuild that runs on every frame of a slider drag. Tile size is the only one of the layer card's sliders whose value appears in that pane state, so dragging it changes the "N.N mm tile" label, the panel relays out, and the UV canvas receives a synchronous wxEVT_SIZE. Its on_size() flushes a paint with Update(), and render() then makes the app's single shared wxGLContext current on the UV drawable in the middle of the 3D canvas's frame. On X11/GLX that out-of-turn context activation is fatal: the drawable the 3D canvas later swaps no longer belongs to the current context, the server returns BadMatch, and GDK turns the unhandled X error into g_error(). EGL on Wayland reports only a status, so the same theft silently corrupts the frame instead of aborting - which is why the guard is not conditioned on the display backend. Add GLCanvas3D::is_frame_in_progress(), backed by a counter that _render_frame() maintains through its existing scope guard, and let the UV canvas skip the synchronous paint while a frame is on the stack, re-queueing it with Refresh() so nothing is dropped. Rebind the context with _set_current() before SwapBuffers() as well, so a surface that steals it by some other route - a modal GL dialog a gizmo opens from its panel - cannot turn the swap into the same fatal error. |
||
|
|
237586675f |
fix(gui): remove legacy fixed-function OpenGL texture calls (#16270)
Debug builds asserted on startup because these calls raise GL errors on a core-profile context. Both draws already use shaders, so removing the calls changes nothing on compatibility contexts. |
||
|
|
6d35ff6b9a |
Rewrite Fix Model on CGAL 6.2, fix Split to parts, remove Win10 SDK leftovers (#16366)
* Add CGAL-based mesh repair, drop Netfabb remnants Add repair_mesh() in MeshRepair.cpp, a CGAL-based repair that replaces MeshBoolean::cgal::repair(). It welds cracks, cancels coincident triangles, fixes orientation, fills holes (including planar annular regions), removes local self-intersections, and unites crossing shells with libigl's exact boolean while following the nonzero winding rule used by slicing. FixModelByCgal now repairs each volume in place without splitting it into parts, and reports whether the mesh was modified so unchanged meshes keep their painting and ids. The texture import repair prompt no longer depends on the Windows SDK, and the FIX_THROUGH_CGAL_ALWAYS conditionals are removed. The commented-out Netfabb/WIN10SDK build code is deleted. libslic3r_cgal now builds with CGAL_LINKED_WITH_TBB and links TBB. Add docs/HLSD/mesh-repair.md and unit tests in test_mesh_repair.cpp. * Parallelize repair while preserving separate shells Repair connected mesh units and shells independently in parallel, keep crossing shells separate, and limit self-intersection remeshing to local defects. Update mesh-repair documentation and tests for the revised behavior. * Make mesh repair deterministic and report its progress Flags written from parallel loops were std::vector<bool>, whose neighbouring bits share a word, so shells could be kept or dropped at random; they are now std::vector<char>. Holes of up to 100 edges are filled with the search over all triangles: the search restricted to a 3D Delaunay triangulation picks between equally good fills differently from run to run. Shells of fewer than four faces are dropped, as the volumes of two triangles back to back need not cancel exactly, and a shell that self-intersection removal changed is split again so the pieces it cut off are dropped too. repair_mesh() takes a progress callback in place of the cancel callback, and Fix Model reports each stage. The object's bounding box is now invalidated even when a cancel or an error stops the repair after some volumes were replaced. The edge-use sort, union-find and box-overlap code shared by several stages is factored out, the topology analysed for welding is reused to build the work units, the orientation pass checks only the edges it cut, and the output is written from the shells without joining them first. * Fix Split to parts crashing on flat pieces and staying offered after a repair ModelVolume::split() added every piece to the object and then deleted the pieces without a convex hull. When the first piece was flat, as often in a triangle soup, that deleted the volume being split while split() and its caller kept using it. A part made only of flat pieces lost all its volumes, and a soup added thousands of pieces, saving a backup of the object for each one it deleted. Pieces without a convex hull are now dropped before any joins the object, the volume being split keeps the first remaining piece, and a part with no solid piece is left whole. Other parts of the object without a convex hull are no longer deleted as a side effect. ModelVolume caches whether its mesh can be split, and set_mesh() kept the cached value, so after Fix Model, Simplify or Emboss replaced a mesh, Split to parts could still be offered for a single piece. set_mesh() and reset_mesh() now clear the cache. * Import STEP surface models with Split compound enabled Splitting a compound kept only its solids, so a surface model, which OCCT reads as a compound of loose faces, gave nothing to mesh and the import failed with "Meshing of a model file failed or no valid shape". Each shell outside a solid now becomes a part too, and loose faces stay together as one part, as they do without splitting. * Add Fix All for plates and re-enable --repair on the command line ModelVolume::repair() applies repair_mesh() to a part and replaces the mesh only when the repair changed it. Fix Model, the new Fix All and Fix All Plates items of the plate context menu, and the command line's --repair option, which had been commented out since the Win10 SDK repair was dropped, all use it. With Keep painted feature after mesh change off, Fix Model cleared the painting of every part of an object before repairing. Now only a part whose mesh changes loses its painting, so fixing a whole project does not wipe it. The notification counts the models that needed no repair instead of listing them as repaired, and objects that did not change are not refreshed. |
||
|
|
a28c9c3a82 |
fix(gui): guard gizmo actions before deferred initialization (#16268)
GLGizmosManager can be called while its gizmo list is empty, either at startup before the canvas has loaded the gizmos or after their init() failed. reset_all_states(), open_gizmo(), open_gizmo_by_shortcut() and activate_gizmo() now return early in that case. This fixes a Debug-build assert when a new project resets the gizmos at startup, and an out-of-bounds read in release builds when a gizmo shortcut key is pressed after gizmo init failed. activate_gizmo(Undefined) still returns true, since no gizmo is open. The reset_all_states() guard was first proposed in #16074 by kenrap. Co-authored-by: kenrap <101818165+kenrap@users.noreply.github.com> |
||
|
|
f0e9cdc470 |
raft expansion (#16416)
Update ConfigManipulation.cpp Co-authored-by: Accelerator <22743445+acceleratorlastorder@users.noreply.github.com> Co-authored-by: Ian Bassi <12130714+ianalexis@users.noreply.github.com> |
||
|
|
58a71331d0 |
fix(gui): balance ImGui style stacks in gizmo windows (#16269)
Pop the FramePadding style var before end() in the move and rotate gizmo windows. It was pushed inside the window and popped after End(), which failed ImGui's style stack check in Debug builds. |
||
|
|
c63bd5d474 |
Switch to the Fast view while UV islands are selected in the texture gizmo
The island highlight was hidden under the Normal view's relief. Normal comes back once no island is selected, unless another view was picked in the meantime. |
||
|
|
c6a338f35f |
Fix stale paint highlights and textures in the texture displacement gizmo
Erase whole model, Select whole model and Capture current view update the highlight at once. A stroke released outside the 3D view ends on the next mouse move, in every paint gizmo, instead of leaving the brush in its pressed colour. The layer textures no longer show an image that was replaced. |
||
|
|
f49d49b5a6 |
Texture displacement: typed values on every panel slider (#16395)
Every slider in the texture displacement panel can now be given an exact number, either by Ctrl+clicking the track or via a small edit icon at its right end. Both routes reuse ImGui's own temp-input mode. Each slider also gets a tooltip with its description and its range, and the panel's `?` popup lists both ways in. Draft: description to be refined. |
||
|
|
d8cae3268a | Merge branch 'main' into feature/texdisp-3mf-persistence | ||
|
|
cccf53850e |
Remove Never-Built and Stub Source Files (#16361)
SysInfoDialog, JumpPointSearch and WebUpdatePlugin are in no CMake list on any platform, and nothing includes ConfigExceptions.hpp, wxinit.h or Utils/Profile.hpp. MultiTaskModel holds only empty namespaces. Also drop the empty GUI_App::system_info() and comments that still mention SysInfoDialog. |
||
|
|
5095733323 |
Fix Include Guards Copied From Other Headers (#16368)
TriangleMeshDeal.hpp reused Timer.hpp's guard and CloneDialog.hpp reused SingleChoiceDialog.hpp's, so including both headers of a pair silently dropped the second one. Use #pragma once instead. |
||
|
|
5371c85362 |
Refresh Dark Mode Colours When the System Theme Differs at Startup (#16390)
On Linux and macOS the app follows the system theme: at startup it overwrites the saved dark_color_mode with the system appearance. The label colours and the cached dark mode flag were already set from the saved value by then, and the later "changed" check reads the mode only after the overwrite, so it never fires. For that session every dialog that maps its colours through UpdateDlgDarkUI keeps the old mode, e.g. white dialogs on a dark main window after switching the desktop to dark, or on a fresh install on a dark desktop. Refresh the label colours and the flag when the system theme differs from the saved mode. |
||
|
|
30e48183e3 |
Merge branch 'main' into feature/texdisp-3mf-persistence
# Conflicts: # src/libslic3r/TextureDisplacement.hpp # tests/libslic3r/test_texture_displacement.cpp |
||
|
|
a243941b72 |
Add favorite filaments to AMS filament dropdown (#13946)
# Description ## Summary: Adds favorite filament support to the AMS material selection dropdown. Users can now click a star icon next to a selectable filament in the AMS Materials Setting dialog to mark it as a favorite. Favorited filaments appear in a new `Favorite filaments` section at the top of the dropdown, while all compatible filaments remain available in the `All filaments` section. This PR also adds generic right-side action icon support to Orca's custom dropdown widget: - Adds optional action icons to `DropDown::Item`. - Adds a new `EVT_DROPDOWN_ITEM_ACTION` event for clicks on the action icon. - Relays the action event through `ComboBox`. - Adds `ComboBox::RefreshDropdownIfOpen()` so callers can refresh an already-open dropdown without changing normal `ForceDropdownOpen()` behavior. The AMS favorites implementation: - Uses existing bundled star assets: - `score_star_dark` for non-favorited filaments - `score_star_light` for favorited filaments - Stores favorite filament IDs in app config under `ams_favorite_filament_ids`. - Stores stable filament IDs instead of display names - Uses existing C-style string escaping helpers for persistence. - Keeps Bambu/RFID/read-only slot behavior unchanged No new external dependencies. **Note:** `AMSMaterialsSetting` now uses Orca's custom `ComboBox` on macOS as well, because the favorite-star UI depends on the custom `DropDown` item renderer. I was able to test this on Windows, but macOS/Linux smoke testing is still needed and would be appreciated. # Screenshots/Recordings: https://github.com/user-attachments/assets/41e881be-d4de-4a7d-8f99-4297567adef2 <img width="494" height="36" alt="Screenshot 2026-05-30 103432" src="https://github.com/user-attachments/assets/e7c34ed3-db7a-4705-a1cd-4bfe7ac1616a" /> ## Tests Tested on Windows: - Built OrcaSlicer locally - Opened the Device tab with Bambu Lab printer with AMS - Opened the AMS Materials Setting dialog from an editable AMS slot - Confirmed clicking a star toggles favorite state. - Star clicks do not select the filament row. - Dropdown remains open after toggling a favorite. - Confirmed favorited filaments appear under `Favorite filaments`. - All compatible filaments remain visible under `All filaments`. - Confirmed Filament still selected as before - Confirmed favorites persist after closing and reopening the dialog. - Confirmed favorites persist after restarting OrcaSlicer. - Confirmed the favorite IDs are saved in `OrcaSlicer.conf` under `ams_favorite_filament_ids`. - Bambu/RFID/read-only filament slots continue using the existing read-only display behavior. Not tested: - macOS - Linux/Wayland These should be tested, `ComboBox` behavior not tested on macOS. [How to Download Pull Requests Artifacts for Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts) |
||
|
|
6a1a5d51e9 | Show an outline star for filaments that are not favorites | ||
|
|
e625845b34 |
Keep Snapshot Suppression for the Whole Object List Edit (#16388)
Five Plater::TakeSnapshot guards in GUI_ObjectList were unnamed temporaries, so snapshots were allowed again as soon as the snapshot was taken. Naming them keeps the suppression until the edit is done, as everywhere else. |
||
|
|
01b340dee1 | Fix regressions in the AMS filament favorites dropdown | ||
|
|
56b559d36a |
Fix texture projection UV orientation
An image laid onto a model came out upside down under the ordinary box (tri-planar) projection, and on three of a cube's six faces it was also mirrored left to right, which reads as a 180 degree rotation. DecodedHeightTexture::sample() defines v = 0 as the height map's first pixel row, i.e. its top. Every projection built v from world +Z, so v grew upward and the image landed bottom row up. The one path that was already right is the projective "project from view" matrix, which measures window y downward. Both halves of the box basis were wrong: - v is now negated on all three world planes, so the top of an image sits at the top of the model. - u now carries a sign for the side of the plane the face is on, chosen so that u x v == -normal. Without it the two faces sharing a plane get one u and one of them is mirrored. The sign flips exactly where that plane's blend weight (the fourth power of a normal component) reaches zero, so the blended tri-planar sum stays continuous and the hard axis switch the old code removed does not come back. The same v convention was missing from the other mapping modes: cylindrical (which also had a left handed frame, right x up rather than up x right, so its wrap ran mirrored), spherical, the affine branch of view projection, and the flat chart fallback of the LSCM unwrap, which now reuses the box basis so a cube face unwraps to the orientation tri-planar would have given it. The basis table is exported as box_projection_axes() and the gizmo's adjust_tangent_basis() calls it instead of restating it, so the placement ring and offset handle cannot drift from the bake. All four preview shaders get the matching change, including projection_axes(), whose tangent and bitangent have to flip with the coordinate or the relief shading inverts. Re-baking an existing project now places its texture upright, so it will differ from how it looked before. Geometry already baked into a 3mf is untouched, so nothing changes until the user re-bakes. Verified by measuring the sampled texture row and column against world position for every mapping mode on all six faces of a cube, and by a full bake of a quadrant coded image, which lands each quadrant within 1e-3 mm of its expected depth. |
||
|
|
ed1f3e52c9 |
Texture displacement: typed values on every panel slider
Every slider in the texture displacement panel now takes an exact number, either by Ctrl+clicking the track or through a small edit icon at its right end. Both routes end in ImGui's own temp-input mode: the icon only asks ImGui to focus the slider, which is what turns it into an input box, so the parsing, the clamping and the Enter/click-away behaviour stay ImGui's rather than becoming a second implementation. The mechanism lives in one helper, typed_row(), with a float and an int form over it. float_row() and int_row() are thin wrappers on those, and the six sliders that previously built their widget inline (brush size, smart fill angle, edge fade amount, remesh sharp angle, refine resolution and the uniform triangle budget) now go through the same path. Each slider carries a tooltip with its description and its range. The range is derived from the bounds and the format string the slider is already given, so no row states them twice and the tooltip cannot drift from the track. The tooltip allows hovering while disabled, since several rows are drawn inside disabled_begin(), where a greyed slider is exactly when the explanation is wanted. The panel's "?" popup gains an entry for each of the two ways in, appended to every tool's shortcut list and shown on their own when no painting tool is selected, where that popup was previously empty. |
||
|
|
00d11a7ba0 | Merge branch 'main' into UI-changes | ||
|
|
f4bde2da0a | Fix missing plate bed override normalization in Configuration Wizard (#16365) | ||
|
|
cab7a7d75f | Translate the web dialogs and pages through the .po catalogs (#16363) | ||
|
|
bd125d32da | Support searching the Speed Dial by English names in any UI language (#16369) | ||
|
|
7369674d8f | Add #pragma once to Headers Without an Include Guard (#16362) | ||
|
|
70da7e89bb | Release the Slice Button's Mouse Capture When a Press Loses Its Release (#16358) | ||
|
|
a671bf605d |
Dismiss the Slice-Plate Popup on Every Path Into the Filament Grouping Dialog (#16359)
#16284 closed the Slice-plate hover popup only when the Slice button opened the filament grouping dialog. The dialog also opens from the plate's filament grouping icon, the preview legend, slicing notification links and the send dialog, and a popup still shown on those paths keeps the mouse capture on macOS and starves the modal dialog of clicks. Dismiss it in FilamentMapDialog::ShowModal() instead, so every path is covered. The popup could also stay up on its own on macOS: an enter event stopped its close timer, and when the timer fired with the cursor inside it returned without re-arming, so without a later leave event the popup never closed. On macOS the timer now keeps running while the popup is shown and closes it once the cursor is outside. The popup also stops and frees its timer when destroyed. |
||
|
|
295094a594 |
Pack bed texture and model into printer bundles (#15965)
* feat(preset): pack bed texture and model into printer bundles Export copies bed_custom_texture and bed_custom_model into the .orca_printer zip under bed/. The manifest lists them in printer_asset. Import extracts each file into the bundle folder and rewrites the preset to that path. A missing file is skipped. A path that leaves the bundle directory is rejected. An old bundle with no printer_asset imports as before. * Add includes flagged by clang-tidy misc-include-cleaner * Gate bed asset packing on an export checkbox and key printer_asset per printer The Export Configs checkbox can leave a custom bed out of a shared printer bundle. printer_asset is keyed by each printer_config zip entry, with files under bed/<n>/, so a later multi-printer bundle does not need a second manifest. The unshipped flat printer_asset object is not read. --------- Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com> |
||
|
|
00cfb5b259 | Refresh translation catalogs and fix xgettext warnings (#16346) | ||
|
|
b81d761605 |
Refresh plate tab after bed type change (#16344)
Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com> |
||
|
|
d715bb760c |
Let Plugin Windows Be Maximized on X11 (#16335)
wxGTK types every wxDialog as a dialog window, and Mutter offers no maximize for anything but a normal window, so the maximize button and shortcut did nothing on GNOME under X11. Modeless plugin windows are now normal windows, kept off the taskbar as before. Modal ones stay dialogs so the window manager keeps routing focus from the main window to them. |
||
|
|
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. |
||
|
|
53c57c94fd |
Fix the 3D view stuttering every few seconds while signed in to Orca Cloud (#16340)
# Description Orbiting or panning the 3D view no longer hitches every five seconds on Windows and macOS while signed in to Orca Cloud. The stutter is a regression from #15710. No change to slicing or to the Orca Cloud connection status shown in the GUI. # Screenshots/Recordings/Graphs <!-- > Please attach relevant screenshots to showcase the UI changes. > Please attach images that can help explain the changes. --> ## 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) |
||
|
|
53eeedb642 | Fix the 3D view stuttering every few seconds while signed in to Orca Cloud | ||
|
|
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 |
||
|
|
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.
|
||
|
|
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) |
||
|
|
0a9e7f33e1 | Link the IDEX/IQEX settings to their wiki sections | ||
|
|
74493429fd | Name extruders in the unsaved changes dialog like the printer tabs | ||
|
|
2ede6d1631 |
Arrange every IDEX/IQEX plate inside its own primary zone
Arrange read the zones of the selected plate only and applied them to every bed it packed. Arranging all plates with a plate in Primary selected spread a parallel plate's parts across its whole bed, and with a parallel plate selected, a Primary plate's parts were squeezed into the zone. Arranging a single plate other than the first also ignored its collision strips: they were tagged with the plate's index, while the plate packs into the arranger's bed 0. The IDEX/IQEX constraints now live in ImexArranger (IMEXArrange), and ArrangeJob only snapshots each plate's zones on the main thread and calls it: - When every bed, plates the arrange may add included, has the same primary zone, the bed shape is that zone, so parts stay centered in it. - Otherwise each plate in a parallel mode fences off the rest of its bed with fixed items on its own bed, and its parts are then arranged again inside the zone, so they sit centered rather than piled against the zone edge nearest the bed's center. Plates in Primary keep the whole bed, and plates the arrange adds take the process preset's mode. - Zone edges inside the bed get the bed's own edge margin, and strips also keep room for the brim. - A part too big for its zone, which libnest2d's first-fit retry places across the fixed items, is left unarranged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
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> |
||
|
|
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) |
||
|
|
d629922060 | Keep the paint highlight off every texture preview between strokes |