Commit Graph
893 Commits
Author SHA1 Message Date
Ian Chua 4a0d5e5edd Merge branch 'main' into fix/plugin-network-permissions 2026-10-20 18:23:02 +08:00
SoftFever 6626377abc Stop re-asking for plugin permissions granted in an earlier session (#16658) 2026-10-20 13:38:44 +08:00
HanifKoh 74d4392dae Keep a Disabled Filament Override Off After a Restart (Key List Variant) (#16650)
Keep a Disabled Filament Override Off After a Restart

A child preset stores "nil" both for an override the user switched off and
for a per-variant column it inherits from its parent, and the loader filled
every nil from the parent. Whenever the parent preset sets Retract Before
Wipe or another override, switching it off lasted only until the next
restart, project open or newer cloud copy.

Treat the keys in is_filament_extruder_override_key, now including the
ironing overrides, as "nil means off": Preset::save and the project-embedded
preset diff write such an option with all its columns, and the loader keeps
its nil columns while still mapping them onto the parent's variants by
name. Every other nullable option keeps nil as "inherit this column". The
cloud diff only force-emits a disabled override whose default is a real
value.
2026-10-20 13:38:20 +08:00
HanifKoh 0d5b3a79d7 Keep a Scalar Config Option's Value When Deserializing Fails (#16641)
A failed stream extraction writes 0 into its target, so Float, Int, Percent and FloatOrPercent options were overwritten before deserialize reported the error, and Point/Point3 kept a partially scanned value. Parse into a temporary and assign only on success; FloatOrPercent sets its percent flag only then too.
2026-10-20 13:02:15 +08:00
S H 6e1d15f8a2 Avoid signed-shift undefined behavior in EdgeGrid distance propagation (#16626)
Danielsson propagation in EdgeGrid::Grid::calculate_sdf() doubles a negative neighbor offset (-1 or -ncols) with a left shift, which is undefined behavior in C++17. Multiply by two instead.

Adds test_edge_grid.cpp, which checks signed distances inside and outside a rectangle beyond the initial edge band. Built with -fsanitize=shift, it aborts on the old code and passes with the fix.
2026-10-19 20:44:24 -05:00
HanifKoh 506dd497db Translate H2C Nozzle Rack Filament IDs at the Printer Boundary (#16612)
The Bambu payload rewrite only matched tray_info_idx, filament_id and filamentId. The H2C nozzle rack puts filament ids in cate (the auto nozzle mapping request) and fila_id (the rack status), so the printer got Orca's OF ids for the filaments and its own ids back for the nozzles in the same request, and the device tab could not name a rack nozzle's filament. Add both keys to one shared list used by the rewrite and its pre-check.
2026-10-19 19:42:05 -05:00
Robert J AudasandRF47 90a5a0b964 Fix JPEG texture imports and filament-matching crash (#16607)
Co-authored-by: RF47 <162915171+RF47@users.noreply.github.com>
2026-10-19 20:30:03 -03:00
Kris Austin a2b1a2f3e7 Keep an offline Moonraker printer from freezing startup (#16668)
Moonraker discovery asked the printer for its name before announcing
it, on the GUI thread, with a 5 s connect timeout. When the selected
printer was off, the window ignored input for 5 s after every launch
and the Home page stayed white.

The request never produced a name. It read machine_name or hostname
from /server/info, and Moonraker sends neither there. Discovery now
announces the preset model name without asking the printer.
2026-10-19 16:21:59 -05:00
yw4z 0585836550 Fetch cloud plugin metadata faster (#16645)
When logged in to Orca Cloud, the plugin fetch at startup no longer refreshes the token a second time while start() is already refreshing it. It now waits for that refresh and checks the token again. The subscribed and owned plugin requests also run in parallel, so the fetch takes about half as long.
2026-10-19 15:56:52 -05:00
5a36120c58 fix(gcode): use Klipper motion planning for time estimates (#15545)
Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
Co-authored-by: raistlin7447 <kris.austin@gmail.com>
2026-10-19 16:15:59 -03:00
Ian BassiandTobias Gloth 69193d5681 Optimize travel: exact/refined path chaining, object tours, and joined infill lines (#16492)
* Shorten the travel between extrusion paths

chain_and_reorder_extrusion_entities() builds the print order of the infill,
ironing and support paths of a region from the nozzle position. It took the
greedy chain, which leaves its longest joins for last; it now shortens it:

- Up to eight paths, chain_segments_exact() finds the shortest chain over
  every order and allowed reversal, by dynamic programming over the subsets
  of paths, with a fixed start and a free end.
- Longer chains are refined by local search: 2-opt, or-opt of up to three
  paths, and moving a run of any length next to the nozzle to the front, each
  tried only toward the 32 end points nearest a link, which come from a
  uniform grid. A move is applied only if it shortens the chain, so the result
  is never longer than the greedy one.
- A nearest-next chain is built too, and refined and used instead when it is
  already shorter than the refined greedy chain, which happens when the greedy
  chain sweeps away from the nozzle and has to come back with a long jump.

The shared greedy chaining also chains two to four segments exactly, which
covers the islands of a layer and the loops of a perimeter collection. Paths
that cannot reverse, such as Grid's, are only reordered, and collections that
keep their order are left alone. The overload without a start point keeps the
greedy chain.

Co-authored-by: Tobias Gloth <tobias.gloth@3dsystems.com>

* Order objects and islands along shorter tours

Objects under print_order Default, and the tour of islands across objects, are
visited in the same order on every layer, so chain_points_with_postprocessing()
orders them as a closed tour. Up to ten points, tsp_exact_cycle() finds the
shortest tour by dynamic programming over subsets, starting at the point
nearest the start point when one is given. Larger sets add or-opt to the
existing 2-opt and crossing removal: runs of up to three points move next to
one of the eight points nearest an end of the run, reversed or not, and
crossing removal runs last so no crossing is left. Snake ordering is
unchanged; Best of strategies gets the shorter Default tour as a candidate.

* Join infill lines into fewer paths

Every separate infill path costs a travel to it, so the fill generators now
leave fewer of them:

- connect_infill() pairs the line ends along each wall in the order that
  joins the most lines: besides shortest arc first, it replays arcs of about
  the same length taken along each contour, forwards and backwards, including
  the last pass that joins neighboring open ends, and keeps the order that
  joins the most. Regular patterns no longer leave loose ends where the
  pairs along a wall fall out of step, and multiline copies print out and back.
- Ends whose anchors would meet are joined along the wall instead, up to two
  anchors and a line width apart.
- An unpaired end anchors toward the nearest open end of another path, where
  the next path usually starts, instead of the side with more free wall. With
  multiline infill, an anchor toward a copy of the same line stops short of
  the copy instead of printing over it.
- Last, sparse infill lines whose ends are at most two line spacings apart are
  joined with a straight piece, shortest first, without closing a loop or
  crossing a line; with anchors off this is the only joining. Copies of
  neighboring multiline groups are joined at the same side, and lines that
  cannot reverse only join end to start.

* Describe the travel optimization design

How the extrusion chains, the object and island tours and the infill line joining shorten the travel, and why each part is built the way it is.

---------

Co-authored-by: Tobias Gloth <tobias.gloth@3dsystems.com>
2026-10-19 15:53:32 -03:00
Ian Chua 6c791532c6 fix: update api to allow requesting network permissions 2026-10-20 01:15:14 +08:00
Ian Bassi 9f6a344162 Make the adaptive infill settings pattern-neutral (#16654) 2026-10-19 12:05:55 -03:00
SoftFever e5b6cbf091 Let plugins make HTTPS requests on macOS (#16638)
* Let the bundled Python verify HTTPS certificates on macOS

* Restore SSL_CERT_FILE after the bundled Python override test
2026-10-19 20:06:14 +08:00
Sergei 252bbbf9f5 Make the exclude-object outline tolerance a user setting (#16503)
* Make the exclude-object outline tolerance configurable

Signed-off-by: Sergei Rozhkov <zmod@zmod.link>

* fix(config): move exclude_object_tolerance to developer mode

Signed-off-by: Sergei Rozhkov <zmod@zmod.link>

* fix(config): move exclude_object_tolerance to expert mode

Signed-off-by: Sergei Rozhkov <zmod@zmod.link>

* feat(ui): hide exclude_object_tolerance for non-Klipper firmwares

mend

Signed-off-by: Sergei Rozhkov <zmod@zmod.link>

* Test that a larger exclude object tolerance writes fewer outline points

Signed-off-by: Sergei Rozhkov <zmod@zmod.link>

---------

Signed-off-by: Sergei Rozhkov <zmod@zmod.link>
2026-10-19 08:17:20 -03:00
Lam Wei Lun 4c2ae6d202 Keep Auto Drop off for projects saved by 2.4.x (#16633)
2.4.0 to 2.4.2 wrote auto_drop on each build item, and 2.4.x objects
carry no auto_drop attribute in model_settings.config. The importer only
read the object attribute and treated a missing one as on, so floating
objects were dropped to the bed on load and the flag was rewritten as on
on the next save.

Record object ids with an item auto_drop="0" and use that as the fallback
when the object has no attribute. The attribute's presence is now tracked
so the fallback applies only when it is absent. Any item set to off turns
auto drop off for the object, since the flag is shared by all instances.
2026-10-19 16:08:59 +08:00
HanifKoh ac022b56ad Wait for the Close Frame in the Moonraker Status Stream Test (#16635)
The fake printer counts the agent's close frame in its read handler, but
Beast answers the close before that read completes, so stop() can return
and the test can read the counter before it is incremented. On a loaded
runner the check saw 0.

Wait for the counter like the other fake-printer counters do, then keep
the exact-count check.
2026-10-19 16:06:41 +08:00
HanifKoh d3699dd9f6 Keep Python Bytecode out of the Bundled Python Home (#16550)
* Keep Python Bytecode out of the Bundled Python Home

The bundled stdlib ships .pyc files that are stale against their sources:
they are compiled when the Python deps are installed, and the copy into the
app does not keep the sources' mtimes. CPython then recompiles every module
it imports and writes the result next to the source. A drag-installed macOS
app rewrites files inside its own bundle on first launch, so its signature
no longer verifies, and read-only installs (AppImage, Flatpak, Program Files)
recompile the same modules on every launch.

Point pycache_prefix at <data_dir>/python/pycache so the interpreter reads
and writes bytecode there. The bundle is never written, the cache survives
between launches, and the data directory is already a plugin read/write root.

* Drop Cached Bytecode Through the Interpreter in the Module Release Test
2026-10-19 13:48:53 +08:00
HanifKoh 24cf1c6bcc Fix Crash Selecting a User Printer Whose Parent Preset Is Not Loaded (#16579)
A user preset can inherit a system preset from a vendor that is not
installed. get_preset_base() returns null for it, and
get_selected_preset_base() dereferenced that unconditionally, so picking
the printer crashed while building the shared profiles notification URL.
Fall back to the selected preset itself. The create printer and create
filament dialogs had the same null dereference on such presets.
2026-10-19 13:29:26 +08:00
Kris Austin da4d766d46 Fix Moonraker status stream hanging app exit and missing dead printers (#16604)
The status stream read the websocket synchronously. Beast's
expires_after() only applies to async operations, so when a printer
stopped sending, ws.read() blocked forever, and stop_status_stream()
joined that thread when the app closed. On Windows the stuck process
also froze the next launch through the shared WebView2 browser.

Run the whole stream on one io_context owned by the stream thread, and
have stop_status_stream() post an abort to it. A stop on an open
websocket sends a close frame and waits up to 1 s for the answer.
Keep-alive pings detect a dead printer, and the stream reconnects until
stopped, with the backoff capped at 32 s. When a quiet printer still
answers pings, its last status is sent again. The stream starts under
connect_mutex after a staleness check, and a stop from the stream's own
callback no longer joins its own thread.

The Moonraker tests move into test_moonraker_printer_agent.cpp.
2026-10-18 19:33:58 -05:00
Kris Austin 0f59cd5d05 Let Global shortcuts run while typing, except keys that edit text (#16558)
#16505 skipped every Global shortcut while ImGui had the keyboard, so
Ctrl+S, Ctrl+R and the others did nothing while the cursor was in the
text tool's box. Skip only the chords that edit text, and only while a
text field (a wx text entry, a spin control, a web view, or an active
ImGui InputText) has the focus. Skip every Global shortcut while an
ImGui modal is open on the focused canvas.

KeyChord::edits_text() defines the text-editing chords. The Global page
of the shortcuts dialog no longer says these shortcuts apply while
typing.
2026-10-18 13:32:41 -05:00
Kris Austin 9b2fcaa53a fix: "bad allocation" when opening a 2.4 project with a custom filament (#16569) 2026-10-16 20:12:24 -03:00
Ian BassiandRodrigo Faselli 0acf1e6680 Fix wall order on narrow tube walls and under outer brims, and test wall order and direction (#16421)
Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
2026-10-16 18:56:55 -03:00
Ján Mišenko c6804fbb68 fix: print statistics placeholders fail in custom G-code (#16348)
The G-code editor lists the Print Statistics placeholders for every
custom G-code field, but total_weight, used_filament, total_cost,
print_time, normal_print_time, silent_print_time,
total_wipe_tower_cost and total_wipe_tower_filament were only defined
for filename_format. Using one in custom G-code, including the File
header G-code, stopped slicing with "Not a variable name".

They are known only once the whole G-code is generated, so they use the
reserved-tag mechanism of print_time_sec and used_filament_length: the
placeholder parser gets a tag such as @TOTAL_WEIGHT@ and
GCodeProcessor::run_post_process() replaces it with the same value
filename_format gets, with numbers written to two decimals. The
per-tag replacement loops are folded into one helper. The running
extruded_* totals are unchanged.

Fixes #15320
Fixes #7870
2026-10-16 16:30:05 -05:00
Ian Chua cefdd9b609 fix: max filepath length on windows (#16552)
# Description

<!--
> Please provide a summary of the changes made in this PR. Include
details such as:
  > * What issue does this PR address or fix?
  > * What new features or enhancements does this PR introduce?
> * Are there any breaking changes or dependencies that need to be
considered?
-->

Windows traditionally limits many file operations to `MAX_PATH` (260
characters). Deeply nested plugin packages can exceed this limit during
`.whl` extraction, leaving files missing and causing the plugin to fail
to load.

This change adds a shared extended_path helper that prepares paths for
Windows file operations using the extended-length prefixes `\\?\` or
`\\?\UNC\`. The helper is used for ZIP archive I/O and extraction
destinations, including plugin wheel extraction. This avoids relying on
the Windows long-path setting, which changes machine-wide settings.

Paths remain unprefixed outside file operations. Code that bypasses the
shared ZIP helpers may still require separate long-path handling.

See [Microsoft's documentation on path length
limitation](https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=registry)

# 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.
-->
Tested on Windows 11 by using a portable installation of OrcaSlicer and
placing it in some really long filepath length e.g.
`C:/somename/<240 characters long folder path>/OrcaSlicer` and then
running and trying to install any plugin locally or on OrcaCloud.

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

[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
2026-10-16 18:47:49 +08:00
Error404JoyNotFound 109d09e0b8 Fix multi-tool standby heaters by turning off unused extruders (#15849)
With ooze prevention on, an extruder that has no layers left is set to 0 °C when it is parked, instead of its standby temperature. On IMEX printers where several filaments share one heater, the heater is only turned off once every filament on it is done.
2026-10-15 16:35:53 -05:00
Matias Alejandro Yocca b00237807d Fix crash when dragging objects in sequential printing mode (#16029)
The clearance preview shown while dragging sorted instances with a comparator that is not a strict weak ordering, which aborts with libc++ debug hardening. The preview now uses object-list order, with each object's copies in their last validated print order, which is the order print validation uses. The height check is now in SequentialPrintClearance.hpp, with unit tests.
2026-10-15 16:13:28 -05:00
SoftFever 4ee1b9c6e9 Add a ResourceTexture plugin capability for height-map texture packs (#16473)
# Description

Adds a fifth Python plugin capability type, `ResourceTexture`, which
contributes
height-map textures to the Texture Displacement picker. A plugin returns
a
directory of images; they appear as selectable textures in the picker,
grouped
under one collapsible section per pack. Disabling or uninstalling a pack
removes
them again, and projects that already use one of its textures keep
working,
because a layer holds its own image bytes.

**The plugin-facing contract** is one method:

```python
class WoodPack(orca.resource_texture.ResourceTexturePluginCapabilityBase):
    def get_name(self):
        return "Wood Pack"
    def get_texture_dir(self):
        return os.path.join(os.path.dirname(__file__), "textures")
```

The host scans one non-recursive level of that directory, accepting the
same
formats a hand-picked image already accepts (`.png/.jpg/.jpeg/.bmp`),
and decodes
them through the same `load_texture_image_data()` the picker uses for
the user's
own images, so a pack texture and a hand-picked one behave identically.

**Distribution is `.whl` only for v1.** `install_plugin` copies exactly
one
file, so a `.py` plugin cannot ship sibling images; a wheel is a single
file and
the loader already extracts it on demand.

**Files of interest**

- `src/slic3r/plugin/pluginTypes/resourceTexture/` — the capability, its
  trampoline and its pybind base, mirroring the Pages type.
- `src/slic3r/plugin/host/PluginResourceTextures.{hpp,cpp}` — host
registry,
  free of wx and GUI includes so it can sit beside `PluginManager`.
- `src/slic3r/GUI/TextureLibrary.{hpp,cpp}` — the third texture source,
the
  `pack_name` provenance on an entry, and a generation counter.
- `src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp` — the picker.
- `src/slic3r/plugin/PluginFsUtils.{hpp,cpp}` —
`collect_resource_texture_files()`.

**Behaviour change worth a reviewer's attention:** the picker popup is
now a
fixed size that scrolls rather than sizing itself to its content, and it
gains
one collapsible section per pack. The sizing applies to every user, not
only
those with packs installed. With no packs installed the picker otherwise
behaves
exactly as before.

**Not in scope for v1:** folder or `.zip` install for non-programmers,
and
preset-referenceable pack textures (which would need a plugin-backed
`ConfigOptionDef::plugin_type`).

# Screenshots/Recordings/Graphs


https://github.com/user-attachments/assets/b96787cf-b475-4698-826d-5db8b9f386f1

## Tests

New suite `tests/slic3rutils/test_plugin_resource_texture.cpp`, 19
cases:

- Validator: a file inside the root is listed; a parent-directory escape
and an
absolute directory outside the root are refused; a symlink pointing
outside is
skipped; files in a subdirectory are not listed; non-regular entries are
skipped; the extension whitelist is case-insensitive and rejects
near-misses;
  the listing stops at 64 files; a missing directory yields an error.
- Listing and lifecycle: a pack's images appear and clear on disable and
unload;
an empty pack contributes nothing; two distinctly named packs each stay
one
contiguous run; a second pack declaring a loaded capability name is
refused;
  reinstalling with different bytes at the same path changes the decoded
  content.
- Hostile overrides: `get_texture_dir()` returning `data_dir()` or
`resources_dir()` is refused and the pack loads with no textures; a
raising
  override still loads the pack.
- Decoding: PNG and BMP from a pack decode through the same path as the
same
  files picked by hand.
- Persistence: a project keeps a pack texture after the pack is
uninstalled,
  bytes unchanged across a re-save.
- Wheel metadata: a minimal resource-texture wheel is accepted.

Known gaps, for transparency: the wheel path is covered only at the
metadata
gate, not through a full extraction-and-import lifecycle; there is no
test for a
registry callback after interpreter shutdown, as there is no seam for it
without
reaching into `src/`; and `.jpg` decode parity is covered by the listing
rather
than by a real fixture, since no JPEG encoder is available to synthesise
one.

Manually exercised in-app on Linux with a locally built pack wheel (five
colour
PNGs): install, enable, textures listed under the pack's own section,
then
disable and uninstall remove them. The new suite itself has not been run
locally — CI is the first execution.
2026-10-16 00:35:30 +08:00
Noisyfox d4fabbf7c8 Fix AMS gradient rendering, color presets and printer writeback (#16252)
* Fix external spool gradient and multicolor rendering

* Display AMS gradients and multicolors in color popup

* Preserve AMS gradient selection through color popup

* Match popup selections by full filament color data

* Align AMS Lite gradient geometry with BambuStudio

* Align AMS color palette with BambuStudio presets

* Match Bambu circular color rendering without background masks

* Preserve AMS gradient colors when applying filament settings

* Match AMS preview gradient swatches to the solid color shape
2026-10-15 19:43:07 +08:00
Arthur Porto 6aa613ccf0 Fix Z contouring failing on objects with several instances (#16461)
* Let Z contouring slice objects that have several instances

The contouring step refused any object with more than one instance. The
instances of a PrintObject differ only by their XY shift, so the mesh is now
placed with the first instance of the PrintObject being contoured.

* Say in the Z contouring comment how instances map to print objects

* Check that Z contouring follows each instance as it is turned
2026-10-15 07:30:57 -03:00
SoftFever 92b5301bf6 Fix trackpad scroll bounce in ImGui panels 2026-10-15 15:34:05 +08:00
Noisyfoxandyw4z 26f5fd6251 Add a themed WebView color picker with persistent favorites (#16197)
* Define validated color picker selection contract

* Integrate web color picker with gradient and alpha controls

Co-authored-by: yw4z <ywsyildiz@gmail.com>

* Apply shared Orca theme to web color picker

* Host color picker in a validated WebView dialog

* Persist color picker favorites in user preferences

* Integrate localized web color picker with filament actions

* Add color picker browser regressions and verify direct includes

* Fit color picker content in a borderless floating panel

* Anchor color picker panels to their triggering controls

* Use accent foreground for selected color picker switches

* Integrate web color picker into generic color fields

* Use web color picker for AMS custom colors

* Use shared web color picker from Linux sidebar menu

* Use web color picker when adding texture import material

* Preserve alpha in filament color picker actions

* Clean up color picker asset whitespace

* Use application translations for color picker validation

* Preserve existing formatting in color picker integration

* Match SpeedDial rounded window handling on macOS

* Align color picker host behavior with SpeedDial

* Consolidate color picker implementation into its dialog

* Remove native color picker fallback

* Limit color picker interfaces to production consumers

* Use shared WebView translation field name

* Reapply anchored color picker placement after GTK show

* update

* fix increment buttons not working on spin inputs

* Use thumb color from its own channel

* init gradients with shifted color for second color

* fix hsl sliders cannot move freely and lock downs other one

* unify save button and color preview & increase stored color count to 32

* switch to hsl based calculation and fix plot and slider drag events

* update

* add support for multi colored gradients

* Update test_filament_color_picker.cpp

* add filtering color palette and store sidebar and combo state

* open dialog with calculated size

* apply shadow to dialog on windows

* add tooltip for gradient add color button

* Restore draggable fixed-size color picker frame

* Fix color picker sidebar sizing in WebKit

* Fix invisible default filament color button on macOS

* Remove duplicated color defs for color picker

* Remove duplicate HTML color picker frame

* Match color picker tests to current theme roles

* update icons

* use focus color on background of filter box

* align gradient switch with hex input

* fix initial window sizing

* Preserve caller color order when drawing filament icons

---------

Co-authored-by: yw4z <ywsyildiz@gmail.com>
2026-10-15 15:29:49 +08:00
SoftFever 20c157b0f4 Merge branch 'main' into feature/resource-texture-pack-plugins 2026-10-15 13:57:38 +08:00
xNoVoSx 5e94368863 Write the first layer temperatures of the printed filaments into the G-code header (#16422)
The `; first_layer_bed_temperature` and `; first_layer_temperature` compatibility lines were always taken from filament 1. Anycubic's GoKlipper reads the bed line to heat the bed, so a plate printed only with another filament got the wrong temperature.

The bed line now follows `bed_temperature_formula` (highest used filament or first printing filament). The nozzle line uses the first printing filament. Both values are computed before the by-object loop.
2026-10-14 16:11:57 -05:00
Ian Bassi c0e6db227d Export G-code statistics as JSON (#16487)
* Export G-code statistics as JSON

Add an export of a plate's G-code statistics for scripts and
spreadsheets that compare prints or settings: print time, travel,
filament and feature totals, the same per layer, and the filament of
each object instance and its supports. The times are the preview's
estimates, summed the way the preview sums them.

The GUI writes <name>.statistics.json next to an exported G-code when
"Export statistics with G-code" is enabled in Preferences (Control >
Slicing). The CLI writes plate_N.statistics.json to the output directory
with --export-statistics, and exits with -25 when it cannot.

The per-instance masses now carry the object's name and the instance's
index, so the export can name them. The vertices the processor adds
inside a move for the actual speed view repeat the move's extrusion, so
the filament sums skip them.

* Move the statistics export preference to the Developer tab

"Export statistics with G-code" now sits in Developer > Settings
instead of Control > Slicing.

* Store the G-code statistics inside exported 3MF files

With the preference on, an exported sliced 3MF file holds each plate's
statistics as Metadata/plate_N.statistics.json, next to its G-code. The
print job sent to a printer and the autosave backup leave them out.

The CLI does the same with --export-3mf, and writes the separate file
only next to the G-code in --outputdir, as without it the G-code is a
temporary file. The file is written in binary mode, so it reads the
same on every platform and matches the copy in the 3MF.

* Address review of the statistics export

- Number each object instance by its position among its model object's
  copies. A copy of another rotation or scale, and every copy on a belt
  printer, slices as a print object of its own, where it was always
  instance 0; a copy after a non-printable one shifted too.
- Take the filament totals from the processor's statistics, which count
  the flush as the preview's filament table does, and list each
  filament's model, support, prime tower and flush use. The summary's
  cost is now filament_cost, as the preview's cost adds the time cost.
- State when the version goes up.
- Drop the preference's default, which get_bool already gives.
2026-10-14 14:22:04 -03:00
Ioannis Giannakasandraistlin7447 ae138090f1 Fix uninitialised object ids in "; printing object" comments (#16115)
Object and copy ids were only assigned in set_object_info(), which runs only for Klipper, Marlin and RRF with Exclude objects on. Every other printer, Bambu included, printed an uninitialized object id and labeled every copy as copy 0. The numbering now runs on every export, so the labels are stable and unique on all printers. Output for Klipper, Marlin and RRF with Exclude objects on is unchanged.

The label test covers Marlin, Klipper and Repetier, plus a BBL row that checks each copy gets its own M624/M625 skip block.

Co-authored-by: raistlin7447 <kris.austin@gmail.com>
2026-10-14 10:51:57 -05:00
packerlschupfer e028476d2a 3mf: record the no-light thumbnail in its own status flag (#16307)
When a CLI export rendered the no-light thumbnail but no plate thumbnail, the no-light loop set the plate thumbnail's status flag. That skipped the fallback that copies the plate thumbnail from disk, so re-exporting a 3mf that has plate_N.png but no plate_no_light_N.png left plate_N.png out of the package. The loop now sets no_light_thumbnail_status instead.
2026-10-14 10:46:35 -05:00
fa1k3 f1658855c8 Apply filament shrinkage compensation on the first slice (#16312)
Print::apply() decided the shrinkage compensation from the existing PrintObjects and their regions. On the first apply() they do not exist yet, so the first slice was not scaled. The CLI calls apply() only once, so filament_shrink and filament_shrinkage_compensation_z had no effect there.

The compensation is now decided before the PrintObjects are built, from the filaments the model uses, and stored in Print. When the filaments have different shrinkage values, every filament assigned to a feature, height range, modifier, support, tool change or the wipe tower counts, even if it does not end up printing. If they disagree, compensation stays off with the existing warning.
2026-10-14 09:39:08 -05:00
packerlschupfer 16efd3458b 3mf: claim the backup lock when the backup's state cannot be established (#16431)
has_restore_data() signals "this backup is in use, leave it alone" by setting
origin to "<lock>". Its caller treats anything else as permission to delete
(Plater.cpp, the EVT_RESTORE_PROJECT handler):

    try {
        if (originfile != "<lock>") // see bbs_3mf.cpp for lock detail
            boost::filesystem::remove_all(last);
    }
    catch (...) {}

So every "return false" that leaves origin empty is a delete instruction, and
three of them fired without anyone having established that the backup was
spent.

lock.txt exists but cannot be read. load_string_file() was outside the try, so
the exception left has_restore_data() altogether. The handler is queued and has
no try around the call, so this took the application down. Fixes #15670.

lock.txt reads but holds no pid. A crash or a full disk mid-write leaves a
zero-length lock.txt, because save_string_file() truncates before it writes.
boost::lexical_cast<int>("") throws into the catch, which returned false with
origin still empty, and the caller deleted the backup. Also #15670.

The .3mf probe could not answer. A path that cannot be probed at all reports
false exactly like a missing one, so an unreachable backup looked spent. The
error_code now separates the two: ENOENT and ENOTDIR mean "not there" and stay
collectable, any other error means the question was never answered.

All three now claim the lock. Declining a restore is recoverable; deleting a
backup nobody could examine is not.

What deliberately did not change: a readable directory with no lock and no
project still comes back with origin empty and stays collectable, as do a lock
held by another process and a directory that does not exist. Claiming the lock
whenever anything looked amiss would strand every spent backup on disk, so
those three cases are tests rather than prose.

Tested in tests/libslic3r/test_3mf.cpp. Against the previous behaviour the new
cases fail as they should -- the two uninterpretable locks come back with an
empty origin, and the unreadable one escapes as

  basic_ios::clear: iostream error

while the three collectable cases pass unchanged, before and after.
2026-10-14 08:57:08 -05:00
Lam Wei Lun 4cf16febf6 Fix plugin unit test 2026-10-14 18:23:32 +08:00
Lam Wei Lun 70e52a105f Fix clang tidy 2026-10-14 17:38:52 +08:00
Lam Wei Lun 86ab598899 Add a ResourceTexture plugin capability for height-map texture packs
A fifth Python plugin capability type, ResourceTexture, contributes
height-map textures to the Texture Displacement picker. A pack returns a
directory of images; the host validates it once at load and lists the
images under a section of their own.

- New capability type ResourceTexture, type string "resource-texture",
  exposed to plugin authors as
  orca.resource_texture.ResourceTexturePluginCapabilityBase with one
  required method, get_texture_dir().
- The directory is resolved once on the load worker, under the GIL, and
  cached as a plain string: the picker runs inside the ImGui frame, where
  a trampoline call would block on the GIL behind a load/unload worker, or
  throw past EndPopup.
- Validation resolves the directory and requires containment in the plugin
  root, re-checked per file so a symlink inside the pack cannot resolve
  outside it. Containment is the only security control here and it guards
  exfiltration: layer bytes are embedded in every saved .3mf, so a pack
  that could return ~/.ssh/id_rsa would leak it through any shared project.
- New host registry PluginResourceTextures, filled from both the plugin
  and the capability lifecycle callbacks plus a startup sweep. It holds
  strings only and never a capability reference, so nothing outlives
  Py_Finalize.
- The picker lists pack textures read-only, one collapsible section per
  pack, in a fixed-size scrolling popup whose height follows the window. A
  generation counter invalidates the cached listing, and each gizmo's
  cached thumbnails, when the set of packs changes.
- Tests in tests/slic3rutils/test_plugin_resource_texture.cpp cover the
  validator (parent-directory and absolute escapes, an escaping symlink,
  the extension whitelist, the 64-file cap), the listing and its
  load/disable/re-enable/unload lifecycle, hostile get_texture_dir()
  overrides, decode parity with hand-picked files, and a .3mf round trip
  with the pack uninstalled.

No persistence work is needed: a layer carries its own image bytes and 3MF
writes them into the archive, so uninstalling a pack leaves existing
projects intact.

Behaviour change worth noting for review: the picker popup is now a fixed
size that scrolls, rather than sizing itself to its content, and it gains
one collapsible section per pack. That sizing applies to every user, not
only those with packs installed.
2026-10-14 17:15:39 +08:00
Lizard 6e42a9b0ae Hot reload local preset bundles (#15424) 2026-10-14 14:18:56 +08:00
SoftFever e77ba5f672 Open .gcode files with their own settings, as .gcode.3mf files are (#16463) 2026-10-14 14:13:58 +08:00
fa1k3 f32591ed64 Check the real print height: count the Z shrinkage compensation once and include the raft (#16313)
The build height check in Print::validate() compared object layers that already include the Z shrinkage compensation against printable_height divided by that compensation. That counted the compensation twice, so objects that fit were rejected. The check also left out the raft, so an object whose top plus the raft is above printable_height passed validation.

The check now adds object_print_z_min to the top layer and applies the compensation once. The shrinkage message is shown only when the object fits without the compensation. Belt printers apply the compensation to the raw model height, since their height does not come from the layers.
2026-10-13 19:36:47 -05:00
packerlschupfer cb752d38a3 Probe config-supplied paths without throwing on an unreadable path (#15669)
The startup probes for download_path and last_backup_path used the throwing
boost::filesystem overloads, so a path that could not be probed at all, such as
one whose parent is not searchable, raised filesystem_error instead of falling
back. init_download_path() runs from the GUI_App constructor inside GUI_Run()'s
try, which left the application showing a message box and exiting with no window
and no log file, the throw preceding the log sink. has_restore_data() runs from
the queued EVT_RESTORE_PROJECT handler, whose try starts after the call.

Five probes in those three functions take the error_code overload instead.
Nothing branches on ec, and readable or missing paths are unaffected because both
overloads return the same value for them. A new test covers has_restore_data()
against a backup directory whose parent has no search permission, and skips on
Windows, as root, and where the mode change is refused or not enforced.
2026-10-13 19:05:36 -05:00
TheLegendTubaGuy de3af0b7de Add Moonraker trusted-client auth support (#15550)
Orca sent `X-Api-Key: 88888888` to Moonraker whenever the printer preset's API Key was empty, which Moonraker rejects with HTTP 401 since Arksine/moonraker@93f5d9c. The preset's API Key is now the only thing that sets the header, and an empty key sends none.

Logging out of a printer now marks it revoked rather than only clearing its code, since an empty code is a valid Moonraker binding. Discovery events are delivered through the network agent and dropped if their printer agent was replaced while queued.
2026-10-13 15:13:07 -05:00
Damir GaleevandIan Bassi 5a427f2eb6 Precise Seam: new full-coverage policy for Seam Blocked (#16170)
* Precise Seam: new full-coverage policy for Seam Blocked

A Seam Blocked modifier that covers a whole perimeter now blocks it, like a
perimeter painted red all round, instead of being skipped. It overrides
painting and lower-priority modifiers, like Enforced and Neutral. A new
warning names the modifier: "Seam Blocked modifier "<name>" of "<object>"
covers an entire perimeter". It is shown only when a perimeter stays
entirely Blocked after all weak modifiers. The full-containment warning now
covers only Center, Left and Right.

* Precise Seam: fix seams on perimeters blocked all round

- SeamComparator::is_first_not_much_worse() compares the types first, so two
  Blocked candidates fall through like Neutral ones. On a perimeter blocked
  all round, by a whole Seam Blocked modifier or by red painting, Random put
  the seam on the closing vertex of every layer and Aligned stopped aligning.
- The whole-perimeter Blocked warning flags only the highest whole zone, the
  one in effect. A lower whole zone it overwrites is no longer reported, and
  the candidate scan runs only when that zone is Blocked.
- The warning msgids take the type name from the menu's "Seam Blocked" msgid.
- PreciseSeamWarnings::set() shares the load-before-store idiom, and every
  weak segment records its modifier.
- Tests: a whole Blocked perimeter gets the seams of an unmarked one under
  every seam position; overwritten whole Blocked zones are not reported.

---------

Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
2026-10-13 16:38:08 -03:00
SoftFever 1591423f91 Fix crash when opening a G-code file exported by OrcaSlicer (#16441) 2026-10-14 00:39:09 +08:00
TheLegendTubaGuy 84feeaa587 Fix duplicate G29 preparation time (#15274)
The time estimate added 260 s for every G29 on non-Bambu printers. Prusa MK4, MK4S and CORE One start G-code splits bed leveling across six G29 commands, so those printers showed about 26 extra minutes. The 260 s is now added once per G-code processing run. Bambu printers still only count a G29 inside an M622 J1 block, and their estimates are unchanged.

Fixes #15088
2026-10-13 11:03:43 -05:00