mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-12 18:31:18 +00:00
4286cb30ba9ebc7382ada26fa34dc5a798ecd05f
32270
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
4286cb30ba |
feat: a colour count instead of Two colours, and a pickable background
Two colours was the N=2 case of a more useful control, so it is replaced by a Colors slider: 0 for off, then 2 to 12. The picture is reduced to that many colours before the filaments are matched to it, so it prints in at most that many. Reduced by k-means in RGB rather than by posterizing the range, because the useful question is which colours the picture is actually made of. A tartan asks for its own handful of greens and reds; an even split of the range would hand it eight shades of mud. The clustering runs over a coarse 16-per-channel histogram, 4096 weighted points instead of a million pixels, which is what makes it cheap enough to sit on a slider, and it is seeded the k-means++ way so a picture with one dominant colour does not get every centre inside it. This is not the displacement tool's "Mixed colors", which caps how many mixed filament slots a bake may create. Nothing like it existed. Background removal can now be told which colour to remove. The four corners stay the default, because a background that does not reach all four of them is not a background and that makes the common case one click, but the panel shows the colour as a swatch that is also a picker, and a dropper beside it takes the colour from the preview. The dropper reads the decoded image rather than the screen: the preview is scaled, and it already has the corrections applied, so a screen read would key the corrected colour against itself. "Corners" puts it back. Measured on Tartan, a cube face, four filaments: run out tris coloured filaments filament shares image, no correction 22534 14544 4 1:91% 3:8% 4:1% image, 2 colors 18232 10268 2 1:66% 3:34% image, 3 colors 24458 16459 3 1:78% 3:18% 4:5% image, 5 colors 23972 15978 4 1:90% 3:9% 4:1% Asking for 2 gives 2 and asking for 3 gives 3. Asking for 5 gives 4, which is the contract working the right way round: only four filaments are loaded, and "at most" is the guarantee. No run moves the surface. |
||
|
|
3ee9c02acf |
feat: sharpen, two colours, and remove background in the Image tool
Three more corrections, chosen because the output is a handful of flat filament colours rather than a continuous image: what helps is whatever decides which filament an area lands in and where the boundary between two of them falls. - Sharpen. An unsharp mask over the colour, reusing the sliding-window blur the Smoothing slider already uses, run per channel. Worth more here than in a photo editor: a soft edge between two colours quantizes into a ragged band of both, and sharpening first makes it land in one place. - Two colours. Thresholds on luma so a logo or line art prints in exactly two filaments instead of speckling across several, with the split point on a slider. - Remove background. The one that is not an RGB transform. DecodedHeightTexture gains an alpha plane, filled from the source PNG's fourth channel when it has one, and the key additionally clears whatever colour the picture's four corners share. A transparent texel is not black: sample_layer_color()'s covered() now reports it the same way it reports being outside an untiled placement, so the triangle keeps the object's own colour and the bake needs no new concept. The preview follows: upload_color_texture() box-filters coverage into the alpha channel it used to fill with 255, and the shader leaves the surface alone where it is below half. The corners rather than an eyedropper: a background that does not reach all four of them is not a background, and a picker would be a second interaction for the case this exists to make one click. The median of the four is taken so one stray corner cannot decide it. Measured on Tartan, a cube face, four filaments: run out tris coloured max offset mm filament shares image, no correction 22534 14544 0.000005 1:91% 3:8% 4:1% image, sharpen 1.0 24780 16783 0.000004 1:84% 3:12% 4:3% image, two colors 14798 6837 0.000005 2:22% 4:78% image, bg removed 14120 2620 0.000005 1:29% 3:16% 4:55% Background removal cuts the painted area from 14544 triangles to 2620, which is the whole point of it; the threshold collapses to two filaments; sharpen shifts the shares and adds boundary triangles. None of them moves the surface. The probe's default scene is now Tartan rather than Colour Bricks, which has too little luma range for the threshold to show anything. |
||
|
|
533aaaed28 |
feat: add Hue to the Image tool's colour correction
Turns every colour of the picture around the colour wheel without changing how light it is. This matters more here than in a photo editor: the filament set is fixed and arbitrary, so hue is what lets the picture meet the palette rather than the other way round. With orange loaded but no red, a small turn puts the reds onto the orange instead of onto whatever happens to be nearest. Implemented as Rodrigues' rotation of the colour about the grey axis, then the Rec. 709 luma is restored exactly: the rotation preserves R+G+B, not the weighted luma, and without the correction turning the wheel would also change which areas read as light and dark. Measured, same scene and harness as the other corrections: run out tris coloured max offset mm filament shares image, no correction 12066 4052 0.000005 1:71% 2:25% 3:4% image, hue +60 8208 268 0.000005 2:100% image, hue -120 8208 268 0.000005 3:100% The reddish brick moves onto a different filament in each direction and the surface does not move. |
||
|
|
4dc87e876e |
feat: show a drop zone while a file is dragged over the view
Dropping a picture already worked, but a drag had no feedback at all: the only way to find out whether letting go would do anything was to let go. While a drag is over the 3D view and the Image tool is open, the canvas now dims and shows a dashed teal border with "Drop a picture to put it on the model" in the middle. It goes away on leave and on drop. Three things worth knowing about how it is wired: - wxWidgets cannot hand us the file names before the drop on GTK, so the zone says nothing about what is actually being dragged. It is worded as an invitation rather than a promise, and anything that is not a picture still falls through to the model loader exactly as before. - A drag is not a mouse move as far as the canvas is concerned, so nothing would repaint and the zone would appear only once something else happened to trigger a frame. set_file_drag_over() marks the canvas dirty and asks for an extra frame. - OnEnter and OnLeave are declared virtual without override, matching the class's existing OnDropFiles. Marking only the new ones trips -Winconsistent-missing-override on that pre-existing line, which is an error here. The zone is drawn on the foreground draw list from inside the panel's ImGui frame, so it covers the panel as well as the view. |
||
|
|
8bb2a1eec5 |
feat: match the icon weight, drop a picture onto the view, handle on by default
- The toolbar icon's stroke is 0.4 at its 16 px canvas, which renders at 1.00 px on the 40 px sprite. Measured rather than judged: every icon on the gizmo bar was rasterized at 10x and the median thin run length taken, and 1.00 px is what thirteen of the seventeen use. The previous 0.75 came out at 1.90, the heaviest on the bar. - Dropping a picture on the 3D view loads it, when the Image tool is the one open. It is imported into the user's library on the way, as the panel's Import tile does, so it is there next time. With the tool closed a dropped PNG goes on doing whatever it did before. This mirrors the SVG drop that already fed the text tool, and sits next to it. - The placement handle comes up by itself the first moment there is something to anchor it to, so a picture can be nudged straight after the click that placed it. Armed once, so turning it off stays off. Two things had to change for that last one to be usable rather than annoying: - A click that misses the handle now falls through to placing the picture. on_mouse() used to return false there and swallow it, which is right for the displacement tool, where the user turned the mode on deliberately, and wrong here, where it is on from the start and would have disabled the tool's one gesture. - update_model_object() re-anchors the handle after a stroke that moved the paint. Previously the anchor was only refreshed on a layer switch or reorder, so clicking a different face left the handle behind on the old one. |
||
|
|
518f961640 |
feat: empty start, user images only, untiled preview, dockable Image panel
Five fixes to the Image tool, from using it: - The layer now starts with no picture on it, so the panel opens on its empty frame with a plus in it. It used to seed the first shipped colour texture, which put somebody else's picture on screen before the user had chosen one. The frame is the only state the panel has until a picture is loaded, and clicking it is what creates the layer. - Its picker offers only the user's own images. The shipped library is grayscale height maps, which is what the displacement tool wants and the opposite of what an image bake can use: it prints colour, and a height map has none. The Built-in section is left out rather than shown full of things that would all warn when picked, and the import hints now talk about pictures instead of height maps. - The preview stopped tiling a layer that is not tiled. The shader had no tile_enabled uniform and wrapped the image unconditionally, so an untiled picture previewed as a repeating pattern the bake would never produce. in_tile() now draws the same line DecodedHeightTexture::sample() does, and gates the colour and all three relief paths on it. Both the 140 and the 110 variant; glslangValidator passes on each. - The panel docks against the right edge of the canvas, where the displacement panel is, with the same undock toggle in its title row, so the two tools do not put their controls in different corners. - The toolbar icon's frame is square and its stroke thinner. At 16 px scaled to the toolbar a 1 px stroke came out at 2.5 px, heavier than every neighbour; 0.75 px matches them. |
||
|
|
680a597567 |
feat: show the picture whole in the Image panel, add tile, move and detail
Five changes to the Image tool, all in its own window bar the presets: - The picture is shown whole, the full width of the window, instead of a row sized thumbnail beside a file name: it is the thing being placed, so that was the wrong size for it. The box shows the corrected colour texture, so moving a correction slider is visible in the panel as well as on the model; a grayscale picture has no colour texture and falls back to the height thumbnail. The file name stays underneath as a caption. - Before anything is loaded the same box is an empty frame with a plus in it, in the same place and the same size, rather than a button of its own. Clicking either state opens the library, which is also where Import lives. - Tile, off by default. A photo or a logo goes on once, so a new layer the Image tool creates starts with a single copy and Size sets how big that copy is. It is a default and not a pin, so the checkbox can turn it on. - Move, the on-canvas placement handle the displacement tool has: drag the handle on the surface to slide the picture, or one of its arrows to slide it along one axis. - Detail, the bake resolution, with the budget pinned to a million triangles. Each triangle prints in one filament, so this is how sharp an edge between two colours can come out, and it is the only thing left that decides it. The auto budget is 750 k, chosen for relief where triangles buy shape; here they buy the whole output, so it is worth spending more. The slider is seeded with the resolution Auto would have picked for the model, and warns when what it asks for no longer fits the budget. Also fixes a short circuit in the panel: apply_standard_mode_presets(mv) || apply_image_mode_presets(mv) skipped the image presets on any frame the standard ones changed something. Measured with the texture-bake harness on the tool's shipped configuration (tiling off, million-triangle budget, the seeded resolution), one cube face, four filaments: run out tris coloured max offset mm filament shares relief, depth 0.4 mm 41040 33902 0.216000 1:91% 2:6% 3:3% image, no correction 12066 4052 0.000005 1:71% 2:25% 3:4% image, brightness +0.5 8208 268 0.000005 4:100% image, contrast +0.8 11980 3910 0.000005 1:78% 2:20% 3:1% image, saturation 0 8208 268 0.000005 3:100% image, gamma 2.2 13176 5175 0.000004 1:67% 4:33% image, negative 8208 268 0.000005 4:100% Fewer coloured triangles than the previous run of the same scene because tiling is now off: one copy covers part of the face rather than repeating across it. The surface still does not move and every correction still changes which filaments the picture prints in. |
||
|
|
e003b61e17 |
feat: make the Image gizmo a minimal load, click, correct, apply tool
The first cut was the displacement panel with depth hidden, which left the brushes, the eight layer slots, the UV editor, seams, tiling and the mesh-preparation sections in a tool that needs none of them, and offered no way to correct the picture at all. This replaces that panel and adds the corrections. The flow is now: load a picture, click the model to put it there, adjust it, press Apply. - The Image tool gets its own window, render_image_input_window(), defined in GLGizmoImage.cpp. on_render_input_window() dispatches to it on the first line, so the displacement panel goes back to exactly what it was rather than being threaded with tests for which tool is drawing. - A click places the picture: the tool pins SMART_FILL with a pointer cursor, so one click floods the surface it lands on out to the first fold sharper than the Spread angle. No brush, no erase toggle. - Projection keeps Flat, Around, Ball and From view. Unwrap is left out and a layer arriving on it falls back to Flat: seams, islands and the UV pane are an editor's worth of interface for a tool that wants a click and a slider. - The preview is pinned to the fast one. With no relief to build it is both instant and exactly the final colour, since the shader's colour path does not depend on depth_mm. - The window shows only what this case needs: the picture, Spread, the four projections, Size, Rotation, the corrections, Apply and Close. Colour correction is new in libslic3r. TextureDisplacementLayer gains brightness, contrast, saturation, gamma and color_invert, all defaulting to the identity, and apply_color_adjustments() applies them to an image's RGB only, never to the height: a brightness slider has no business moving geometry. It runs at the single point every bake and preview job reads a layer's colour from, prepare_sampleable_layers(), and at the two caches that would otherwise miss a change the image_data pointer does not show: the fast preview's GPU texture and the mix ranking. Measured with the texture-bake harness, one cube face painted with a colour image, four filaments: run out tris coloured max offset mm filament shares relief, depth 0.4 mm 41040 33902 0.216000 1:91% 2:6% 3:3% image, no correction 24226 17090 0.000007 1:71% 2:25% 3:4% image, brightness +0.5 8204 1068 0.000004 4:100% image, contrast +0.8 22688 15552 0.000007 1:75% 2:24% 3:1% image, saturation 0 8204 1068 0.000004 3:100% image, gamma 2.2 24754 17618 0.000007 1:66% 4:34% image, negative 8204 1068 0.000004 4:100% Max offset is the distance from every output vertex to the original surface, not a vertex-array diff: the bake refines, so a new vertex only counts as unmoved if it lies in the old surface. Every correction moves which filaments the picture prints in, and none of them moves the surface. |
||
|
|
a800e0c186 |
feat: add an Image gizmo for zero-displacement colour
A separate toolbar tool for the simple case of texture displacement: put a colour picture on the model with no relief at all. It is a thin wrapper, not a fork. GLGizmoImage subclasses GLGizmoTextureDisplacement and overrides one new protected virtual, image_mode(). Painting, the layer stack, the paint masks, the texture library, the previews and the bake are the same code reached the same way; a zero-depth bake refines the painted area and writes the per-triangle filament indices exactly as a relief bake does, it just displaces by nothing. In the base gizmo, image_mode() does three things: - apply_image_mode_presets() pins every layer to depth 0 and turns colour on wherever the image has any, alongside the existing Standard-mode presets. - The panel leaves out Depth, Midlevel, Invert, Edge fade and Blend, and the Standard/Pro switch, which is what keeps the mesh-preparation sections hidden. Bake is disabled when no image carries colour, since that bake would refine the mesh and produce nothing. - add_texture_layer() seeds a new layer from the first library texture that has colour, rather than the first one, because most of the shipped library is grayscale height maps. The two tools share one layer stack, so opening the Image tool on a volume whose layers carry relief flattens them. Only depth changes: the paint, the image, the tiling, the mapping and any relief already baked into the mesh are untouched. The toolbar icon follows Orca's menu-icon style: 16 px, 1 px #949494 frame, #009688 on the picture inside. The shortcut is registered but unbound: every single letter the Plater context can reach is already taken, and a default that shadows one of them is worse than none. docs/HLSD/keyboard-shortcuts.md now documents that an empty default chord is a supported choice. Measured with a probe built against this tree in the texture-bake harness: a depth-0 bake of a painted cube face leaves every output vertex within 7 nm of the original surface (tolerance 1 um) while colouring 17090 of 24226 output triangles across 3 filaments; the same scene at depth 0.4 mm moves vertices up to 0.216 mm. |
||
|
|
d007134001 | fix: update translation for Assistant(HMS) in Chinese localization | ||
|
|
70f518d87a |
Added new profiles for the FELIX Printers vendor (#16314)
* Added new profiles for the FELIX Printers vendor * fixed all errors from profiles |
||
|
|
f4bde2da0a | Fix missing plate bed override normalization in Configuration Wizard (#16365) | ||
|
|
d5a11eb076 | ci: cancel Build all runs of closed pull requests (#16380) | ||
|
|
461275d9bb | ci: build pushes that change deps_src or the build scripts (#16378) | ||
|
|
95d3e8eafc |
Queue Pull Request macOS Builds for a Free Runner (#16343)
* ci: wait for the base branch's macOS deps instead of rebuilding them * Hold Pull Request macOS Builds Until a Runner Is Free A pull request's macOS build now waits in one repo-wide line, from a Linux job, until the macOS runners every active Build all run holds or still needs leave two free. A run is let in whole: its first arch takes both runners and its second goes straight through, since letting arches in one at a time deadlocks with every runner held by a run waiting for its other arch. Push, nightly and manual runs reserve two runners until their macOS work is done, and a pull request run keeps its two until its universal build and tests finish, so jobs that never pass the line are accounted for. Queued jobs count as busy. API errors and a four-hour limit let the build in rather than block it. Co-authored-by: raistlin7447 <kris.austin@gmail.com> * Count Pending Runs That Hold macOS Runners Co-authored-by: raistlin7447 <kris.austin@gmail.com> * Show the macOS Admission Checks Live in the Job Log Co-authored-by: raistlin7447 <kris.austin@gmail.com> * Let a Labelled Pull Request Go Ahead of the macOS Line A pull request labelled macos-priority when its run starts waits in its own line under the same rule, and the normal line counts every priority run still waiting as holding two runners, so the next free runners go to it. It still waits for runners in use and for main's reserve. Co-authored-by: raistlin7447 <kris.austin@gmail.com> * Let Each macOS Arch In on Its Own Without Holding an Idle Runner Letting a pull request run in whole deadlocks when its second arch reaches the line behind another pull request: the run holds two runners, the pull request in front waits for them, and the second arch never gets to check. Each arch now waits on its own. A run with one arch let in holds a runner only until that arch's app build is done, and a run with both holds two until its universal build and tests finish, so whoever is first in line always gets in eventually. Co-authored-by: raistlin7447 <kris.austin@gmail.com> * Stop Holding Runners for a Pull Request's Universal Build and Tests Holding two runners from a pull request's second arch until its universal build and tests finished kept one idle through the slower arch's build. Replaying the week of 10-03 against the real queue, that idle capacity pushed the median pull request from under three hours to over sixteen. Those jobs take minutes, so they now queue like any other job, and a pull request holds a runner only for an arch that is still building. Each arch then always needs exactly one runner. Co-authored-by: raistlin7447 <kris.austin@gmail.com> * Count Runs That Move Between Status Lists A run is listed as pending while any of its jobs waits in a concurrency group, so it moves to in_progress the moment its last gate leaves the line. A check made in those seconds read the in_progress list before the move and the pending list after it, missed the run, and let in a second arch the limit had no room for. Runs created in the last day are now listed by creation time as well, and the run doing the check is read directly. Co-authored-by: raistlin7447 <kris.austin@gmail.com> * Log API Calls and the Rate Limit Left on Each macOS Admission Check The calls per check grow with the number of active runs, and the token's hourly limit is shared by the whole repository, so each check now logs how many calls it made and what the last response says is left. * Give the macOS Admission Jobs Fixed Names A skipped job is listed under its unevaluated name, so every Linux and Windows leg showed the label expression. Split the waiting job into a normal and a priority job with fixed names, sharing their steps through a YAML anchor. --------- Co-authored-by: raistlin7447 <kris.austin@gmail.com> |
||
|
|
52eeb0f228 | Fix arc fitting with overhang fan control (#16297) | ||
|
|
8145b0792f | Free macOS Disk Space Only When the Build Might Not Fit (#16364) | ||
|
|
2ccd73759f |
Show English context for Speed Dial search matches
Display English action names and breadcrumbs for English-only matches Show English tab titles when tabs match only by their English names |
||
|
|
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. |
||
|
|
bae8f64b8b |
Complete and refine Simplified Chinese translations (#16356)
* Complete Simplified Chinese translations * Fix Simplified Chinese 3D view, camera and extruder terms * Use Chinese CAD view names on the navigator cube |
||
|
|
2df07b7ed8 | Verify the TLS Certificate When Downloading a Cloud Plugin (#16253) | ||
|
|
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> |
||
|
|
210bc927bc | Revert "Add Thai Localization (Educational / Hybrid Translation)" (#16349) | ||
|
|
00cfb5b259 | Refresh translation catalogs and fix xgettext warnings (#16346) | ||
|
|
2bd105b0ce |
Add Thai Localization (Educational / Hybrid Translation) (#15940)
Hi OrcaSlicer Team,I have completed the Thai localization for OrcaSlicer (over 7,000 lines).Please note: This is a "Hybrid Translation" designed specifically for educational purposes. Instead of direct literal translations (which often confuse beginners due to technical jargon), I have included brief explanations of the mechanical and physics principles directly within the UI strings.Example: For "TPU is not supported by AMS", the translation briefly explains why (because the material is soft, rubber-like, and will jam the long tubes).This approach was developed for the EDrobot project in Khon Kaen, Thailand to help STEM students and beginners understand 3D printing concepts immediately without a steep language barrier.[cite: 8] We focus on empowering students to learn robotics and 3D design effectively.[cite: 8] I understand this makes some strings longer than standard localizations, but it has proven incredibly helpful for beginners. I would be honored if you consider adding this to the official release as the Thai language option. I am happy to maintain this file for future updates.Thank you for building such an amazing slicer!Best regards,Chetsit Srichan (EDrobot) |
||
|
|
b81d761605 |
Refresh plate tab after bed type change (#16344)
Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com> |
||
|
|
6922edfc20 |
Fix Anycubic Vyper start G-code hotend wait (#16216)
* Fix Anycubic Vyper start gcode hotend wait * Bump Anycubic profile version --------- Co-authored-by: 陈诗健 <chenshijian@chenshijiandeMacBook-Air.local> Co-authored-by: SoftFever <softfeverever@gmail.com> Co-authored-by: SoftFever <103989404+SoftFever@users.noreply.github.com> |
||
|
|
7ec147603f | Default Bambu profiles to 3MF output (#14767) | ||
|
|
8ee9e5915a | Fix Prusa CORE One pressure advance condition in filament profiles (#16093) | ||
|
|
cd5b786704 |
Add AnkerMake PLA+ filament profiles (Basic, Matte, Metallic, Silk, Glitter) (#16293)
# Description Adds Anker's own AnkerMake PLA+ filament line to the Anker vendor bundle: **Basic, Matte, Metallic, Silk, Glitter**. Until now the bundle only had `Generic PLA+ @Anker`. Each filament follows the existing Anker layout. A `@Anker base` profile (`instantiation: false`, inherits `Generic PLA+ @Anker base`, `filament_vendor: AnkerMake`) holds the settings. An instantiated `@Anker` profile holds only `compatible_printers`. `filament_id` and `setting_id` were minted with `scripts/orca_profile_tool.py generate-id`, and `Anker.json` was re-indexed with `update-index`. The Anker vendor version goes from `02.04.00.05` to `02.04.00.06`. ## Source of the values The values come from the AnkerMake filament profiles bundled with **eufyMake Studio** (Anker's PrusaSlicer 2.6 fork, AGPL-3.0, source at https://github.com/eufymake/eufyMake-PrusaSlicer-Release), in `profiles/Anker-ini/AnkerMake base/base.ini`. OrcaSlicer is also AGPL-3.0. Conversion notes: - PrusaSlicer keys are mapped to their Orca equivalents. For example, `bed_temperature`/`first_layer_bed_temperature` go to `hot_plate_temp*` and `textured_plate_temp*`, `fan_below_layer_time` goes to `fan_cooling_layer_time`, `slowdown_below_layer_time` goes to `slow_down_layer_time`, and `extrusion_multiplier` goes to `filament_flow_ratio`. Only keys that differ from the inherited `Generic PLA+ @Anker base` chain are written. - Where Studio sets retraction but leaves deretraction unset (Metallic, Silk), `filament_deretraction_speed` is set equal to the retraction speed. That matches Prusa's "0 = same as retract" meaning. - **Max volumetric speed is new.** Studio ships `filament_max_volumetric_speed = 0` (uncapped) for these filaments, and Orca has no equivalent. The caps were derived from the peak flow that Studio's own M5C "Fast" process pushes: Basic/Matte/Glitter 22 mm³/s, Metallic 20, Silk 13. Please treat these as the least-validated numbers in the PR. - Studio has pressure advance disabled for these filaments, so no PA values are carried over. - Colour variants are not included because they differ only in colour. `default_filament_colour` uses each family's base colour. - `compatible_printers` follows Studio's `compatible_printers_condition` for each family, limited to the 0.4/0.6 printers that `Generic PLA+ @Anker` covers: - Basic, Glitter: M5, M5 All-Metal, M5C at 0.4 and 0.6 - Silk: 0.4 on all three, plus M5C 0.6 and M5 All-Metal 0.6 (Studio excludes M5 0.6) - Matte, Metallic: 0.4 only (Studio restricts these to 0.4) - Studio also allows Basic on 0.2 nozzles. That variant is not added, because the 0.2 Generic profiles use a separate low-flow cap. # Screenshots/Recordings/Graphs N/A (profile-only change). ## Tests - `scripts/check_profile.sh` (local twin of the CI job, run against the full profile tree with the nightly macOS validator) passes all five checks: `profile_tool`, `validate_system`, `validate_slice`, `validate_filament_subtypes`, `validate_custom` (fixtures v1.9.0 to v2.4.2). - `python3 -m unittest discover -s scripts/tests -t scripts`: OK. - The submitter printed with these filament settings in eufyMake Studio on the M5C's stock brass 0.4 nozzle without issues. This Orca conversion has been loaded and sliced in Orca 2.4.2 (as user presets inheriting the Anker generic PLA+) but not yet printed on brass. The printer now has a different nozzle fitted. [How to Download Pull Requests Artifacts for Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts) 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
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. |
||
|
|
07a12f5fe4 |
Read a local plugin's version from its file header (#16118)
* Read a local plugin's version from its file header read_install_state() let the sidecar's installed_version override the version parsed from the entry file's PEP 723 header. That is right for cloud plugins, whose header may be stale, but a local plugin has no other source of truth, so the UI kept showing the version recorded at first install after the file was edited. For local installs, use the header version. * Keep the cloud installed version of an unsubscribed plugin A cloud plugin kept as local after unsubscribing is rewritten with installed_from=local and no cloud_uuid, which the local-header rule could not tell apart from a real local install. Its installed_version is still the cloud one while the header may lag, so the next scan showed the stale header version. Record the conversion in the install state and exclude those plugins from the header rule. Add tests for a local plugin following its header and for an unsubscribed cloud plugin keeping its cloud version. * Add tests for the version of a local plugin A local plugin's version comes from its entry file header. Cover the case of a plugin edited after install, and a plugin unsubscribed from the cloud, which is now a normal local package. |
||
|
|
ea10c84d6b |
Fix scale and mirror of rotated mirrored objects (#16131)
* Fix decomposition of mirrored transformations computeRotationScaling() may place the mirror on any axis, giving unstable rotation/scale for mirrored matrices. Decompose them by flipping each local axis and choosing the one needing the least rotation (lowest axis on ties). Merge the extract helpers into a single extract_rotation_scale() and simplify contains_skew() to check that the scale is diagonal. set_scaling_factor() now preserves the mirror sign on the scaled axis. Selection::scale_and_translate() uses Transformation's rotation and scale instead of calling computeRotationScaling() directly, so scaling a mirrored instance adds no skew. Add tests for mirrored decomposition, scaling, and skew detection. * Use Eigen polar decomposition for instance scaling Selection::scale_and_translate now splits the instance matrix with computeRotationScaling instead of get_rotation_matrix(). Adds a Model test checking that an instance created from another's offset, scale, rotation and mirror reproduces its matrix (as Add instance and Fill bed do). * tidy |
||
|
|
e5d5fb47ca |
Fix BBL infill retraction reduction default (#16201)
# Description Restore `"reduce_infill_retraction": "1"` in the shared Bambu Lab process profile. The BambuStudio profile sync (#15851) replaced it with `reduce_infill_retraction_mode`, which OrcaSlicer does not support, causing the setting to fall back to disabled. This restores the pre-sync behavior and bumps the BBL bundle version to 02.08.00.13. # Validation - All 233 selectable BBL process presets inherit the restored setting with no other resolved settings change. - Static profile validation, system loading, filament validation, and all 1,263 slice cases pass. Fixes #16194 <!-- > 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) |
||
|
|
63918b44a8 | Merge branch 'main' into anker-ankermake-pla-plus-filaments | ||
|
|
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) |
||
|
|
d59155b27f |
Moonraker: pass print=true in upload — fix Upload & Print race (fixes #14945) (#15032)
* Moonraker: pass print=true in upload — fixes Upload & Print race with power-on-upload Closes #14945. Upload & Print on the Moonraker (Klipper) host type failed with HTTP 503 "Klippy Host not connected" on any printer that Moonraker powers up in response to an upload (the [power] on_when_upload_queued feature). The file landed on disk, the print never started, and the user hit an error dialog. Root cause: after POST /server/files/upload succeeds we immediately fire POST /printer/print/start. On a cold printer that Moonraker just powered up, Klippy is still coming up when /printer/print/start arrives, so Moonraker returns 503. Fix: add `print=true` to the upload multipart form. Moonraker's own upload endpoint queues the print inside the upload response — and when a [power] device with on_when_upload_queued is configured, it powers the printer on and waits for Klippy READY before starting. That's the whole point of the power-on-upload feature; our second POST was defeating it. Also read `result.print_started` from the upload response — when true, skip our explicit /printer/print/start (Moonraker handled it); when false (older Moonraker or buddy-fork that ignores the print flag), fall back to the explicit call so the existing behaviour is preserved for those servers. Reporter and root-cause identification: @RubenOllesch. (cherry picked from commit |
||
|
|
640dfad6e9 | List IDEX/IQEX parallel printing in the README's main features | ||
|
|
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> |