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.
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.
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.
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.
* 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>
* 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>
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.
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.
* 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
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.
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.
#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.
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#15320Fixes#7870
# 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)
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.
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.
# 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.
* 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
* 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
* 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>
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.
* 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.
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>
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.
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.
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.
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.
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.
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.
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.
* 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>
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
* Keep User Filament Presets on Printers Their Parent Listed in 2.4.2
User presets store only their diff, so they inherit the parent's
compatible_printers. Profile changes since 2.4.2 renamed and narrowed many
system filament presets, so user presets made against them disappear from
printers they were made for and the saved selection silently falls back to
another filament at startup.
orca_profile_tool.py legacy-compat writes, per current system filament
preset, the printers it covered in an older release and no longer lists,
kept only where a preset with the same filament_id still covers the printer
and they share an extruder variant. The loader gives these printers to user
presets that inherit their parent's list, without writing any file; saving
a preset detached, cloned or with its own list drops them.
* Ask Before Migrating User Filament Presets to Their Legacy Printers
Instead of giving user presets their parent's 2.4.2 printers in memory,
detect the user presets that inherit their parent's compatible_printers
and are missing printers from the legacy map, and offer to migrate them
at startup or from Help > Migrate Filament Presets.
Migrating first backs up the user folder to
filament_preset_migration_<datetime>_backup.zip in the configuration
folder, and migrates nothing if that fails. It then writes the printers
into each preset's compatible_printers, patching only that key so the
preset keeps the parent name it was saved with and older releases can
still resolve it. Cloud-synced presets are marked for upload.
No asks again at the next launch; "Don't remind me again" stops the
startup prompt.
* Key the legacy filament preset map by the parent's 2.4.2 name
* Check the legacy filament preset map in CI
* Limit the 2.4.2 filament preset migration to presets that need it
Only presets that still name their parent as 2.4.2 did and have no printer
list of their own are offered, and a preset changed after it was found is
left alone. The startup prompt appears once, after upgrading from 2.4.2 or
earlier, as a standard dialog that keeps "Don't remind me again"; Help >
Migrate Filament Presets is unchanged. Migrating no longer overwrites an
unsaved printer-list edit or marks the project modified.
* Ask on Every Start and Take the User Presets Lock Once When Migrating Filament Presets
The startup prompt no longer depends on the version the previous run
recorded: whoever still has user filament presets that lost printers is
asked, including users already on a 2.5.0 nightly or alpha. Answering No
asks again at the next start; "Don't remind me again" stops it.
Migration takes the user presets lock once, around the backup and every
file write, instead of once per preset. A lock another instance holds is
reported once ("another OrcaSlicer instance is saving presets") with
nothing written, rather than as one failed preset after another once the
lock's cool-down kicks in. Detection records each preset's file and the
list it will get, so the writes need no collection lock while the user
presets lock is held, the order InstanceLock requires; the loaded presets
are updated afterwards.
* Run the Legacy Compatible Printers Map Check in check_profile.sh and .ps1
CI's "Check profiles" job checks resources/info/legacy_compatible_printers.json
against the v2.4.2 profiles, and its failure message points at these scripts
as the local reproduction, so they run the same check: legacy_compat, right
after profile_tool. The v2.4.2 tree comes from git archive of the tag, is
cached in the work dir like the other downloads, re-extracted with --refresh,
and fetched from upstream first when the checkout lacks the tag.
* Back Up Before the User Presets Lock and Merge Migrated Printers Into the Live List
The lock guard is meant for a few file operations, and zipping a large user
folder is not one, so the backup is taken before it and the guard covers the
writes only; each write re-reads its file, so a save that slipped in before
is still respected.
The in-memory update adds the lost printers to the list the preset holds at
that moment rather than replacing it with the one found at detection, and
leaves a pending cloud sync state alone: a download waiting to be saved kept
its newer list and its "save" flag, which "update" would have cancelled.
The lock message no longer blames another instance alone, since the lock is
also refused for a while after a miss and when the lock file cannot be
opened.
---------
Co-authored-by: SoftFever <softfeverever@gmail.com>
Auto-arrange centered the pile on `best_object_pos`, so on printers with an off-center position (A1 mini, H2C, H2D, H2D Pro, X2D) a large pile could end up partly off the bed. The final alignment now always clamps the pile to the bed, and the `clamp_to_bin` flag is removed. Belt printers already clamped, and centered alignment is unchanged.
The check against fixed parts and excluded regions now tests the position the pile is actually moved to.
Fixes#15727
Print::validate sizes the pre-slice prime tower from Print::extruders(),
which lists every mixed filament slot under its own id, so the estimator
planned one purge block per slot. The tool ordering resolves a mixed slot
to one physical component on every layer, with or without colour
sublayers, and the generated tower only purges those. The GUI preview and
its placement clamp already expand the slots, so the tower was drawn
inside the bed and then refused as partially outside it: with 8 mixed
slots at 0.08 mm layers the estimate was 114 mm for a tower that prints
at 64 mm.
The estimator now expands mixed slots to their components itself, so
validation, the GUI and the CLI placement size the same tower.