Compare commits

...
Author SHA1 Message Date
dependabot[bot] 5bf43d92b9 chore(deps): bump actions/setup-python from 6 to 7
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 6 to 7.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-01 16:24:21 +00:00
Dipl.-Ing. Raoul Rubien, BSc 95b781745d Fixes 4 Compiler Warnings (#10727)
* fixes: may be used uninitialized [-Wmaybe-uninitialized]

* fixes: arc_len_next may be used uninitialized [-Wmaybe-uninitialized]

* review result: reverts {} initializer with = to keep code style consistent
2026-08-01 18:07:15 +08:00
Kenneth Raplee 80e64f80a6 Fix 32-bit build in LayerResult::make_nop_layer_result (#15036)
LayerResult's second field is typed size_t, so std::numeric_limits::max
should also use size_t and not something related to coordinates for the
layer_id.
2026-08-01 11:49:28 +08:00
yw4z 477208a969 Remove borders and paddings from native controls on Linux (#14873)
* init

* update

* Update SpinInput.cpp

* possible fix for em_unit

* button alignment

* match titlebar height

* revert em_value for macOS and Windows

* Update GUI_Utils.hpp

* Merge branch 'main' into linux-black-borders-2

* Revert "button alignment"

This reverts commit 3fc7461071.

* Revert "match titlebar height"

This reverts commit c4aa1d9f1e.

* revert dpi changes

* match platform tags

* remove radio box borders

* Fix code indent
2026-08-01 11:28:07 +08:00
Rodrigo FaselliandIan Bassi 2e08b19d6b Outline MSAA (#14835)
Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
2026-07-31 20:51:48 -03:00
SoftFever ea7117b7ca Fix stringing between the model and the wipe tower (#15038)
# Description

On multi-tool printers using the type 2 wipe tower, the travel to the
tower ignored the configured Z hop type and always used a plain vertical
hop, so the nozzle rose in place over the part and oozed instead of
lifting away with the travel. It now follows the filament's Z hop
setting, matching what the type 1 tower already does.
Only toolchange travels to a type 2 tower change. Normal Lift and z_hop
= 0 are unaffected, and no extrusion moves change in any mode.

# Screenshots/Recordings/Graphs


## 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)
2026-07-31 18:26:48 +08:00
SoftFever 603a8f9c8f Fix stringing between the model and the wipe tower
The tower travel took retract()'s default vertical Z hop instead of the
configured one, so the nozzle rose in place over the part and oozed
rather than departing with the travel. Pass the filament's z_hop_types
through, mapping Auto to a spiral lift as append_tcr does.
2026-07-31 18:16:06 +08:00
SoftFeverandCopilot Autofix powered by AI 6a2a57bdf4 Bring the rib wall and skip-points prime tower to all printers (#15035)
* Sync WipeTower from BambuStudio(through ca1881761)

* Fix post-slice self-invalidation on custom multi-extruder printers

* Complete the rib wipe tower port in WipeTower2

The rib tower is now always square (prime_tower_width is ignored, as the
GUI already implies), carries the rib origin offset like the BBL tower so
the rib tips sit inside the configured position, clamps the rib length to
the tower diagonal, and extends the ribs for short towers.

* Use the squared rib tower size in arrange estimates

estimate_wipe_tower_polygon reserved the arrange footprint and clamped the
tower X position with the raw prime_tower_width, under-reserving space
whenever the rib wall squares the tower to a different width.

* Print the WipeTower2 shell with a non-support, non-soluble filament

Like the BBL tower: the layer's sparse infill, wall, and brim go to the
first toolchange to a non-support/non-soluble filament, or are printed
with the incoming filament before any toolchange. The minimal-purge
clamp now also covers toolchanges that get no finish-layer saving.
Output is unchanged when no support/soluble filament is used.

* Port the skip-points gap wall to WipeTower2

prime_tower_skip_points was stubbed for Type2 towers: the wall call
hard-coded skip_points=false, the gap cutter received an empty vector,
and append_tcr2 never routed the entry travel. Now the toolchange entry
positions are precomputed from the finalized plan, the wall is cut open
at each entry, and the entry travel approaches around the tower bounding
box through the opening when it starts outside the tower. The geometry
helpers are re-synced with the BBL versions (add_extra_point guards,
per-point side selection). The cone wall keeps its separate path, where
the option stays inert.

Behavior change: non-BBL towers now honor the (default-on) checkbox with
gap walls and routed entries; with the option off the output is
unchanged, and the BBL tower path is untouched.

* Route the in-place toolchange tower entry through the skip-point gap

On multi-tool printers without ramming the tool changes away from the
tower and the entry travel is the tcr's own positioning move, which went
straight across the printed wall. Append the avoid-perimeter path to the
change-filament gcode instead, so the head approaches around the tower
and enters through the wall opening (append_tcr parity).

* Iron the purge start out through the skip-point gap in WipeTower2

Port the BBL tower's entry line ironing: extrude the first 3 mm of the
purge, retract, drag the nozzle 1.5x back out through the wall gap at
F600, creep back at F240 and unretract, so the toolchange start blob
ends up in the gap instead of on the wall. Fires only when the purge
starts at the left-edge entry heading right (in-place toolchangers);
SEMM ram/cooling wipes start mid-box and the priming line has no wall,
so both keep their previous output.

* Reserve WipeTower2 toolchange depth to match the printed purge

The planner reserved ramming rows gated only on enable_filament_ramming and
sized them with the SEMM 0.25s time step, while toolchange_Unload rams on
(semm && enable_filament_ramming) || filament_multitool_ramming with the
multitool time step. Disabling multitool ramming therefore left ~3 unprinted
rows per toolchange as blank bands in the tower. Without ramming the first
wipe line also needs reserved depth of its own (it no longer rides the last
ramming row), plus the y_step/2 offset the wipe start inherits from the
ramming start position - otherwise the tightened boxes truncate the ordered
purge at the box edge.

* Tile WipeTower2 purge rows contiguously across toolchange blocks

Without ramming, each purge block reserved one wipe pitch more than its
rows occupy (ceil+1 rounding plus the ram-geometry start offset), and the
wipe began a full pitch inside the block, leaving a blank band of exactly
two pitches between adjacent blocks. Plan the block as whole wipe rows,
start the first row so the row lattice continues across the block
boundary, and fill the reserved box instead of stopping at the ordered
volume, mirroring how the BBL WipeTower keeps planned depth identical to
printed rows. Ram-printing toolchanges (SEMM with ramming enabled,
multitool ramming) are unchanged.

* Scrub the WipeTower2 toolchange entry with the BBL flat-ironing spiral

The entry scrub now matches the BBL tower's toolchange_wipe_new sequence:
after the ironing drag the retracted nozzle runs a dry expanding-square
spiral centred on the wall-gap entry point before resuming the purge row.
The spiral runs whenever the gap wall is on (disable per filament via
filament_tower_ironing_area = 0); WipeTower2 no longer reads
prime_tower_flat_ironing.

* Restart the WipeTower2 wipe at the box boundary after multitool ramming

With the gap wall on a multi-tool printer, quantize the ram band up to its
whole reserved rows (as the BBL tower does for the old-tool purge) and start
CP TOOLCHANGE WIPE at the left-edge boundary on a fresh row below it instead
of continuing from wherever the ram serpentine ended. The entry scrub then
runs at the wall gap on ram toolchanges too, and the wipe box is whole rows,
so it is filled completely like the no-ram case. SEMM and skip-points-off
behavior is unchanged.

* Move the WipeTower2 wall gap to the wipe start row for ram toolchanges

* code cleanup

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* fix typo

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-07-31 18:08:55 +08:00
SoftFever c8aacea176 fix typo 2026-07-31 18:07:46 +08:00
SoftFeverandCopilot Autofix powered by AI 4824a171f1 Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-07-31 15:34:04 +08:00
Ian Chua 101f43b2d8 fix: resolve plugins that are missing locally (physical file deleted) (#14861)
* fix: resolve plugins that are missing locally (physical file deleted)

* fix: don't treat file not found as an error
2026-07-31 14:15:51 +08:00
Ian Chua e01ac1f0a6 fix: keep orphaned cloud plugins runnable (#14859)
* fix: keep orphaned cloud plugins runnable

* fix: tests
2026-07-31 14:15:22 +08:00
Ian Chua b51db32bb5 fix: ota profile race (#14993)
# 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?
-->
This PR updates OTA vendor profile handling and fixes a Windows-only
startup race that could terminate OrcaSlicer silently.

## Changes
- Correct vendor profile version comparison logic.
- Trigger vendor profile synchronization after the startup printer
preset is restored.
- Remove duplicate vendor synchronization from the general startup
updater.
- Replace global temporary archive cleanup with targeted per-vendor
cleanup.
- Add updater-thread exception handling to prevent uncaught filesystem
errors from terminating the process.

# 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)
2026-07-31 14:14:34 +08:00
Ian Chua 603b02f0e7 Merge branch 'main' into fix/ota-profile-race 2026-07-31 14:14:23 +08:00
Kiss Lorand 54dc5a2f1d Hungarian localization overhaul (#15024) 2026-07-30 15:08:04 -03:00
Kris Austin 303be94262 feat(msix): add execution alias and web link associations to the Store package (#14799) 2026-07-30 10:02:44 -03:00
Ian Bassi 58bad17af9 AI translation update (#15018) 2026-07-29 18:09:32 -03:00
Nathan Schulte ddce030322 add space after "Arranging" message (#15017) 2026-07-29 17:37:23 -03:00
2d4b431d5f Improve dimmed layers (#15001)
Co-authored-by: yw4z <yw4z@outlook.com>
Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
2026-07-29 16:27:15 -03:00
Ian Chua 27c468754c feat(plugin): expose orca.host.app_language() for plugin localization (#14997)
# Description

Python plugins currently have no way to localize their own dialogs to
match the app: the UI language is stored in `OrcaSlicer.conf`, which the
plugin audit hook deny-lists by design (the file sits next to cloud
secrets), and the host API exposes no app info. As a result, localized
plugins have to guess the language from the OS locale, which does not
always match the slicer UI (and the embedded interpreter often gets no
`LANG` at all in GUI sessions).

This PR adds a minimal read-only accessor:

```python
orca.host.app_language()   # -> "en_US", "ru_RU", ...
```

It returns `GUI_App::current_language_code_safe()` — only the language
code string, nothing else from the config, so the audit-hook security
model is untouched.

No breaking changes; one file, +10 lines.

# Screenshots/Recordings/Graphs

(screenshots of the test dialog will be attached below)

## Tests

- Built on macOS (arm64, Ninja) with this change — compiles clean.
- Ran a minimal script-capability plugin calling
`orca.host.app_language()` on a system with Russian UI: the dialog shows
`'ru_RU'`.
- Guard before GUI init follows the same exception pattern as the
neighbouring `plater()`/`preset_bundle()` accessors.
<img width="903" height="826" alt="Снимок экрана 2026-07-28 в 23 10 48"
src="https://github.com/user-attachments/assets/27088265-b55d-4958-8602-7c3ab4993003"
/>
<img width="437" height="276" alt="Снимок экрана 2026-07-28 в 23 10 56"
src="https://github.com/user-attachments/assets/9845b401-dc8e-4353-aa24-5ace678270d6"
/>
2026-07-30 01:19:09 +08:00
SoftFever 3ab9cf53d0 code cleanup 2026-07-30 00:55:09 +08:00
BartokandIan Bassi 88632710d5 docs: add Microsoft Store install path for Windows (#15009)
Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
Signed-off-by: Bartok <danielrpike9@gmail.com>
2026-07-29 10:53:20 -03:00
Nathan Schulte d434e35488 fix STEP progress message percent format (#14994) 2026-07-29 10:48:01 -03:00
yw4z 7bd53b1283 Fix cannot type values to spin control if first digit of desired value is less then min value (#15002) 2026-07-29 10:19:10 -03:00
SoftFever 252df70ec4 Move the WipeTower2 wall gap to the wipe start row for ram toolchanges 2026-07-29 20:58:21 +08:00
Ian Bassi 6489b4cad3 Fix: Only one wall top surfaces (#14929) 2026-07-29 09:23:35 -03:00
SoftFever 086bbf986b Restart the WipeTower2 wipe at the box boundary after multitool ramming
With the gap wall on a multi-tool printer, quantize the ram band up to its
whole reserved rows (as the BBL tower does for the old-tool purge) and start
CP TOOLCHANGE WIPE at the left-edge boundary on a fresh row below it instead
of continuing from wherever the ram serpentine ended. The entry scrub then
runs at the wall gap on ram toolchanges too, and the wipe box is whole rows,
so it is filled completely like the no-ram case. SEMM and skip-points-off
behavior is unchanged.
2026-07-29 19:59:33 +08:00
SoftFever 67e1ce03a6 Scrub the WipeTower2 toolchange entry with the BBL flat-ironing spiral
The entry scrub now matches the BBL tower's toolchange_wipe_new sequence:
after the ironing drag the retracted nozzle runs a dry expanding-square
spiral centred on the wall-gap entry point before resuming the purge row.
The spiral runs whenever the gap wall is on (disable per filament via
filament_tower_ironing_area = 0); WipeTower2 no longer reads
prime_tower_flat_ironing.
2026-07-29 18:48:10 +08:00
SoftFever 36bd453ac8 Tile WipeTower2 purge rows contiguously across toolchange blocks
Without ramming, each purge block reserved one wipe pitch more than its
rows occupy (ceil+1 rounding plus the ram-geometry start offset), and the
wipe began a full pitch inside the block, leaving a blank band of exactly
two pitches between adjacent blocks. Plan the block as whole wipe rows,
start the first row so the row lattice continues across the block
boundary, and fill the reserved box instead of stopping at the ordered
volume, mirroring how the BBL WipeTower keeps planned depth identical to
printed rows. Ram-printing toolchanges (SEMM with ramming enabled,
multitool ramming) are unchanged.
2026-07-29 16:37:06 +08:00
SoftFever 9292db2f9f Reserve WipeTower2 toolchange depth to match the printed purge
The planner reserved ramming rows gated only on enable_filament_ramming and
sized them with the SEMM 0.25s time step, while toolchange_Unload rams on
(semm && enable_filament_ramming) || filament_multitool_ramming with the
multitool time step. Disabling multitool ramming therefore left ~3 unprinted
rows per toolchange as blank bands in the tower. Without ramming the first
wipe line also needs reserved depth of its own (it no longer rides the last
ramming row), plus the y_step/2 offset the wipe start inherits from the
ramming start position - otherwise the tightened boxes truncate the ordered
purge at the box edge.
2026-07-29 13:23:29 +08:00
Ru 0dc14aa876 feat(plugin): expose orca.host.app_language() for plugin localization
Plugins have no way to localize their own dialogs: the UI language lives in
OrcaSlicer.conf, which the plugin audit hook deny-lists because the file sits
next to cloud secrets. Add a read-only host accessor that returns just the
language code (current_language_code_safe), so plugins can match the app
language without touching the config file.
2026-07-28 23:11:05 +03:00
SoftFever bef47b2c70 Iron the purge start out through the skip-point gap in WipeTower2
Port the BBL tower's entry line ironing: extrude the first 3 mm of the
purge, retract, drag the nozzle 1.5x back out through the wall gap at
F600, creep back at F240 and unretract, so the toolchange start blob
ends up in the gap instead of on the wall. Fires only when the purge
starts at the left-edge entry heading right (in-place toolchangers);
SEMM ram/cooling wipes start mid-box and the priming line has no wall,
so both keep their previous output.
2026-07-28 21:21:50 +08:00
peachismomo 013a9452af fix: prevent Windows OTA vendor profile update race 2026-07-28 19:48:10 +08:00
SoftFever 5b475e5e98 Route the in-place toolchange tower entry through the skip-point gap
On multi-tool printers without ramming the tool changes away from the
tower and the entry travel is the tcr's own positioning move, which went
straight across the printed wall. Append the avoid-perimeter path to the
change-filament gcode instead, so the head approaches around the tower
and enters through the wall opening (append_tcr parity).
2026-07-28 00:15:59 +08:00
SoftFever 56810c8c7f Port the skip-points gap wall to WipeTower2
prime_tower_skip_points was stubbed for Type2 towers: the wall call
hard-coded skip_points=false, the gap cutter received an empty vector,
and append_tcr2 never routed the entry travel. Now the toolchange entry
positions are precomputed from the finalized plan, the wall is cut open
at each entry, and the entry travel approaches around the tower bounding
box through the opening when it starts outside the tower. The geometry
helpers are re-synced with the BBL versions (add_extra_point guards,
per-point side selection). The cone wall keeps its separate path, where
the option stays inert.

Behavior change: non-BBL towers now honor the (default-on) checkbox with
gap walls and routed entries; with the option off the output is
unchanged, and the BBL tower path is untouched.
2026-07-27 21:18:53 +08:00
SoftFever c3c37e474a Print the WipeTower2 shell with a non-support, non-soluble filament
Like the BBL tower: the layer's sparse infill, wall, and brim go to the
first toolchange to a non-support/non-soluble filament, or are printed
with the incoming filament before any toolchange. The minimal-purge
clamp now also covers toolchanges that get no finish-layer saving.
Output is unchanged when no support/soluble filament is used.
2026-07-27 12:30:57 +08:00
SoftFever 1696d5ca39 Use the squared rib tower size in arrange estimates
estimate_wipe_tower_polygon reserved the arrange footprint and clamped the
tower X position with the raw prime_tower_width, under-reserving space
whenever the rib wall squares the tower to a different width.
2026-07-27 03:08:47 +08:00
SoftFever 466c36eaa3 Complete the rib wipe tower port in WipeTower2
The rib tower is now always square (prime_tower_width is ignored, as the
GUI already implies), carries the rib origin offset like the BBL tower so
the rib tips sit inside the configured position, clamps the rib length to
the tower diagonal, and extends the ribs for short towers.
2026-07-27 03:07:07 +08:00
SoftFever 5792fef805 Merge branch 'main' into feature/update_wipetower 2026-07-27 00:51:29 +08:00
SoftFever bc016af1c9 Fix post-slice self-invalidation on custom multi-extruder printers 2026-07-27 00:49:08 +08:00
SoftFever 7a378d2fc4 Sync WipeTower from BambuStudio(through ca1881761) 2026-07-27 00:48:44 +08:00
87 changed files with 6810 additions and 2331 deletions
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
uses: actions/checkout@v7
- name: Setup Python
uses: actions/setup-python@v6
uses: actions/setup-python@v7
with:
python-version: '3.12'
+4
View File
@@ -119,6 +119,10 @@ Download the **Windows Installer exe** for your preferred version from the [rele
- This file may already be available on your computer if you've installed visual studio. Check the following location: `%VCINSTALLDIR%Redist\MSVC\v142`
</details>
### Microsoft Store
Install from the [Microsoft Store](https://apps.microsoft.com/detail/9mv6gl23xm59) when you prefer a Store-signed package (helps on Windows 11 Smart App Control).
### Windows Package Manager
```shell
+78 -2
View File
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-07-26 21:59-0300\n"
"POT-Creation-Date: 2026-07-29 17:40-0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -3608,6 +3608,9 @@ msgstr ""
msgid "Arranging"
msgstr ""
msgid "Arranging "
msgstr ""
msgid "Arranging canceled."
msgstr ""
@@ -8661,6 +8664,17 @@ msgstr ""
msgid "When scrubbing the layer slider in the sliced preview, render the layers below the current one darkened so that only the layer being viewed is shown at full brightness."
msgstr ""
msgid "Dimmed layer brightness"
msgstr ""
msgid "%"
msgstr ""
msgid ""
"How brightly the dimmed layers are rendered when \"Dim lower layers\" is enabled.\n"
"99% is barely darkened, 0% renders them black. Capped at 99% because 100% would be the same as disabling the option."
msgstr ""
msgid "Login region"
msgstr ""
@@ -12357,12 +12371,26 @@ msgstr ""
msgid "Intra-layer order"
msgstr ""
msgid "Print order within a single layer."
msgid ""
"Order in which object instances are visited within a single layer, which controls how much travel is spent moving between them.\n"
"\n"
"Default: nearest-neighbor chaining, refined with 2-opt and crossing removal. A good general choice.\n"
"As object list: instances are printed in the same order as the object list, without any path optimization. Use it when you need a predictable, manually controlled order.\n"
"Best of all (shortest path): every strategy is evaluated and the shortest one is used. The object instance order is decided once for the whole print, while the ordering of individual islands is decided per layer, so different layers may end up using different strategies. Slightly slower to slice.\n"
"Snake: serpentine row-by-row traversal, refined with 2-opt. Well suited to regular grids of many small parts.\n"
"\n"
"With multiple filaments or tools in the same layer, minimizing tool changes takes priority: objects are grouped by filament first and this setting only orders the instances within each filament group, so the overall sequence may not look like the shortest path across the plate."
msgstr ""
msgid "As object list"
msgstr ""
msgid "Best of all (shortest path)"
msgstr ""
msgid "Snake"
msgstr ""
msgid "Slow printing down for better layer cooling"
msgstr ""
@@ -17218,6 +17246,15 @@ msgid ""
"Please select one that should be used."
msgstr ""
msgid "Auto-scale for nozzle"
msgstr ""
msgid ""
"This model is designed around a 0.4 mm nozzle with a 0.2 mm layer height. \n"
"When the scaling option is enabled (recommended), it dynamically resizes to match your current nozzle diameter and an appropriate layer height, making the test both accurate and easy to read.\n"
"Turn scaling off only if you wish to print the reference model exactly as-is."
msgstr ""
msgid "PA Calibration"
msgstr ""
@@ -17340,6 +17377,12 @@ msgstr ""
msgid "End speed: "
msgstr ""
msgid "Auto-adjust to max volumetric speed"
msgstr ""
msgid "If the end speed would exceed the filament's maximum volumetric speed, automatically lower the layer height (keeping standard values and staying within the machine's limits) to reach it. If even the minimum layer height is not enough, lower the end speed instead."
msgstr ""
msgid ""
"Please input valid values:\n"
"start > 10\n"
@@ -17347,6 +17390,39 @@ msgid ""
"end > start + step"
msgstr ""
#, possible-c-format, possible-boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s), which limits the outer wall to about %.0f mm/s at this line width and layer height.\n"
" Speeds above this will be clamped, so the upper blocks of the tower will not print at the requested speed.\n"
"\n"
"%s"
msgstr ""
#, possible-c-format, possible-boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s) at the default layer height (%.2f mm).\n"
"\n"
"The layer height has been reduced to %.2f mm (a value used by this printer's profiles) so the tower can reach the requested speed."
msgstr ""
#, possible-c-format, possible-boost-format
msgid ""
"Even at the smallest layer height used by this printer's profiles (%.2f mm) the end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s).\n"
"\n"
"The layer height will be set to %.2f mm and the end speed lowered to %.0f mm/s.\n"
"\n"
"Continue?"
msgstr ""
msgid "Continue anyway?"
msgstr ""
msgid "Enable \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr ""
msgid "Enable \"Auto-scale for nozzle\" and \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr ""
msgid "Start retraction length: "
msgstr ""
+123 -6
View File
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-07-26 21:59-0300\n"
"POT-Creation-Date: 2026-07-29 17:40-0300\n"
"PO-Revision-Date: 2025-03-15 10:55+0100\n"
"Last-Translator: \n"
"Language-Team: \n"
@@ -3931,6 +3931,10 @@ msgstr "Organitzant..."
msgid "Arranging"
msgstr "Organitzant"
# AI Translated
msgid "Arranging "
msgstr "Organitzant "
msgid "Arranging canceled."
msgstr "S'ha cancel·lat l'ordenació."
@@ -9350,6 +9354,21 @@ msgstr "Enfosquir les capes inferiors"
msgid "When scrubbing the layer slider in the sliced preview, render the layers below the current one darkened so that only the layer being viewed is shown at full brightness."
msgstr "En desplaçar el control lliscant de capes a la previsualització laminada, mostra enfosquides les capes per sota de l'actual, de manera que només la capa que s'està visualitzant es vegi amb la lluminositat completa."
# AI Translated
msgid "Dimmed layer brightness"
msgstr "Brillantor de les capes enfosquides"
msgid "%"
msgstr "%"
# AI Translated
msgid ""
"How brightly the dimmed layers are rendered when \"Dim lower layers\" is enabled.\n"
"99% is barely darkened, 0% renders them black. Capped at 99% because 100% would be the same as disabling the option."
msgstr ""
"Amb quina brillantor es mostren les capes enfosquides quan \"Enfosquir les capes inferiors\" està activat.\n"
"99% amb prou feines s'enfosqueix, 0% les mostra negres. Limitat al 99% perquè el 100% seria el mateix que desactivar l'opció."
msgid "Login region"
msgstr "Regió d'inici de sessió"
@@ -13494,12 +13513,37 @@ msgstr "Objecte"
msgid "Intra-layer order"
msgstr "Ordre intracapa"
msgid "Print order within a single layer."
msgstr "Ordre d'impressió dins d'una sola capa"
# AI Translated
msgid ""
"Order in which object instances are visited within a single layer, which controls how much travel is spent moving between them.\n"
"\n"
"Default: nearest-neighbor chaining, refined with 2-opt and crossing removal. A good general choice.\n"
"As object list: instances are printed in the same order as the object list, without any path optimization. Use it when you need a predictable, manually controlled order.\n"
"Best of all (shortest path): every strategy is evaluated and the shortest one is used. The object instance order is decided once for the whole print, while the ordering of individual islands is decided per layer, so different layers may end up using different strategies. Slightly slower to slice.\n"
"Snake: serpentine row-by-row traversal, refined with 2-opt. Well suited to regular grids of many small parts.\n"
"\n"
"With multiple filaments or tools in the same layer, minimizing tool changes takes priority: objects are grouped by filament first and this setting only orders the instances within each filament group, so the overall sequence may not look like the shortest path across the plate."
msgstr ""
"Ordre en què es visiten les instàncies dels objectes dins d'una mateixa capa, que determina quant recorregut es dedica a moure's entre elles.\n"
"\n"
"Per defecte: encadenament pel veí més proper, refinat amb 2-opt i eliminació de creuaments. Una bona opció general.\n"
"Com a llista d'objectes: les instàncies s'imprimeixen en el mateix ordre que la llista d'objectes, sense cap optimització del recorregut. Feu-lo servir quan necessiteu un ordre previsible i controlat manualment.\n"
"La millor de totes (camí més curt): s'avaluen totes les estratègies i s'utilitza la més curta. L'ordre de les instàncies dels objectes es decideix una sola vegada per a tota la impressió, mentre que l'ordre de les illes individuals es decideix per capa, de manera que capes diferents poden acabar utilitzant estratègies diferents. El laminat és una mica més lent.\n"
"Serpentí: recorregut en serpentina, fila per fila, refinat amb 2-opt. Adequat per a graelles regulars de moltes peces petites.\n"
"\n"
"Amb diversos filaments o eines a la mateixa capa, minimitzar els canvis d'eina té prioritat: els objectes s'agrupen primer per filament i aquest paràmetre només ordena les instàncies dins de cada grup de filament, de manera que la seqüència global pot no semblar el camí més curt per la safata."
msgid "As object list"
msgstr "Com a llista d'objectes"
# AI Translated
msgid "Best of all (shortest path)"
msgstr "La millor de totes (camí més curt)"
# AI Translated
msgid "Snake"
msgstr "Serpentí"
msgid "Slow printing down for better layer cooling"
msgstr "Reduir la velocitat d'impressió per millorar la refrigeració de les capes"
@@ -18927,6 +18971,20 @@ msgstr ""
"Hi ha diverses adreces IP que responen al nom d'amfitrió( host ) %1%.\n"
"Seleccioneu-ne la que s'hagi d'utilitzar."
# AI Translated
msgid "Auto-scale for nozzle"
msgstr "Escala automàtica segons el broquet"
# AI Translated
msgid ""
"This model is designed around a 0.4 mm nozzle with a 0.2 mm layer height. \n"
"When the scaling option is enabled (recommended), it dynamically resizes to match your current nozzle diameter and an appropriate layer height, making the test both accurate and easy to read.\n"
"Turn scaling off only if you wish to print the reference model exactly as-is."
msgstr ""
"Aquest model està dissenyat per a un broquet de 0,4 mm amb una alçada de capa de 0,2 mm. \n"
"Quan l'opció d'escalat està activada (recomanat), es redimensiona dinàmicament per adaptar-se al diàmetre del broquet actual i a una alçada de capa adequada, cosa que fa que la prova sigui precisa i fàcil de llegir.\n"
"Desactiveu l'escalat només si voleu imprimir el model de referència exactament tal com és."
msgid "PA Calibration"
msgstr "Calibratge PA( Pressure Advance )"
@@ -19063,6 +19121,14 @@ msgstr "Velocitat d'inici: "
msgid "End speed: "
msgstr "Velocitat final: "
# AI Translated
msgid "Auto-adjust to max volumetric speed"
msgstr "Ajust automàtic a la velocitat volumètrica màxima"
# AI Translated
msgid "If the end speed would exceed the filament's maximum volumetric speed, automatically lower the layer height (keeping standard values and staying within the machine's limits) to reach it. If even the minimum layer height is not enough, lower the end speed instead."
msgstr "Si la velocitat final superés la velocitat volumètrica màxima del filament, redueix automàticament l'alçada de capa (mantenint valors estàndard i respectant els límits de la màquina) per assolir-la. Si ni tan sols l'alçada de capa mínima és suficient, redueix la velocitat final."
msgid ""
"Please input valid values:\n"
"start > 10\n"
@@ -19074,6 +19140,57 @@ msgstr ""
"pas >= 0\n"
"final >inici + pas )"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s), which limits the outer wall to about %.0f mm/s at this line width and layer height.\n"
" Speeds above this will be clamped, so the upper blocks of the tower will not print at the requested speed.\n"
"\n"
"%s"
msgstr ""
"La velocitat final (%.0f mm/s) supera la velocitat volumètrica màxima del filament (%.1f mm³/s), cosa que limita el perímetre exterior a uns %.0f mm/s amb aquesta amplada de línia i alçada de capa.\n"
" Les velocitats superiors es retallaran, de manera que els blocs superiors de la torre no s'imprimiran a la velocitat sol·licitada.\n"
"\n"
"%s"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s) at the default layer height (%.2f mm).\n"
"\n"
"The layer height has been reduced to %.2f mm (a value used by this printer's profiles) so the tower can reach the requested speed."
msgstr ""
"La velocitat final (%.0f mm/s) supera la velocitat volumètrica màxima del filament (%.1f mm³/s) amb l'alçada de capa per defecte (%.2f mm).\n"
"\n"
"L'alçada de capa s'ha reduït a %.2f mm (un valor utilitzat pels perfils d'aquesta impressora) perquè la torre pugui assolir la velocitat sol·licitada."
# AI Translated
#, c-format, boost-format
msgid ""
"Even at the smallest layer height used by this printer's profiles (%.2f mm) the end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s).\n"
"\n"
"The layer height will be set to %.2f mm and the end speed lowered to %.0f mm/s.\n"
"\n"
"Continue?"
msgstr ""
"Fins i tot amb l'alçada de capa més petita utilitzada pels perfils d'aquesta impressora (%.2f mm), la velocitat final (%.0f mm/s) supera la velocitat volumètrica màxima del filament (%.1f mm³/s).\n"
"\n"
"L'alçada de capa s'establirà a %.2f mm i la velocitat final es reduirà a %.0f mm/s.\n"
"\n"
"Voleu continuar?"
# AI Translated
msgid "Continue anyway?"
msgstr "Voleu continuar igualment?"
# AI Translated
msgid "Enable \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "Voleu activar \"Ajust automàtic\" per corregir-ho automàticament o continuar igualment?"
# AI Translated
msgid "Enable \"Auto-scale for nozzle\" and \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "Voleu activar \"Escala automàtica segons el broquet\" i \"Ajust automàtic\" per corregir-ho automàticament o continuar igualment?"
msgid "Start retraction length: "
msgstr "Longitud de la retracció d'inici: "
@@ -21770,6 +21887,9 @@ msgstr ""
"Evitar la deformació( warping )\n"
"Sabíeu que quan imprimiu materials propensos a deformar-se, com ara l'ABS, augmentar adequadament la temperatura del llit pot reduir la probabilitat de deformació?"
#~ msgid "Print order within a single layer."
#~ msgstr "Ordre d'impressió dins d'una sola capa"
#~ msgid "Bottom"
#~ msgstr "Inferior"
@@ -21856,9 +21976,6 @@ msgstr ""
#~ msgid "°C"
#~ msgstr "°C"
#~ msgid "%"
#~ msgstr "%"
#~ msgid "Continue to sync filaments"
#~ msgstr "Continua sincronitzant filaments"
+123 -6
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-07-26 21:59-0300\n"
"POT-Creation-Date: 2026-07-29 17:40-0300\n"
"PO-Revision-Date: \n"
"Last-Translator: Jakub Hencl\n"
"Language-Team: \n"
@@ -3888,6 +3888,10 @@ msgstr "Uspořádání..."
msgid "Arranging"
msgstr "Uspořádání"
# AI Translated
msgid "Arranging "
msgstr "Rozkládání "
msgid "Arranging canceled."
msgstr "Uspořádání zrušeno."
@@ -9305,6 +9309,21 @@ msgstr "Ztmavit nižší vrstvy"
msgid "When scrubbing the layer slider in the sliced preview, render the layers below the current one darkened so that only the layer being viewed is shown at full brightness."
msgstr "Při posouvání posuvníku vrstev v náhledu po slicování vykresluje vrstvy pod aktuální ztmavené, takže v plném jasu je zobrazena pouze prohlížená vrstva."
# AI Translated
msgid "Dimmed layer brightness"
msgstr "Jas ztmavených vrstev"
msgid "%"
msgstr "%"
# AI Translated
msgid ""
"How brightly the dimmed layers are rendered when \"Dim lower layers\" is enabled.\n"
"99% is barely darkened, 0% renders them black. Capped at 99% because 100% would be the same as disabling the option."
msgstr ""
"Jak jasně se vykreslují ztmavené vrstvy, když je zapnuta volba „Ztmavit nižší vrstvy“.\n"
"99 % znamená sotva znatelné ztmavení, 0 % je vykreslí černě. Maximum je 99 %, protože 100 % by odpovídalo vypnutí této volby."
msgid "Login region"
msgstr "Oblast přihlášení"
@@ -13475,12 +13494,37 @@ msgstr "Podle objektu"
msgid "Intra-layer order"
msgstr "Pořadí v rámci vrstvy"
msgid "Print order within a single layer."
msgstr "Pořadí tisku v rámci jedné vrstvy."
# AI Translated
msgid ""
"Order in which object instances are visited within a single layer, which controls how much travel is spent moving between them.\n"
"\n"
"Default: nearest-neighbor chaining, refined with 2-opt and crossing removal. A good general choice.\n"
"As object list: instances are printed in the same order as the object list, without any path optimization. Use it when you need a predictable, manually controlled order.\n"
"Best of all (shortest path): every strategy is evaluated and the shortest one is used. The object instance order is decided once for the whole print, while the ordering of individual islands is decided per layer, so different layers may end up using different strategies. Slightly slower to slice.\n"
"Snake: serpentine row-by-row traversal, refined with 2-opt. Well suited to regular grids of many small parts.\n"
"\n"
"With multiple filaments or tools in the same layer, minimizing tool changes takes priority: objects are grouped by filament first and this setting only orders the instances within each filament group, so the overall sequence may not look like the shortest path across the plate."
msgstr ""
"Pořadí, v jakém se v rámci jedné vrstvy navštěvují instance objektů; určuje, kolik přejezdů se stráví přesuny mezi nimi.\n"
"\n"
"Výchozí: řetězení metodou nejbližšího souseda, doladěné algoritmem 2-opt a odstraněním křížení. Dobrá volba pro obecné použití.\n"
"Jako seznam objektů: instance se tisknou ve stejném pořadí jako v seznamu objektů, bez jakékoli optimalizace dráhy. Použijte, když potřebujete předvídatelné, ručně řízené pořadí.\n"
"Nejlepší ze všech (nejkratší dráha): vyhodnotí se všechny strategie a použije se ta nejkratší. Pořadí instancí objektů se určí jednou pro celý tisk, zatímco pořadí jednotlivých ostrůvků se určuje pro každou vrstvu zvlášť, takže různé vrstvy mohou nakonec používat různé strategie. Slicování je mírně pomalejší.\n"
"Hadovitě: klikaté procházení řádek po řádku, doladěné algoritmem 2-opt. Vhodné pro pravidelné mřížky mnoha malých dílů.\n"
"\n"
"Pokud je v téže vrstvě více filamentů nebo nástrojů, má přednost minimalizace výměn nástroje: objekty se nejprve seskupí podle filamentu a toto nastavení pak řadí pouze instance uvnitř každé skupiny, takže celková posloupnost nemusí vypadat jako nejkratší dráha po desce."
msgid "As object list"
msgstr "Jako seznam objektů"
# AI Translated
msgid "Best of all (shortest path)"
msgstr "Nejlepší ze všech (nejkratší dráha)"
# AI Translated
msgid "Snake"
msgstr "Hadovitě"
msgid "Slow printing down for better layer cooling"
msgstr "Zpomalit tisk pro lepší chlazení vrstvy"
@@ -18850,6 +18894,20 @@ msgstr ""
"Ke jménu hostitele %1% je přiřazeno několik IP adres.\n"
"Vyberte prosím jednu, která má být použita."
# AI Translated
msgid "Auto-scale for nozzle"
msgstr "Automatické měřítko podle trysky"
# AI Translated
msgid ""
"This model is designed around a 0.4 mm nozzle with a 0.2 mm layer height. \n"
"When the scaling option is enabled (recommended), it dynamically resizes to match your current nozzle diameter and an appropriate layer height, making the test both accurate and easy to read.\n"
"Turn scaling off only if you wish to print the reference model exactly as-is."
msgstr ""
"Tento model je navržen pro trysku 0,4 mm a výšku vrstvy 0,2 mm. \n"
"Je-li volba změny měřítka zapnuta (doporučeno), model se dynamicky přizpůsobí průměru vaší současné trysky a odpovídající výšce vrstvy, takže je test přesný a dobře čitelný.\n"
"Měřítko vypněte pouze tehdy, chcete-li vytisknout referenční model přesně tak, jak je."
msgid "PA Calibration"
msgstr "PA kalibrace"
@@ -18988,6 +19046,14 @@ msgstr "Počáteční rychlost: "
msgid "End speed: "
msgstr "Konec rychlosti: "
# AI Translated
msgid "Auto-adjust to max volumetric speed"
msgstr "Automaticky přizpůsobit maximální objemové rychlosti"
# AI Translated
msgid "If the end speed would exceed the filament's maximum volumetric speed, automatically lower the layer height (keeping standard values and staying within the machine's limits) to reach it. If even the minimum layer height is not enough, lower the end speed instead."
msgstr "Pokud by koncová rychlost překročila maximální objemovou rychlost filamentu, automaticky snížit výšku vrstvy (se zachováním standardních hodnot a v rámci limitů stroje), aby jí bylo možné dosáhnout. Pokud nestačí ani minimální výška vrstvy, sníží se místo toho koncová rychlost."
msgid ""
"Please input valid values:\n"
"start > 10\n"
@@ -18999,6 +19065,57 @@ msgstr ""
"krok >= 0\n"
"konec > start + krok"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s), which limits the outer wall to about %.0f mm/s at this line width and layer height.\n"
" Speeds above this will be clamped, so the upper blocks of the tower will not print at the requested speed.\n"
"\n"
"%s"
msgstr ""
"Koncová rychlost (%.0f mm/s) překračuje maximální objemovou rychlost filamentu (%.1f mm³/s), která při této šířce čáry a výšce vrstvy omezuje vnější stěnu na přibližně %.0f mm/s.\n"
" Vyšší rychlosti budou oříznuty, takže horní bloky věže se nevytisknou požadovanou rychlostí.\n"
"\n"
"%s"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s) at the default layer height (%.2f mm).\n"
"\n"
"The layer height has been reduced to %.2f mm (a value used by this printer's profiles) so the tower can reach the requested speed."
msgstr ""
"Koncová rychlost (%.0f mm/s) překračuje maximální objemovou rychlost filamentu (%.1f mm³/s) při výchozí výšce vrstvy (%.2f mm).\n"
"\n"
"Výška vrstvy byla snížena na %.2f mm (hodnota používaná profily této tiskárny), aby věž mohla dosáhnout požadované rychlosti."
# AI Translated
#, c-format, boost-format
msgid ""
"Even at the smallest layer height used by this printer's profiles (%.2f mm) the end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s).\n"
"\n"
"The layer height will be set to %.2f mm and the end speed lowered to %.0f mm/s.\n"
"\n"
"Continue?"
msgstr ""
"I při nejmenší výšce vrstvy používané profily této tiskárny (%.2f mm) koncová rychlost (%.0f mm/s) překračuje maximální objemovou rychlost filamentu (%.1f mm³/s).\n"
"\n"
"Výška vrstvy bude nastavena na %.2f mm a koncová rychlost snížena na %.0f mm/s.\n"
"\n"
"Pokračovat?"
# AI Translated
msgid "Continue anyway?"
msgstr "Přesto pokračovat?"
# AI Translated
msgid "Enable \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "Zapnout „Automaticky přizpůsobit“ pro automatickou nápravu, nebo přesto pokračovat?"
# AI Translated
msgid "Enable \"Auto-scale for nozzle\" and \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "Zapnout „Automatické měřítko podle trysky“ a „Automaticky přizpůsobit“ pro automatickou nápravu, nebo přesto pokračovat?"
msgid "Start retraction length: "
msgstr "Počáteční délka retrakce: "
@@ -21756,6 +21873,9 @@ msgstr ""
"Zamezte kroucení\n"
"Víte, že při tisku materiálů náchylných ke kroucení, jako je ABS, může vhodné zvýšení teploty vyhřívané desky snížit pravděpodobnost kroucení?"
#~ msgid "Print order within a single layer."
#~ msgstr "Pořadí tisku v rámci jedné vrstvy."
#~ msgid "Bottom"
#~ msgstr "Dole"
@@ -21824,9 +21944,6 @@ msgstr ""
#~ msgid "°C"
#~ msgstr "°C"
#~ msgid "%"
#~ msgstr "%"
#~ msgid "Align infill direction to model"
#~ msgstr "Zarovnat směr výplně podle modelu"
+123 -6
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-07-26 21:59-0300\n"
"POT-Creation-Date: 2026-07-29 17:40-0300\n"
"PO-Revision-Date: \n"
"Last-Translator: Heiko Liebscher <hliebschergmail.com>\n"
"Language-Team: \n"
@@ -3804,6 +3804,10 @@ msgstr "Anordnen..."
msgid "Arranging"
msgstr "Anordnen"
# AI Translated
msgid "Arranging "
msgstr "Anordnen "
msgid "Arranging canceled."
msgstr "Anordnen abgebrochen."
@@ -9152,6 +9156,21 @@ msgstr "Untere Schichten abdunkeln"
msgid "When scrubbing the layer slider in the sliced preview, render the layers below the current one darkened so that only the layer being viewed is shown at full brightness."
msgstr "Beim Bewegen des Schichtreglers in der geslicten Vorschau werden die Schichten unterhalb der aktuellen abgedunkelt dargestellt, sodass nur die betrachtete Schicht in voller Helligkeit angezeigt wird."
# AI Translated
msgid "Dimmed layer brightness"
msgstr "Helligkeit abgedunkelter Schichten"
msgid "%"
msgstr "%"
# AI Translated
msgid ""
"How brightly the dimmed layers are rendered when \"Dim lower layers\" is enabled.\n"
"99% is barely darkened, 0% renders them black. Capped at 99% because 100% would be the same as disabling the option."
msgstr ""
"Wie hell die abgedunkelten Schichten dargestellt werden, wenn \"Untere Schichten abdunkeln\" aktiviert ist.\n"
"99% ist kaum abgedunkelt, 0% stellt sie schwarz dar. Auf 99% begrenzt, da 100% dem Deaktivieren der Option entsprechen würde."
msgid "Login region"
msgstr "Anmeldungsregion"
@@ -13180,12 +13199,37 @@ msgstr "Nach Objekt"
msgid "Intra-layer order"
msgstr "Intra-Schicht-Reihenfolge"
msgid "Print order within a single layer."
msgstr "Druckreihenfolge innerhalb einer einzelnen Schicht"
# AI Translated
msgid ""
"Order in which object instances are visited within a single layer, which controls how much travel is spent moving between them.\n"
"\n"
"Default: nearest-neighbor chaining, refined with 2-opt and crossing removal. A good general choice.\n"
"As object list: instances are printed in the same order as the object list, without any path optimization. Use it when you need a predictable, manually controlled order.\n"
"Best of all (shortest path): every strategy is evaluated and the shortest one is used. The object instance order is decided once for the whole print, while the ordering of individual islands is decided per layer, so different layers may end up using different strategies. Slightly slower to slice.\n"
"Snake: serpentine row-by-row traversal, refined with 2-opt. Well suited to regular grids of many small parts.\n"
"\n"
"With multiple filaments or tools in the same layer, minimizing tool changes takes priority: objects are grouped by filament first and this setting only orders the instances within each filament group, so the overall sequence may not look like the shortest path across the plate."
msgstr ""
"Reihenfolge, in der die Objektinstanzen innerhalb einer einzelnen Schicht angefahren werden. Sie bestimmt, wie viel Eilgang für die Bewegung zwischen ihnen aufgewendet wird.\n"
"\n"
"Standard: Verkettung nach dem nächsten Nachbarn, verfeinert mit 2-opt und Entfernen von Überkreuzungen. Eine gute allgemeine Wahl.\n"
"Als Objektliste: Die Instanzen werden in derselben Reihenfolge wie in der Objektliste gedruckt, ohne jede Pfadoptimierung. Verwenden Sie diese Option, wenn Sie eine vorhersehbare, manuell gesteuerte Reihenfolge benötigen.\n"
"Bestes Ergebnis (kürzester Weg): Alle Strategien werden ausgewertet und die kürzeste wird verwendet. Die Reihenfolge der Objektinstanzen wird einmal für den gesamten Druck festgelegt, während die Reihenfolge der einzelnen Inseln pro Schicht bestimmt wird, sodass verschiedene Schichten unterschiedliche Strategien verwenden können. Etwas langsameres Slicing.\n"
"Schlangenlinie: Zeilenweiser Mäanderverlauf, verfeinert mit 2-opt. Gut geeignet für regelmäßige Raster aus vielen kleinen Teilen.\n"
"\n"
"Bei mehreren Filamenten oder Werkzeugen in derselben Schicht hat das Minimieren der Werkzeugwechsel Vorrang: Objekte werden zuerst nach Filament gruppiert und diese Einstellung ordnet nur die Instanzen innerhalb jeder Filamentgruppe. Die Gesamtabfolge sieht daher möglicherweise nicht wie der kürzeste Weg über die Platte aus."
msgid "As object list"
msgstr "Als Objektliste"
# AI Translated
msgid "Best of all (shortest path)"
msgstr "Bestes Ergebnis (kürzester Weg)"
# AI Translated
msgid "Snake"
msgstr "Schlangenlinie"
msgid "Slow printing down for better layer cooling"
msgstr "Verlangsamen Sie den Druck für eine bessere Schichtkühlung"
@@ -18487,6 +18531,20 @@ msgstr ""
"Es gibt mehrere IP-Adressen, die zu Hostname %1% auflösen.\n"
"Bitte wählen Sie eine aus, die verwendet werden soll."
# AI Translated
msgid "Auto-scale for nozzle"
msgstr "Automatisch an Düse skalieren"
# AI Translated
msgid ""
"This model is designed around a 0.4 mm nozzle with a 0.2 mm layer height. \n"
"When the scaling option is enabled (recommended), it dynamically resizes to match your current nozzle diameter and an appropriate layer height, making the test both accurate and easy to read.\n"
"Turn scaling off only if you wish to print the reference model exactly as-is."
msgstr ""
"Dieses Modell ist für eine 0,4 mm Düse mit einer Schichthöhe von 0,2 mm ausgelegt. \n"
"Wenn die Skalierungsoption aktiviert ist (empfohlen), wird das Modell dynamisch an Ihren aktuellen Düsendurchmesser und eine passende Schichthöhe angepasst, sodass der Test sowohl genau als auch gut ablesbar ist.\n"
"Deaktivieren Sie die Skalierung nur, wenn Sie das Referenzmodell exakt so drucken möchten, wie es ist."
msgid "PA Calibration"
msgstr "PA Kalibrierung"
@@ -18623,6 +18681,14 @@ msgstr "Startgeschwindigkeit"
msgid "End speed: "
msgstr "Endgeschwindigkeit"
# AI Translated
msgid "Auto-adjust to max volumetric speed"
msgstr "Automatisch an maximale Volumengeschwindigkeit anpassen"
# AI Translated
msgid "If the end speed would exceed the filament's maximum volumetric speed, automatically lower the layer height (keeping standard values and staying within the machine's limits) to reach it. If even the minimum layer height is not enough, lower the end speed instead."
msgstr "Wenn die Endgeschwindigkeit die maximale Volumengeschwindigkeit des Filaments überschreiten würde, wird die Schichthöhe automatisch verringert (unter Beibehaltung üblicher Werte und innerhalb der Grenzen der Maschine), um sie zu erreichen. Reicht selbst die minimale Schichthöhe nicht aus, wird stattdessen die Endgeschwindigkeit gesenkt."
msgid ""
"Please input valid values:\n"
"start > 10\n"
@@ -18634,6 +18700,57 @@ msgstr ""
"Schritt >= 0\n"
"Ende > Start + Schritt"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s), which limits the outer wall to about %.0f mm/s at this line width and layer height.\n"
" Speeds above this will be clamped, so the upper blocks of the tower will not print at the requested speed.\n"
"\n"
"%s"
msgstr ""
"Die Endgeschwindigkeit (%.0f mm/s) überschreitet die maximale Volumengeschwindigkeit des Filaments (%.1f mm³/s), wodurch die Außenwand bei dieser Linienbreite und Schichthöhe auf etwa %.0f mm/s begrenzt wird.\n"
" Höhere Geschwindigkeiten werden begrenzt, sodass die oberen Blöcke des Turms nicht mit der gewünschten Geschwindigkeit gedruckt werden.\n"
"\n"
"%s"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s) at the default layer height (%.2f mm).\n"
"\n"
"The layer height has been reduced to %.2f mm (a value used by this printer's profiles) so the tower can reach the requested speed."
msgstr ""
"Die Endgeschwindigkeit (%.0f mm/s) überschreitet die maximale Volumengeschwindigkeit des Filaments (%.1f mm³/s) bei der Standard-Schichthöhe (%.2f mm).\n"
"\n"
"Die Schichthöhe wurde auf %.2f mm verringert (ein Wert, der in den Profilen dieses Druckers verwendet wird), damit der Turm die gewünschte Geschwindigkeit erreichen kann."
# AI Translated
#, c-format, boost-format
msgid ""
"Even at the smallest layer height used by this printer's profiles (%.2f mm) the end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s).\n"
"\n"
"The layer height will be set to %.2f mm and the end speed lowered to %.0f mm/s.\n"
"\n"
"Continue?"
msgstr ""
"Selbst bei der kleinsten Schichthöhe, die in den Profilen dieses Druckers verwendet wird (%.2f mm), überschreitet die Endgeschwindigkeit (%.0f mm/s) die maximale Volumengeschwindigkeit des Filaments (%.1f mm³/s).\n"
"\n"
"Die Schichthöhe wird auf %.2f mm gesetzt und die Endgeschwindigkeit auf %.0f mm/s verringert.\n"
"\n"
"Fortfahren?"
# AI Translated
msgid "Continue anyway?"
msgstr "Trotzdem fortfahren?"
# AI Translated
msgid "Enable \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "\"Automatisch anpassen\" aktivieren, um dies automatisch zu beheben, oder trotzdem fortfahren?"
# AI Translated
msgid "Enable \"Auto-scale for nozzle\" and \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "\"Automatisch an Düse skalieren\" und \"Automatisch anpassen\" aktivieren, um dies automatisch zu beheben, oder trotzdem fortfahren?"
msgid "Start retraction length: "
msgstr "Start Rückzugslänge"
@@ -21169,6 +21286,9 @@ msgstr ""
"Verwerfungen vermeiden\n"
"Wussten Sie, dass beim Drucken von Materialien, die zu Verwerfungen neigen, wie z.B. ABS, durch eine entsprechende Erhöhung der Heizbetttemperatur die Wahrscheinlichkeit von Verwerfungen verringert werden kann?"
#~ msgid "Print order within a single layer."
#~ msgstr "Druckreihenfolge innerhalb einer einzelnen Schicht"
#~ msgid "Bottom"
#~ msgstr "Unten"
@@ -21261,9 +21381,6 @@ msgstr ""
#~ msgid "°C"
#~ msgstr "°C"
#~ msgid "%"
#~ msgstr "%"
#~ msgid "Renders cast shadows on the plate in realistic view."
#~ msgstr "Zeigt geworfene Schatten auf der Platte in der realistischen Ansicht an."
+78 -2
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-07-26 21:59-0300\n"
"POT-Creation-Date: 2026-07-29 17:40-0300\n"
"PO-Revision-Date: 2026-06-17 15:44-0300\n"
"Last-Translator: Alexandre Folle de Menezes\n"
"Language-Team: \n"
@@ -3604,6 +3604,9 @@ msgstr ""
msgid "Arranging"
msgstr ""
msgid "Arranging "
msgstr ""
msgid "Arranging canceled."
msgstr ""
@@ -8657,6 +8660,17 @@ msgstr ""
msgid "When scrubbing the layer slider in the sliced preview, render the layers below the current one darkened so that only the layer being viewed is shown at full brightness."
msgstr ""
msgid "Dimmed layer brightness"
msgstr ""
msgid "%"
msgstr ""
msgid ""
"How brightly the dimmed layers are rendered when \"Dim lower layers\" is enabled.\n"
"99% is barely darkened, 0% renders them black. Capped at 99% because 100% would be the same as disabling the option."
msgstr ""
msgid "Login region"
msgstr ""
@@ -12353,12 +12367,26 @@ msgstr ""
msgid "Intra-layer order"
msgstr ""
msgid "Print order within a single layer."
msgid ""
"Order in which object instances are visited within a single layer, which controls how much travel is spent moving between them.\n"
"\n"
"Default: nearest-neighbor chaining, refined with 2-opt and crossing removal. A good general choice.\n"
"As object list: instances are printed in the same order as the object list, without any path optimization. Use it when you need a predictable, manually controlled order.\n"
"Best of all (shortest path): every strategy is evaluated and the shortest one is used. The object instance order is decided once for the whole print, while the ordering of individual islands is decided per layer, so different layers may end up using different strategies. Slightly slower to slice.\n"
"Snake: serpentine row-by-row traversal, refined with 2-opt. Well suited to regular grids of many small parts.\n"
"\n"
"With multiple filaments or tools in the same layer, minimizing tool changes takes priority: objects are grouped by filament first and this setting only orders the instances within each filament group, so the overall sequence may not look like the shortest path across the plate."
msgstr ""
msgid "As object list"
msgstr ""
msgid "Best of all (shortest path)"
msgstr ""
msgid "Snake"
msgstr ""
msgid "Slow printing down for better layer cooling"
msgstr ""
@@ -17214,6 +17242,15 @@ msgid ""
"Please select one that should be used."
msgstr ""
msgid "Auto-scale for nozzle"
msgstr ""
msgid ""
"This model is designed around a 0.4 mm nozzle with a 0.2 mm layer height. \n"
"When the scaling option is enabled (recommended), it dynamically resizes to match your current nozzle diameter and an appropriate layer height, making the test both accurate and easy to read.\n"
"Turn scaling off only if you wish to print the reference model exactly as-is."
msgstr ""
msgid "PA Calibration"
msgstr ""
@@ -17336,6 +17373,12 @@ msgstr ""
msgid "End speed: "
msgstr ""
msgid "Auto-adjust to max volumetric speed"
msgstr ""
msgid "If the end speed would exceed the filament's maximum volumetric speed, automatically lower the layer height (keeping standard values and staying within the machine's limits) to reach it. If even the minimum layer height is not enough, lower the end speed instead."
msgstr ""
msgid ""
"Please input valid values:\n"
"start > 10\n"
@@ -17343,6 +17386,39 @@ msgid ""
"end > start + step"
msgstr ""
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s), which limits the outer wall to about %.0f mm/s at this line width and layer height.\n"
" Speeds above this will be clamped, so the upper blocks of the tower will not print at the requested speed.\n"
"\n"
"%s"
msgstr ""
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s) at the default layer height (%.2f mm).\n"
"\n"
"The layer height has been reduced to %.2f mm (a value used by this printer's profiles) so the tower can reach the requested speed."
msgstr ""
#, c-format, boost-format
msgid ""
"Even at the smallest layer height used by this printer's profiles (%.2f mm) the end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s).\n"
"\n"
"The layer height will be set to %.2f mm and the end speed lowered to %.0f mm/s.\n"
"\n"
"Continue?"
msgstr ""
msgid "Continue anyway?"
msgstr ""
msgid "Enable \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr ""
msgid "Enable \"Auto-scale for nozzle\" and \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr ""
msgid "Start retraction length: "
msgstr ""
+123 -6
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-07-26 21:59-0300\n"
"POT-Creation-Date: 2026-07-29 17:40-0300\n"
"PO-Revision-Date: \n"
"Last-Translator: Ian A. Bassi <>\n"
"Language-Team: \n"
@@ -3686,6 +3686,10 @@ msgstr "Organizando..."
msgid "Arranging"
msgstr "Organizando"
# AI Translated
msgid "Arranging "
msgstr "Organizando "
msgid "Arranging canceled."
msgstr "Organización cancelada."
@@ -8931,6 +8935,21 @@ msgstr "Atenuar las capas inferiores"
msgid "When scrubbing the layer slider in the sliced preview, render the layers below the current one darkened so that only the layer being viewed is shown at full brightness."
msgstr "Al desplazar el control deslizante de capas en la vista previa laminada, oscurece las capas inferiores a la actual para que solo la capa visualizada se muestre a pleno brillo."
# AI Translated
msgid "Dimmed layer brightness"
msgstr "Brillo de las capas atenuadas"
msgid "%"
msgstr "%"
# AI Translated
msgid ""
"How brightly the dimmed layers are rendered when \"Dim lower layers\" is enabled.\n"
"99% is barely darkened, 0% renders them black. Capped at 99% because 100% would be the same as disabling the option."
msgstr ""
"Con qué brillo se muestran las capas atenuadas cuando \"Atenuar las capas inferiores\" está activado.\n"
"99% apenas se oscurece y 0% las muestra en negro. Está limitado al 99% porque el 100% equivaldría a desactivar la opción."
msgid "Login region"
msgstr "Región de inicio de sesión"
@@ -12881,12 +12900,37 @@ msgstr "Por objeto"
msgid "Intra-layer order"
msgstr "Orden dentro de la capa"
msgid "Print order within a single layer."
msgstr "Orden de impresión dentro de cada capa."
# AI Translated
msgid ""
"Order in which object instances are visited within a single layer, which controls how much travel is spent moving between them.\n"
"\n"
"Default: nearest-neighbor chaining, refined with 2-opt and crossing removal. A good general choice.\n"
"As object list: instances are printed in the same order as the object list, without any path optimization. Use it when you need a predictable, manually controlled order.\n"
"Best of all (shortest path): every strategy is evaluated and the shortest one is used. The object instance order is decided once for the whole print, while the ordering of individual islands is decided per layer, so different layers may end up using different strategies. Slightly slower to slice.\n"
"Snake: serpentine row-by-row traversal, refined with 2-opt. Well suited to regular grids of many small parts.\n"
"\n"
"With multiple filaments or tools in the same layer, minimizing tool changes takes priority: objects are grouped by filament first and this setting only orders the instances within each filament group, so the overall sequence may not look like the shortest path across the plate."
msgstr ""
"Orden en el que se recorren las instancias de objeto dentro de una misma capa, lo que determina cuánto desplazamiento se emplea para moverse entre ellas.\n"
"\n"
"Por defecto: encadenado por vecino más cercano, refinado con 2-opt y eliminación de cruces. Una buena opción general.\n"
"Como lista de objetos: las instancias se imprimen en el mismo orden que la lista de objetos, sin ninguna optimización de trayectoria. Úselo cuando necesite un orden predecible y controlado manualmente.\n"
"La mejor de todas (trayectoria más corta): se evalúan todas las estrategias y se utiliza la más corta. El orden de las instancias de objeto se decide una sola vez para toda la impresión, mientras que el orden de las islas individuales se decide capa por capa, por lo que distintas capas pueden acabar usando estrategias diferentes. El laminado es algo más lento.\n"
"Serpentina: recorrido serpenteante fila por fila, refinado con 2-opt. Muy adecuado para rejillas regulares de muchas piezas pequeñas.\n"
"\n"
"Con varios filamentos o herramientas en la misma capa, minimizar los cambios de herramienta tiene prioridad: los objetos se agrupan primero por filamento y este ajuste solo ordena las instancias dentro de cada grupo de filamento, por lo que la secuencia global puede no parecer la trayectoria más corta a lo largo de la cama."
msgid "As object list"
msgstr "Como lista de objetos"
# AI Translated
msgid "Best of all (shortest path)"
msgstr "La mejor de todas (trayectoria más corta)"
# AI Translated
msgid "Snake"
msgstr "Serpentina"
msgid "Slow printing down for better layer cooling"
msgstr "Reducir la velocidad de impresión para mejorar la refrigeración de las capas"
@@ -18122,6 +18166,20 @@ msgstr ""
"Hay varias direcciones IP resueltas del nombre del host %1%.\n"
"Por favor, seleccione la que debe usarse."
# AI Translated
msgid "Auto-scale for nozzle"
msgstr "Escalar automáticamente para la boquilla"
# AI Translated
msgid ""
"This model is designed around a 0.4 mm nozzle with a 0.2 mm layer height. \n"
"When the scaling option is enabled (recommended), it dynamically resizes to match your current nozzle diameter and an appropriate layer height, making the test both accurate and easy to read.\n"
"Turn scaling off only if you wish to print the reference model exactly as-is."
msgstr ""
"Este modelo está diseñado para una boquilla de 0,4 mm con una altura de la capa de 0,2 mm. \n"
"Cuando la opción de escalado está activada (recomendado), el modelo se redimensiona dinámicamente para adaptarse al diámetro de boquilla actual y a una altura de capa adecuada, lo que hace que la prueba sea precisa y fácil de leer.\n"
"Desactive el escalado solo si desea imprimir el modelo de referencia exactamente tal cual."
msgid "PA Calibration"
msgstr "Calibración PA"
@@ -18258,6 +18316,14 @@ msgstr "Velocidad inicial: "
msgid "End speed: "
msgstr "Velocidad final: "
# AI Translated
msgid "Auto-adjust to max volumetric speed"
msgstr "Ajustar automáticamente a la velocidad volumétrica máxima"
# AI Translated
msgid "If the end speed would exceed the filament's maximum volumetric speed, automatically lower the layer height (keeping standard values and staying within the machine's limits) to reach it. If even the minimum layer height is not enough, lower the end speed instead."
msgstr "Si la velocidad final superara la velocidad volumétrica máxima del filamento, se reduce automáticamente la altura de la capa (manteniendo valores estándar y dentro de los límites de la máquina) para alcanzarla. Si ni siquiera la altura de capa mínima es suficiente, se reduce la velocidad final."
msgid ""
"Please input valid values:\n"
"start > 10\n"
@@ -18269,6 +18335,57 @@ msgstr ""
"incremento >=0\n"
"final > inicio + paso"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s), which limits the outer wall to about %.0f mm/s at this line width and layer height.\n"
" Speeds above this will be clamped, so the upper blocks of the tower will not print at the requested speed.\n"
"\n"
"%s"
msgstr ""
"La velocidad final (%.0f mm/s) supera la velocidad volumétrica máxima del filamento (%.1f mm³/s), lo que limita el perímetro externo a unos %.0f mm/s con este ancho de línea y esta altura de capa.\n"
" Las velocidades superiores se recortarán, por lo que los bloques superiores de la torre no se imprimirán a la velocidad solicitada.\n"
"\n"
"%s"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s) at the default layer height (%.2f mm).\n"
"\n"
"The layer height has been reduced to %.2f mm (a value used by this printer's profiles) so the tower can reach the requested speed."
msgstr ""
"La velocidad final (%.0f mm/s) supera la velocidad volumétrica máxima del filamento (%.1f mm³/s) con la altura de capa predeterminada (%.2f mm).\n"
"\n"
"La altura de la capa se ha reducido a %.2f mm (un valor usado por los perfiles de esta impresora) para que la torre pueda alcanzar la velocidad solicitada."
# AI Translated
#, c-format, boost-format
msgid ""
"Even at the smallest layer height used by this printer's profiles (%.2f mm) the end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s).\n"
"\n"
"The layer height will be set to %.2f mm and the end speed lowered to %.0f mm/s.\n"
"\n"
"Continue?"
msgstr ""
"Incluso con la altura de capa más pequeña usada por los perfiles de esta impresora (%.2f mm), la velocidad final (%.0f mm/s) supera la velocidad volumétrica máxima del filamento (%.1f mm³/s).\n"
"\n"
"La altura de la capa se establecerá en %.2f mm y la velocidad final se reducirá a %.0f mm/s.\n"
"\n"
"¿Continuar?"
# AI Translated
msgid "Continue anyway?"
msgstr "¿Continuar de todos modos?"
# AI Translated
msgid "Enable \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "¿Activar \"Ajuste automático\" para corregir esto automáticamente o continuar de todos modos?"
# AI Translated
msgid "Enable \"Auto-scale for nozzle\" and \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "¿Activar \"Escalar automáticamente para la boquilla\" y \"Ajuste automático\" para corregir esto automáticamente o continuar de todos modos?"
msgid "Start retraction length: "
msgstr "Longitud de retracción inicial: "
@@ -20744,6 +20861,9 @@ msgstr ""
"Evita la deformación\n"
"¿Sabías que al imprimir materiales propensos a la deformación como el ABS, aumentar adecuadamente la temperatura de la cama térmica puede reducir la probabilidad de deformaciones?"
#~ msgid "Print order within a single layer."
#~ msgstr "Orden de impresión dentro de cada capa."
#~ msgid "Bottom"
#~ msgstr "Inferior"
@@ -20837,9 +20957,6 @@ msgstr ""
#~ msgid "°C"
#~ msgstr "°C"
#~ msgid "%"
#~ msgstr "%"
#~ msgid "Anisotropic surfaces"
#~ msgstr "Superficies anisótropas"
+123 -6
View File
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-07-26 21:59-0300\n"
"POT-Creation-Date: 2026-07-29 17:40-0300\n"
"PO-Revision-Date: 2026-07-20 13:33+0200\n"
"Last-Translator: Manu Goiogana <mgoiogana@gmail.com>\n"
"Language-Team: \n"
@@ -3724,6 +3724,10 @@ msgstr "Antolatzen..."
msgid "Arranging"
msgstr "Antolaketa"
# AI Translated
msgid "Arranging "
msgstr "Antolatzen "
msgid "Arranging canceled."
msgstr "Antolaketa bertan behera utzi da."
@@ -8999,6 +9003,21 @@ msgstr "Ilundu beheko geruzak"
msgid "When scrubbing the layer slider in the sliced preview, render the layers below the current one darkened so that only the layer being viewed is shown at full brightness."
msgstr "Xerratutako aurrebistan geruza-graduatzailea mugitzean, unekoaren azpiko geruzak ilunduta erakusten ditu, ikusten ari den geruza soilik distira osoz ager dadin."
# AI Translated
msgid "Dimmed layer brightness"
msgstr "Ilundutako geruzen distira"
msgid "%"
msgstr "%"
# AI Translated
msgid ""
"How brightly the dimmed layers are rendered when \"Dim lower layers\" is enabled.\n"
"99% is barely darkened, 0% renders them black. Capped at 99% because 100% would be the same as disabling the option."
msgstr ""
"\"Ilundu beheko geruzak\" gaituta dagoenean ilundutako geruzak zein distiratsu marrazten diren.\n"
"%99 balioak ozta-ozta iluntzen ditu, %0 balioak beltz bihurtzen ditu. Gehienez %99 onartzen da, %100 balioak aukera desgaitzearen gauza bera egingo bailuke."
msgid "Login region"
msgstr "Saio-hasierako eskualdea"
@@ -12992,12 +13011,37 @@ msgstr "Objektuka"
msgid "Intra-layer order"
msgstr "Geruza barruko ordena"
msgid "Print order within a single layer."
msgstr "Geruza bakarreko inprimatze-ordena."
# AI Translated
msgid ""
"Order in which object instances are visited within a single layer, which controls how much travel is spent moving between them.\n"
"\n"
"Default: nearest-neighbor chaining, refined with 2-opt and crossing removal. A good general choice.\n"
"As object list: instances are printed in the same order as the object list, without any path optimization. Use it when you need a predictable, manually controlled order.\n"
"Best of all (shortest path): every strategy is evaluated and the shortest one is used. The object instance order is decided once for the whole print, while the ordering of individual islands is decided per layer, so different layers may end up using different strategies. Slightly slower to slice.\n"
"Snake: serpentine row-by-row traversal, refined with 2-opt. Well suited to regular grids of many small parts.\n"
"\n"
"With multiple filaments or tools in the same layer, minimizing tool changes takes priority: objects are grouped by filament first and this setting only orders the instances within each filament group, so the overall sequence may not look like the shortest path across the plate."
msgstr ""
"Geruza bakar baten barruan objektu-instantziak zein ordenatan bisitatzen diren; horrek zehazten du zenbat desplazamendu behar den haien artean mugitzeko.\n"
"\n"
"Lehenetsia: auzokide hurbilenaren araberako kateaketa, 2-opt bidez eta gurutzaketak kenduz findua. Aukera orokor ona.\n"
"Objektu-zerrenda bezala: instantziak objektu-zerrendaren ordena berean inprimatzen dira, ibilbidea batere optimizatu gabe. Erabili ordena aurreikusgarri eta eskuz kontrolatu bat behar duzunean.\n"
"Guztien onena (ibilbide laburrena): estrategia guztiak ebaluatzen dira eta laburrena erabiltzen da. Objektu-instantzien ordena behin erabakitzen da inprimaketa osorako, eta uharte bakoitzaren ordena geruzaz geruza erabakitzen da; beraz, geruza desberdinek estrategia desberdinak erabil ditzakete. Xerratzea apur bat motelagoa da.\n"
"Sigi-saga: errenkadaz errenkadako sigi-saga ibilbidea, 2-opt bidez findua. Oso egokia pieza txiki askoren sareta erregularretarako.\n"
"\n"
"Geruza berean filamentu edo tresna bat baino gehiago daudenean, tresna-aldaketak gutxitzeak du lehentasuna: objektuak filamentuaren arabera multzokatzen dira lehenik, eta ezarpen honek filamentu-multzo bakoitzaren barruko instantziak baino ez ditu ordenatzen; beraz, baliteke sekuentzia orokorrak plaka osoko ibilbide laburrenaren itxurarik ez izatea."
msgid "As object list"
msgstr "Objektu-zerrenda bezala"
# AI Translated
msgid "Best of all (shortest path)"
msgstr "Guztien onena (ibilbide laburrena)"
# AI Translated
msgid "Snake"
msgstr "Sigi-saga"
msgid "Slow printing down for better layer cooling"
msgstr "Moteldu inprimaketa geruza hobeto hozteko"
@@ -18269,6 +18313,20 @@ msgstr ""
"Hainbat IP helbide ebazten dira %1% ostalari-izenerako.\n"
"Hautatu erabili beharrekoa."
# AI Translated
msgid "Auto-scale for nozzle"
msgstr "Eskalatze automatikoa pitarako"
# AI Translated
msgid ""
"This model is designed around a 0.4 mm nozzle with a 0.2 mm layer height. \n"
"When the scaling option is enabled (recommended), it dynamically resizes to match your current nozzle diameter and an appropriate layer height, making the test both accurate and easy to read.\n"
"Turn scaling off only if you wish to print the reference model exactly as-is."
msgstr ""
"Modelo hau 0,4 mm-ko pita eta 0,2 mm-ko geruza-altuera kontuan hartuta diseinatu da. \n"
"Eskalatze-aukera gaituta dagoenean (gomendatua), tamaina dinamikoki egokitzen zaie zure uneko pita-diametroari eta geruza-altuera egoki bati, testa zehatza eta irakurterraza izan dadin.\n"
"Itzali eskalatzea erreferentzia-modeloa dagoen-dagoenean inprimatu nahi baduzu bakarrik."
msgid "PA Calibration"
msgstr "PA kalibrazioa"
@@ -18406,6 +18464,14 @@ msgstr "Hasierako abiadura: "
msgid "End speed: "
msgstr "Amaierako abiadura: "
# AI Translated
msgid "Auto-adjust to max volumetric speed"
msgstr "Doikuntza automatikoa abiadura bolumetriko maximora"
# AI Translated
msgid "If the end speed would exceed the filament's maximum volumetric speed, automatically lower the layer height (keeping standard values and staying within the machine's limits) to reach it. If even the minimum layer height is not enough, lower the end speed instead."
msgstr "Amaierako abiadurak filamentuaren abiadura bolumetriko maximoa gaindituko balu, geruza-altuera automatikoki jaisten da (balio estandarrak mantenduz eta makinaren mugen barruan) hura lortzeko. Gutxieneko geruza-altuerarekin ere nahikoa ez bada, amaierako abiadura jaisten da haren ordez."
msgid ""
"Please input valid values:\n"
"start > 10\n"
@@ -18417,6 +18483,57 @@ msgstr ""
"urratsa >= 0\n"
"amaiera > hasiera + urratsa"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s), which limits the outer wall to about %.0f mm/s at this line width and layer height.\n"
" Speeds above this will be clamped, so the upper blocks of the tower will not print at the requested speed.\n"
"\n"
"%s"
msgstr ""
"Amaierako abiadurak (%.0f mm/s) filamentuaren abiadura bolumetriko maximoa (%.1f mm³/s) gainditzen du, eta horrek kanpoko horma gutxi gorabehera %.0f mm/s-ra mugatzen du lerro-zabalera eta geruza-altuera honekin.\n"
" Horren gaineko abiadurak mugatu egingo dira, beraz dorrearen goiko blokeak ez dira eskatutako abiaduran inprimatuko.\n"
"\n"
"%s"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s) at the default layer height (%.2f mm).\n"
"\n"
"The layer height has been reduced to %.2f mm (a value used by this printer's profiles) so the tower can reach the requested speed."
msgstr ""
"Amaierako abiadurak (%.0f mm/s) filamentuaren abiadura bolumetriko maximoa (%.1f mm³/s) gainditzen du geruza-altuera lehenetsian (%.2f mm).\n"
"\n"
"Geruza-altuera %.2f mm-ra jaitsi da (inprimagailu honen aurrezarpenek erabiltzen duten balio bat), dorreak eskatutako abiadura lor dezan."
# AI Translated
#, c-format, boost-format
msgid ""
"Even at the smallest layer height used by this printer's profiles (%.2f mm) the end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s).\n"
"\n"
"The layer height will be set to %.2f mm and the end speed lowered to %.0f mm/s.\n"
"\n"
"Continue?"
msgstr ""
"Inprimagailu honen aurrezarpenek erabiltzen duten geruza-altuera txikienarekin ere (%.2f mm), amaierako abiadurak (%.0f mm/s) filamentuaren abiadura bolumetriko maximoa (%.1f mm³/s) gainditzen du.\n"
"\n"
"Geruza-altuera %.2f mm-ra ezarriko da eta amaierako abiadura %.0f mm/s-ra jaitsiko da.\n"
"\n"
"Jarraitu?"
# AI Translated
msgid "Continue anyway?"
msgstr "Hala ere jarraitu?"
# AI Translated
msgid "Enable \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "Gaitu \"Doikuntza automatikoa\" hau automatikoki konpontzeko, edo hala ere jarraitu?"
# AI Translated
msgid "Enable \"Auto-scale for nozzle\" and \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "Gaitu \"Eskalatze automatikoa pitarako\" eta \"Doikuntza automatikoa\" hau automatikoki konpontzeko, edo hala ere jarraitu?"
msgid "Start retraction length: "
msgstr "Hasierako atzera-egite luzera: "
@@ -20899,6 +21016,9 @@ msgstr ""
"Saihestu okertzea\n"
"Ba al zenekien ABS bezalako okertzeko joera duten materialak inprimatzean ohe beroaren tenperatura egoki igotzeak okertzeko probabilitatea murriztu dezakeela?"
#~ msgid "Print order within a single layer."
#~ msgstr "Geruza bakarreko inprimatze-ordena."
#~ msgid "Bottom"
#~ msgstr "Behekoa"
@@ -21028,9 +21148,6 @@ msgstr ""
#~ msgid "°"
#~ msgstr "°"
#~ msgid "%"
#~ msgstr "%"
#~ msgid "uniform scale"
#~ msgstr "eskala uniformea"
+123 -6
View File
@@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-07-26 21:59-0300\n"
"POT-Creation-Date: 2026-07-29 17:40-0300\n"
"PO-Revision-Date: \n"
"Last-Translator: \n"
"Language-Team: Guislain Cyril, Thomas Lété\n"
@@ -3760,6 +3760,10 @@ msgstr "Agencement…"
msgid "Arranging"
msgstr "Agencement"
# AI Translated
msgid "Arranging "
msgstr "Agencement "
msgid "Arranging canceled."
msgstr "Agencement annulé."
@@ -9068,6 +9072,21 @@ msgstr "Assombrir les couches inférieures"
msgid "When scrubbing the layer slider in the sliced preview, render the layers below the current one darkened so that only the layer being viewed is shown at full brightness."
msgstr "Lors du défilement du curseur de couche dans l'aperçu découpé, affiche les couches situées sous la couche actuelle assombries, de sorte que seule la couche visualisée soit affichée en pleine luminosité."
# AI Translated
msgid "Dimmed layer brightness"
msgstr "Luminosité des couches assombries"
msgid "%"
msgstr "%"
# AI Translated
msgid ""
"How brightly the dimmed layers are rendered when \"Dim lower layers\" is enabled.\n"
"99% is barely darkened, 0% renders them black. Capped at 99% because 100% would be the same as disabling the option."
msgstr ""
"Luminosité de rendu des couches assombries lorsque « Assombrir les couches inférieures » est activé.\n"
"99% correspond à un assombrissement à peine perceptible, 0% les affiche en noir. Limité à 99% car 100% reviendrait à désactiver loption."
msgid "Login region"
msgstr "Région d'origine"
@@ -13087,12 +13106,37 @@ msgstr "Par objet"
msgid "Intra-layer order"
msgstr "Ordre intra-couche"
msgid "Print order within a single layer."
msgstr "Ordre dimpression au sein dune même couche"
# AI Translated
msgid ""
"Order in which object instances are visited within a single layer, which controls how much travel is spent moving between them.\n"
"\n"
"Default: nearest-neighbor chaining, refined with 2-opt and crossing removal. A good general choice.\n"
"As object list: instances are printed in the same order as the object list, without any path optimization. Use it when you need a predictable, manually controlled order.\n"
"Best of all (shortest path): every strategy is evaluated and the shortest one is used. The object instance order is decided once for the whole print, while the ordering of individual islands is decided per layer, so different layers may end up using different strategies. Slightly slower to slice.\n"
"Snake: serpentine row-by-row traversal, refined with 2-opt. Well suited to regular grids of many small parts.\n"
"\n"
"With multiple filaments or tools in the same layer, minimizing tool changes takes priority: objects are grouped by filament first and this setting only orders the instances within each filament group, so the overall sequence may not look like the shortest path across the plate."
msgstr ""
"Ordre dans lequel les instances dobjets sont parcourues au sein dune même couche, ce qui détermine la quantité de déplacements effectués entre elles.\n"
"\n"
"Par défaut : chaînage par plus proche voisin, affiné par 2-opt et suppression des croisements. Un bon choix général.\n"
"En tant que liste dobjets : les instances sont imprimées dans le même ordre que la liste dobjets, sans aucune optimisation de trajet. À utiliser lorsque vous avez besoin dun ordre prévisible et contrôlé manuellement.\n"
"Meilleur de tous (trajet le plus court) : toutes les stratégies sont évaluées et la plus courte est retenue. Lordre des instances dobjets est déterminé une seule fois pour toute limpression, tandis que lordre des îlots individuels est déterminé couche par couche ; différentes couches peuvent donc utiliser des stratégies différentes. Découpage légèrement plus lent.\n"
"Serpentin : parcours en serpentin, rangée par rangée, affiné par 2-opt. Bien adapté aux grilles régulières de nombreuses petites pièces.\n"
"\n"
"Avec plusieurs filaments ou outils dans la même couche, la réduction des changements doutil est prioritaire : les objets sont dabord regroupés par filament et ce réglage nordonne que les instances au sein de chaque groupe de filament ; la séquence globale peut donc ne pas ressembler au trajet le plus court sur la plaque."
msgid "As object list"
msgstr "En tant que liste dobjets"
# AI Translated
msgid "Best of all (shortest path)"
msgstr "Meilleur de tous (trajet le plus court)"
# AI Translated
msgid "Snake"
msgstr "Serpentin"
msgid "Slow printing down for better layer cooling"
msgstr "Impression lente pour un meilleur refroidissement des couches"
@@ -18381,6 +18425,20 @@ msgstr ""
"Il existe plusieurs adresses IP résolues par le nom dhôte %1%.\n"
"Veuillez en sélectionner une qui doit être utilisée."
# AI Translated
msgid "Auto-scale for nozzle"
msgstr "Mise à l’échelle automatique selon la buse"
# AI Translated
msgid ""
"This model is designed around a 0.4 mm nozzle with a 0.2 mm layer height. \n"
"When the scaling option is enabled (recommended), it dynamically resizes to match your current nozzle diameter and an appropriate layer height, making the test both accurate and easy to read.\n"
"Turn scaling off only if you wish to print the reference model exactly as-is."
msgstr ""
"Ce modèle est conçu pour une buse de 0,4 mm avec une hauteur de couche de 0,2 mm. \n"
"Lorsque loption de mise à l’échelle est activée (recommandé), il est redimensionné dynamiquement pour correspondre au diamètre de buse actuel et à une hauteur de couche appropriée, ce qui rend le test à la fois précis et facile à lire.\n"
"Ne désactivez la mise à l’échelle que si vous souhaitez imprimer le modèle de référence exactement tel quel."
msgid "PA Calibration"
msgstr "Calibration Pressure Advance"
@@ -18517,6 +18575,14 @@ msgstr "Vitesse de début: "
msgid "End speed: "
msgstr "Vitesse de fin: "
# AI Translated
msgid "Auto-adjust to max volumetric speed"
msgstr "Ajustement automatique à la vitesse volumétrique maximale"
# AI Translated
msgid "If the end speed would exceed the filament's maximum volumetric speed, automatically lower the layer height (keeping standard values and staying within the machine's limits) to reach it. If even the minimum layer height is not enough, lower the end speed instead."
msgstr "Si la vitesse finale devait dépasser la vitesse volumétrique maximale du filament, la hauteur de couche est automatiquement réduite (en conservant des valeurs standard et en restant dans les limites de la machine) pour latteindre. Si même la hauteur de couche minimale ne suffit pas, cest la vitesse finale qui est réduite."
msgid ""
"Please input valid values:\n"
"start > 10\n"
@@ -18528,6 +18594,57 @@ msgstr ""
"intervalles >= 0\n"
"Fin > Début + Intervalle"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s), which limits the outer wall to about %.0f mm/s at this line width and layer height.\n"
" Speeds above this will be clamped, so the upper blocks of the tower will not print at the requested speed.\n"
"\n"
"%s"
msgstr ""
"La vitesse finale (%.0f mm/s) dépasse la vitesse volumétrique maximale du filament (%.1f mm³/s), ce qui limite la paroi extérieure à environ %.0f mm/s pour cette largeur de ligne et cette hauteur de couche.\n"
" Les vitesses supérieures seront bridées : les blocs supérieurs de la tour ne seront donc pas imprimés à la vitesse demandée.\n"
"\n"
"%s"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s) at the default layer height (%.2f mm).\n"
"\n"
"The layer height has been reduced to %.2f mm (a value used by this printer's profiles) so the tower can reach the requested speed."
msgstr ""
"La vitesse finale (%.0f mm/s) dépasse la vitesse volumétrique maximale du filament (%.1f mm³/s) avec la hauteur de couche par défaut (%.2f mm).\n"
"\n"
"La hauteur de couche a été réduite à %.2f mm (une valeur utilisée par les profils de cette imprimante) afin que la tour puisse atteindre la vitesse demandée."
# AI Translated
#, c-format, boost-format
msgid ""
"Even at the smallest layer height used by this printer's profiles (%.2f mm) the end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s).\n"
"\n"
"The layer height will be set to %.2f mm and the end speed lowered to %.0f mm/s.\n"
"\n"
"Continue?"
msgstr ""
"Même avec la plus petite hauteur de couche utilisée par les profils de cette imprimante (%.2f mm), la vitesse finale (%.0f mm/s) dépasse la vitesse volumétrique maximale du filament (%.1f mm³/s).\n"
"\n"
"La hauteur de couche sera réglée sur %.2f mm et la vitesse finale abaissée à %.0f mm/s.\n"
"\n"
"Continuer ?"
# AI Translated
msgid "Continue anyway?"
msgstr "Continuer quand même ?"
# AI Translated
msgid "Enable \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "Activer « Ajustement automatique » pour corriger cela automatiquement, ou continuer quand même ?"
# AI Translated
msgid "Enable \"Auto-scale for nozzle\" and \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "Activer « Mise à l’échelle automatique selon la buse » et « Ajustement automatique » pour corriger cela automatiquement, ou continuer quand même ?"
msgid "Start retraction length: "
msgstr "Longueur de rétraction de début: "
@@ -21059,6 +21176,9 @@ msgstr ""
"Éviter la déformation\n"
"Saviez-vous que lors de limpression de matériaux susceptibles de se déformer, tels que lABS, une augmentation appropriée de la température du plateau chauffant peut réduire la probabilité de déformation?"
#~ msgid "Print order within a single layer."
#~ msgstr "Ordre dimpression au sein dune même couche"
#~ msgid "Bottom"
#~ msgstr "Dessous"
@@ -21149,9 +21269,6 @@ msgstr ""
#~ msgid "°C"
#~ msgstr "°C"
#~ msgid "%"
#~ msgstr "%"
#~ msgid "Renders cast shadows on the plate in realistic view."
#~ msgstr "Affiche les ombres portées sur la plaque dans la vue réaliste."
File diff suppressed because it is too large Load Diff
+123 -6
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-07-26 21:59-0300\n"
"POT-Creation-Date: 2026-07-29 17:40-0300\n"
"PO-Revision-Date: \n"
"Last-Translator: \n"
"Language-Team: \n"
@@ -3850,6 +3850,10 @@ msgstr "Disposizione..."
msgid "Arranging"
msgstr "Disposizione"
# AI Translated
msgid "Arranging "
msgstr "Disposizione "
msgid "Arranging canceled."
msgstr "Disposizione annullata."
@@ -9229,6 +9233,21 @@ msgstr "Attenua gli strati inferiori"
msgid "When scrubbing the layer slider in the sliced preview, render the layers below the current one darkened so that only the layer being viewed is shown at full brightness."
msgstr "Quando si scorre il cursore degli strati nell'anteprima elaborata, gli strati al di sotto di quello corrente vengono visualizzati scuriti, in modo che solo lo strato in visualizzazione sia mostrato alla massima luminosità."
# AI Translated
msgid "Dimmed layer brightness"
msgstr "Luminosità degli strati attenuati"
msgid "%"
msgstr "%"
# AI Translated
msgid ""
"How brightly the dimmed layers are rendered when \"Dim lower layers\" is enabled.\n"
"99% is barely darkened, 0% renders them black. Capped at 99% because 100% would be the same as disabling the option."
msgstr ""
"Quanto luminosi appaiono gli strati attenuati quando \"Attenua gli strati inferiori\" è attivo.\n"
"99% è appena scurito, 0% li rende neri. Limitato al 99% perché 100% equivarrebbe a disattivare l'opzione."
msgid "Login region"
msgstr "Regione di accesso"
@@ -13326,12 +13345,37 @@ msgstr "Per oggetto"
msgid "Intra-layer order"
msgstr "Ordine intra-strato"
msgid "Print order within a single layer."
msgstr "Ordine di stampa all'interno di un singolo strato."
# AI Translated
msgid ""
"Order in which object instances are visited within a single layer, which controls how much travel is spent moving between them.\n"
"\n"
"Default: nearest-neighbor chaining, refined with 2-opt and crossing removal. A good general choice.\n"
"As object list: instances are printed in the same order as the object list, without any path optimization. Use it when you need a predictable, manually controlled order.\n"
"Best of all (shortest path): every strategy is evaluated and the shortest one is used. The object instance order is decided once for the whole print, while the ordering of individual islands is decided per layer, so different layers may end up using different strategies. Slightly slower to slice.\n"
"Snake: serpentine row-by-row traversal, refined with 2-opt. Well suited to regular grids of many small parts.\n"
"\n"
"With multiple filaments or tools in the same layer, minimizing tool changes takes priority: objects are grouped by filament first and this setting only orders the instances within each filament group, so the overall sequence may not look like the shortest path across the plate."
msgstr ""
"Ordine in cui le istanze degli oggetti vengono percorse all'interno di un singolo strato; determina quanti spostamenti servono per passare dall'una all'altra.\n"
"\n"
"Predefinito: concatenamento con il vicino più prossimo, affinato con 2-opt e rimozione degli incroci. Una buona scelta generale.\n"
"Come elenco di oggetti: le istanze vengono stampate nello stesso ordine dell'elenco degli oggetti, senza alcuna ottimizzazione del percorso. Da usare quando serve un ordine prevedibile e controllato manualmente.\n"
"Il migliore di tutti (percorso più breve): tutte le strategie vengono valutate e viene usata la più breve. L'ordine delle istanze degli oggetti viene deciso una sola volta per l'intera stampa, mentre l'ordine delle singole isole viene deciso strato per strato, quindi strati diversi possono usare strategie diverse. Slicing leggermente più lento.\n"
"Serpentina: percorso a serpentina, riga per riga, affinato con 2-opt. Adatto a griglie regolari di molti pezzi piccoli.\n"
"\n"
"Con più filamenti o strumenti nello stesso strato, ridurre i cambi strumento ha la priorità: gli oggetti vengono prima raggruppati per filamento e questa impostazione ordina solo le istanze all'interno di ciascun gruppo di filamento, quindi la sequenza complessiva potrebbe non sembrare il percorso più breve sul piatto."
msgid "As object list"
msgstr "Come elenco di oggetti"
# AI Translated
msgid "Best of all (shortest path)"
msgstr "Il migliore di tutti (percorso più breve)"
# AI Translated
msgid "Snake"
msgstr "Serpentina"
msgid "Slow printing down for better layer cooling"
msgstr "Rallenta stampa per miglior raffreddamento degli strati"
@@ -18679,6 +18723,20 @@ msgstr ""
"Esistono diversi indirizzi IP che risolvono il nome host %1%.\n"
"Selezionare quello da utilizzare."
# AI Translated
msgid "Auto-scale for nozzle"
msgstr "Scala automaticamente in base all'ugello"
# AI Translated
msgid ""
"This model is designed around a 0.4 mm nozzle with a 0.2 mm layer height. \n"
"When the scaling option is enabled (recommended), it dynamically resizes to match your current nozzle diameter and an appropriate layer height, making the test both accurate and easy to read.\n"
"Turn scaling off only if you wish to print the reference model exactly as-is."
msgstr ""
"Questo modello è progettato per un ugello da 0,4 mm con un'altezza strato di 0,2 mm. \n"
"Quando l'opzione di ridimensionamento è attiva (consigliata), il modello viene ridimensionato dinamicamente in base al diametro dell'ugello attuale e a un'altezza strato adeguata, rendendo il test preciso e facile da leggere.\n"
"Disattiva il ridimensionamento solo se desideri stampare il modello di riferimento esattamente com'è."
msgid "PA Calibration"
msgstr "Calibrazione AP"
@@ -18815,6 +18873,14 @@ msgstr "Velocità iniziale: "
msgid "End speed: "
msgstr "Velocità finale: "
# AI Translated
msgid "Auto-adjust to max volumetric speed"
msgstr "Adatta automaticamente alla velocità volumetrica massima"
# AI Translated
msgid "If the end speed would exceed the filament's maximum volumetric speed, automatically lower the layer height (keeping standard values and staying within the machine's limits) to reach it. If even the minimum layer height is not enough, lower the end speed instead."
msgstr "Se la velocità finale superasse la velocità volumetrica massima del filamento, l'altezza strato viene ridotta automaticamente (mantenendo valori standard e restando entro i limiti della macchina) per raggiungerla. Se anche l'altezza strato minima non è sufficiente, viene ridotta invece la velocità finale."
msgid ""
"Please input valid values:\n"
"start > 10\n"
@@ -18826,6 +18892,57 @@ msgstr ""
"incremento >= 0\n"
"fine > inizio + incremento"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s), which limits the outer wall to about %.0f mm/s at this line width and layer height.\n"
" Speeds above this will be clamped, so the upper blocks of the tower will not print at the requested speed.\n"
"\n"
"%s"
msgstr ""
"La velocità finale (%.0f mm/s) supera la velocità volumetrica massima del filamento (%.1f mm³/s), il che limita la parete esterna a circa %.0f mm/s con questa larghezza linea e questa altezza strato.\n"
" Le velocità superiori verranno limitate, quindi i blocchi superiori della torre non verranno stampati alla velocità richiesta.\n"
"\n"
"%s"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s) at the default layer height (%.2f mm).\n"
"\n"
"The layer height has been reduced to %.2f mm (a value used by this printer's profiles) so the tower can reach the requested speed."
msgstr ""
"La velocità finale (%.0f mm/s) supera la velocità volumetrica massima del filamento (%.1f mm³/s) con l'altezza strato predefinita (%.2f mm).\n"
"\n"
"L'altezza strato è stata ridotta a %.2f mm (un valore usato dai profili di questa stampante) affinché la torre possa raggiungere la velocità richiesta."
# AI Translated
#, c-format, boost-format
msgid ""
"Even at the smallest layer height used by this printer's profiles (%.2f mm) the end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s).\n"
"\n"
"The layer height will be set to %.2f mm and the end speed lowered to %.0f mm/s.\n"
"\n"
"Continue?"
msgstr ""
"Anche con l'altezza strato più piccola usata dai profili di questa stampante (%.2f mm), la velocità finale (%.0f mm/s) supera la velocità volumetrica massima del filamento (%.1f mm³/s).\n"
"\n"
"L'altezza strato verrà impostata su %.2f mm e la velocità finale ridotta a %.0f mm/s.\n"
"\n"
"Continuare?"
# AI Translated
msgid "Continue anyway?"
msgstr "Continuare comunque?"
# AI Translated
msgid "Enable \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "Attivare \"Adatta automaticamente\" per correggere automaticamente il problema oppure continuare comunque?"
# AI Translated
msgid "Enable \"Auto-scale for nozzle\" and \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "Attivare \"Scala automaticamente in base all'ugello\" e \"Adatta automaticamente\" per correggere automaticamente il problema oppure continuare comunque?"
msgid "Start retraction length: "
msgstr "Lunghezza di retrazione iniziale: "
@@ -21514,6 +21631,9 @@ msgstr ""
"Evita le deformazioni\n"
"Sapevi che quando si stampano materiali soggetti a deformazioni come l'ABS, aumentare in modo appropriato la temperatura del piano riscaldato può ridurre la probabilità di deformazione?"
#~ msgid "Print order within a single layer."
#~ msgstr "Ordine di stampa all'interno di un singolo strato."
#~ msgid "Bottom"
#~ msgstr "Inferiore"
@@ -21600,9 +21720,6 @@ msgstr ""
#~ msgid "°C"
#~ msgstr "°C"
#~ msgid "%"
#~ msgstr "%"
#~ msgid "Continue to sync filaments"
#~ msgstr "Continua la sincronizzazione dei filamenti"
+123 -6
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-07-26 21:59-0300\n"
"POT-Creation-Date: 2026-07-29 17:40-0300\n"
"PO-Revision-Date: \n"
"Last-Translator: \n"
"Language-Team: \n"
@@ -3860,6 +3860,10 @@ msgstr "レイアウト中"
msgid "Arranging"
msgstr "レイアウト中"
# AI Translated
msgid "Arranging "
msgstr "レイアウト中 "
msgid "Arranging canceled."
msgstr "レイアウトを取り消しました"
@@ -9249,6 +9253,21 @@ msgstr "下の積層を暗くする"
msgid "When scrubbing the layer slider in the sliced preview, render the layers below the current one darkened so that only the layer being viewed is shown at full brightness."
msgstr "スライスプレビューで積層スライダーを操作する際、現在の層より下の積層を暗く描画し、表示中の積層のみを明るく表示します。"
# AI Translated
msgid "Dimmed layer brightness"
msgstr "暗くした積層の明るさ"
msgid "%"
msgstr "%"
# AI Translated
msgid ""
"How brightly the dimmed layers are rendered when \"Dim lower layers\" is enabled.\n"
"99% is barely darkened, 0% renders them black. Capped at 99% because 100% would be the same as disabling the option."
msgstr ""
"「下の積層を暗くする」を有効にしたときに、暗くした積層をどの程度の明るさで表示するかを指定します。\n"
"99%ではほとんど暗くならず、0%では真っ黒になります。100%はオプションを無効にした場合と同じになるため、上限は99%です。"
msgid "Login region"
msgstr "地域"
@@ -13416,12 +13435,37 @@ msgstr "オブジェクト順"
msgid "Intra-layer order"
msgstr "レイヤー内の順序"
msgid "Print order within a single layer."
msgstr "単一レイヤー内の印刷順序。"
# AI Translated
msgid ""
"Order in which object instances are visited within a single layer, which controls how much travel is spent moving between them.\n"
"\n"
"Default: nearest-neighbor chaining, refined with 2-opt and crossing removal. A good general choice.\n"
"As object list: instances are printed in the same order as the object list, without any path optimization. Use it when you need a predictable, manually controlled order.\n"
"Best of all (shortest path): every strategy is evaluated and the shortest one is used. The object instance order is decided once for the whole print, while the ordering of individual islands is decided per layer, so different layers may end up using different strategies. Slightly slower to slice.\n"
"Snake: serpentine row-by-row traversal, refined with 2-opt. Well suited to regular grids of many small parts.\n"
"\n"
"With multiple filaments or tools in the same layer, minimizing tool changes takes priority: objects are grouped by filament first and this setting only orders the instances within each filament group, so the overall sequence may not look like the shortest path across the plate."
msgstr ""
"1つの積層内でオブジェクトインスタンスを巡回する順序です。インスタンス間の移動距離に影響します。\n"
"\n"
"デフォルト:最近傍法でつなぎ、2-optと交差の除去で改善します。一般的な用途に適した選択です。\n"
"オブジェクトリスト順:パスの最適化を行わず、オブジェクトリストと同じ順序でインスタンスを造形します。手動で管理できる予測しやすい順序が必要な場合に使用します。\n"
"すべてを比較(最短経路):すべての方式を評価し、最も短いものを使用します。オブジェクトインスタンスの順序は造形全体で1回だけ決定され、個々のアイランドの順序は積層ごとに決定されるため、積層によって異なる方式が使われる場合があります。スライスがやや遅くなります。\n"
"蛇行:行ごとに折り返しながら蛇行して巡回し、2-optで改善します。小さなパーツが規則的に並んだ配置に適しています。\n"
"\n"
"同じ積層内で複数のフィラメントやツールを使用する場合は、ツール交換の削減が優先されます。オブジェクトはまずフィラメントごとにグループ化され、この設定は各フィラメントグループ内のインスタンスの順序のみを決めるため、全体の順序はプレート全体での最短経路には見えないことがあります。"
msgid "As object list"
msgstr "オブジェクトリスト順"
# AI Translated
msgid "Best of all (shortest path)"
msgstr "すべてを比較(最短経路)"
# AI Translated
msgid "Snake"
msgstr "蛇行"
msgid "Slow printing down for better layer cooling"
msgstr "冷却の為減速"
@@ -19167,6 +19211,20 @@ msgstr ""
"ホスト名%1%には、いくつかのIPアドレスがあります。\n"
"使用するIPアドレスを1つ選んでください。"
# AI Translated
msgid "Auto-scale for nozzle"
msgstr "ノズルに合わせて自動スケール"
# AI Translated
msgid ""
"This model is designed around a 0.4 mm nozzle with a 0.2 mm layer height. \n"
"When the scaling option is enabled (recommended), it dynamically resizes to match your current nozzle diameter and an appropriate layer height, making the test both accurate and easy to read.\n"
"Turn scaling off only if you wish to print the reference model exactly as-is."
msgstr ""
"このモデルは、0.4 mmノズルと0.2 mmの積層ピッチを基準に設計されています。 \n"
"スケールオプションを有効にすると(推奨)、現在のノズル径と適切な積層ピッチに合わせてサイズが動的に調整され、テストの精度と読み取りやすさが向上します。\n"
"参照モデルをそのままの状態で造形したい場合のみ、スケールを無効にしてください。"
msgid "PA Calibration"
msgstr "PAキャリブレーション"
@@ -19306,6 +19364,14 @@ msgstr "開始速度: "
msgid "End speed: "
msgstr "終了速度: "
# AI Translated
msgid "Auto-adjust to max volumetric speed"
msgstr "最大体積速度に合わせて自動調整"
# AI Translated
msgid "If the end speed would exceed the filament's maximum volumetric speed, automatically lower the layer height (keeping standard values and staying within the machine's limits) to reach it. If even the minimum layer height is not enough, lower the end speed instead."
msgstr "終了速度がフィラメントの最大体積速度を超える場合、その速度に到達できるよう積層ピッチを自動的に下げます(標準的な値を使用し、プリンタの制限内に収めます)。最小の積層ピッチでも足りない場合は、代わりに終了速度を下げます。"
# AI Translated
msgid ""
"Please input valid values:\n"
@@ -19318,6 +19384,57 @@ msgstr ""
"ステップ >= 0\n"
"終了 > 開始 + ステップ"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s), which limits the outer wall to about %.0f mm/s at this line width and layer height.\n"
" Speeds above this will be clamped, so the upper blocks of the tower will not print at the requested speed.\n"
"\n"
"%s"
msgstr ""
"終了速度(%.0f mm/s)がフィラメントの最大体積速度(%.1f mm³/s)を超えています。この押出線幅と積層ピッチでは、外壁は約 %.0f mm/s に制限されます。\n"
" これを超える速度は制限されるため、タワーの上部ブロックは指定した速度で造形されません。\n"
"\n"
"%s"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s) at the default layer height (%.2f mm).\n"
"\n"
"The layer height has been reduced to %.2f mm (a value used by this printer's profiles) so the tower can reach the requested speed."
msgstr ""
"終了速度(%.0f mm/s)がフィラメントの最大体積速度(%.1f mm³/s)を超えています(デフォルトの積層ピッチ %.2f mm の場合)。\n"
"\n"
"タワーが指定した速度に到達できるよう、積層ピッチを %.2f mm(このプリンタのプロファイルで使用されている値)に下げました。"
# AI Translated
#, c-format, boost-format
msgid ""
"Even at the smallest layer height used by this printer's profiles (%.2f mm) the end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s).\n"
"\n"
"The layer height will be set to %.2f mm and the end speed lowered to %.0f mm/s.\n"
"\n"
"Continue?"
msgstr ""
"このプリンタのプロファイルで使用されている最小の積層ピッチ(%.2f mm)でも、終了速度(%.0f mm/s)がフィラメントの最大体積速度(%.1f mm³/s)を超えています。\n"
"\n"
"積層ピッチを %.2f mm に設定し、終了速度を %.0f mm/s に下げます。\n"
"\n"
"続行しますか?"
# AI Translated
msgid "Continue anyway?"
msgstr "このまま続行しますか?"
# AI Translated
msgid "Enable \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "「自動調整」を有効にすると自動的に修正されます。このまま続行しますか?"
# AI Translated
msgid "Enable \"Auto-scale for nozzle\" and \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "「ノズルに合わせて自動スケール」と「自動調整」を有効にすると自動的に修正されます。このまま続行しますか?"
msgid "Start retraction length: "
msgstr "開始リトラクション長さ: "
@@ -22077,6 +22194,9 @@ msgstr ""
"反りを避ける\n"
"ABSのような反りやすい素材を印刷する場合、ヒートベッドの温度を適切に上げることで、反りが発生する確率を下げることができることをご存知ですか?"
#~ msgid "Print order within a single layer."
#~ msgstr "単一レイヤー内の印刷順序。"
#~ msgid "Bottom"
#~ msgstr "底面"
@@ -22157,9 +22277,6 @@ msgstr ""
#~ msgid "°C"
#~ msgstr "°C"
#~ msgid "%"
#~ msgstr "%"
#~ msgid "Continue to sync filaments"
#~ msgstr "フィラメントの同期を続行"
+126 -9
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-07-26 21:59-0300\n"
"POT-Creation-Date: 2026-07-29 17:40-0300\n"
"PO-Revision-Date: 2025-06-02 17:12+0900\n"
"Last-Translator: crwusiz <crwusiz@gmail.com>\n"
"Language-Team: \n"
@@ -3864,6 +3864,10 @@ msgstr "정렬 중..."
msgid "Arranging"
msgstr "정렬 중"
# AI Translated
msgid "Arranging "
msgstr "정렬 중 "
msgid "Arranging canceled."
msgstr "정렬 취소됨."
@@ -9324,6 +9328,21 @@ msgstr "아래 레이어 어둡게 표시"
msgid "When scrubbing the layer slider in the sliced preview, render the layers below the current one darkened so that only the layer being viewed is shown at full brightness."
msgstr "슬라이스된 미리보기에서 레이어 슬라이더를 움직일 때 현재 레이어보다 아래에 있는 레이어를 어둡게 렌더링하여, 보고 있는 레이어만 완전한 밝기로 표시합니다."
# AI Translated
msgid "Dimmed layer brightness"
msgstr "어둡게 표시된 레이어의 밝기"
msgid "%"
msgstr "%"
# AI Translated
msgid ""
"How brightly the dimmed layers are rendered when \"Dim lower layers\" is enabled.\n"
"99% is barely darkened, 0% renders them black. Capped at 99% because 100% would be the same as disabling the option."
msgstr ""
"\"아래 레이어 어둡게 표시\"를 활성화했을 때 어둡게 표시되는 레이어의 밝기입니다.\n"
"99%는 거의 어두워지지 않고, 0%는 완전히 검게 표시됩니다. 100%는 이 옵션을 비활성화한 것과 같으므로 최대 99%로 제한됩니다."
msgid "Login region"
msgstr "로그인 지역"
@@ -13539,12 +13558,37 @@ msgstr "객체별"
msgid "Intra-layer order"
msgstr "레이어 내 순서"
msgid "Print order within a single layer."
msgstr "단일 레이어 내의 출력 순서"
# AI Translated
msgid ""
"Order in which object instances are visited within a single layer, which controls how much travel is spent moving between them.\n"
"\n"
"Default: nearest-neighbor chaining, refined with 2-opt and crossing removal. A good general choice.\n"
"As object list: instances are printed in the same order as the object list, without any path optimization. Use it when you need a predictable, manually controlled order.\n"
"Best of all (shortest path): every strategy is evaluated and the shortest one is used. The object instance order is decided once for the whole print, while the ordering of individual islands is decided per layer, so different layers may end up using different strategies. Slightly slower to slice.\n"
"Snake: serpentine row-by-row traversal, refined with 2-opt. Well suited to regular grids of many small parts.\n"
"\n"
"With multiple filaments or tools in the same layer, minimizing tool changes takes priority: objects are grouped by filament first and this setting only orders the instances within each filament group, so the overall sequence may not look like the shortest path across the plate."
msgstr ""
"단일 레이어 내에서 객체 인스턴스를 방문하는 순서로, 인스턴스 사이를 오가는 이동량을 결정합니다.\n"
"\n"
"기본값: 최근접 이웃 방식으로 경로를 연결하고 2-opt와 교차 제거로 개선합니다. 일반적으로 무난한 선택입니다.\n"
"객체 목록으로: 경로 최적화 없이 객체 목록과 동일한 순서로 인스턴스를 출력합니다. 예측 가능하고 수동으로 제어되는 순서가 필요할 때 사용하십시오.\n"
"전체 비교(최단 경로): 모든 전략을 평가하여 가장 짧은 것을 사용합니다. 객체 인스턴스 순서는 출력 전체에 대해 한 번만 결정되고 개별 아일랜드의 순서는 레이어마다 결정되므로, 레이어에 따라 서로 다른 전략이 사용될 수 있습니다. 슬라이싱이 약간 느려집니다.\n"
"사행형: 행 단위로 앞뒤를 오가며 사행하듯 순회하고 2-opt로 개선합니다. 작은 부품이 규칙적인 격자로 배치된 경우에 적합합니다.\n"
"\n"
"같은 레이어에서 여러 필라멘트나 툴을 사용하는 경우에는 툴 교체 최소화가 우선합니다. 객체를 먼저 필라멘트별로 그룹화하며 이 설정은 각 필라멘트 그룹 내의 인스턴스 순서만 결정하므로, 전체 순서가 플레이트 전체의 최단 경로처럼 보이지 않을 수 있습니다."
msgid "As object list"
msgstr "객체 목록으로"
# AI Translated
msgid "Best of all (shortest path)"
msgstr "전체 비교(최단 경로)"
# AI Translated
msgid "Snake"
msgstr "사행형"
msgid "Slow printing down for better layer cooling"
msgstr "레이어 냉각 향상을 위한 감속"
@@ -14248,7 +14292,7 @@ msgstr "플러시 체적 속도"
# AI Translated
msgid "Volumetric speed when flushing filament. 0 indicates the max volumetric speed."
msgstr "필라멘트를 플러시할 때의 체적 속도입니다. 0은 최대 체적 속도를 의미합니다."
msgstr "필라멘트를 플러시할 때의 압출 속도입니다. 0은 최대 압출 속도를 의미합니다."
msgid "This setting is the volume of filament that can be melted and extruded per second. Printing speed is limited by max volumetric speed, in case of too high and unreasonable speed setting. This value cannot be zero."
msgstr "이 설정은 초당 얼마나 많은 양의 필라멘트를 녹이고 압출할 수 있는지를 나타냅니다. 너무 높고 부적절한 속도 설정의 경우 출력 속도는 최대 압출 속도에 의해 제한됩니다. 0이 될 수 없습니다"
@@ -18715,7 +18759,7 @@ msgstr ""
"이제 다양한 필라멘트에 대한 자동 교정 기능이 추가되었습니다. 완전히 자동으로 수행되며 결과는 나중에 사용할 수 있도록 프린터에 저장됩니다. 다음과 같은 제한된 경우에만 교정을 수행하면 됩니다:\n"
"1. 다른 브랜드/모델의 새 필라멘트를 사용하거나 필라멘트가 눅눅해진 경우\n"
"2. 노즐이 마모되었거나 새 노즐로 교체한 경우\n"
"3. 필라멘트 설정에서 최대 체적 속도나 출력 온도를 변경한 경우."
"3. 필라멘트 설정에서 최대 압출 속도나 출력 온도를 변경한 경우."
msgid "About this calibration"
msgstr "교정 정보"
@@ -19033,6 +19077,20 @@ msgstr ""
"호스트 이름 %1%으로 확인되는 IP 주소가 여러 개 있습니다.\n"
"사용 할 IP를 선택해 주세요."
# AI Translated
msgid "Auto-scale for nozzle"
msgstr "노즐에 맞춰 자동 크기 조정"
# AI Translated
msgid ""
"This model is designed around a 0.4 mm nozzle with a 0.2 mm layer height. \n"
"When the scaling option is enabled (recommended), it dynamically resizes to match your current nozzle diameter and an appropriate layer height, making the test both accurate and easy to read.\n"
"Turn scaling off only if you wish to print the reference model exactly as-is."
msgstr ""
"이 모델은 0.4 mm 노즐과 0.2 mm 레이어 높이를 기준으로 설계되었습니다. \n"
"크기 조정 옵션을 활성화하면(권장) 현재 노즐 직경과 적절한 레이어 높이에 맞춰 크기가 동적으로 조정되어 테스트가 정확하고 읽기 쉬워집니다.\n"
"참조 모델을 있는 그대로 출력하려는 경우에만 크기 조정을 끄십시오."
msgid "PA Calibration"
msgstr "PA 교정"
@@ -19171,6 +19229,14 @@ msgstr "시작 속도: "
msgid "End speed: "
msgstr "종료 속도: "
# AI Translated
msgid "Auto-adjust to max volumetric speed"
msgstr "최대 압출 속도에 맞춰 자동 조정"
# AI Translated
msgid "If the end speed would exceed the filament's maximum volumetric speed, automatically lower the layer height (keeping standard values and staying within the machine's limits) to reach it. If even the minimum layer height is not enough, lower the end speed instead."
msgstr "종료 속도가 필라멘트의 최대 압출 속도를 초과하는 경우, 해당 속도에 도달할 수 있도록 레이어 높이를 자동으로 낮춥니다(표준 값을 유지하고 장비의 한계 내에서 조정). 최소 레이어 높이로도 부족하면 대신 종료 속도를 낮춥니다."
msgid ""
"Please input valid values:\n"
"start > 10\n"
@@ -19182,6 +19248,57 @@ msgstr ""
"단계 >= 0\n"
"끝 > 시작 + 단계)"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s), which limits the outer wall to about %.0f mm/s at this line width and layer height.\n"
" Speeds above this will be clamped, so the upper blocks of the tower will not print at the requested speed.\n"
"\n"
"%s"
msgstr ""
"종료 속도(%.0f mm/s)가 필라멘트의 최대 압출 속도(%.1f mm³/s)를 초과합니다. 현재 선 너비와 레이어 높이에서는 외벽이 약 %.0f mm/s로 제한됩니다.\n"
" 이보다 빠른 속도는 제한되므로 타워의 상단 블록은 요청한 속도로 출력되지 않습니다.\n"
"\n"
"%s"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s) at the default layer height (%.2f mm).\n"
"\n"
"The layer height has been reduced to %.2f mm (a value used by this printer's profiles) so the tower can reach the requested speed."
msgstr ""
"종료 속도(%.0f mm/s)가 필라멘트의 최대 압출 속도(%.1f mm³/s)를 초과합니다(기본 레이어 높이 %.2f mm 기준).\n"
"\n"
"타워가 요청한 속도에 도달할 수 있도록 레이어 높이를 %.2f mm(이 프린터의 프로파일에서 사용하는 값)로 낮췄습니다."
# AI Translated
#, c-format, boost-format
msgid ""
"Even at the smallest layer height used by this printer's profiles (%.2f mm) the end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s).\n"
"\n"
"The layer height will be set to %.2f mm and the end speed lowered to %.0f mm/s.\n"
"\n"
"Continue?"
msgstr ""
"이 프린터의 프로파일에서 사용하는 가장 작은 레이어 높이(%.2f mm)에서도 종료 속도(%.0f mm/s)가 필라멘트의 최대 압출 속도(%.1f mm³/s)를 초과합니다.\n"
"\n"
"레이어 높이를 %.2f mm로 설정하고 종료 속도를 %.0f mm/s로 낮춥니다.\n"
"\n"
"계속하시겠습니까?"
# AI Translated
msgid "Continue anyway?"
msgstr "그래도 계속하시겠습니까?"
# AI Translated
msgid "Enable \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "\"자동 조정\"을 활성화하면 자동으로 해결됩니다. 그래도 계속하시겠습니까?"
# AI Translated
msgid "Enable \"Auto-scale for nozzle\" and \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "\"노즐에 맞춰 자동 크기 조정\"과 \"자동 조정\"을 활성화하면 자동으로 해결됩니다. 그래도 계속하시겠습니까?"
msgid "Start retraction length: "
msgstr "후퇴 시작 길이: "
@@ -21289,7 +21406,7 @@ msgstr "채워넣기 전혀 없음"
# AI Translated
msgid "Volumetric speed"
msgstr "체적 속도"
msgstr "압출 속도"
msgid "Step file import parameters"
msgstr "스텝 파일 가져오기 매개변수"
@@ -21940,6 +22057,9 @@ msgstr ""
"뒤틀림 방지\n"
"ABS와 같이 뒤틀림이 발생하기 쉬운 소재를 출력할 때, 히트베드 온도를 적절하게 높이면 뒤틀림 가능성을 줄일 수 있다는 사실을 알고 계셨나요?"
#~ msgid "Print order within a single layer."
#~ msgstr "단일 레이어 내의 출력 순서"
#~ msgid "Bottom"
#~ msgstr "아래"
@@ -22008,9 +22128,6 @@ msgstr ""
#~ msgid "°C"
#~ msgstr "°C"
#~ msgid "%"
#~ msgstr "%"
#~ msgid "Continue to sync filaments"
#~ msgstr "필라멘트 동기화 계속하기"
+123 -6
View File
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-07-26 21:59-0300\n"
"POT-Creation-Date: 2026-07-29 17:40-0300\n"
"PO-Revision-Date: 2026-07-02 14:13+0300\n"
"Last-Translator: Gintaras Kučinskas <sharanchius@gmail.com>\n"
"Language-Team: \n"
@@ -3840,6 +3840,10 @@ msgstr "Išdėstoma..."
msgid "Arranging"
msgstr "Išdėstymas"
# AI Translated
msgid "Arranging "
msgstr "Išdėstoma "
msgid "Arranging canceled."
msgstr "Išdėstymas atšauktas."
@@ -9186,6 +9190,21 @@ msgstr "Pritemdyti apatinius sluoksnius"
msgid "When scrubbing the layer slider in the sliced preview, render the layers below the current one darkened so that only the layer being viewed is shown at full brightness."
msgstr "Slenkant sluoksnių slankiklį pjaustytoje peržiūroje, atvaizduoti žemiau esančius sluoksnius pritemdytus, kad visu ryškumu būtų rodomas tik peržiūrimas sluoksnis."
# AI Translated
msgid "Dimmed layer brightness"
msgstr "Pritemdytų sluoksnių ryškumas"
msgid "%"
msgstr "%"
# AI Translated
msgid ""
"How brightly the dimmed layers are rendered when \"Dim lower layers\" is enabled.\n"
"99% is barely darkened, 0% renders them black. Capped at 99% because 100% would be the same as disabling the option."
msgstr ""
"Kaip ryškiai atvaizduojami pritemdyti sluoksniai, kai įjungta parinktis „Pritemdyti apatinius sluoksnius“.\n"
"99 % pritemdymas vos pastebimas, 0 % sluoksniai atvaizduojami juodai. Riba yra 99 %, nes 100 % prilygtų parinkties išjungimui."
msgid "Login region"
msgstr "Prisijungimo regionas"
@@ -13222,12 +13241,37 @@ msgstr "Objektas po objekto"
msgid "Intra-layer order"
msgstr "Eiliškumas sluoksnio viduje"
msgid "Print order within a single layer."
msgstr "Elementų spausdinimo eiliškumas vieno sluoksnio ribose."
# AI Translated
msgid ""
"Order in which object instances are visited within a single layer, which controls how much travel is spent moving between them.\n"
"\n"
"Default: nearest-neighbor chaining, refined with 2-opt and crossing removal. A good general choice.\n"
"As object list: instances are printed in the same order as the object list, without any path optimization. Use it when you need a predictable, manually controlled order.\n"
"Best of all (shortest path): every strategy is evaluated and the shortest one is used. The object instance order is decided once for the whole print, while the ordering of individual islands is decided per layer, so different layers may end up using different strategies. Slightly slower to slice.\n"
"Snake: serpentine row-by-row traversal, refined with 2-opt. Well suited to regular grids of many small parts.\n"
"\n"
"With multiple filaments or tools in the same layer, minimizing tool changes takes priority: objects are grouped by filament first and this setting only orders the instances within each filament group, so the overall sequence may not look like the shortest path across the plate."
msgstr ""
"Tvarka, kuria objektų kopijos aplankomos viename sluoksnyje; ji lemia, kiek tuščiosios eigos sugaištama pereinant tarp jų.\n"
"\n"
"Numatytasis: grandinės sudarymas artimiausio kaimyno metodu, patobulintas 2-opt algoritmu ir sankirtų šalinimu. Geras bendras pasirinkimas.\n"
"Kaip objektų sąrašas: kopijos spausdinamos tokia pačia tvarka kaip objektų sąraše, be jokio kelio optimizavimo. Naudokite, kai reikia nuspėjamos, rankiniu būdu valdomos tvarkos.\n"
"Geriausias iš visų (trumpiausias kelias): įvertinamos visos strategijos ir naudojama trumpiausia. Objektų kopijų tvarka nustatoma vieną kartą visam spausdinimui, o atskirų salelių tvarka kiekvienam sluoksniui atskirai, todėl skirtinguose sluoksniuose gali būti naudojamos skirtingos strategijos. Sluoksniuojama šiek tiek lėčiau.\n"
"Gyvatėle: vingiuotas ėjimas eilutė po eilutės, patobulintas 2-opt algoritmu. Gerai tinka taisyklingiems daugelio mažų detalių tinkleliams.\n"
"\n"
"Kai tame pačiame sluoksnyje naudojamos kelios gijos ar keli įrankiai, pirmenybė teikiama įrankio keitimų mažinimui: objektai pirmiausia grupuojami pagal giją, o ši nuostata rikiuoja tik kopijas kiekvienoje grupėje, todėl bendra seka gali neatrodyti kaip trumpiausias kelias per plokštę."
msgid "As object list"
msgstr "Kaip objektų sąrašas"
# AI Translated
msgid "Best of all (shortest path)"
msgstr "Geriausias iš visų (trumpiausias kelias)"
# AI Translated
msgid "Snake"
msgstr "Gyvatėle"
msgid "Slow printing down for better layer cooling"
msgstr "Sulėtinti spausdinimą geresniam sluoksnių aušinimui"
@@ -18537,6 +18581,20 @@ msgstr ""
"Yra keli IP adresai, susieti su mazgo pavadinimu %1%.\n"
"Pasirinkite tą, kurį norite naudoti."
# AI Translated
msgid "Auto-scale for nozzle"
msgstr "Automatinis mastelis pagal purkštuką"
# AI Translated
msgid ""
"This model is designed around a 0.4 mm nozzle with a 0.2 mm layer height. \n"
"When the scaling option is enabled (recommended), it dynamically resizes to match your current nozzle diameter and an appropriate layer height, making the test both accurate and easy to read.\n"
"Turn scaling off only if you wish to print the reference model exactly as-is."
msgstr ""
"Šis modelis suprojektuotas 0,4 mm purkštukui ir 0,2 mm sluoksnio aukščiui. \n"
"Kai mastelio keitimo parinktis įjungta (rekomenduojama), modelio dydis dinamiškai pritaikomas prie jūsų dabartinio purkštuko skersmens ir tinkamo sluoksnio aukščio, todėl testas yra ir tikslus, ir lengvai įskaitomas.\n"
"Mastelio keitimą išjunkite tik tuo atveju, jei norite spausdinti etaloninį modelį tiksliai tokį, koks jis yra."
msgid "PA Calibration"
msgstr "PA kalibravimas (Pressure Advance)"
@@ -18673,6 +18731,14 @@ msgstr "Pradinis greitis: "
msgid "End speed: "
msgstr "Galinis greitis: "
# AI Translated
msgid "Auto-adjust to max volumetric speed"
msgstr "Automatiškai pritaikyti prie maksimalaus tūrinio greičio"
# AI Translated
msgid "If the end speed would exceed the filament's maximum volumetric speed, automatically lower the layer height (keeping standard values and staying within the machine's limits) to reach it. If even the minimum layer height is not enough, lower the end speed instead."
msgstr "Jei galutinis greitis viršytų maksimalų gijos tūrinį greitį, automatiškai sumažinti sluoksnio aukštį (išlaikant standartines reikšmes ir neperžengiant įrenginio apribojimų), kad jį pasiektų. Jei net mažiausio sluoksnio aukščio nepakanka, vietoj to sumažinamas galutinis greitis."
msgid ""
"Please input valid values:\n"
"start > 10\n"
@@ -18684,6 +18750,57 @@ msgstr ""
"žingsnis >= 0\n"
"galinis > pradinis + žingsnis"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s), which limits the outer wall to about %.0f mm/s at this line width and layer height.\n"
" Speeds above this will be clamped, so the upper blocks of the tower will not print at the requested speed.\n"
"\n"
"%s"
msgstr ""
"Galutinis greitis (%.0f mm/s) viršija maksimalų gijos tūrinį greitį (%.1f mm³/s), kuris esant tokiam linijos pločiui ir sluoksnio aukščiui riboja išorinę sienelę iki maždaug %.0f mm/s.\n"
" Didesni greičiai bus apriboti, todėl viršutiniai bokšto blokai nebus spausdinami pageidaujamu greičiu.\n"
"\n"
"%s"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s) at the default layer height (%.2f mm).\n"
"\n"
"The layer height has been reduced to %.2f mm (a value used by this printer's profiles) so the tower can reach the requested speed."
msgstr ""
"Galutinis greitis (%.0f mm/s) viršija maksimalų gijos tūrinį greitį (%.1f mm³/s) esant numatytajam sluoksnio aukščiui (%.2f mm).\n"
"\n"
"Sluoksnio aukštis sumažintas iki %.2f mm (reikšmė, naudojama šio spausdintuvo profiliuose), kad bokštas galėtų pasiekti pageidaujamą greitį."
# AI Translated
#, c-format, boost-format
msgid ""
"Even at the smallest layer height used by this printer's profiles (%.2f mm) the end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s).\n"
"\n"
"The layer height will be set to %.2f mm and the end speed lowered to %.0f mm/s.\n"
"\n"
"Continue?"
msgstr ""
"Net ir esant mažiausiam sluoksnio aukščiui, naudojamam šio spausdintuvo profiliuose (%.2f mm), galutinis greitis (%.0f mm/s) viršija maksimalų gijos tūrinį greitį (%.1f mm³/s).\n"
"\n"
"Sluoksnio aukštis bus nustatytas į %.2f mm, o galutinis greitis sumažintas iki %.0f mm/s.\n"
"\n"
"Tęsti?"
# AI Translated
msgid "Continue anyway?"
msgstr "Vis tiek tęsti?"
# AI Translated
msgid "Enable \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "Įjungti „Automatiškai pritaikyti“, kad tai būtų ištaisyta automatiškai, ar vis tiek tęsti?"
# AI Translated
msgid "Enable \"Auto-scale for nozzle\" and \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "Įjungti „Automatinis mastelis pagal purkštuką“ ir „Automatiškai pritaikyti“, kad tai būtų ištaisyta automatiškai, ar vis tiek tęsti?"
msgid "Start retraction length: "
msgstr "Pradinis įtraukimo ilgis: "
@@ -21219,6 +21336,9 @@ msgstr ""
"Venkite deformacijų (warping)\n"
"Ar žinojote, kad spausdinant medžiagas, kurios yra linkusios trauktis ir riestis (pvz., ABS), tinkamas kaitinamojo pagrindo temperatūros padidinimas gali sumažinti deformacijų (warping) tikimybę?"
#~ msgid "Print order within a single layer."
#~ msgstr "Elementų spausdinimo eiliškumas vieno sluoksnio ribose."
#~ msgid "Bottom"
#~ msgstr "Apačia"
@@ -21306,9 +21426,6 @@ msgstr ""
#~ msgid "°C"
#~ msgstr "°C"
#~ msgid "%"
#~ msgstr "%"
#~ msgid "Renders cast shadows on the plate in realistic view."
#~ msgstr "Realistiniame vaizde atvaizduoja krentančius šešėlius ant spausdinimo pagrindo."
+123 -6
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-07-26 21:59-0300\n"
"POT-Creation-Date: 2026-07-29 17:40-0300\n"
"PO-Revision-Date: \n"
"Last-Translator: \n"
"Language-Team: \n"
@@ -4172,6 +4172,10 @@ msgstr "Rangschikken..."
msgid "Arranging"
msgstr "Rangschikken"
# AI Translated
msgid "Arranging "
msgstr "Rangschikken "
msgid "Arranging canceled."
msgstr "Rangschikken geannuleerd."
@@ -10076,6 +10080,21 @@ msgstr "Onderliggende lagen dimmen"
msgid "When scrubbing the layer slider in the sliced preview, render the layers below the current one darkened so that only the layer being viewed is shown at full brightness."
msgstr "Bij het verschuiven van de laagschuifregelaar in de slicevoorvertoning worden de lagen onder de huidige laag verduisterd weergegeven, zodat alleen de bekeken laag op volle helderheid wordt getoond."
# AI Translated
msgid "Dimmed layer brightness"
msgstr "Helderheid van gedimde lagen"
msgid "%"
msgstr "%"
# AI Translated
msgid ""
"How brightly the dimmed layers are rendered when \"Dim lower layers\" is enabled.\n"
"99% is barely darkened, 0% renders them black. Capped at 99% because 100% would be the same as disabling the option."
msgstr ""
"Hoe helder de gedimde lagen worden weergegeven wanneer \"Onderliggende lagen dimmen\" is ingeschakeld.\n"
"99% is nauwelijks donkerder, 0% maakt ze zwart. Beperkt tot 99%, omdat 100% hetzelfde zou zijn als de optie uitschakelen."
msgid "Login region"
msgstr "Inlogregio"
@@ -14544,13 +14563,37 @@ msgid "Intra-layer order"
msgstr "Volgorde binnen een laag"
# AI Translated
msgid "Print order within a single layer."
msgstr "Printvolgorde binnen één laag."
msgid ""
"Order in which object instances are visited within a single layer, which controls how much travel is spent moving between them.\n"
"\n"
"Default: nearest-neighbor chaining, refined with 2-opt and crossing removal. A good general choice.\n"
"As object list: instances are printed in the same order as the object list, without any path optimization. Use it when you need a predictable, manually controlled order.\n"
"Best of all (shortest path): every strategy is evaluated and the shortest one is used. The object instance order is decided once for the whole print, while the ordering of individual islands is decided per layer, so different layers may end up using different strategies. Slightly slower to slice.\n"
"Snake: serpentine row-by-row traversal, refined with 2-opt. Well suited to regular grids of many small parts.\n"
"\n"
"With multiple filaments or tools in the same layer, minimizing tool changes takes priority: objects are grouped by filament first and this setting only orders the instances within each filament group, so the overall sequence may not look like the shortest path across the plate."
msgstr ""
"De volgorde waarin objectinstanties binnen één laag worden bezocht, wat bepaalt hoeveel verplaatsing er nodig is om ertussen te bewegen.\n"
"\n"
"Standaard: aaneenschakeling via de dichtstbijzijnde buur, verfijnd met 2-opt en het verwijderen van kruisingen. Een goede algemene keuze.\n"
"Zoals de objectlijst: instanties worden afgedrukt in dezelfde volgorde als de objectlijst, zonder enige padoptimalisatie. Gebruik dit wanneer je een voorspelbare, handmatig bepaalde volgorde nodig hebt.\n"
"Beste van allemaal (kortste pad): elke strategie wordt beoordeeld en de kortste wordt gebruikt. De volgorde van de objectinstanties wordt eenmalig voor de hele print bepaald, terwijl de volgorde van de afzonderlijke eilanden per laag wordt bepaald, waardoor verschillende lagen uiteindelijk verschillende strategieën kunnen gebruiken. Het slicen duurt iets langer.\n"
"Slingerend: slingerend traject, rij voor rij, verfijnd met 2-opt. Zeer geschikt voor regelmatige rasters van veel kleine onderdelen.\n"
"\n"
"Bij meerdere filamenten of gereedschappen in dezelfde laag heeft het beperken van het aantal gereedschapswissels voorrang: objecten worden eerst per filament gegroepeerd en deze instelling bepaalt alleen de volgorde van de instanties binnen elke filamentgroep, waardoor de totale volgorde er niet uitziet als het kortste pad over het printbed."
# AI Translated
msgid "As object list"
msgstr "Zoals de objectlijst"
# AI Translated
msgid "Best of all (shortest path)"
msgstr "Beste van allemaal (kortste pad)"
# AI Translated
msgid "Snake"
msgstr "Slingerend"
msgid "Slow printing down for better layer cooling"
msgstr "Printsnelheid omlaag brengen zodat de laag beter kan koelen"
@@ -20593,6 +20636,20 @@ msgstr ""
"Er zijn meerdere IP-adressen die verwijzen naar hostname %1%.\n"
"Selecteer er een die gebruikt moet worden."
# AI Translated
msgid "Auto-scale for nozzle"
msgstr "Automatisch schalen naar mondstuk"
# AI Translated
msgid ""
"This model is designed around a 0.4 mm nozzle with a 0.2 mm layer height. \n"
"When the scaling option is enabled (recommended), it dynamically resizes to match your current nozzle diameter and an appropriate layer height, making the test both accurate and easy to read.\n"
"Turn scaling off only if you wish to print the reference model exactly as-is."
msgstr ""
"Dit model is ontworpen voor een mondstuk van 0,4 mm met een laaghoogte van 0,2 mm. \n"
"Wanneer de schaaloptie is ingeschakeld (aanbevolen), wordt het model dynamisch aangepast aan je huidige mondstukdiameter en een passende laaghoogte, waardoor de test zowel nauwkeurig als goed afleesbaar is.\n"
"Schakel het schalen alleen uit als je het referentiemodel precies zo wilt printen als het is."
msgid "PA Calibration"
msgstr "PA-kalibratie"
@@ -20736,6 +20793,14 @@ msgstr "Startsnelheid:"
msgid "End speed: "
msgstr "Eindsnelheid:"
# AI Translated
msgid "Auto-adjust to max volumetric speed"
msgstr "Automatisch aanpassen aan max. volumetrische snelheid"
# AI Translated
msgid "If the end speed would exceed the filament's maximum volumetric speed, automatically lower the layer height (keeping standard values and staying within the machine's limits) to reach it. If even the minimum layer height is not enough, lower the end speed instead."
msgstr "Als de eindsnelheid de maximale volumetrische snelheid van het filament zou overschrijden, wordt de laaghoogte automatisch verlaagd (met behoud van standaardwaarden en binnen de limieten van de machine) om die snelheid te halen. Als zelfs de minimale laaghoogte niet volstaat, wordt in plaats daarvan de eindsnelheid verlaagd."
# AI Translated
msgid ""
"Please input valid values:\n"
@@ -20748,6 +20813,57 @@ msgstr ""
"stap >= 0\n"
"einde > start + stap"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s), which limits the outer wall to about %.0f mm/s at this line width and layer height.\n"
" Speeds above this will be clamped, so the upper blocks of the tower will not print at the requested speed.\n"
"\n"
"%s"
msgstr ""
"De eindsnelheid (%.0f mm/s) overschrijdt de maximale volumetrische snelheid van het filament (%.1f mm³/s), waardoor de buitenste wand bij deze lijndikte en laaghoogte beperkt wordt tot ongeveer %.0f mm/s.\n"
" Hogere snelheden worden begrensd, waardoor de bovenste blokken van de toren niet op de gevraagde snelheid worden geprint.\n"
"\n"
"%s"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s) at the default layer height (%.2f mm).\n"
"\n"
"The layer height has been reduced to %.2f mm (a value used by this printer's profiles) so the tower can reach the requested speed."
msgstr ""
"De eindsnelheid (%.0f mm/s) overschrijdt de maximale volumetrische snelheid van het filament (%.1f mm³/s) bij de standaard laaghoogte (%.2f mm).\n"
"\n"
"De laaghoogte is verlaagd naar %.2f mm (een waarde die door de profielen van deze printer wordt gebruikt), zodat de toren de gevraagde snelheid kan halen."
# AI Translated
#, c-format, boost-format
msgid ""
"Even at the smallest layer height used by this printer's profiles (%.2f mm) the end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s).\n"
"\n"
"The layer height will be set to %.2f mm and the end speed lowered to %.0f mm/s.\n"
"\n"
"Continue?"
msgstr ""
"Zelfs bij de kleinste laaghoogte die door de profielen van deze printer wordt gebruikt (%.2f mm) overschrijdt de eindsnelheid (%.0f mm/s) de maximale volumetrische snelheid van het filament (%.1f mm³/s).\n"
"\n"
"De laaghoogte wordt ingesteld op %.2f mm en de eindsnelheid verlaagd naar %.0f mm/s.\n"
"\n"
"Doorgaan?"
# AI Translated
msgid "Continue anyway?"
msgstr "Toch doorgaan?"
# AI Translated
msgid "Enable \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "\"Automatisch aanpassen\" inschakelen om dit automatisch op te lossen, of toch doorgaan?"
# AI Translated
msgid "Enable \"Auto-scale for nozzle\" and \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "\"Automatisch schalen naar mondstuk\" en \"Automatisch aanpassen\" inschakelen om dit automatisch op te lossen, of toch doorgaan?"
msgid "Start retraction length: "
msgstr "Begin terugtreklengte:"
@@ -23665,6 +23781,10 @@ msgstr ""
"Kromtrekken voorkomen\n"
"Wist je dat bij het printen van materialen die gevoelig zijn voor kromtrekken, zoals ABS, een juiste verhoging van de temperatuur van het warmtebed de kans op kromtrekken kan verkleinen?"
# AI Translated
#~ msgid "Print order within a single layer."
#~ msgstr "Printvolgorde binnen één laag."
#~ msgid "Bottom"
#~ msgstr "Onderkant"
@@ -23707,9 +23827,6 @@ msgstr ""
#~ msgid "°C"
#~ msgstr "°C"
#~ msgid "%"
#~ msgstr "%"
#~ msgctxt "Sync_Nozzle_AMS"
#~ msgid "Cancel"
#~ msgstr "Annuleren"
+123 -6
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: OrcaSlicer 2.3.0-rc\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-07-26 21:59-0300\n"
"POT-Creation-Date: 2026-07-29 17:40-0300\n"
"PO-Revision-Date: \n"
"Last-Translator: Krzysztof Morga <<tlumaczeniebs@gmail.com>>\n"
"Language-Team: \n"
@@ -3926,6 +3926,10 @@ msgstr "Układanie..."
msgid "Arranging"
msgstr "Układanie"
# AI Translated
msgid "Arranging "
msgstr "Rozmieszczanie "
msgid "Arranging canceled."
msgstr "Układanie anulowane."
@@ -9479,6 +9483,21 @@ msgstr "Przyciemnij niższe warstwy"
msgid "When scrubbing the layer slider in the sliced preview, render the layers below the current one darkened so that only the layer being viewed is shown at full brightness."
msgstr "Podczas przewijania suwaka warstw w podglądzie po cięciu renderuj warstwy poniżej bieżącej w przyciemnieniu, tak aby tylko oglądana warstwa była w pełnej jasności."
# AI Translated
msgid "Dimmed layer brightness"
msgstr "Jasność przyciemnionych warstw"
msgid "%"
msgstr "%"
# AI Translated
msgid ""
"How brightly the dimmed layers are rendered when \"Dim lower layers\" is enabled.\n"
"99% is barely darkened, 0% renders them black. Capped at 99% because 100% would be the same as disabling the option."
msgstr ""
"Jak jasno renderowane są przyciemnione warstwy, gdy włączona jest opcja „Przyciemnij niższe warstwy”.\n"
"99% oznacza ledwo zauważalne przyciemnienie, 0% renderuje je na czarno. Wartość jest ograniczona do 99%, ponieważ 100% oznaczałoby to samo co wyłączenie opcji."
msgid "Login region"
msgstr "Region logowania"
@@ -13709,12 +13728,37 @@ msgstr "Wg obiektu"
msgid "Intra-layer order"
msgstr "Kolejność warstw"
msgid "Print order within a single layer."
msgstr "Kolejność druku obiektów w obrębie jednej warstwy. Domyślnie lub według listy obiektów"
# AI Translated
msgid ""
"Order in which object instances are visited within a single layer, which controls how much travel is spent moving between them.\n"
"\n"
"Default: nearest-neighbor chaining, refined with 2-opt and crossing removal. A good general choice.\n"
"As object list: instances are printed in the same order as the object list, without any path optimization. Use it when you need a predictable, manually controlled order.\n"
"Best of all (shortest path): every strategy is evaluated and the shortest one is used. The object instance order is decided once for the whole print, while the ordering of individual islands is decided per layer, so different layers may end up using different strategies. Slightly slower to slice.\n"
"Snake: serpentine row-by-row traversal, refined with 2-opt. Well suited to regular grids of many small parts.\n"
"\n"
"With multiple filaments or tools in the same layer, minimizing tool changes takes priority: objects are grouped by filament first and this setting only orders the instances within each filament group, so the overall sequence may not look like the shortest path across the plate."
msgstr ""
"Kolejność, w jakiej instancje obiektów są odwiedzane w obrębie jednej warstwy; decyduje o tym, ile przemieszczeń zajmuje przechodzenie między nimi.\n"
"\n"
"Domyślny: łączenie metodą najbliższego sąsiada, dopracowane algorytmem 2-opt i usuwaniem przecięć. Dobry wybór ogólny.\n"
"Wg listy obiektów: instancje są drukowane w tej samej kolejności co na liście obiektów, bez optymalizacji ścieżki. Użyj, gdy potrzebujesz przewidywalnej, ręcznie ustalonej kolejności.\n"
"Najlepsza ze wszystkich (najkrótsza ścieżka): oceniane są wszystkie strategie i wybierana jest najkrótsza. Kolejność instancji obiektów ustalana jest raz dla całego wydruku, natomiast kolejność poszczególnych wysp jest ustalana dla każdej warstwy, więc różne warstwy mogą ostatecznie korzystać z różnych strategii. Cięcie trwa nieco dłużej.\n"
"Wężykiem: serpentynowe przechodzenie rząd po rzędzie, dopracowane algorytmem 2-opt. Dobrze sprawdza się przy regularnych siatkach wielu małych elementów.\n"
"\n"
"Gdy w tej samej warstwie używanych jest wiele filamentów lub narzędzi, priorytetem jest minimalizacja zmian narzędzia: obiekty są najpierw grupowane według filamentu, a to ustawienie porządkuje jedynie instancje w obrębie każdej grupy, więc ogólna sekwencja może nie wyglądać jak najkrótsza ścieżka po płycie."
msgid "As object list"
msgstr "Wg listy obiektów"
# AI Translated
msgid "Best of all (shortest path)"
msgstr "Najlepsza ze wszystkich (najkrótsza ścieżka)"
# AI Translated
msgid "Snake"
msgstr "Wężykiem"
msgid "Slow printing down for better layer cooling"
msgstr "Zwolnienie druku dla lepszego chłodzenia warstw"
@@ -19211,6 +19255,20 @@ msgstr ""
"Jest kilka adresów IP przypisanych do nazwy hosta %1%.\n"
"Proszę wybrać jeden, który ma być używany."
# AI Translated
msgid "Auto-scale for nozzle"
msgstr "Automatyczne skalowanie do dyszy"
# AI Translated
msgid ""
"This model is designed around a 0.4 mm nozzle with a 0.2 mm layer height. \n"
"When the scaling option is enabled (recommended), it dynamically resizes to match your current nozzle diameter and an appropriate layer height, making the test both accurate and easy to read.\n"
"Turn scaling off only if you wish to print the reference model exactly as-is."
msgstr ""
"Ten model zaprojektowano dla dyszy 0,4 mm i wysokości warstwy 0,2 mm. \n"
"Gdy opcja skalowania jest włączona (zalecane), model dynamicznie dopasowuje rozmiar do średnicy Twojej obecnej dyszy i odpowiedniej wysokości warstwy, dzięki czemu test jest dokładny i łatwy do odczytania.\n"
"Wyłącz skalowanie tylko wtedy, gdy chcesz wydrukować model referencyjny dokładnie w oryginalnej postaci."
msgid "PA Calibration"
msgstr "Kalibracja PA"
@@ -19349,6 +19407,14 @@ msgstr "Rozpocznij z prędkością: "
msgid "End speed: "
msgstr "Zakończ z prędkością: "
# AI Translated
msgid "Auto-adjust to max volumetric speed"
msgstr "Automatyczne dopasowanie do maksymalnej prędkości przepływu"
# AI Translated
msgid "If the end speed would exceed the filament's maximum volumetric speed, automatically lower the layer height (keeping standard values and staying within the machine's limits) to reach it. If even the minimum layer height is not enough, lower the end speed instead."
msgstr "Jeśli prędkość końcowa przekroczyłaby maksymalną prędkość przepływu filamentu, automatycznie obniż wysokość warstwy (zachowując standardowe wartości i pozostając w limitach maszyny), aby ją osiągnąć. Jeśli nawet minimalna wysokość warstwy nie wystarczy, obniżona zostanie prędkość końcowa."
msgid ""
"Please input valid values:\n"
"start > 10\n"
@@ -19360,6 +19426,57 @@ msgstr ""
"krok >= 0\n"
"koniec > start + krok)"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s), which limits the outer wall to about %.0f mm/s at this line width and layer height.\n"
" Speeds above this will be clamped, so the upper blocks of the tower will not print at the requested speed.\n"
"\n"
"%s"
msgstr ""
"Prędkość końcowa (%.0f mm/s) przekracza maksymalną prędkość przepływu filamentu (%.1f mm³/s), która przy tej szerokości linii i wysokości warstwy ogranicza zewnętrzną ścianę do około %.0f mm/s.\n"
" Wyższe prędkości zostaną ograniczone, więc górne bloki wieży nie zostaną wydrukowane z żądaną prędkością.\n"
"\n"
"%s"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s) at the default layer height (%.2f mm).\n"
"\n"
"The layer height has been reduced to %.2f mm (a value used by this printer's profiles) so the tower can reach the requested speed."
msgstr ""
"Prędkość końcowa (%.0f mm/s) przekracza maksymalną prędkość przepływu filamentu (%.1f mm³/s) przy domyślnej wysokości warstwy (%.2f mm).\n"
"\n"
"Wysokość warstwy została zmniejszona do %.2f mm (wartość stosowana w profilach tej drukarki), aby wieża mogła osiągnąć żądaną prędkość."
# AI Translated
#, c-format, boost-format
msgid ""
"Even at the smallest layer height used by this printer's profiles (%.2f mm) the end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s).\n"
"\n"
"The layer height will be set to %.2f mm and the end speed lowered to %.0f mm/s.\n"
"\n"
"Continue?"
msgstr ""
"Nawet przy najmniejszej wysokości warstwy stosowanej w profilach tej drukarki (%.2f mm) prędkość końcowa (%.0f mm/s) przekracza maksymalną prędkość przepływu filamentu (%.1f mm³/s).\n"
"\n"
"Wysokość warstwy zostanie ustawiona na %.2f mm, a prędkość końcowa obniżona do %.0f mm/s.\n"
"\n"
"Kontynuować?"
# AI Translated
msgid "Continue anyway?"
msgstr "Kontynuować mimo to?"
# AI Translated
msgid "Enable \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "Włączyć „Automatyczne dopasowanie”, aby naprawić to automatycznie, czy kontynuować mimo to?"
# AI Translated
msgid "Enable \"Auto-scale for nozzle\" and \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "Włączyć „Automatyczne skalowanie do dyszy” i „Automatyczne dopasowanie”, aby naprawić to automatycznie, czy kontynuować mimo to?"
msgid "Start retraction length: "
msgstr "Długość retrakcji na początku: "
@@ -22117,6 +22234,9 @@ msgstr ""
"Unikaj odkształceń\n"
"Czy wiesz, że podczas drukowania filamentami podatnymi na odkształcenia, takimi jak ABS, odpowiednie zwiększenie temperatury podgrzewanej płyty może zmniejszyć prawdopodobieństwo odkształceń?"
#~ msgid "Print order within a single layer."
#~ msgstr "Kolejność druku obiektów w obrębie jednej warstwy. Domyślnie lub według listy obiektów"
#~ msgid "Bottom"
#~ msgstr "Dół"
@@ -22191,9 +22311,6 @@ msgstr ""
#~ msgid "°C"
#~ msgstr "°C"
#~ msgid "%"
#~ msgstr "%"
#~ msgid "Continue to sync filaments"
#~ msgstr "Kontynuuj aby zsynchronizować filamenty"
+124 -3
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-07-26 21:59-0300\n"
"POT-Creation-Date: 2026-07-29 17:40-0300\n"
"PO-Revision-Date: 2026-07-26 11:14-0300\n"
"Last-Translator: Alexandre Folle de Menezes\n"
"Language-Team: Portuguese, Brazilian\n"
@@ -3700,6 +3700,10 @@ msgstr "Organizando…"
msgid "Arranging"
msgstr "Organizando"
# AI Translated
msgid "Arranging "
msgstr "Organizando "
msgid "Arranging canceled."
msgstr "Organização cancelada."
@@ -8975,6 +8979,22 @@ msgstr "Escurecer camadas inferiores"
msgid "When scrubbing the layer slider in the sliced preview, render the layers below the current one darkened so that only the layer being viewed is shown at full brightness."
msgstr "Ao mover o controle deslizante de camadas na pré-visualização fatiada, renderiza as camadas abaixo da atual escurecidas, de modo que apenas a camada visualizada seja exibida com brilho total."
# AI Translated
msgid "Dimmed layer brightness"
msgstr "Brilho das camadas escurecidas"
# AI Translated
msgid "%"
msgstr "%"
# AI Translated
msgid ""
"How brightly the dimmed layers are rendered when \"Dim lower layers\" is enabled.\n"
"99% is barely darkened, 0% renders them black. Capped at 99% because 100% would be the same as disabling the option."
msgstr ""
"Com que brilho as camadas escurecidas são exibidas quando \"Escurecer camadas inferiores\" está ativado.\n"
"99% quase não escurece, 0% as deixa pretas. Limitado a 99% porque 100% seria o mesmo que desativar a opção."
msgid "Login region"
msgstr "Região de login"
@@ -12983,12 +13003,37 @@ msgstr "Por objeto"
msgid "Intra-layer order"
msgstr "Ordem intra-camada"
msgid "Print order within a single layer."
msgstr "Ordem de impressão dentro de uma única camada."
# AI Translated
msgid ""
"Order in which object instances are visited within a single layer, which controls how much travel is spent moving between them.\n"
"\n"
"Default: nearest-neighbor chaining, refined with 2-opt and crossing removal. A good general choice.\n"
"As object list: instances are printed in the same order as the object list, without any path optimization. Use it when you need a predictable, manually controlled order.\n"
"Best of all (shortest path): every strategy is evaluated and the shortest one is used. The object instance order is decided once for the whole print, while the ordering of individual islands is decided per layer, so different layers may end up using different strategies. Slightly slower to slice.\n"
"Snake: serpentine row-by-row traversal, refined with 2-opt. Well suited to regular grids of many small parts.\n"
"\n"
"With multiple filaments or tools in the same layer, minimizing tool changes takes priority: objects are grouped by filament first and this setting only orders the instances within each filament group, so the overall sequence may not look like the shortest path across the plate."
msgstr ""
"Ordem em que as instâncias dos objetos são visitadas dentro de uma mesma camada, o que determina quanto deslocamento é gasto no trajeto entre elas.\n"
"\n"
"Padrão: encadeamento por vizinho mais próximo, refinado com 2-opt e remoção de cruzamentos. Uma boa escolha geral.\n"
"Como lista de objetos: as instâncias são impressas na mesma ordem da lista de objetos, sem nenhuma otimização de trajeto. Use quando precisar de uma ordem previsível e controlada manualmente.\n"
"Melhor de todas (caminho mais curto): todas as estratégias são avaliadas e a mais curta é usada. A ordem das instâncias dos objetos é definida uma única vez para toda a impressão, enquanto a ordem das ilhas individuais é definida por camada, de modo que camadas diferentes podem acabar usando estratégias diferentes. O fatiamento fica um pouco mais lento.\n"
"Serpentina: percurso em serpentina, linha por linha, refinado com 2-opt. Adequado a grades regulares de muitas peças pequenas.\n"
"\n"
"Com vários filamentos ou ferramentas na mesma camada, minimizar as trocas de ferramenta tem prioridade: os objetos são agrupados primeiro por filamento e esta configuração ordena apenas as instâncias dentro de cada grupo de filamento, portanto a sequência geral pode não parecer o caminho mais curto pela placa."
msgid "As object list"
msgstr "Como lista de objetos"
# AI Translated
msgid "Best of all (shortest path)"
msgstr "Melhor de todas (caminho mais curto)"
# AI Translated
msgid "Snake"
msgstr "Serpentina"
msgid "Slow printing down for better layer cooling"
msgstr "Diminuir a velocidade de impressão para melhor resfriamento de camada"
@@ -18278,6 +18323,20 @@ msgstr ""
"Há vários endereços IP resolvendo para o nome do host %1%.\n"
"Por favor, selecione um que deve ser usado."
# AI Translated
msgid "Auto-scale for nozzle"
msgstr "Escala automática para o bico"
# AI Translated
msgid ""
"This model is designed around a 0.4 mm nozzle with a 0.2 mm layer height. \n"
"When the scaling option is enabled (recommended), it dynamically resizes to match your current nozzle diameter and an appropriate layer height, making the test both accurate and easy to read.\n"
"Turn scaling off only if you wish to print the reference model exactly as-is."
msgstr ""
"Este modelo foi projetado para um bico de 0,4 mm com altura de camada de 0,2 mm. \n"
"Quando a opção de escala está ativada (recomendado), ele é redimensionado dinamicamente para corresponder ao diâmetro do bico atual e a uma altura de camada apropriada, tornando o teste preciso e fácil de ler.\n"
"Desative a escala apenas se quiser imprimir o modelo de referência exatamente como está."
msgid "PA Calibration"
msgstr "Calibração de PA"
@@ -18414,6 +18473,14 @@ msgstr "Velocidade Inicial: "
msgid "End speed: "
msgstr "Velocidade Final: "
# AI Translated
msgid "Auto-adjust to max volumetric speed"
msgstr "Ajuste automático à velocidade volumétrica máxima"
# AI Translated
msgid "If the end speed would exceed the filament's maximum volumetric speed, automatically lower the layer height (keeping standard values and staying within the machine's limits) to reach it. If even the minimum layer height is not enough, lower the end speed instead."
msgstr "Se a velocidade final ultrapassar a velocidade volumétrica máxima do filamento, reduz automaticamente a altura de camada (mantendo valores padrão e respeitando os limites da máquina) para alcançá-la. Se nem mesmo a altura de camada mínima for suficiente, reduz a velocidade final."
msgid ""
"Please input valid values:\n"
"start > 10\n"
@@ -18425,6 +18492,57 @@ msgstr ""
"passo >= 0\n"
"fim > início + passo"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s), which limits the outer wall to about %.0f mm/s at this line width and layer height.\n"
" Speeds above this will be clamped, so the upper blocks of the tower will not print at the requested speed.\n"
"\n"
"%s"
msgstr ""
"A velocidade final (%.0f mm/s) ultrapassa a velocidade volumétrica máxima do filamento (%.1f mm³/s), o que limita a parede externa a cerca de %.0f mm/s com esta largura de linha e altura de camada.\n"
" Velocidades acima disso serão limitadas, portanto os blocos superiores da torre não serão impressos na velocidade solicitada.\n"
"\n"
"%s"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s) at the default layer height (%.2f mm).\n"
"\n"
"The layer height has been reduced to %.2f mm (a value used by this printer's profiles) so the tower can reach the requested speed."
msgstr ""
"A velocidade final (%.0f mm/s) ultrapassa a velocidade volumétrica máxima do filamento (%.1f mm³/s) na altura de camada padrão (%.2f mm).\n"
"\n"
"A altura de camada foi reduzida para %.2f mm (um valor usado pelos perfis desta impressora) para que a torre possa atingir a velocidade solicitada."
# AI Translated
#, c-format, boost-format
msgid ""
"Even at the smallest layer height used by this printer's profiles (%.2f mm) the end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s).\n"
"\n"
"The layer height will be set to %.2f mm and the end speed lowered to %.0f mm/s.\n"
"\n"
"Continue?"
msgstr ""
"Mesmo na menor altura de camada usada pelos perfis desta impressora (%.2f mm), a velocidade final (%.0f mm/s) ultrapassa a velocidade volumétrica máxima do filamento (%.1f mm³/s).\n"
"\n"
"A altura de camada será definida como %.2f mm e a velocidade final reduzida para %.0f mm/s.\n"
"\n"
"Continuar?"
# AI Translated
msgid "Continue anyway?"
msgstr "Continuar mesmo assim?"
# AI Translated
msgid "Enable \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "Ativar \"Ajuste automático\" para corrigir isso automaticamente ou continuar mesmo assim?"
# AI Translated
msgid "Enable \"Auto-scale for nozzle\" and \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "Ativar \"Escala automática para o bico\" e \"Ajuste automático\" para corrigir isso automaticamente ou continuar mesmo assim?"
msgid "Start retraction length: "
msgstr "Distância de Retração Inicial: "
@@ -20948,6 +21066,9 @@ msgstr ""
"Evitar empenamento\n"
"Você sabia que ao imprimir materiais propensos ao empenamento como ABS, aumentar adequadamente a temperatura da mesa aquecida pode reduzir a probabilidade de empenamento?"
#~ msgid "Print order within a single layer."
#~ msgstr "Ordem de impressão dentro de uma única camada."
#~ msgid "Bottom"
#~ msgstr "Inferior"
+123 -6
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: OrcaSlicer V2.5.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-07-26 21:59-0300\n"
"POT-Creation-Date: 2026-07-29 17:40-0300\n"
"PO-Revision-Date: 2026-02-25 13:38+0300\n"
"Last-Translator: Felix14_v2\n"
"Language-Team: Felix14_v2 (ДС/ТГ: @felix14_v2, почта: aleks111001@list.ru), Andylg <andylg@yandex.ru>\n"
@@ -3888,6 +3888,10 @@ msgstr "Расстановка..."
msgid "Arranging"
msgstr "Расстановка"
# AI Translated
msgid "Arranging "
msgstr "Расстановка "
msgid "Arranging canceled."
msgstr "Расстановка отменена."
@@ -9387,6 +9391,21 @@ msgstr "Затемнять нижние слои"
msgid "When scrubbing the layer slider in the sliced preview, render the layers below the current one darkened so that only the layer being viewed is shown at full brightness."
msgstr "При перемещении ползунка слоёв в предпросмотре нарезки слои ниже текущего отображаются затемнёнными, так что на полной яркости показан только просматриваемый слой."
# AI Translated
msgid "Dimmed layer brightness"
msgstr "Яркость затемнённых слоёв"
msgid "%"
msgstr "%"
# AI Translated
msgid ""
"How brightly the dimmed layers are rendered when \"Dim lower layers\" is enabled.\n"
"99% is barely darkened, 0% renders them black. Capped at 99% because 100% would be the same as disabling the option."
msgstr ""
"Насколько ярко отображаются затемнённые слои, когда включена опция «Затемнять нижние слои».\n"
"99% — затемнение почти незаметно, 0% — слои становятся чёрными. Максимум ограничен 99%, так как 100% равносильно отключению опции."
msgid "Login region"
msgstr "Регион входа"
@@ -13531,12 +13550,37 @@ msgstr "По очереди"
msgid "Intra-layer order"
msgstr "Очерёдность моделей"
msgid "Print order within a single layer."
msgstr "Последовательность печати моделей в пределах одного слоя."
# AI Translated
msgid ""
"Order in which object instances are visited within a single layer, which controls how much travel is spent moving between them.\n"
"\n"
"Default: nearest-neighbor chaining, refined with 2-opt and crossing removal. A good general choice.\n"
"As object list: instances are printed in the same order as the object list, without any path optimization. Use it when you need a predictable, manually controlled order.\n"
"Best of all (shortest path): every strategy is evaluated and the shortest one is used. The object instance order is decided once for the whole print, while the ordering of individual islands is decided per layer, so different layers may end up using different strategies. Slightly slower to slice.\n"
"Snake: serpentine row-by-row traversal, refined with 2-opt. Well suited to regular grids of many small parts.\n"
"\n"
"With multiple filaments or tools in the same layer, minimizing tool changes takes priority: objects are grouped by filament first and this setting only orders the instances within each filament group, so the overall sequence may not look like the shortest path across the plate."
msgstr ""
"Порядок обхода экземпляров моделей в пределах одного слоя; определяет, сколько перемещений тратится на переходы между ними.\n"
"\n"
"По умолчанию: построение цепочки методом ближайшего соседа с последующим улучшением алгоритмом 2-opt и устранением пересечений. Хороший универсальный вариант.\n"
"По списку: экземпляры печатаются в том же порядке, что и в списке моделей, без какой-либо оптимизации пути. Используйте, когда нужен предсказуемый, задаваемый вручную порядок.\n"
"Лучший из всех (кратчайший путь): оцениваются все стратегии и применяется та, что даёт кратчайший путь. Порядок экземпляров моделей определяется один раз для всей печати, а порядок отдельных островков — для каждого слоя, поэтому на разных слоях могут использоваться разные стратегии. Нарезка идёт немного медленнее.\n"
"Змейкой: змеевидный обход ряд за рядом с улучшением алгоритмом 2-opt. Хорошо подходит для регулярных сеток из множества мелких деталей.\n"
"\n"
"Если в одном слое используется несколько материалов или инструментов, приоритет отдаётся минимизации смен инструмента: модели сначала группируются по материалу, и эта настройка упорядочивает только экземпляры внутри каждой группы, поэтому общая последовательность может не выглядеть как кратчайший путь по столу."
msgid "As object list"
msgstr "По списку"
# AI Translated
msgid "Best of all (shortest path)"
msgstr "Лучший из всех (кратчайший путь)"
# AI Translated
msgid "Snake"
msgstr "Змейкой"
msgid "Slow printing down for better layer cooling"
msgstr "Замедлять печать для охлаждения слоёв"
@@ -19413,6 +19457,20 @@ msgstr ""
"По имени хоста %1% обнаружено несколько IP-адресов.\n"
"Выберите адрес для использования."
# AI Translated
msgid "Auto-scale for nozzle"
msgstr "Автомасштабирование под сопло"
# AI Translated
msgid ""
"This model is designed around a 0.4 mm nozzle with a 0.2 mm layer height. \n"
"When the scaling option is enabled (recommended), it dynamically resizes to match your current nozzle diameter and an appropriate layer height, making the test both accurate and easy to read.\n"
"Turn scaling off only if you wish to print the reference model exactly as-is."
msgstr ""
"Эта модель рассчитана на сопло 0,4 мм и высоту слоя 0,2 мм. \n"
"Если включено масштабирование (рекомендуется), размер модели динамически подстраивается под диаметр вашего текущего сопла и подходящую высоту слоя, благодаря чему тест получается и точным, и легко читаемым.\n"
"Отключайте масштабирование, только если хотите напечатать эталонную модель ровно в исходном виде."
# В заголовке окна куча места
msgid "PA Calibration"
msgstr "Калибровка Pressure Advance"
@@ -19549,6 +19607,14 @@ msgstr "Начальная скорость: "
msgid "End speed: "
msgstr "Конечная скорость: "
# AI Translated
msgid "Auto-adjust to max volumetric speed"
msgstr "Автоподстройка под предел объёмного расхода"
# AI Translated
msgid "If the end speed would exceed the filament's maximum volumetric speed, automatically lower the layer height (keeping standard values and staying within the machine's limits) to reach it. If even the minimum layer height is not enough, lower the end speed instead."
msgstr "Если конечная скорость превысит предел объёмного расхода материала, автоматически уменьшать высоту слоя (сохраняя стандартные значения и оставаясь в пределах ограничений принтера), чтобы её достичь. Если даже минимальной высоты слоя недостаточно, вместо этого снижается конечная скорость."
msgid ""
"Please input valid values:\n"
"start > 10\n"
@@ -19560,6 +19626,57 @@ msgstr ""
"Шаг ≥ 0\n"
"Конечное > начальное + шаг"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s), which limits the outer wall to about %.0f mm/s at this line width and layer height.\n"
" Speeds above this will be clamped, so the upper blocks of the tower will not print at the requested speed.\n"
"\n"
"%s"
msgstr ""
"Конечная скорость (%.0f мм/с) превышает предел объёмного расхода материала (%.1f мм³/с), который при такой ширине линии и высоте слоя ограничивает скорость внешних периметров примерно до %.0f мм/с.\n"
" Более высокие скорости будут ограничены, поэтому верхние блоки башни не напечатаются с заданной скоростью.\n"
"\n"
"%s"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s) at the default layer height (%.2f mm).\n"
"\n"
"The layer height has been reduced to %.2f mm (a value used by this printer's profiles) so the tower can reach the requested speed."
msgstr ""
"Конечная скорость (%.0f мм/с) превышает предел объёмного расхода материала (%.1f мм³/с) при высоте слоя по умолчанию (%.2f мм).\n"
"\n"
"Высота слоя уменьшена до %.2f мм (значение, используемое профилями этого принтера), чтобы башня могла достичь заданной скорости."
# AI Translated
#, c-format, boost-format
msgid ""
"Even at the smallest layer height used by this printer's profiles (%.2f mm) the end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s).\n"
"\n"
"The layer height will be set to %.2f mm and the end speed lowered to %.0f mm/s.\n"
"\n"
"Continue?"
msgstr ""
"Даже при наименьшей высоте слоя, используемой профилями этого принтера (%.2f мм), конечная скорость (%.0f мм/с) превышает предел объёмного расхода материала (%.1f мм³/с).\n"
"\n"
"Высота слоя будет установлена в %.2f мм, а конечная скорость снижена до %.0f мм/с.\n"
"\n"
"Продолжить?"
# AI Translated
msgid "Continue anyway?"
msgstr "Всё равно продолжить?"
# AI Translated
msgid "Enable \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "Включить «Автоподстройку» для автоматического исправления или всё равно продолжить?"
# AI Translated
msgid "Enable \"Auto-scale for nozzle\" and \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "Включить «Автомасштабирование под сопло» и «Автоподстройку» для автоматического исправления или всё равно продолжить?"
msgid "Start retraction length: "
msgstr "Начальная длина отката: "
@@ -22153,6 +22270,9 @@ msgstr ""
"Предотвращение коробления материала\n"
"Знаете ли вы, что при печати материалами, склонными к короблению, таких как ABS, повышение температуры подогреваемого стола может снизить эту вероятность?"
#~ msgid "Print order within a single layer."
#~ msgstr "Последовательность печати моделей в пределах одного слоя."
#~ msgid "Bottom"
#~ msgstr "Снизу"
@@ -22226,9 +22346,6 @@ msgstr ""
#~ msgid "°C"
#~ msgstr "°C"
#~ msgid "%"
#~ msgstr "%"
#~ msgid "Renders cast shadows on the plate in realistic view."
#~ msgstr "Отрисовывать тени в режиме продвинутой графики."
+123 -6
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-07-26 21:59-0300\n"
"POT-Creation-Date: 2026-07-29 17:40-0300\n"
"Language: sv\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -4245,6 +4245,10 @@ msgstr "Placerar..."
msgid "Arranging"
msgstr "Placerar"
# AI Translated
msgid "Arranging "
msgstr "Placerar "
msgid "Arranging canceled."
msgstr "Placering avbruten."
@@ -10187,6 +10191,21 @@ msgstr "Dämpa underliggande lager"
msgid "When scrubbing the layer slider in the sliced preview, render the layers below the current one darkened so that only the layer being viewed is shown at full brightness."
msgstr "När du drar i lagerreglaget i den beredda förhandsgranskningen renderas lagren under det aktuella mörkare, så att endast det lager du tittar på visas med full ljusstyrka."
# AI Translated
msgid "Dimmed layer brightness"
msgstr "Ljusstyrka för dämpade lager"
msgid "%"
msgstr "%"
# AI Translated
msgid ""
"How brightly the dimmed layers are rendered when \"Dim lower layers\" is enabled.\n"
"99% is barely darkened, 0% renders them black. Capped at 99% because 100% would be the same as disabling the option."
msgstr ""
"Hur ljust de dämpade lagren återges när \"Dämpa underliggande lager\" är aktiverat.\n"
"99% är knappt mörkare, 0% gör dem helt svarta. Begränsat till 99% eftersom 100% skulle vara detsamma som att stänga av alternativet."
msgid "Login region"
msgstr "Logga in region"
@@ -14716,13 +14735,37 @@ msgid "Intra-layer order"
msgstr "Ordning inom lager"
# AI Translated
msgid "Print order within a single layer."
msgstr "Utskriftsordning inom ett enskilt lager."
msgid ""
"Order in which object instances are visited within a single layer, which controls how much travel is spent moving between them.\n"
"\n"
"Default: nearest-neighbor chaining, refined with 2-opt and crossing removal. A good general choice.\n"
"As object list: instances are printed in the same order as the object list, without any path optimization. Use it when you need a predictable, manually controlled order.\n"
"Best of all (shortest path): every strategy is evaluated and the shortest one is used. The object instance order is decided once for the whole print, while the ordering of individual islands is decided per layer, so different layers may end up using different strategies. Slightly slower to slice.\n"
"Snake: serpentine row-by-row traversal, refined with 2-opt. Well suited to regular grids of many small parts.\n"
"\n"
"With multiple filaments or tools in the same layer, minimizing tool changes takes priority: objects are grouped by filament first and this setting only orders the instances within each filament group, so the overall sequence may not look like the shortest path across the plate."
msgstr ""
"Ordningen som objektinstanserna besöks i inom ett och samma lager, vilket avgör hur mycket förflyttning som går åt mellan dem.\n"
"\n"
"Standard: kedjning efter närmaste granne, förfinad med 2-opt och borttagning av korsningar. Ett bra generellt val.\n"
"Som objektlistan: instanserna skrivs ut i samma ordning som i objektlistan, utan någon vägoptimering. Använd det när du behöver en förutsägbar ordning som du styr manuellt.\n"
"Bäst av alla (kortaste vägen): varje strategi utvärderas och den kortaste används. Ordningen för objektinstanserna bestäms en gång för hela utskriften, medan ordningen för enskilda öar bestäms per lager, så olika lager kan sluta med att använda olika strategier. Beredningen blir något långsammare.\n"
"Slingrande: slingrande färdväg rad för rad, förfinad med 2-opt. Passar bra för regelbundna rutnät med många små detaljer.\n"
"\n"
"Med flera filament eller verktyg i samma lager prioriteras att minimera antalet verktygsbyten: objekten grupperas först efter filament och den här inställningen ordnar bara instanserna inom varje filamentgrupp, så den totala sekvensen kanske inte ser ut som den kortaste vägen över plattan."
# AI Translated
msgid "As object list"
msgstr "Som objektlistan"
# AI Translated
msgid "Best of all (shortest path)"
msgstr "Bäst av alla (kortaste vägen)"
# AI Translated
msgid "Snake"
msgstr "Slingrande"
msgid "Slow printing down for better layer cooling"
msgstr "Sakta ner utskrift för bättre kylning av lager"
@@ -20823,6 +20866,20 @@ msgstr ""
"Det finns flera IP-adresser som pekar på värdnamnet %1%.\n"
"Välj vilken som ska användas."
# AI Translated
msgid "Auto-scale for nozzle"
msgstr "Autoskala efter nozzel"
# AI Translated
msgid ""
"This model is designed around a 0.4 mm nozzle with a 0.2 mm layer height. \n"
"When the scaling option is enabled (recommended), it dynamically resizes to match your current nozzle diameter and an appropriate layer height, making the test both accurate and easy to read.\n"
"Turn scaling off only if you wish to print the reference model exactly as-is."
msgstr ""
"Den här modellen är utformad för en nozzel på 0,4 mm med en lagerhöjd på 0,2 mm. \n"
"När skalningsalternativet är aktiverat (rekommenderas) ändras storleken dynamiskt så att den matchar din aktuella nozzeldiameter och en lämplig lagerhöjd, vilket gör testet både noggrant och lättläst.\n"
"Stäng bara av skalningen om du vill skriva ut referensmodellen exakt som den är."
msgid "PA Calibration"
msgstr "PA kalibrering"
@@ -20969,6 +21026,14 @@ msgstr "Start hastighet: "
msgid "End speed: "
msgstr "Sluthastighet: "
# AI Translated
msgid "Auto-adjust to max volumetric speed"
msgstr "Autojustera till max volymetrisk hastighet"
# AI Translated
msgid "If the end speed would exceed the filament's maximum volumetric speed, automatically lower the layer height (keeping standard values and staying within the machine's limits) to reach it. If even the minimum layer height is not enough, lower the end speed instead."
msgstr "Om sluthastigheten skulle överskrida filamentets maximala volymetriska hastighet sänks lagerhöjden automatiskt (med bibehållna standardvärden och inom maskinens gränser) för att nå den. Om inte ens den minsta lagerhöjden räcker sänks sluthastigheten i stället."
# AI Translated
msgid ""
"Please input valid values:\n"
@@ -20981,6 +21046,57 @@ msgstr ""
"steg >= 0\n"
"slut > start + steg"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s), which limits the outer wall to about %.0f mm/s at this line width and layer height.\n"
" Speeds above this will be clamped, so the upper blocks of the tower will not print at the requested speed.\n"
"\n"
"%s"
msgstr ""
"Sluthastigheten (%.0f mm/s) överskrider filamentets maximala volymetriska hastighet (%.1f mm³/s), vilket begränsar den yttre väggen till omkring %.0f mm/s vid den här linjebredden och lagerhöjden.\n"
" Högre hastigheter kapas, så tornets övre block skrivs inte ut med den begärda hastigheten.\n"
"\n"
"%s"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s) at the default layer height (%.2f mm).\n"
"\n"
"The layer height has been reduced to %.2f mm (a value used by this printer's profiles) so the tower can reach the requested speed."
msgstr ""
"Sluthastigheten (%.0f mm/s) överskrider filamentets maximala volymetriska hastighet (%.1f mm³/s) vid standardlagerhöjden (%.2f mm).\n"
"\n"
"Lagerhöjden har sänkts till %.2f mm (ett värde som används av den här skrivarens profiler) så att tornet kan nå den begärda hastigheten."
# AI Translated
#, c-format, boost-format
msgid ""
"Even at the smallest layer height used by this printer's profiles (%.2f mm) the end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s).\n"
"\n"
"The layer height will be set to %.2f mm and the end speed lowered to %.0f mm/s.\n"
"\n"
"Continue?"
msgstr ""
"Även vid den minsta lagerhöjd som används av den här skrivarens profiler (%.2f mm) överskrider sluthastigheten (%.0f mm/s) filamentets maximala volymetriska hastighet (%.1f mm³/s).\n"
"\n"
"Lagerhöjden ställs in på %.2f mm och sluthastigheten sänks till %.0f mm/s.\n"
"\n"
"Vill du fortsätta?"
# AI Translated
msgid "Continue anyway?"
msgstr "Vill du fortsätta ändå?"
# AI Translated
msgid "Enable \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "Vill du aktivera \"Autojustera\" för att åtgärda detta automatiskt, eller fortsätta ändå?"
# AI Translated
msgid "Enable \"Auto-scale for nozzle\" and \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "Vill du aktivera \"Autoskala efter nozzel\" och \"Autojustera\" för att åtgärda detta automatiskt, eller fortsätta ändå?"
msgid "Start retraction length: "
msgstr "Starta retraktion längd: "
@@ -23955,6 +24071,10 @@ msgstr ""
"Undvik vridning\n"
"Visste du att när du skriver ut material som är benägna att vrida, såsom ABS, kan en lämplig ökning av värmebäddens temperatur minska sannolikheten för vridning?"
# AI Translated
#~ msgid "Print order within a single layer."
#~ msgstr "Utskriftsordning inom ett enskilt lager."
#~ msgid "Bottom"
#~ msgstr "Botten"
@@ -24000,9 +24120,6 @@ msgstr ""
#~ msgid "°C"
#~ msgstr "° C"
#~ msgid "%"
#~ msgstr "%"
#~ msgctxt "Sync_Nozzle_AMS"
#~ msgid "Cancel"
#~ msgstr "Avbryt"
+123 -6
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-07-26 21:59-0300\n"
"POT-Creation-Date: 2026-07-29 17:40-0300\n"
"PO-Revision-Date: 2026-06-19 13:40+0700\n"
"Last-Translator: Icezaza\n"
"Language-Team: Thai\n"
@@ -3834,6 +3834,10 @@ msgstr "กำลังจัด..."
msgid "Arranging"
msgstr "การจัด"
# AI Translated
msgid "Arranging "
msgstr "กำลังจัด "
msgid "Arranging canceled."
msgstr "ยกเลิกการจัดเตรียมแล้ว"
@@ -9156,6 +9160,21 @@ msgstr "หรี่เลเยอร์ด้านล่าง"
msgid "When scrubbing the layer slider in the sliced preview, render the layers below the current one darkened so that only the layer being viewed is shown at full brightness."
msgstr "เมื่อเลื่อนแถบเลเยอร์ในตัวอย่างที่สไลซ์แล้ว จะแสดงเลเยอร์ที่อยู่ต่ำกว่าเลเยอร์ปัจจุบันแบบมืดลง เพื่อให้เห็นเฉพาะเลเยอร์ที่กำลังดูอยู่ด้วยความสว่างเต็มที่"
# AI Translated
msgid "Dimmed layer brightness"
msgstr "ความสว่างของเลเยอร์ที่หรี่"
msgid "%"
msgstr "%"
# AI Translated
msgid ""
"How brightly the dimmed layers are rendered when \"Dim lower layers\" is enabled.\n"
"99% is barely darkened, 0% renders them black. Capped at 99% because 100% would be the same as disabling the option."
msgstr ""
"ความสว่างในการแสดงผลของเลเยอร์ที่ถูกหรี่ เมื่อเปิดใช้งาน \"หรี่เลเยอร์ด้านล่าง\"\n"
"99% คือหรี่ลงเพียงเล็กน้อย ส่วน 0% จะแสดงเป็นสีดำ ค่าสูงสุดถูกจำกัดไว้ที่ 99% เพราะ 100% จะให้ผลเหมือนกับการปิดตัวเลือกนี้"
msgid "Login region"
msgstr "เข้าสู่ระบบภูมิภาค"
@@ -13190,12 +13209,37 @@ msgstr "ตามวัตถุ"
msgid "Intra-layer order"
msgstr "คำสั่งภายในชั้น"
msgid "Print order within a single layer."
msgstr "สั่งพิมพ์ภายในชั้นเดียว"
# AI Translated
msgid ""
"Order in which object instances are visited within a single layer, which controls how much travel is spent moving between them.\n"
"\n"
"Default: nearest-neighbor chaining, refined with 2-opt and crossing removal. A good general choice.\n"
"As object list: instances are printed in the same order as the object list, without any path optimization. Use it when you need a predictable, manually controlled order.\n"
"Best of all (shortest path): every strategy is evaluated and the shortest one is used. The object instance order is decided once for the whole print, while the ordering of individual islands is decided per layer, so different layers may end up using different strategies. Slightly slower to slice.\n"
"Snake: serpentine row-by-row traversal, refined with 2-opt. Well suited to regular grids of many small parts.\n"
"\n"
"With multiple filaments or tools in the same layer, minimizing tool changes takes priority: objects are grouped by filament first and this setting only orders the instances within each filament group, so the overall sequence may not look like the shortest path across the plate."
msgstr ""
"ลำดับการเข้าถึงอินสแตนซ์ของวัตถุภายในเลเยอร์เดียวกัน ซึ่งกำหนดว่าต้องเดินหัวเปล่าระหว่างกันมากเพียงใด\n"
"\n"
"ค่าเริ่มต้น: การเชื่อมต่อแบบเพื่อนบ้านที่ใกล้ที่สุด ปรับปรุงด้วย 2-opt และการกำจัดเส้นทางที่ตัดกัน เป็นตัวเลือกทั่วไปที่ดี\n"
"เป็นรายการวัตถุ: พิมพ์อินสแตนซ์ตามลำดับเดียวกับรายการวัตถุ โดยไม่มีการปรับปรุงเส้นทางใด ๆ ใช้เมื่อคุณต้องการลำดับที่คาดเดาได้และควบคุมเองได้\n"
"ดีที่สุดจากทั้งหมด (เส้นทางสั้นที่สุด): จะประเมินทุกกลยุทธ์แล้วเลือกใช้กลยุทธ์ที่สั้นที่สุด ลำดับอินสแตนซ์ของวัตถุจะถูกกำหนดครั้งเดียวสำหรับทั้งงานพิมพ์ ส่วนลำดับของแต่ละเกาะจะถูกกำหนดแยกในแต่ละเลเยอร์ ดังนั้นเลเยอร์ต่าง ๆ อาจใช้กลยุทธ์ที่ต่างกัน สไลซ์ช้าลงเล็กน้อย\n"
"แบบงูเลื้อย: การไล่ทีละแถวแบบงูเลื้อย ปรับปรุงด้วย 2-opt เหมาะกับกริดที่เป็นระเบียบของชิ้นงานเล็ก ๆ จำนวนมาก\n"
"\n"
"เมื่อมีเส้นพลาสติกหรือหัวพิมพ์หลายชนิดในเลเยอร์เดียวกัน การลดจำนวนการเปลี่ยนหัวพิมพ์จะมีความสำคัญก่อน โดยวัตถุจะถูกจัดกลุ่มตามเส้นพลาสติกก่อน และการตั้งค่านี้จะจัดลำดับเฉพาะอินสแตนซ์ภายในแต่ละกลุ่มเส้นพลาสติกเท่านั้น ลำดับโดยรวมจึงอาจดูไม่เหมือนเส้นทางที่สั้นที่สุดบนฐานพิมพ์"
msgid "As object list"
msgstr "เป็นรายการวัตถุ"
# AI Translated
msgid "Best of all (shortest path)"
msgstr "ดีที่สุดจากทั้งหมด (เส้นทางสั้นที่สุด)"
# AI Translated
msgid "Snake"
msgstr "แบบงูเลื้อย"
msgid "Slow printing down for better layer cooling"
msgstr "ชะลอการพิมพ์ลงเพื่อการระบายความร้อนที่ดีขึ้น"
@@ -18524,6 +18568,20 @@ msgstr ""
"มีที่อยู่ IP หลายแห่งที่ใช้ชื่อโฮสต์ %1%\n"
"โปรดเลือกอันที่ควรใช้"
# AI Translated
msgid "Auto-scale for nozzle"
msgstr "ปรับขนาดอัตโนมัติตามหัวฉีด"
# AI Translated
msgid ""
"This model is designed around a 0.4 mm nozzle with a 0.2 mm layer height. \n"
"When the scaling option is enabled (recommended), it dynamically resizes to match your current nozzle diameter and an appropriate layer height, making the test both accurate and easy to read.\n"
"Turn scaling off only if you wish to print the reference model exactly as-is."
msgstr ""
"โมเดลนี้ออกแบบมาสำหรับหัวฉีดขนาด 0.4 mm และความสูงเลเยอร์ 0.2 mm \n"
"เมื่อเปิดใช้งานตัวเลือกการปรับขนาด (แนะนำ) โมเดลจะปรับขนาดแบบไดนามิกให้ตรงกับเส้นผ่านศูนย์กลางหัวฉีดปัจจุบันและความสูงเลเยอร์ที่เหมาะสม ทำให้การทดสอบทั้งแม่นยำและอ่านค่าได้ง่าย\n"
"ปิดการปรับขนาดเฉพาะเมื่อคุณต้องการพิมพ์โมเดลอ้างอิงตามขนาดเดิมทุกประการ"
msgid "PA Calibration"
msgstr "ปรับเทียบ PA"
@@ -18660,6 +18718,14 @@ msgstr "ความเร็วเริ่มต้น:"
msgid "End speed: "
msgstr "ความเร็วสิ้นสุด:"
# AI Translated
msgid "Auto-adjust to max volumetric speed"
msgstr "ปรับอัตโนมัติตามความเร็วปริมาตรสูงสุด"
# AI Translated
msgid "If the end speed would exceed the filament's maximum volumetric speed, automatically lower the layer height (keeping standard values and staying within the machine's limits) to reach it. If even the minimum layer height is not enough, lower the end speed instead."
msgstr "หากความเร็วปลายทางจะเกินความเร็วปริมาตรสูงสุดของเส้นพลาสติก ระบบจะลดความสูงเลเยอร์โดยอัตโนมัติ (โดยคงค่ามาตรฐานไว้และอยู่ภายในขีดจำกัดของเครื่อง) เพื่อให้ถึงค่าดังกล่าว หากแม้แต่ความสูงเลเยอร์ต่ำสุดยังไม่เพียงพอ ระบบจะลดความเร็วปลายทางแทน"
msgid ""
"Please input valid values:\n"
"start > 10\n"
@@ -18671,6 +18737,57 @@ msgstr ""
"ขั้นตอน >= 0\n"
"สิ้นสุด> เริ่มต้น + ขั้นตอน"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s), which limits the outer wall to about %.0f mm/s at this line width and layer height.\n"
" Speeds above this will be clamped, so the upper blocks of the tower will not print at the requested speed.\n"
"\n"
"%s"
msgstr ""
"ความเร็วปลายทาง (%.0f mm/s) เกินความเร็วปริมาตรสูงสุดของเส้นพลาสติก (%.1f mm³/s) ซึ่งจำกัดผนังด้านนอกไว้ที่ประมาณ %.0f mm/s ที่ความกว้างเส้นและความสูงเลเยอร์นี้\n"
" ความเร็วที่สูงกว่านี้จะถูกจำกัดไว้ ดังนั้นบล็อกด้านบนของทาวเวอร์จะไม่พิมพ์ด้วยความเร็วที่ร้องขอ\n"
"\n"
"%s"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s) at the default layer height (%.2f mm).\n"
"\n"
"The layer height has been reduced to %.2f mm (a value used by this printer's profiles) so the tower can reach the requested speed."
msgstr ""
"ความเร็วปลายทาง (%.0f mm/s) เกินความเร็วปริมาตรสูงสุดของเส้นพลาสติก (%.1f mm³/s) ที่ความสูงเลเยอร์เริ่มต้น (%.2f mm)\n"
"\n"
"ความสูงเลเยอร์ถูกลดลงเป็น %.2f mm (ค่าที่ใช้ในพรีเซ็ตของเครื่องพิมพ์นี้) เพื่อให้ทาวเวอร์ถึงความเร็วที่ร้องขอได้"
# AI Translated
#, c-format, boost-format
msgid ""
"Even at the smallest layer height used by this printer's profiles (%.2f mm) the end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s).\n"
"\n"
"The layer height will be set to %.2f mm and the end speed lowered to %.0f mm/s.\n"
"\n"
"Continue?"
msgstr ""
"แม้แต่ที่ความสูงเลเยอร์ต่ำสุดที่ใช้ในพรีเซ็ตของเครื่องพิมพ์นี้ (%.2f mm) ความเร็วปลายทาง (%.0f mm/s) ก็ยังเกินความเร็วปริมาตรสูงสุดของเส้นพลาสติก (%.1f mm³/s)\n"
"\n"
"ความสูงเลเยอร์จะถูกตั้งเป็น %.2f mm และความเร็วปลายทางจะถูกลดลงเป็น %.0f mm/s\n"
"\n"
"ดำเนินการต่อหรือไม่?"
# AI Translated
msgid "Continue anyway?"
msgstr "ดำเนินการต่ออยู่ดีหรือไม่?"
# AI Translated
msgid "Enable \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "เปิดใช้งาน \"ปรับอัตโนมัติ\" เพื่อแก้ไขปัญหานี้โดยอัตโนมัติ หรือดำเนินการต่ออยู่ดี?"
# AI Translated
msgid "Enable \"Auto-scale for nozzle\" and \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "เปิดใช้งาน \"ปรับขนาดอัตโนมัติตามหัวฉีด\" และ \"ปรับอัตโนมัติ\" เพื่อแก้ไขปัญหานี้โดยอัตโนมัติ หรือดำเนินการต่ออยู่ดี?"
msgid "Start retraction length: "
msgstr "เริ่มต้นความยาวการดึงกลับ:"
@@ -21244,6 +21361,9 @@ msgstr ""
"หลีกเลี่ยงการบิดเบี้ยว\n"
"คุณรู้หรือไม่ว่าเมื่อพิมพ์วัสดุที่มีแนวโน้มที่จะเกิดการบิดเบี้ยว เช่น ABS การเพิ่มอุณหภูมิฐานพิมพ์อย่างเหมาะสมสามารถลดความน่าจะเป็นของการบิดเบี้ยวได้"
#~ msgid "Print order within a single layer."
#~ msgstr "สั่งพิมพ์ภายในชั้นเดียว"
#~ msgid "Bottom"
#~ msgstr "ล่าง"
@@ -21331,9 +21451,6 @@ msgstr ""
#~ msgid "°C"
#~ msgstr "°C"
#~ msgid "%"
#~ msgstr "%"
#~ msgid "Renders cast shadows on the plate in realistic view."
#~ msgstr "แสดงเงาแบบทอดบนเพลตในมุมมองแบบสมจริง"
+123 -6
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-07-26 21:59-0300\n"
"POT-Creation-Date: 2026-07-29 17:40-0300\n"
"PO-Revision-Date: 2026-04-08 23:59+0300\n"
"Last-Translator: GlauTech\n"
"Language-Team: \n"
@@ -3913,6 +3913,10 @@ msgstr "Hizalanıyor..."
msgid "Arranging"
msgstr "Hizalanıyor"
# AI Translated
msgid "Arranging "
msgstr "Hizalanıyor "
msgid "Arranging canceled."
msgstr "Hizalama iptal edildi."
@@ -9321,6 +9325,21 @@ msgstr "Alt katmanları karart"
msgid "When scrubbing the layer slider in the sliced preview, render the layers below the current one darkened so that only the layer being viewed is shown at full brightness."
msgstr "Dilimlenmiş önizlemede katman kaydırıcısı gezdirilirken, geçerli katmanın altındaki katmanları koyulaştırarak yalnızca görüntülenen katmanın tam parlaklıkta gösterilmesini sağlar."
# AI Translated
msgid "Dimmed layer brightness"
msgstr "Karartılmış katman parlaklığı"
msgid "%"
msgstr "%"
# AI Translated
msgid ""
"How brightly the dimmed layers are rendered when \"Dim lower layers\" is enabled.\n"
"99% is barely darkened, 0% renders them black. Capped at 99% because 100% would be the same as disabling the option."
msgstr ""
"\"Alt katmanları karart\" etkinleştirildiğinde karartılmış katmanların ne kadar parlak görüntüleneceği.\n"
"%99 neredeyse hiç karartmaz, %0 ise tamamen siyah gösterir. Üst sınır %99 olarak belirlenmiştir, çünkü %100 seçeneği devre dışı bırakmakla aynı olurdu."
msgid "Login region"
msgstr "Giriş bölgesi"
@@ -13468,12 +13487,37 @@ msgstr "Nesneye göre"
msgid "Intra-layer order"
msgstr "Katman içi sıra"
msgid "Print order within a single layer."
msgstr "Tek bir katmanda yazdırma sırası."
# AI Translated
msgid ""
"Order in which object instances are visited within a single layer, which controls how much travel is spent moving between them.\n"
"\n"
"Default: nearest-neighbor chaining, refined with 2-opt and crossing removal. A good general choice.\n"
"As object list: instances are printed in the same order as the object list, without any path optimization. Use it when you need a predictable, manually controlled order.\n"
"Best of all (shortest path): every strategy is evaluated and the shortest one is used. The object instance order is decided once for the whole print, while the ordering of individual islands is decided per layer, so different layers may end up using different strategies. Slightly slower to slice.\n"
"Snake: serpentine row-by-row traversal, refined with 2-opt. Well suited to regular grids of many small parts.\n"
"\n"
"With multiple filaments or tools in the same layer, minimizing tool changes takes priority: objects are grouped by filament first and this setting only orders the instances within each filament group, so the overall sequence may not look like the shortest path across the plate."
msgstr ""
"Tek bir katman içinde nesne örneklerinin hangi sırayla ziyaret edileceği; bu da aralarında ne kadar seyahat harcanacağını belirler.\n"
"\n"
"Varsayılan: en yakın komşu zincirlemesi, 2-opt ve kesişim giderme ile iyileştirilir. İyi bir genel seçim.\n"
"Nesne listesi olarak: örnekler, herhangi bir yol optimizasyonu olmadan nesne listesindeki sırayla yazdırılır. Öngörülebilir, elle denetlenen bir sıraya ihtiyacınız olduğunda kullanın.\n"
"Hepsinin en iyisi (en kısa yol): her strateji değerlendirilir ve en kısa olanı kullanılır. Nesne örneklerinin sırası tüm baskı için bir kez belirlenir, tek tek adaların sırası ise her katman için ayrı belirlenir; bu nedenle farklı katmanlar farklı stratejiler kullanabilir. Dilimleme biraz daha yavaştır.\n"
"Yılankavi: satır satır ilerleyen yılankavi geçiş, 2-opt ile iyileştirilir. Çok sayıda küçük parçadan oluşan düzenli ızgaralar için çok uygundur.\n"
"\n"
"Aynı katmanda birden fazla filament veya araç varsa, araç değişimlerini en aza indirmek önceliklidir: nesneler önce filamente göre gruplanır ve bu ayar yalnızca her filament grubu içindeki örnekleri sıralar; bu nedenle genel sıra, tabla genelindeki en kısa yol gibi görünmeyebilir."
msgid "As object list"
msgstr "Nesne listesi olarak"
# AI Translated
msgid "Best of all (shortest path)"
msgstr "Hepsinin en iyisi (en kısa yol)"
# AI Translated
msgid "Snake"
msgstr "Yılankavi"
msgid "Slow printing down for better layer cooling"
msgstr "Daha iyi katman soğutması için baskıyı yavaşlat"
@@ -18899,6 +18943,20 @@ msgstr ""
"%1% ana bilgisayar adına çözümlenen birkaç IP adresi var.\n"
"Hangisinin kullanılacağını seçin."
# AI Translated
msgid "Auto-scale for nozzle"
msgstr "Nozul için otomatik ölçekleme"
# AI Translated
msgid ""
"This model is designed around a 0.4 mm nozzle with a 0.2 mm layer height. \n"
"When the scaling option is enabled (recommended), it dynamically resizes to match your current nozzle diameter and an appropriate layer height, making the test both accurate and easy to read.\n"
"Turn scaling off only if you wish to print the reference model exactly as-is."
msgstr ""
"Bu model, 0,4 mm nozul ve 0,2 mm katman yüksekliği esas alınarak tasarlanmıştır. \n"
"Ölçekleme seçeneği etkinleştirildiğinde (önerilir), model mevcut nozul çapınıza ve uygun bir katman yüksekliğine uyacak şekilde dinamik olarak yeniden boyutlandırılır; böylece test hem doğru hem de kolay okunur olur.\n"
"Ölçeklemeyi yalnızca referans modeli olduğu gibi yazdırmak istiyorsanız kapatın."
msgid "PA Calibration"
msgstr "PA Kalibrasyonu"
@@ -19036,6 +19094,14 @@ msgstr "Başlangıç hızı: "
msgid "End speed: "
msgstr "Bitiş hızı: "
# AI Translated
msgid "Auto-adjust to max volumetric speed"
msgstr "Maksimum hacimsel hıza otomatik ayarlama"
# AI Translated
msgid "If the end speed would exceed the filament's maximum volumetric speed, automatically lower the layer height (keeping standard values and staying within the machine's limits) to reach it. If even the minimum layer height is not enough, lower the end speed instead."
msgstr "Bitiş hızı filamentin maksimum hacimsel hızını aşacak olursa, bu hıza ulaşabilmek için katman yüksekliği otomatik olarak düşürülür (standart değerler korunarak ve makinenin sınırları içinde kalınarak). Minimum katman yüksekliği bile yeterli değilse, bunun yerine bitiş hızı düşürülür."
msgid ""
"Please input valid values:\n"
"start > 10\n"
@@ -19047,6 +19113,57 @@ msgstr ""
"adım >= 0\n"
"bitiş > başlangıç + adım)"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s), which limits the outer wall to about %.0f mm/s at this line width and layer height.\n"
" Speeds above this will be clamped, so the upper blocks of the tower will not print at the requested speed.\n"
"\n"
"%s"
msgstr ""
"Bitiş hızı (%.0f mm/s), filamentin maksimum hacimsel hızını (%.1f mm³/s) aşıyor; bu da dış duvarı bu çizgi genişliği ve katman yüksekliğinde yaklaşık %.0f mm/s ile sınırlıyor.\n"
" Bunun üzerindeki hızlar sınırlanacağından, kulenin üst blokları istenen hızda yazdırılmayacak.\n"
"\n"
"%s"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s) at the default layer height (%.2f mm).\n"
"\n"
"The layer height has been reduced to %.2f mm (a value used by this printer's profiles) so the tower can reach the requested speed."
msgstr ""
"Bitiş hızı (%.0f mm/s), filamentin maksimum hacimsel hızını (%.1f mm³/s) varsayılan katman yüksekliğinde (%.2f mm) aşıyor.\n"
"\n"
"Kulenin istenen hıza ulaşabilmesi için katman yüksekliği %.2f mm değerine düşürüldü (bu yazıcının ön ayarlarında kullanılan bir değer)."
# AI Translated
#, c-format, boost-format
msgid ""
"Even at the smallest layer height used by this printer's profiles (%.2f mm) the end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s).\n"
"\n"
"The layer height will be set to %.2f mm and the end speed lowered to %.0f mm/s.\n"
"\n"
"Continue?"
msgstr ""
"Bu yazıcının ön ayarlarında kullanılan en küçük katman yüksekliğinde bile (%.2f mm), bitiş hızı (%.0f mm/s) filamentin maksimum hacimsel hızını (%.1f mm³/s) aşıyor.\n"
"\n"
"Katman yüksekliği %.2f mm olarak ayarlanacak ve bitiş hızı %.0f mm/s değerine düşürülecek.\n"
"\n"
"Devam edilsin mi?"
# AI Translated
msgid "Continue anyway?"
msgstr "Yine de devam edilsin mi?"
# AI Translated
msgid "Enable \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "Bunu otomatik olarak düzeltmek için \"Otomatik ayarlama\" seçeneğini etkinleştirin ya da yine de devam edilsin mi?"
# AI Translated
msgid "Enable \"Auto-scale for nozzle\" and \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "Bunu otomatik olarak düzeltmek için \"Nozul için otomatik ölçekleme\" ve \"Otomatik ayarlama\" seçeneklerini etkinleştirin ya da yine de devam edilsin mi?"
msgid "Start retraction length: "
msgstr "Geri çekme uzunluğu başlangıcı: "
@@ -21742,6 +21859,9 @@ msgstr ""
"Eğilmeyi önleyin\n"
"ABS gibi bükülmeye yatkın malzemelere baskı yaparken, ısıtma yatağı sıcaklığının uygun şekilde arttırılmasının bükülme olasılığını azaltabileceğini biliyor muydunuz?"
#~ msgid "Print order within a single layer."
#~ msgstr "Tek bir katmanda yazdırma sırası."
#~ msgid "Bottom"
#~ msgstr "Alt"
@@ -21822,9 +21942,6 @@ msgstr ""
#~ msgid "°C"
#~ msgstr "°C"
#~ msgid "%"
#~ msgstr "%"
#~ msgid "Continue to sync filaments"
#~ msgstr "Filamentleri senkronize etmeye devam edin"
+123 -6
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: orcaslicerua\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-07-26 21:59-0300\n"
"POT-Creation-Date: 2026-07-29 17:40-0300\n"
"PO-Revision-Date: 2026-07-17 16:25+0300\n"
"Last-Translator: Andrij Mizyk <andm1zyk@proton.me>\n"
"Language-Team: Ukrainian\n"
@@ -3796,6 +3796,10 @@ msgstr "Організація..."
msgid "Arranging"
msgstr "Організація"
# AI Translated
msgid "Arranging "
msgstr "Впорядкування "
msgid "Arranging canceled."
msgstr "Організацію скасовано."
@@ -9297,6 +9301,21 @@ msgstr "Затемнювати нижні шари"
msgid "When scrubbing the layer slider in the sliced preview, render the layers below the current one darkened so that only the layer being viewed is shown at full brightness."
msgstr "Під час прокручування повзунка шарів у попередньому перегляді нарізки відображати шари нижче поточного затемненими, щоб лише переглядуваний шар показувався з повною яскравістю."
# AI Translated
msgid "Dimmed layer brightness"
msgstr "Яскравість затемнених шарів"
msgid "%"
msgstr "%"
# AI Translated
msgid ""
"How brightly the dimmed layers are rendered when \"Dim lower layers\" is enabled.\n"
"99% is barely darkened, 0% renders them black. Capped at 99% because 100% would be the same as disabling the option."
msgstr ""
"Наскільки яскраво відображаються затемнені шари, коли увімкнено параметр «Затемнювати нижні шари».\n"
"99% — затемнення майже непомітне, 0% — шари стають чорними. Максимум обмежено 99%, оскільки 100% дорівнювало б вимкненню параметра."
msgid "Login region"
msgstr "Регіон входу"
@@ -13531,12 +13550,37 @@ msgstr "По обʼєктах"
msgid "Intra-layer order"
msgstr "Внутрішній порядок шарів"
msgid "Print order within a single layer."
msgstr "Друк замовлення в один шар"
# AI Translated
msgid ""
"Order in which object instances are visited within a single layer, which controls how much travel is spent moving between them.\n"
"\n"
"Default: nearest-neighbor chaining, refined with 2-opt and crossing removal. A good general choice.\n"
"As object list: instances are printed in the same order as the object list, without any path optimization. Use it when you need a predictable, manually controlled order.\n"
"Best of all (shortest path): every strategy is evaluated and the shortest one is used. The object instance order is decided once for the whole print, while the ordering of individual islands is decided per layer, so different layers may end up using different strategies. Slightly slower to slice.\n"
"Snake: serpentine row-by-row traversal, refined with 2-opt. Well suited to regular grids of many small parts.\n"
"\n"
"With multiple filaments or tools in the same layer, minimizing tool changes takes priority: objects are grouped by filament first and this setting only orders the instances within each filament group, so the overall sequence may not look like the shortest path across the plate."
msgstr ""
"Порядок, у якому обходяться екземпляри обʼєктів у межах одного шару; він визначає, скільки переміщень витрачається на переходи між ними.\n"
"\n"
"Типово: побудова ланцюга методом найближчого сусіда з подальшим удосконаленням алгоритмом 2-opt та усуненням перетинів. Хороший універсальний вибір.\n"
"За порядком у списку: екземпляри друкуються в тому самому порядку, що й у списку обʼєктів, без жодної оптимізації шляху. Використовуйте, коли потрібен передбачуваний порядок, заданий вручну.\n"
"Найкращий з усіх (найкоротший шлях): оцінюються всі стратегії й застосовується та, що дає найкоротший шлях. Порядок екземплярів обʼєктів визначається один раз для всього друку, а порядок окремих острівців — для кожного шару окремо, тож різні шари можуть використовувати різні стратегії. Нарізка триває трохи довше.\n"
"Змійкою: змієподібний обхід ряд за рядом з удосконаленням алгоритмом 2-opt. Добре підходить для регулярних сіток із багатьох дрібних деталей.\n"
"\n"
"Якщо в одному шарі використовується кілька філаментів або інструментів, пріоритет має мінімізація змін інструмента: обʼєкти спочатку групуються за філаментом, і цей параметр упорядковує лише екземпляри в межах кожної групи, тож загальна послідовність може не виглядати як найкоротший шлях по пластині."
msgid "As object list"
msgstr "За порядком у списку"
# AI Translated
msgid "Best of all (shortest path)"
msgstr "Найкращий з усіх (найкоротший шлях)"
# AI Translated
msgid "Snake"
msgstr "Змійкою"
msgid "Slow printing down for better layer cooling"
msgstr "Сповільнювати друк для кращого охолодження шару"
@@ -19089,6 +19133,20 @@ msgstr ""
"Є кілька IP-адрес, які перетворюються на ім’я хоста %1%.\n"
"Будь ласка, виберіть той, який слід використовувати."
# AI Translated
msgid "Auto-scale for nozzle"
msgstr "Автомасштабування під сопло"
# AI Translated
msgid ""
"This model is designed around a 0.4 mm nozzle with a 0.2 mm layer height. \n"
"When the scaling option is enabled (recommended), it dynamically resizes to match your current nozzle diameter and an appropriate layer height, making the test both accurate and easy to read.\n"
"Turn scaling off only if you wish to print the reference model exactly as-is."
msgstr ""
"Ця модель розрахована на сопло 0,4 мм і висоту шару 0,2 мм. \n"
"Коли увімкнено масштабування (рекомендовано), розмір моделі динамічно підлаштовується під діаметр вашого поточного сопла та відповідну висоту шару, завдяки чому тест є точним і легко читається.\n"
"Вимикайте масштабування, лише якщо хочете надрукувати еталонну модель точно в первісному вигляді."
msgid "PA Calibration"
msgstr "Калібрування ВТ (РА)"
@@ -19230,6 +19288,14 @@ msgstr "Початкова швидкість: "
msgid "End speed: "
msgstr "Кінцева швидкість: "
# AI Translated
msgid "Auto-adjust to max volumetric speed"
msgstr "Автопідлаштування під максимальну обʼємну швидкість"
# AI Translated
msgid "If the end speed would exceed the filament's maximum volumetric speed, automatically lower the layer height (keeping standard values and staying within the machine's limits) to reach it. If even the minimum layer height is not enough, lower the end speed instead."
msgstr "Якщо кінцева швидкість перевищить максимальну обʼємну швидкість філаменту, автоматично зменшувати висоту шару (зберігаючи стандартні значення та не виходячи за обмеження машини), щоб її досягти. Якщо навіть мінімальної висоти шару не досить, натомість буде знижено кінцеву швидкість."
msgid ""
"Please input valid values:\n"
"start > 10\n"
@@ -19241,6 +19307,57 @@ msgstr ""
"крок >= 0\n"
"кінець > початок + крок)"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s), which limits the outer wall to about %.0f mm/s at this line width and layer height.\n"
" Speeds above this will be clamped, so the upper blocks of the tower will not print at the requested speed.\n"
"\n"
"%s"
msgstr ""
"Кінцева швидкість (%.0f мм/с) перевищує максимальну обʼємну швидкість філаменту (%.1f мм³/с), яка за такої ширини лінії та висоти шару обмежує зовнішню стінку приблизно до %.0f мм/с.\n"
" Вищі швидкості будуть обмежені, тож верхні блоки вежі не надрукуються із заданою швидкістю.\n"
"\n"
"%s"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s) at the default layer height (%.2f mm).\n"
"\n"
"The layer height has been reduced to %.2f mm (a value used by this printer's profiles) so the tower can reach the requested speed."
msgstr ""
"Кінцева швидкість (%.0f мм/с) перевищує максимальну обʼємну швидкість філаменту (%.1f мм³/с) за типової висоти шару (%.2f мм).\n"
"\n"
"Висоту шару зменшено до %.2f мм (значення, яке використовують профілі цього принтера), щоб вежа могла досягти заданої швидкості."
# AI Translated
#, c-format, boost-format
msgid ""
"Even at the smallest layer height used by this printer's profiles (%.2f mm) the end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s).\n"
"\n"
"The layer height will be set to %.2f mm and the end speed lowered to %.0f mm/s.\n"
"\n"
"Continue?"
msgstr ""
"Навіть за найменшої висоти шару, яку використовують профілі цього принтера (%.2f мм), кінцева швидкість (%.0f мм/с) перевищує максимальну обʼємну швидкість філаменту (%.1f мм³/с).\n"
"\n"
"Висоту шару буде встановлено на %.2f мм, а кінцеву швидкість знижено до %.0f мм/с.\n"
"\n"
"Продовжити?"
# AI Translated
msgid "Continue anyway?"
msgstr "Усе одно продовжити?"
# AI Translated
msgid "Enable \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "Увімкнути «Автопідлаштування», щоб виправити це автоматично, чи все одно продовжити?"
# AI Translated
msgid "Enable \"Auto-scale for nozzle\" and \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "Увімкнути «Автомасштабування під сопло» та «Автопідлаштування», щоб виправити це автоматично, чи все одно продовжити?"
msgid "Start retraction length: "
msgstr "Початкова довжина ретракту: "
@@ -21862,6 +21979,9 @@ msgstr ""
"Уникнення деформації\n"
"Чи знаєте ви, що при друку матеріалами, схильними до деформації, такими як ABS, відповідне підвищення температури столу може зменшити ймовірність деформації?"
#~ msgid "Print order within a single layer."
#~ msgstr "Друк замовлення в один шар"
#~ msgid "Bottom"
#~ msgstr "Низ"
@@ -21940,9 +22060,6 @@ msgstr ""
#~ msgid "°C"
#~ msgstr "℃"
#~ msgid "%"
#~ msgstr "%"
#~ msgctxt "Sync_Nozzle_AMS"
#~ msgid "Cancel"
#~ msgstr "Скасувати"
+123 -6
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-07-26 21:59-0300\n"
"POT-Creation-Date: 2026-07-29 17:40-0300\n"
"PO-Revision-Date: 2025-10-02 17:43+0700\n"
"Last-Translator: \n"
"Language-Team: hainguyen.ts13@gmail.com\n"
@@ -4031,6 +4031,10 @@ msgstr "Đang sắp xếp..."
msgid "Arranging"
msgstr "Đang sắp xếp"
# AI Translated
msgid "Arranging "
msgstr "Đang sắp xếp "
msgid "Arranging canceled."
msgstr "Hủy sắp xếp."
@@ -9779,6 +9783,21 @@ msgstr "Làm mờ các lớp bên dưới"
msgid "When scrubbing the layer slider in the sliced preview, render the layers below the current one darkened so that only the layer being viewed is shown at full brightness."
msgstr "Khi kéo thanh trượt lớp trong bản xem trước đã slice, kết xuất các lớp bên dưới lớp hiện tại ở dạng tối đi để chỉ lớp đang xem hiển thị với độ sáng đầy đủ."
# AI Translated
msgid "Dimmed layer brightness"
msgstr "Độ sáng của lớp bị làm mờ"
msgid "%"
msgstr "%"
# AI Translated
msgid ""
"How brightly the dimmed layers are rendered when \"Dim lower layers\" is enabled.\n"
"99% is barely darkened, 0% renders them black. Capped at 99% because 100% would be the same as disabling the option."
msgstr ""
"Mức độ sáng khi hiển thị các lớp bị làm mờ nếu bật \"Làm mờ các lớp bên dưới\".\n"
"99% gần như không tối đi, 0% khiến chúng đen hoàn toàn. Giới hạn ở 99% vì 100% sẽ giống hệt như tắt tùy chọn này."
# AI Translated
msgid "Login region"
msgstr "Khu vực đăng nhập"
@@ -14093,12 +14112,37 @@ msgstr "Theo đối tượng"
msgid "Intra-layer order"
msgstr "Thứ tự trong lớp"
msgid "Print order within a single layer."
msgstr "Thứ tự in trong một lớp đơn."
# AI Translated
msgid ""
"Order in which object instances are visited within a single layer, which controls how much travel is spent moving between them.\n"
"\n"
"Default: nearest-neighbor chaining, refined with 2-opt and crossing removal. A good general choice.\n"
"As object list: instances are printed in the same order as the object list, without any path optimization. Use it when you need a predictable, manually controlled order.\n"
"Best of all (shortest path): every strategy is evaluated and the shortest one is used. The object instance order is decided once for the whole print, while the ordering of individual islands is decided per layer, so different layers may end up using different strategies. Slightly slower to slice.\n"
"Snake: serpentine row-by-row traversal, refined with 2-opt. Well suited to regular grids of many small parts.\n"
"\n"
"With multiple filaments or tools in the same layer, minimizing tool changes takes priority: objects are grouped by filament first and this setting only orders the instances within each filament group, so the overall sequence may not look like the shortest path across the plate."
msgstr ""
"Thứ tự các instance của đối tượng được đi qua trong cùng một lớp, quyết định lượng di chuyển tiêu tốn khi đi giữa chúng.\n"
"\n"
"Mặc định: nối chuỗi theo láng giềng gần nhất, tinh chỉnh bằng 2-opt và loại bỏ các đoạn cắt nhau. Lựa chọn chung tốt.\n"
"Như danh sách đối tượng: các instance được in theo đúng thứ tự trong danh sách đối tượng, không tối ưu hóa đường đi. Dùng khi bạn cần một thứ tự dễ đoán và tự kiểm soát.\n"
"Tốt nhất trong tất cả (đường đi ngắn nhất): mọi chiến lược đều được đánh giá và chiến lược ngắn nhất được sử dụng. Thứ tự các instance của đối tượng được quyết định một lần cho toàn bộ bản in, còn thứ tự của từng đảo được quyết định theo từng lớp, nên các lớp khác nhau có thể dùng chiến lược khác nhau. Slice hơi chậm hơn một chút.\n"
"Ngoằn ngoèo: duyệt lần lượt từng hàng theo kiểu ngoằn ngoèo, tinh chỉnh bằng 2-opt. Rất phù hợp với các lưới đều gồm nhiều chi tiết nhỏ.\n"
"\n"
"Khi có nhiều filament hoặc đầu công cụ trong cùng một lớp, việc giảm thiểu số lần đổi đầu công cụ được ưu tiên: các đối tượng được nhóm theo filament trước, và thiết lập này chỉ sắp xếp các instance trong từng nhóm filament, nên trình tự tổng thể có thể không giống đường đi ngắn nhất trên bàn in."
msgid "As object list"
msgstr "Như danh sách đối tượng"
# AI Translated
msgid "Best of all (shortest path)"
msgstr "Tốt nhất trong tất cả (đường đi ngắn nhất)"
# AI Translated
msgid "Snake"
msgstr "Ngoằn ngoèo"
msgid "Slow printing down for better layer cooling"
msgstr "Giảm tốc độ in để làm mát lớp tốt hơn"
@@ -19626,6 +19670,20 @@ msgstr ""
"Có nhiều địa chỉ IP phân giải thành tên máy chủ %1%.\n"
"Vui lòng chọn một địa chỉ nên được sử dụng."
# AI Translated
msgid "Auto-scale for nozzle"
msgstr "Tự động chia tỷ lệ theo đầu phun"
# AI Translated
msgid ""
"This model is designed around a 0.4 mm nozzle with a 0.2 mm layer height. \n"
"When the scaling option is enabled (recommended), it dynamically resizes to match your current nozzle diameter and an appropriate layer height, making the test both accurate and easy to read.\n"
"Turn scaling off only if you wish to print the reference model exactly as-is."
msgstr ""
"Mô hình này được thiết kế cho đầu phun 0.4 mm với chiều cao lớp 0.2 mm. \n"
"Khi bật tùy chọn chia tỷ lệ (khuyến nghị), mô hình sẽ tự động thay đổi kích thước cho khớp với đường kính đầu phun hiện tại và một chiều cao lớp phù hợp, giúp bài kiểm tra vừa chính xác vừa dễ đọc.\n"
"Chỉ tắt chia tỷ lệ nếu bạn muốn in mô hình tham chiếu đúng nguyên trạng."
msgid "PA Calibration"
msgstr "Hiệu chỉnh PA"
@@ -19764,6 +19822,14 @@ msgstr "Tốc độ bắt đầu: "
msgid "End speed: "
msgstr "Tốc độ kết thúc: "
# AI Translated
msgid "Auto-adjust to max volumetric speed"
msgstr "Tự động điều chỉnh theo tốc độ thể tích tối đa"
# AI Translated
msgid "If the end speed would exceed the filament's maximum volumetric speed, automatically lower the layer height (keeping standard values and staying within the machine's limits) to reach it. If even the minimum layer height is not enough, lower the end speed instead."
msgstr "Nếu tốc độ kết thúc vượt quá tốc độ thể tích tối đa của filament, tự động giảm chiều cao lớp (giữ các giá trị tiêu chuẩn và nằm trong giới hạn của máy) để đạt được tốc độ đó. Nếu ngay cả chiều cao lớp nhỏ nhất vẫn chưa đủ, thì giảm tốc độ kết thúc thay vào đó."
msgid ""
"Please input valid values:\n"
"start > 10\n"
@@ -19775,6 +19841,57 @@ msgstr ""
"bước >= 0\n"
"kết thúc > bắt đầu + bước"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s), which limits the outer wall to about %.0f mm/s at this line width and layer height.\n"
" Speeds above this will be clamped, so the upper blocks of the tower will not print at the requested speed.\n"
"\n"
"%s"
msgstr ""
"Tốc độ kết thúc (%.0f mm/s) vượt quá tốc độ thể tích tối đa của filament (%.1f mm³/s), khiến thành ngoài bị giới hạn ở khoảng %.0f mm/s với độ rộng đường và chiều cao lớp hiện tại.\n"
" Các tốc độ cao hơn mức này sẽ bị cắt bớt, nên những khối phía trên của tháp sẽ không in ở tốc độ yêu cầu.\n"
"\n"
"%s"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s) at the default layer height (%.2f mm).\n"
"\n"
"The layer height has been reduced to %.2f mm (a value used by this printer's profiles) so the tower can reach the requested speed."
msgstr ""
"Tốc độ kết thúc (%.0f mm/s) vượt quá tốc độ thể tích tối đa của filament (%.1f mm³/s) ở chiều cao lớp mặc định (%.2f mm).\n"
"\n"
"Chiều cao lớp đã được giảm xuống %.2f mm (một giá trị được dùng trong các cài đặt sẵn của máy in này) để tháp có thể đạt tốc độ yêu cầu."
# AI Translated
#, c-format, boost-format
msgid ""
"Even at the smallest layer height used by this printer's profiles (%.2f mm) the end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s).\n"
"\n"
"The layer height will be set to %.2f mm and the end speed lowered to %.0f mm/s.\n"
"\n"
"Continue?"
msgstr ""
"Ngay cả ở chiều cao lớp nhỏ nhất được dùng trong các cài đặt sẵn của máy in này (%.2f mm), tốc độ kết thúc (%.0f mm/s) vẫn vượt quá tốc độ thể tích tối đa của filament (%.1f mm³/s).\n"
"\n"
"Chiều cao lớp sẽ được đặt thành %.2f mm và tốc độ kết thúc giảm xuống %.0f mm/s.\n"
"\n"
"Tiếp tục?"
# AI Translated
msgid "Continue anyway?"
msgstr "Vẫn tiếp tục?"
# AI Translated
msgid "Enable \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "Bật \"Tự động điều chỉnh\" để khắc phục việc này tự động, hoặc vẫn tiếp tục?"
# AI Translated
msgid "Enable \"Auto-scale for nozzle\" and \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "Bật \"Tự động chia tỷ lệ theo đầu phun\" và \"Tự động điều chỉnh\" để khắc phục việc này tự động, hoặc vẫn tiếp tục?"
msgid "Start retraction length: "
msgstr "Độ dài rút bắt đầu: "
@@ -22585,6 +22702,9 @@ msgstr ""
"Tránh cong vênh\n"
"Bạn có biết rằng khi in vật liệu dễ cong vênh như ABS, tăng nhiệt độ bàn nóng một cách thích hợp có thể giảm xác suất cong vênh không?"
#~ msgid "Print order within a single layer."
#~ msgstr "Thứ tự in trong một lớp đơn."
#~ msgid "Bottom"
#~ msgstr "Dưới"
@@ -22627,9 +22747,6 @@ msgstr ""
#~ msgid "°C"
#~ msgstr "°C"
#~ msgid "%"
#~ msgstr "%"
#~ msgctxt "Sync_Nozzle_AMS"
#~ msgid "Cancel"
#~ msgstr "Hủy"
+123 -6
View File
@@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Slic3rPE\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-07-26 21:59-0300\n"
"POT-Creation-Date: 2026-07-29 17:40-0300\n"
"PO-Revision-Date: 2026-06-11 12:37-0300\n"
"Last-Translator: Handle <mail@bysb.net>\n"
"Language-Team: \n"
@@ -3694,6 +3694,10 @@ msgstr "自动摆放中..."
msgid "Arranging"
msgstr "自动摆放"
# AI Translated
msgid "Arranging "
msgstr "自动摆放 "
msgid "Arranging canceled."
msgstr "已取消自动摆放。"
@@ -8978,6 +8982,21 @@ msgstr "调暗下方图层"
msgid "When scrubbing the layer slider in the sliced preview, render the layers below the current one darkened so that only the layer being viewed is shown at full brightness."
msgstr "在切片预览中拖动图层滑块时,将当前图层下方的图层渲染为变暗状态,以便只有正在查看的图层以完整亮度显示。"
# AI Translated
msgid "Dimmed layer brightness"
msgstr "调暗图层的亮度"
msgid "%"
msgstr "%"
# AI Translated
msgid ""
"How brightly the dimmed layers are rendered when \"Dim lower layers\" is enabled.\n"
"99% is barely darkened, 0% renders them black. Capped at 99% because 100% would be the same as disabling the option."
msgstr ""
"启用“调暗下方图层”时,被调暗的图层以多高的亮度显示。\n"
"99% 表示几乎不变暗,0% 表示显示为纯黑。上限为 99%,因为 100% 与关闭该选项的效果相同。"
msgid "Login region"
msgstr "登录区域"
@@ -12949,12 +12968,37 @@ msgstr "逐件"
msgid "Intra-layer order"
msgstr "层内打印顺序"
msgid "Print order within a single layer."
msgstr "同一层内的打印顺序"
# AI Translated
msgid ""
"Order in which object instances are visited within a single layer, which controls how much travel is spent moving between them.\n"
"\n"
"Default: nearest-neighbor chaining, refined with 2-opt and crossing removal. A good general choice.\n"
"As object list: instances are printed in the same order as the object list, without any path optimization. Use it when you need a predictable, manually controlled order.\n"
"Best of all (shortest path): every strategy is evaluated and the shortest one is used. The object instance order is decided once for the whole print, while the ordering of individual islands is decided per layer, so different layers may end up using different strategies. Slightly slower to slice.\n"
"Snake: serpentine row-by-row traversal, refined with 2-opt. Well suited to regular grids of many small parts.\n"
"\n"
"With multiple filaments or tools in the same layer, minimizing tool changes takes priority: objects are grouped by filament first and this setting only orders the instances within each filament group, so the overall sequence may not look like the shortest path across the plate."
msgstr ""
"在同一层内访问各对象实例的顺序,它决定了在实例之间移动所花费的空驶量。\n"
"\n"
"默认:以最近邻方式串联,并通过 2-opt 和交叉消除进行优化。通常是较好的选择。\n"
"按对象列表中的顺序:不做任何路径优化,按对象列表中的顺序打印各实例。需要可预测、手动控制的顺序时使用。\n"
"全部比较(最短路径):评估所有策略并采用最短的一种。对象实例的顺序在整个打印任务中只确定一次,而各个岛的排序则逐层确定,因此不同层可能采用不同的策略。切片速度略慢。\n"
"蛇形:逐行往复的蛇形遍历,并通过 2-opt 进行优化。非常适合由大量小零件组成的规则阵列。\n"
"\n"
"当同一层中使用多种耗材丝或工具时,优先减少换料次数:对象会先按耗材丝分组,本设置仅决定每个耗材丝分组内实例的顺序,因此整体顺序看起来可能不是整个热床上的最短路径。"
msgid "As object list"
msgstr "按对象列表中的顺序"
# AI Translated
msgid "Best of all (shortest path)"
msgstr "全部比较(最短路径)"
# AI Translated
msgid "Snake"
msgstr "蛇形"
msgid "Slow printing down for better layer cooling"
msgstr "降低打印速度 以得到更好的冷却"
@@ -18278,6 +18322,20 @@ msgstr ""
"主机名 %1% 指向了多个IP地址\n"
"请在其中选择一个正在使用的地址。"
# AI Translated
msgid "Auto-scale for nozzle"
msgstr "根据喷嘴自动缩放"
# AI Translated
msgid ""
"This model is designed around a 0.4 mm nozzle with a 0.2 mm layer height. \n"
"When the scaling option is enabled (recommended), it dynamically resizes to match your current nozzle diameter and an appropriate layer height, making the test both accurate and easy to read.\n"
"Turn scaling off only if you wish to print the reference model exactly as-is."
msgstr ""
"该模型是按 0.4 mm 喷嘴和 0.2 mm 层高设计的。 \n"
"启用缩放选项后(推荐),模型会根据当前喷嘴直径和合适的层高动态调整尺寸,使测试既准确又易于读取。\n"
"只有当您希望完全按原样打印参考模型时,才关闭缩放。"
msgid "PA Calibration"
msgstr "压力提前/PA校准"
@@ -18412,6 +18470,14 @@ msgstr "起始速度"
msgid "End speed: "
msgstr "结束速度"
# AI Translated
msgid "Auto-adjust to max volumetric speed"
msgstr "自动调整以适应最大体积流量"
# AI Translated
msgid "If the end speed would exceed the filament's maximum volumetric speed, automatically lower the layer height (keeping standard values and staying within the machine's limits) to reach it. If even the minimum layer height is not enough, lower the end speed instead."
msgstr "如果结束速度会超过耗材丝的最大体积流量,则自动降低层高(保持标准数值并处于机器限制范围内)以达到该速度。如果连最小层高也不够,则改为降低结束速度。"
msgid ""
"Please input valid values:\n"
"start > 10\n"
@@ -18423,6 +18489,57 @@ msgstr ""
"步进长度 >= 0\n"
"结束 > 开始 + 步进长度)"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s), which limits the outer wall to about %.0f mm/s at this line width and layer height.\n"
" Speeds above this will be clamped, so the upper blocks of the tower will not print at the requested speed.\n"
"\n"
"%s"
msgstr ""
"结束速度(%.0f mm/s)超过了耗材丝的最大体积流量(%.1f mm³/s),在当前线宽和层高下,外墙被限制在约 %.0f mm/s。\n"
" 超过该值的速度会被钳制,因此塔的上部区块不会以请求的速度打印。\n"
"\n"
"%s"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s) at the default layer height (%.2f mm).\n"
"\n"
"The layer height has been reduced to %.2f mm (a value used by this printer's profiles) so the tower can reach the requested speed."
msgstr ""
"结束速度(%.0f mm/s)超过了耗材丝的最大体积流量(%.1f mm³/s)(默认层高 %.2f mm 时)。\n"
"\n"
"层高已降至 %.2f mm(该打印机配置中使用的数值),以便塔能够达到请求的速度。"
# AI Translated
#, c-format, boost-format
msgid ""
"Even at the smallest layer height used by this printer's profiles (%.2f mm) the end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s).\n"
"\n"
"The layer height will be set to %.2f mm and the end speed lowered to %.0f mm/s.\n"
"\n"
"Continue?"
msgstr ""
"即使采用该打印机配置中使用的最小层高(%.2f mm),结束速度(%.0f mm/s)仍超过耗材丝的最大体积流量(%.1f mm³/s)。\n"
"\n"
"层高将设为 %.2f mm,结束速度将降至 %.0f mm/s。\n"
"\n"
"是否继续?"
# AI Translated
msgid "Continue anyway?"
msgstr "仍要继续吗?"
# AI Translated
msgid "Enable \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "启用“自动调整”可自动解决该问题,或者仍要继续?"
# AI Translated
msgid "Enable \"Auto-scale for nozzle\" and \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "启用“根据喷嘴自动缩放”和“自动调整”可自动解决该问题,或者仍要继续?"
msgid "Start retraction length: "
msgstr "起始回抽长度"
@@ -20994,6 +21111,9 @@ msgstr ""
"避免翘曲\n"
"您知道吗?打印ABS这类易翘曲材料时,适当提高热床温度可以降低翘曲的概率。"
#~ msgid "Print order within a single layer."
#~ msgstr "同一层内的打印顺序"
#~ msgid "Bottom"
#~ msgstr "底部"
@@ -21086,9 +21206,6 @@ msgstr ""
#~ msgid "°C"
#~ msgstr "°C"
#~ msgid "%"
#~ msgstr "%"
#~ msgid "Renders cast shadows on the plate in realistic view."
#~ msgstr "在写实渲染中渲染投射到打印板上的阴影。"
+124 -7
View File
@@ -9,7 +9,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-07-26 21:59-0300\n"
"POT-Creation-Date: 2026-07-29 17:40-0300\n"
"PO-Revision-Date: 2025-11-28 13:48-0600\n"
"Last-Translator: tntchn <15895303+tntchn@users.noreply.github.com>\n"
"Language-Team: \n"
@@ -3795,6 +3795,10 @@ msgstr "自動擺放中..."
msgid "Arranging"
msgstr "自動擺放"
# AI Translated
msgid "Arranging "
msgstr "自動擺放 "
msgid "Arranging canceled."
msgstr "已取消自動擺放。"
@@ -4675,7 +4679,7 @@ msgid ""
"Too small max volumetric speed.\n"
"Value was reset to 0.5"
msgstr ""
"最大體積速度設定過小\n"
"最大體積流量設定過小\n"
"重設為 0.5"
#, c-format, boost-format
@@ -9151,6 +9155,21 @@ msgstr "使下方層變暗"
msgid "When scrubbing the layer slider in the sliced preview, render the layers below the current one darkened so that only the layer being viewed is shown at full brightness."
msgstr "在切片預覽中拖曳層滑桿時,將目前層以下的各層算繪為變暗,如此只有正在檢視的層以全亮度顯示。"
# AI Translated
msgid "Dimmed layer brightness"
msgstr "變暗層的亮度"
msgid "%"
msgstr "%"
# AI Translated
msgid ""
"How brightly the dimmed layers are rendered when \"Dim lower layers\" is enabled.\n"
"99% is barely darkened, 0% renders them black. Capped at 99% because 100% would be the same as disabling the option."
msgstr ""
"啟用「使下方層變暗」時,變暗的層以多高的亮度顯示。\n"
"99% 幾乎不會變暗,0% 會顯示為全黑。上限為 99%,因為 100% 與停用此選項的效果相同。"
msgid "Login region"
msgstr "登入區域"
@@ -13162,12 +13181,37 @@ msgstr "逐件"
msgid "Intra-layer order"
msgstr "單層順序"
msgid "Print order within a single layer."
msgstr "每一層的列印順序"
# AI Translated
msgid ""
"Order in which object instances are visited within a single layer, which controls how much travel is spent moving between them.\n"
"\n"
"Default: nearest-neighbor chaining, refined with 2-opt and crossing removal. A good general choice.\n"
"As object list: instances are printed in the same order as the object list, without any path optimization. Use it when you need a predictable, manually controlled order.\n"
"Best of all (shortest path): every strategy is evaluated and the shortest one is used. The object instance order is decided once for the whole print, while the ordering of individual islands is decided per layer, so different layers may end up using different strategies. Slightly slower to slice.\n"
"Snake: serpentine row-by-row traversal, refined with 2-opt. Well suited to regular grids of many small parts.\n"
"\n"
"With multiple filaments or tools in the same layer, minimizing tool changes takes priority: objects are grouped by filament first and this setting only orders the instances within each filament group, so the overall sequence may not look like the shortest path across the plate."
msgstr ""
"在同一層內走訪各物件實例的順序,這會決定在實例之間移動所花費的空駛量。\n"
"\n"
"預設:以最近鄰方式串接,並以 2-opt 與交叉消除進行改善。通常是不錯的選擇。\n"
"按照物件清單排序:不做任何路徑最佳化,依照物件清單的順序列印各實例。需要可預測、手動控制的順序時使用。\n"
"全部比較(最短路徑):評估所有策略並採用最短的一種。物件實例的順序在整個列印工作中只決定一次,而個別島嶼的排序則逐層決定,因此不同層可能採用不同的策略。切片速度略慢。\n"
"蛇形:逐行往復的蛇形走訪,並以 2-opt 進行改善。非常適合由大量小零件組成的規則陣列。\n"
"\n"
"當同一層中使用多種線材或工具時,會優先減少換料次數:物件會先依線材分組,此設定僅決定每個線材群組內實例的順序,因此整體順序看起來可能不是整個列印板上的最短路徑。"
msgid "As object list"
msgstr "按照物件清單排序"
# AI Translated
msgid "Best of all (shortest path)"
msgstr "全部比較(最短路徑)"
# AI Translated
msgid "Snake"
msgstr "蛇形"
msgid "Slow printing down for better layer cooling"
msgstr "降低列印速度 以得到更好的冷卻"
@@ -18461,6 +18505,20 @@ msgstr ""
"有多個 IP 位址解析到主機名稱 %1%。\n"
"請選擇一個要使用的 IP 位址。"
# AI Translated
msgid "Auto-scale for nozzle"
msgstr "依噴嘴自動縮放"
# AI Translated
msgid ""
"This model is designed around a 0.4 mm nozzle with a 0.2 mm layer height. \n"
"When the scaling option is enabled (recommended), it dynamically resizes to match your current nozzle diameter and an appropriate layer height, making the test both accurate and easy to read.\n"
"Turn scaling off only if you wish to print the reference model exactly as-is."
msgstr ""
"此模型是以 0.4 mm 噴嘴與 0.2 mm 層高為基準設計的。 \n"
"啟用縮放選項後(建議),模型會依目前的噴嘴直徑與合適的層高動態調整尺寸,使測試既準確又容易判讀。\n"
"只有在您想完全按原樣列印參考模型時,才關閉縮放。"
msgid "PA Calibration"
msgstr "PA 校正"
@@ -18597,6 +18655,14 @@ msgstr "起始速度:"
msgid "End speed: "
msgstr "結束速度:"
# AI Translated
msgid "Auto-adjust to max volumetric speed"
msgstr "自動調整以符合最大體積流量"
# AI Translated
msgid "If the end speed would exceed the filament's maximum volumetric speed, automatically lower the layer height (keeping standard values and staying within the machine's limits) to reach it. If even the minimum layer height is not enough, lower the end speed instead."
msgstr "如果結束速度會超過線材的最大體積流量,則自動降低層高(維持標準數值並處於機器限制範圍內)以達到該速度。如果連最小層高也不夠,則改為降低結束速度。"
msgid ""
"Please input valid values:\n"
"start > 10\n"
@@ -18608,6 +18674,57 @@ msgstr ""
"步距 >= 0\n"
"結束 > 開始 + 步距)"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s), which limits the outer wall to about %.0f mm/s at this line width and layer height.\n"
" Speeds above this will be clamped, so the upper blocks of the tower will not print at the requested speed.\n"
"\n"
"%s"
msgstr ""
"結束速度(%.0f mm/s)超過線材的最大體積流量(%.1f mm³/s),在目前的線寬與層高下,外牆被限制在約 %.0f mm/s。\n"
" 超過該值的速度會被箝制,因此塔的上部區塊不會以要求的速度列印。\n"
"\n"
"%s"
# AI Translated
#, c-format, boost-format
msgid ""
"The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s) at the default layer height (%.2f mm).\n"
"\n"
"The layer height has been reduced to %.2f mm (a value used by this printer's profiles) so the tower can reach the requested speed."
msgstr ""
"結束速度(%.0f mm/s)超過線材的最大體積流量(%.1f mm³/s)(預設層高 %.2f mm 時)。\n"
"\n"
"層高已降至 %.2f mm(此印表機的設定檔中使用的數值),以便塔能夠達到要求的速度。"
# AI Translated
#, c-format, boost-format
msgid ""
"Even at the smallest layer height used by this printer's profiles (%.2f mm) the end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed (%.1f mm³/s).\n"
"\n"
"The layer height will be set to %.2f mm and the end speed lowered to %.0f mm/s.\n"
"\n"
"Continue?"
msgstr ""
"即使採用此印表機的設定檔中使用的最小層高(%.2f mm),結束速度(%.0f mm/s)仍超過線材的最大體積流量(%.1f mm³/s)。\n"
"\n"
"層高將設為 %.2f mm,結束速度將降至 %.0f mm/s。\n"
"\n"
"是否繼續?"
# AI Translated
msgid "Continue anyway?"
msgstr "仍要繼續嗎?"
# AI Translated
msgid "Enable \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "啟用「自動調整」可自動解決此問題,或者仍要繼續?"
# AI Translated
msgid "Enable \"Auto-scale for nozzle\" and \"Auto-adjust\" to fix this automatically, or continue anyway?"
msgstr "啟用「依噴嘴自動縮放」和「自動調整」可自動解決此問題,或者仍要繼續?"
msgid "Start retraction length: "
msgstr "起始回抽長度:"
@@ -21206,6 +21323,9 @@ msgstr ""
"避免翹曲\n"
"您知道嗎?當列印容易翹曲的材料(如 ABS)時,適當提高熱床溫度可以降低翹曲的機率。"
#~ msgid "Print order within a single layer."
#~ msgstr "每一層的列印順序"
#~ msgid "Bottom"
#~ msgstr "底部"
@@ -21298,9 +21418,6 @@ msgstr ""
#~ msgid "°C"
#~ msgstr "°C"
#~ msgid "%"
#~ msgstr "%"
#~ msgid "Renders cast shadows on the plate in realistic view."
#~ msgstr "在擬真檢視中於列印板上算繪投射陰影。"
+71 -26
View File
@@ -1,4 +1,7 @@
#version 140
// Multisample depth texture for the anti-aliased outline (see 3DScene.cpp render_with_outline).
// Optional on the GLSL 140 path: if unavailable, fallback to a non-multisample depth texture.
#extension GL_ARB_texture_multisample : enable
const vec3 ZERO = vec3(0.0, 0.0, 0.0);
//BBS: add grey and orange
@@ -36,7 +39,14 @@ uniform SlopeDetection slope;
//BBS: add outline_color
uniform bool is_outline;
// The outline is a per-fragment discard mask, which the framebuffer MSAA cannot smooth, so the
// silhouette is resolved per sample from a multisample copy of the outlined model's depth buffer.
#ifdef GL_ARB_texture_multisample
uniform sampler2DMS depth_tex;
uniform int msaa_samples; // samples in depth_tex, 1 when MSAA is off
#else
uniform sampler2D depth_tex;
#endif
uniform vec2 screen_size;
#ifdef ENABLE_ENVIRONMENT_MAP
@@ -99,45 +109,87 @@ float GetTolerance(float d, float k)
return -k*(d+A)*(d+A)/B;
}
float DetectSilho(vec2 fragCoord, vec2 dir)
// Depth of sample s at integer pixel coord.
#ifdef GL_ARB_texture_multisample
float FetchDepth(ivec2 coord, int s)
{
// texelFetch has no wrap mode, so clamp to the edge texel (sampler2D used CLAMP_TO_EDGE).
ivec2 sz = textureSize(depth_tex);
return abs(texelFetch(depth_tex, clamp(coord, ivec2(0), sz - 1), s).r);
}
#else
float FetchDepth(ivec2 coord, int s)
{
return abs(texture(depth_tex, (vec2(coord) + 0.5) / screen_size).r);
}
#endif
float DetectSilho(ivec2 coord, ivec2 dir, int s)
{
// -------------------------------------------
// x0 ___ x1----o
// :\ :
// x0 ___ x1----o
// :\ :
// r0 : \ : r1
// : \ :
// : \ :
// o---x2 ___ x3
//
// r0 and r1 are the differences between actual
// and expected (as if x0..3 where on the same
// plane) depth values.
// -------------------------------------------
float x0 = abs(texture(depth_tex, (fragCoord + dir*-2.0) / screen_size).r);
float x1 = abs(texture(depth_tex, (fragCoord + dir*-1.0) / screen_size).r);
float x2 = abs(texture(depth_tex, (fragCoord + dir* 0.0) / screen_size).r);
float x3 = abs(texture(depth_tex, (fragCoord + dir* 1.0) / screen_size).r);
float x0 = FetchDepth(coord + dir*-2, s);
float x1 = FetchDepth(coord + dir*-1, s);
float x2 = FetchDepth(coord, s);
float x3 = FetchDepth(coord + dir* 1, s);
float d0 = (x1-x0);
float d1 = (x2-x3);
float r0 = x1 + d0 - x2;
float r1 = x2 + d1 - x1;
float tol = GetTolerance(x2, 0.04);
return smoothstep(0.0, tol*tol, max( - r0*r1, 0.0));
float tol = GetTolerance(x2, 0.04);
return smoothstep(0.0, tol*tol, max( - r0*r1, 0.0));
}
float DetectSilho(vec2 fragCoord)
float DetectSilho(ivec2 coord, int s)
{
return max(
DetectSilho(fragCoord, vec2(1,0)), // Horizontal
DetectSilho(fragCoord, vec2(0,1)) // Vertical
DetectSilho(coord, ivec2(1,0), s), // Horizontal
DetectSilho(coord, ivec2(0,1), s) // Vertical
);
}
// Full response of one sample. Reduce the max() per sample and average only afterwards:
// max(mean) <= mean(max), and averaging first hollows out diagonal and curved lines.
float DetectSilhoSample(ivec2 coord, int s)
{
float v = DetectSilho(coord, s);
// Makes silhouettes thicker.
for (int i = 1; i <= INFLATE; ++i)
{
v = max(v, DetectSilho(coord + ivec2(i, 0), s));
v = max(v, DetectSilho(coord + ivec2(0, i), s));
}
return v;
}
// Average the per-sample coverage into the sub-pixel anti-aliasing of the line.
float DetectSilho(vec2 fragCoord)
{
ivec2 coord = ivec2(fragCoord);
#ifdef GL_ARB_texture_multisample
int n = max(msaa_samples, 1);
#else
const int n = 1;
#endif
float acc = 0.0;
for (int s = 0; s < n; ++s)
acc += DetectSilhoSample(coord, s);
return acc / float(n);
}
// Returns a lighting multiplier in [1 - shadow_intensity, 1]: < 1 where the fragment is
// occluded from the light in the shadow map. 3x3 PCF softens the edges.
float shadow_shade()
@@ -224,14 +276,7 @@ void main()
//BBS: add outline_color
if (is_outline) {
color = vec4((vec3(intensity.y) + color.rgb * intensity.x) * shade, color.a);
vec2 fragCoord = gl_FragCoord.xy;
float s = DetectSilho(fragCoord);
// Makes silhouettes thicker.
for(int i=1;i<=INFLATE; i++)
{
s = max(s, DetectSilho(fragCoord.xy + vec2(i, 0)));
s = max(s, DetectSilho(fragCoord.xy + vec2(0, i)));
}
float s = DetectSilho(gl_FragCoord.xy);
if (s < 0.01)
discard;
out_color = vec4(mix(color.rgb, getBackfaceColor(color.rgb), s), color.a);
+63 -16
View File
@@ -1,4 +1,7 @@
#version 140
// Multisample depth texture for the anti-aliased outline (see 3DScene.cpp render_with_outline).
// Optional on the GLSL 140 path: if unavailable, fallback to a non-multisample depth texture.
#extension GL_ARB_texture_multisample : enable
const vec3 ZERO = vec3(0.0, 0.0, 0.0);
const vec3 LightRed = vec3(0.78, 0.0, 0.0);
@@ -51,7 +54,14 @@ uniform SlopeDetection slope;
//BBS: add outline_color
uniform bool is_outline;
// The outline is a per-fragment discard mask, which the framebuffer MSAA cannot smooth, so the
// silhouette is resolved per sample from a multisample copy of the outlined model's depth buffer.
#ifdef GL_ARB_texture_multisample
uniform sampler2DMS depth_tex;
uniform int msaa_samples; // samples in depth_tex, 1 when MSAA is off
#else
uniform sampler2D depth_tex;
#endif
uniform vec2 screen_size;
#ifdef ENABLE_ENVIRONMENT_MAP
@@ -100,12 +110,27 @@ float GetTolerance(float d, float k)
return -k*(d+A)*(d+A)/B;
}
float DetectSilho(vec2 fragCoord, vec2 dir)
// Depth of sample s at integer pixel coord.
#ifdef GL_ARB_texture_multisample
float FetchDepth(ivec2 coord, int s)
{
float x0 = abs(texture(depth_tex, (fragCoord + dir*-2.0) / screen_size).r);
float x1 = abs(texture(depth_tex, (fragCoord + dir*-1.0) / screen_size).r);
float x2 = abs(texture(depth_tex, (fragCoord + dir* 0.0) / screen_size).r);
float x3 = abs(texture(depth_tex, (fragCoord + dir* 1.0) / screen_size).r);
// texelFetch has no wrap mode, so clamp to the edge texel (sampler2D used CLAMP_TO_EDGE).
ivec2 sz = textureSize(depth_tex);
return abs(texelFetch(depth_tex, clamp(coord, ivec2(0), sz - 1), s).r);
}
#else
float FetchDepth(ivec2 coord, int s)
{
return abs(texture(depth_tex, (vec2(coord) + 0.5) / screen_size).r);
}
#endif
float DetectSilho(ivec2 coord, ivec2 dir, int s)
{
float x0 = FetchDepth(coord + dir*-2, s);
float x1 = FetchDepth(coord + dir*-1, s);
float x2 = FetchDepth(coord, s);
float x3 = FetchDepth(coord + dir* 1, s);
float d0 = (x1-x0);
float d1 = (x2-x3);
@@ -116,17 +141,45 @@ float DetectSilho(vec2 fragCoord, vec2 dir)
float tol = GetTolerance(x2, 0.04);
return smoothstep(0.0, tol*tol, max( - r0*r1, 0.0));
}
float DetectSilho(vec2 fragCoord)
float DetectSilho(ivec2 coord, int s)
{
return max(
DetectSilho(fragCoord, vec2(1,0)),
DetectSilho(fragCoord, vec2(0,1))
DetectSilho(coord, ivec2(1,0), s),
DetectSilho(coord, ivec2(0,1), s)
);
}
// Full response of one sample. Reduce the max() per sample and average only afterwards:
// max(mean) <= mean(max), and averaging first hollows out diagonal and curved lines.
float DetectSilhoSample(ivec2 coord, int s)
{
float v = DetectSilho(coord, s);
// Makes silhouettes thicker.
for (int i = 1; i <= INFLATE; ++i)
{
v = max(v, DetectSilho(coord + ivec2(i, 0), s));
v = max(v, DetectSilho(coord + ivec2(0, i), s));
}
return v;
}
// Average the per-sample coverage into the sub-pixel anti-aliasing of the line.
float DetectSilho(vec2 fragCoord)
{
ivec2 coord = ivec2(fragCoord);
#ifdef GL_ARB_texture_multisample
int n = max(msaa_samples, 1);
#else
const int n = 1;
#endif
float acc = 0.0;
for (int s = 0; s < n; ++s)
acc += DetectSilhoSample(coord, s);
return acc / float(n);
}
float compute_ssao_factor(vec3 normal, vec3 view_dir, vec3 eye_pos)
{
vec3 normal_dx = dFdx(normal);
@@ -270,13 +323,7 @@ void main()
if (is_outline) {
vec3 shaded_rgb = (vec3(specular) + window_reflection + color.rgb * diffuse) * PHONG_BRIGHTNESS * shade;
vec4 shaded_color = vec4(clamp(shaded_rgb, vec3(0.0), vec3(1.0)), color.a);
vec2 fragCoord = gl_FragCoord.xy;
float s = DetectSilho(fragCoord);
for(int i=1;i<=INFLATE; i++)
{
s = max(s, DetectSilho(fragCoord.xy + vec2(i, 0)));
s = max(s, DetectSilho(fragCoord.xy + vec2(0, i)));
}
float s = DetectSilho(gl_FragCoord.xy);
if (s < 0.01)
discard;
out_color = vec4(mix(shaded_color.rgb, getBackfaceColor(shaded_color.rgb), s), shaded_color.a);
+14 -5
View File
@@ -602,15 +602,17 @@ function SourceLabel(source) {
return "Mine";
case "subscribed":
return "Subscribed";
case "orphaned":
return "Orphaned";
default:
return "Local";
}
}
// Shared Local/Subscribed/Mine pill, used both after the row name and in the info panel.
// Shared source pill, used both after the row name and in the info panel.
function SourceBadge(source) {
const normalized = String(source || "").toLowerCase();
const variant = (normalized === "mine" || normalized === "subscribed") ? normalized : "local";
const variant = (normalized === "mine" || normalized === "subscribed" || normalized === "orphaned") ? normalized : "local";
const badge = document.createElement("span");
badge.className = `plugin-source-badge source-${variant}`;
badge.textContent = SourceLabel(source);
@@ -654,7 +656,7 @@ function LabelCell(plugin, isExpanded = false, capabilityCount = 0, nameRanges =
const labelCell = document.createElement("span");
labelCell.className = "label-cell";
const hasCloudLink = plugin.source === "mine" || plugin.source === "subscribed";
const hasCloudLink = plugin.source === "mine" || plugin.source === "subscribed" || plugin.source === "orphaned";
const pluginLabelText = plugin.label || plugin.name || plugin.plugin_id || "";
const canExpand = capabilityCount > 0;
@@ -705,7 +707,7 @@ function LabelCell(plugin, isExpanded = false, capabilityCount = 0, nameRanges =
function SourceCell(plugin) {
const cell = document.createElement("span");
const normalized = String(plugin.source || "").toLowerCase();
const variant = (normalized === "mine" || normalized === "subscribed") ? normalized : "local";
const variant = (normalized === "mine" || normalized === "subscribed" || normalized === "orphaned") ? normalized : "local";
cell.className = `source-cell source-${variant}`;
const sourceLabel = document.createElement("span");
@@ -1233,7 +1235,7 @@ function RenderDescription(plugin) {
return;
}
const isCloud = plugin && (plugin.source === "mine" || plugin.source === "subscribed");
const isCloud = plugin && (plugin.source === "mine" || plugin.source === "subscribed" || plugin.source === "orphaned");
if (isCloud && String(plugin?.sharing_token || "")) {
node.appendChild(document.createTextNode("View on OrcaCloud "));
const link = document.createElement("a");
@@ -1370,6 +1372,13 @@ function RenderDetailSummary(container, plugin) {
message.textContent = errorText || StatusDescription(plugin);
container.appendChild(message);
if (plugin.orphaned === true) {
const warning = document.createElement("div");
warning.className = "detail-description detail-warning-text";
warning.textContent = "Orphaned: This plugin is no longer subscribed or available in OrcaCloud. The local copy remains installed and can still be used.";
container.appendChild(warning);
}
const updateStatus = GetUpdateStatus(plugin);
if (updateStatus === "update_available") {
const note = document.createElement("div");
@@ -251,6 +251,11 @@ body.pane-resizing {
font-weight: 600;
}
.source-cell.source-orphaned {
color: var(--plugin-status-warn);
font-weight: 600;
}
.source-cell.source-local {
color: var(--plugin-source-neutral-text);
}
@@ -648,6 +653,10 @@ body.pane-resizing {
color: var(--plugin-status-danger);
}
.detail-warning-text {
color: var(--plugin-status-warn);
}
.detail-status-chip {
display: inline-flex;
align-items: center;
@@ -915,6 +924,11 @@ body.pane-resizing {
color: var(--plugin-source-subscribed-text);
}
.plugin-source-badge.source-orphaned {
background: var(--plugin-status-warn-bg);
color: var(--plugin-status-warn);
}
.plugin-cloud-link {
color: var(--plugin-link-text);
cursor: pointer;
+17 -1
View File
@@ -2,11 +2,13 @@
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
xmlns:rescap3="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities/3"
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
xmlns:desktop6="http://schemas.microsoft.com/appx/manifest/desktop/windows10/6"
xmlns:virtualization="http://schemas.microsoft.com/appx/manifest/virtualization/windows10"
IgnorableNamespaces="uap rescap rescap3 desktop6 virtualization">
IgnorableNamespaces="uap uap3 rescap rescap3 desktop desktop6 virtualization">
<Identity Name="@MSIX_IDENTITY_NAME@"
Publisher="@MSIX_PUBLISHER@"
@@ -64,6 +66,20 @@
<uap:Extension Category="windows.protocol">
<uap:Protocol Name="orcaslicer" />
</uap:Extension>
<uap:Extension Category="windows.protocol">
<uap:Protocol Name="prusaslicer" />
</uap:Extension>
<uap:Extension Category="windows.protocol">
<uap:Protocol Name="bambustudio" />
</uap:Extension>
<uap:Extension Category="windows.protocol">
<uap:Protocol Name="cura" />
</uap:Extension>
<uap3:Extension Category="windows.appExecutionAlias" EntryPoint="Windows.FullTrustApplication">
<uap3:AppExecutionAlias>
<desktop:ExecutionAlias Alias="orca-slicer.exe" />
</uap3:AppExecutionAlias>
</uap3:Extension>
</Extensions>
</Application>
</Applications>
+16 -1
View File
@@ -202,10 +202,25 @@ void AppConfig::set_defaults()
if (get("seq_top_layer_only").empty())
set("seq_top_layer_only", "1");
// ORCA: darken layers below the current one while scrubbing the preview (ported from preFlight)
// ORCA: darken the layers the preview layer slider is not scrubbed to
if (get("preview_dim_previous_layers").empty())
set_bool("preview_dim_previous_layers", false);
// ORCA: brightness of those dimmed layers, in percent. 0 = black, capped at 99 because
// 100 would render them unchanged, which is what disabling the option already does
if (get("preview_dim_previous_layers_brightness").empty())
set("preview_dim_previous_layers_brightness", "40");
else {
int brightness = 40;
try {
brightness = std::stoi(get("preview_dim_previous_layers_brightness"));
}
catch (...) {
brightness = 40;
}
set("preview_dim_previous_layers_brightness", std::to_string(std::max(0, std::min(brightness, 99))));
}
if (get("filaments_area_preferred_count").empty())
set("filaments_area_preferred_count", "10");
+1 -1
View File
@@ -25,7 +25,7 @@ public:
min(p1), max(p1), defined(false) { merge(p2); merge(p3); }
template<class It, class = IteratorOnly<It>>
BoundingBoxBase(It from, It to)
BoundingBoxBase(It from, It to) : BoundingBoxBase()
{ construct(*this, from, to); }
BoundingBoxBase(const PointsType &points)
+18 -3
View File
@@ -2,6 +2,7 @@
#define slic3r_Config_hpp_
#include <assert.h>
#include <algorithm>
#include <map>
#include <climits>
#include <cfloat>
@@ -780,10 +781,14 @@ public:
this->values[i] = rhs_vec->values[i];
modified = true;
} else {
if ((i < default_index.size()) && (default_index[i] < default_value.size()))
// Orca: a negative slot (failed variant lookup) must not silently collapse the
// whole array to the first slot's value — the int-vs-size_t comparison used to
// promote -1 past the bounds check. Keep the slot's own value (get_at-style
// clamp) when no valid index is available.
if ((i < default_index.size()) && (default_index[i] >= 0) && (size_t(default_index[i]) < default_value.size()))
this->values[i] = default_value[default_index[i]];
else
this->values[i] = default_value[0];
this->values[i] = default_value[std::min(i, default_value.size() - 1)];
}
}
return modified;
@@ -2106,6 +2111,11 @@ public:
throw ConfigurationError("ConfigOptionEnumGeneric: Assigning an incompatible type");
// rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum<T>
this->value = rhs->getInt();
// Orca: options embedded in a StaticPrintConfig are constructed without a keys_map;
// adopt the source's so a later serialize() can emit names.
if (this->keys_map == nullptr)
if (auto rhs_generic = dynamic_cast<const ConfigOptionEnumGeneric *>(rhs))
this->keys_map = rhs_generic->keys_map;
}
std::string serialize() const override
@@ -2162,7 +2172,12 @@ public:
if (rhs->type() != this->type())
throw ConfigurationError("ConfigOptionEnumGeneric: Assigning an incompatible type");
// rhs could be of the following type: ConfigOptionEnumsGeneric
this->values = dynamic_cast<const ConfigOptionEnumsGenericTempl *>(rhs)->values;
auto rhs_enums = dynamic_cast<const ConfigOptionEnumsGenericTempl *>(rhs);
this->values = rhs_enums->values;
// Orca: options embedded in a StaticPrintConfig are constructed without a keys_map;
// adopt the source's so a later serialize() emits names instead of empty tokens.
if (this->keys_map == nullptr)
this->keys_map = rhs_enums->keys_map;
}
std::string serialize() const override
+5 -5
View File
@@ -1857,12 +1857,12 @@ static inline void base_support_extend_infill_lines(Polylines &infill, BoundaryI
const bool first = graph.first(cp);
int extend_next_idx = -1;
int extend_prev_idx = -1;
coord_t dist_y_prev;
coord_t dist_y_next;
double arc_len_prev;
double arc_len_next;
coord_t dist_y_prev = 0;
coord_t dist_y_next = 0;
double arc_len_prev = 0;
double arc_len_next = 0;
if (! graph.next_vertical(cp)){
if (! graph.next_vertical(cp)) {
size_t i = cp.point_idx;
size_t j = next_idx_modulo(i, contour);
while (j != cp.next_on_contour->point_idx) {
+1 -1
View File
@@ -78,7 +78,7 @@ public:
Standard_Boolean UserBreak() override { return should_stop.load(); }
void Show(const Message_ProgressScope&, const Standard_Boolean) override {
std::cout << "Progress: " << GetPosition() << "%" << std::endl;
std::cout << "Progress: " << std::fixed << std::setprecision(2) << 100.0 * GetPosition() << "%" << std::endl;
}
private:
std::atomic<bool>& should_stop;
+132 -41
View File
@@ -13,6 +13,7 @@
#include "GCode/PrintExtents.hpp"
#include "GCode/Thumbnails.hpp"
#include "GCode/WipeTower.hpp"
#include "GCode/WipeTower2.hpp"
#include "ShortestPath.hpp"
#include "GCode/OrderingStrategies.hpp"
#include "Print.hpp"
@@ -889,6 +890,65 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
return res;
}
// Type2 tower-local point -> bed frame. The rib-wall offset is tower-local, so it
// rotates with the tower (unlike the BBL tower in append_tcr, which never rotates).
Vec2f WipeTowerIntegration::transform_wt2_pt(const Vec2f &pt) const
{
const float alpha = m_wipe_tower_rotation / 180.f * float(M_PI);
return Eigen::Rotation2Df(alpha) * (pt + m_rib_offset) + m_wipe_tower_pos;
}
// Printable-area bounds for tower-approach routing, in object coordinates (shared by
// the BBL avoid-perimeter path in append_tcr and the Type2 skip-points router).
// Multi-nozzle: clamp the travel bounds to the region every extruder can reach
// (get_extruder_shared_printable_polygon) instead of the full bed. Gated on the
// multi-nozzle predicate so every existing single/dual printer keeps the historic
// full-printable_area routing byte-identical.
BoundingBox WipeTowerIntegration::printer_travel_bounds(GCode &gcodegen) const
{
const Vec2f plate_origin_2d(m_plate_origin(0), m_plate_origin(1));
BoundingBox printer_bbx;
if (is_multi_nozzle_printer(gcodegen.m_config)) {
printer_bbx = get_extents(gcodegen.m_print->get_extruder_shared_printable_polygon());
printer_bbx.min = wipe_tower_point_to_object_point(gcodegen, unscaled<float>(printer_bbx.min) + plate_origin_2d);
printer_bbx.max = wipe_tower_point_to_object_point(gcodegen, unscaled<float>(printer_bbx.max) + plate_origin_2d);
} else {
Points bed_points;
for (const auto& p : gcodegen.m_config.printable_area.values)
bed_points.push_back(wipe_tower_point_to_object_point(gcodegen, p.cast<float>() + plate_origin_2d));
printer_bbx = BoundingBox(bed_points);
}
return printer_bbx;
}
// With skip points enabled the Type2 tower wall has an opening at each toolchange's
// entry (tcr.start_pos): route the approach around the tower's bounding box so the
// nozzle enters through that opening instead of dragging across the printed wall
// (append_tcr parity). Emits only the waypoints leading up to the opening — the
// caller still travels to start_wipe_pos itself. Returns an empty string when the
// gap wall is off (option off or cone wall) or the approach already starts inside
// the tower: such hops never cross the wall and must stay direct.
std::string WipeTowerIntegration::travel_to_tower_gap(GCode &gcodegen, const Point &route_start, const Point &start_wipe_pos) const
{
if (!WipeTower2::use_gap_wall(gcodegen.m_config))
return {};
const Vec2f plate_origin_2d(m_plate_origin(0), m_plate_origin(1));
// Transform the tower-local bbx corners exactly like the tcr points; a rotated
// tower gets a conservative axis-aligned envelope.
Polygon avoid_points = scaled(m_wipe_tower_bbx).polygon();
for (auto& p : avoid_points.points)
p = wipe_tower_point_to_object_point(gcodegen, transform_wt2_pt(unscale(p).cast<float>()) + plate_origin_2d);
BoundingBox avoid_bbx(avoid_points.points);
if (avoid_bbx.contains(route_start))
return {};
Polyline travel_polyline = generate_path_to_wipe_tower(route_start, start_wipe_pos, avoid_bbx, printer_travel_bounds(gcodegen));
std::string gcode;
// The polyline's last point is start_wipe_pos itself — emitted by the caller.
for (size_t i = 0; i + 1 < travel_polyline.points.size(); ++i)
gcode += gcodegen.travel_to(travel_polyline.points[i], erMixed, "Travel to a Wipe Tower");
return gcode;
}
std::string WipeTowerIntegration::append_tcr(GCode& gcodegen, const WipeTower::ToolChangeResult& tcr, int new_filament_id, double z) const
{
if (new_filament_id != -1 && new_filament_id != tcr.new_tool)
@@ -999,6 +1059,7 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
std::string change_filament_gcode = gcodegen.config().change_filament_gcode.value;
bool is_used_travel_avoid_perimeter = gcodegen.m_config.prime_tower_skip_points.value;
if (is_nozzle_change && !tcr.nozzle_change_result.is_extruder_change) is_used_travel_avoid_perimeter = false;
// add nozzle change gcode into change filament gcode
std::string nozzle_change_gcode_trans;
@@ -1261,24 +1322,7 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
Vec2f gcode_last_pos2d{gcode_last_pos[0], gcode_last_pos[1]};
Point gcode_last_pos2d_object = gcodegen.gcode_to_point(gcode_last_pos2d.cast<double>() + plate_origin_2d.cast<double>());
Point start_wipe_pos = wipe_tower_point_to_object_point(gcodegen, tool_change_start_pos + plate_origin_2d);
BoundingBox avoid_bbx, printer_bbx;
{
// set printer_bbx
// Multi-nozzle: clamp the avoid-perimeter travel bounds to the region every
// extruder can reach (get_extruder_shared_printable_polygon) instead of the full
// bed. Gated on the multi-nozzle predicate so H2D and every existing single/dual
// printer keep the historic full-printable_area routing byte-identical.
if (is_multi_nozzle_printer(gcodegen.m_config)) {
printer_bbx = get_extents(gcodegen.m_print->get_extruder_shared_printable_polygon());
printer_bbx.min = wipe_tower_point_to_object_point(gcodegen, unscaled<float>(printer_bbx.min) + plate_origin_2d);
printer_bbx.max = wipe_tower_point_to_object_point(gcodegen, unscaled<float>(printer_bbx.max) + plate_origin_2d);
} else {
Pointfs bed_pointsf = gcodegen.m_config.printable_area.values;
Points bed_points;
for (auto p : bed_pointsf) { bed_points.push_back(wipe_tower_point_to_object_point(gcodegen, p.cast<float>() + plate_origin_2d)); }
printer_bbx = BoundingBox(bed_points);
}
}
BoundingBox avoid_bbx, printer_bbx = printer_travel_bounds(gcodegen);
{
// set avoid_bbx
avoid_bbx = scaled(m_wipe_tower_bbx);
@@ -1308,20 +1352,23 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
}
// do unretract after setting current extruder_id
// PETG filaments on a device with a filament switcher get a small (2 mm) pre-extrusion
// before the tool change. has_filament_switcher is a develop-only key read defensively from the
// full config (Orca does not carry it as a static PrintConfig member — same convention as
// enable_filament_dynamic_map); no shipping profile sets it (grep resources/profiles = 0), so
// is_petg_pre_extrusion is always false -> extra_unretract stays 0 -> byte-identical to the plain
// unretract() fleet-wide. The tower-interface contact pre-extrusion length (the
// is_contact_pre_extrusion branch) is NOT applied here; it is only computed as the guard used to
// give the contact path priority over PETG.
// BBS pattern: the wipe tower shifts the toolchange start position outward for the
// tower-interface (contact) pre-extrusion and for the PETG-with-filament-switcher case;
// the pre-extrusion material itself is laid down here as extra unretract on the approach.
// has_filament_switcher is a develop-only key read defensively from the full config (Orca
// does not carry it as a static PrintConfig member — same convention as
// enable_filament_dynamic_map); no shipping profile sets it, so is_petg_pre_extrusion is
// always false fleet-wide.
const ConfigOptionBool* has_filament_switcher_opt = gcodegen.m_print->full_print_config().option<ConfigOptionBool>("has_filament_switcher");
bool is_contact_pre_extrusion = tcr.is_contact && gcodegen.m_config.enable_tower_interface_features;
bool is_petg_pre_extrusion = !is_contact_pre_extrusion
&& gcodegen.config().filament_type.get_at(tcr.new_tool) == "PETG"
&& has_filament_switcher_opt && has_filament_switcher_opt->value;
float extra_unretract = is_petg_pre_extrusion ? 2.f : 0.f;
float extra_unretract = 0.f;
if (is_contact_pre_extrusion)
extra_unretract = gcodegen.m_config.filament_tower_interface_pre_extrusion_length.get_at(tcr.new_tool);
else if (is_petg_pre_extrusion)
extra_unretract = 2.f;
std::string toolchange_unretract_str = (extra_unretract > 0.f) ? gcodegen.unretract(extra_unretract) : gcodegen.unretract();
check_add_eol(toolchange_unretract_str);
@@ -1419,20 +1466,16 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
// We want to rotate and shift all extrusions (gcode postprocessing) and starting and ending position
float alpha = m_wipe_tower_rotation / 180.f * float(M_PI);
auto transform_wt_pt = [&alpha, this](const Vec2f &pt) -> Vec2f {
Vec2f out = Eigen::Rotation2Df(alpha) * pt;
out += m_wipe_tower_pos;
return out;
};
// Priming lines are absolute bed moves; everything else is tower-local
// (transform_wt2_pt).
Vec2f start_pos = tcr.start_pos;
Vec2f end_pos = tcr.end_pos;
if (!tcr.priming) {
start_pos = transform_wt_pt(start_pos);
end_pos = transform_wt_pt(end_pos);
start_pos = transform_wt2_pt(start_pos);
end_pos = transform_wt2_pt(end_pos);
}
Vec2f wipe_tower_offset = tcr.priming ? Vec2f::Zero() : m_wipe_tower_pos;
Vec2f wipe_tower_offset = tcr.priming ? Vec2f::Zero() : Vec2f(m_wipe_tower_pos + Eigen::Rotation2Df(alpha) * m_rib_offset);
float wipe_tower_rotation = tcr.priming ? 0.f : alpha;
Vec2f plate_origin_2d(m_plate_origin(0), m_plate_origin(1));
@@ -1462,16 +1505,34 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|| is_ramming
|| tool_change_on_wipe_tower);
if (should_travel_to_tower || gcodegen.m_need_change_layer_lift_z) {
const Point start_wipe_pos = wipe_tower_point_to_object_point(gcodegen, start_pos + plate_origin_2d);
const bool travel_to_tower_now = should_travel_to_tower || gcodegen.m_need_change_layer_lift_z;
if (travel_to_tower_now) {
// FIXME: It would be better if the wipe tower set the force_travel flag for all toolchanges,
// then we could simplify the condition and make it more readable.
gcode += gcodegen.retract();
// Orca: pass the configured lift type, as append_tcr does above. lazy_lift() keeps
// the first type it is given, so the NormalLift default would pin this hop to a
// standing move. Slope and spiral both need a known head position.
LiftType lift_type = LiftType::NormalLift;
if (gcodegen.writer().filament() != nullptr && gcodegen.writer().is_current_position_clear()) {
ZHopType z_hop_type = ZHopType(gcodegen.config().z_hop_types.get_at(
gcodegen.get_filament_config_index((int) gcodegen.writer().filament()->id())));
if (z_hop_type == ZHopType::zhtAuto)
z_hop_type = ZHopType::zhtSpiral;
lift_type = gcodegen.to_lift_type(z_hop_type);
}
gcode += gcodegen.retract(false, false, lift_type);
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once();
gcode += gcodegen.travel_to(wipe_tower_point_to_object_point(gcodegen, start_pos + plate_origin_2d), erMixed, "Travel to a Wipe Tower");
if (!tcr.priming && gcodegen.last_pos_defined())
gcode += travel_to_tower_gap(gcodegen, gcodegen.last_pos(), start_wipe_pos);
gcode += gcodegen.travel_to(start_wipe_pos, erMixed, "Travel to a Wipe Tower");
gcode += gcodegen.unretract();
} else {
// When this is multiextruder printer without any ramming, we can just change
// the tool without travelling to the tower.
// the tool without travelling to the tower. The tower entry travel then lives
// inside the tcr gcode; with skip points on it is rerouted below, once the
// toolchange gcode (and the head position it ends at) is known.
}
if (will_go_down) {
@@ -1494,6 +1555,36 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
toolchange_temp_override = interface_temp;
}
toolchange_gcode_str = gcodegen.set_extruder(new_extruder_id, tcr.print_z, false, toolchange_temp_override); // TODO: toolchange_z vs print_z
if (!travel_to_tower_now && !tcr.priming && WipeTower2::use_gap_wall(gcodegen.m_config)) {
// The tool changed in place (multi-tool printer without ramming), so the
// tower entry is the tcr's own positioning move — a straight line across
// the printed wall. Route it around the tower and in through the wall
// opening instead, riding at the end of the change_filament_gcode
// substitution so the generator's positioning move degrades to a
// zero-length one (append_tcr parity: travel after the filament change,
// retracted, with the new filament).
Vec3f last_gcode_pos = gcodegen.writer().get_position().cast<float>();
Point route_start;
bool have_start = false;
if (GCodeProcessor::get_last_position_from_gcode(toolchange_gcode_str, last_gcode_pos)) {
// A custom change_filament_gcode may have moved the head (tool docks
// etc.); recover the real position from the emitted gcode.
route_start = gcodegen.gcode_to_point(Vec2d(last_gcode_pos.x(), last_gcode_pos.y()) + plate_origin_2d.cast<double>());
have_start = true;
} else if (gcodegen.last_pos_defined()) {
route_start = gcodegen.last_pos();
have_start = true;
}
if (have_start) {
gcodegen.set_last_pos(route_start);
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once();
std::string travel = travel_to_tower_gap(gcodegen, route_start, start_wipe_pos);
travel += gcodegen.travel_to(start_wipe_pos, erMixed, "Travel to a Wipe Tower");
check_add_eol(travel);
toolchange_gcode_str += travel;
gcodegen.set_last_pos(start_wipe_pos);
}
}
if (gcodegen.config().enable_prime_tower) {
deretraction_str += gcodegen.writer().travel_to_z(z, "Force restore layer Z", true);
Vec3d position{gcodegen.writer().get_position()};
@@ -1679,7 +1770,7 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
// Prepare a future wipe.
gcodegen.m_wipe.reset_path();
for (const Vec2f& wipe_pt : tcr.wipe_path)
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, transform_wt_pt(wipe_pt) + plate_origin_2d));
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, transform_wt2_pt(wipe_pt) + plate_origin_2d));
}
// Let the planner know we are traveling between objects.
+4 -1
View File
@@ -132,6 +132,9 @@ private:
std::string append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id, double z = -1.) const;
Polyline generate_path_to_wipe_tower(const Point &start_pos, const Point &end_pos, const BoundingBox &avoid_polygon, const BoundingBox &printer_bbx) const;
std::string append_tcr2(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id, double z = -1.) const;
std::string travel_to_tower_gap(GCode &gcodegen, const Point &route_start, const Point &start_wipe_pos) const;
Vec2f transform_wt2_pt(const Vec2f &pt) const;
BoundingBox printer_travel_bounds(GCode &gcodegen) const;
// Postprocesses gcode: rotates and moves G1 extrusions and returns result
std::string post_process_wipe_tower_moves(const WipeTower::ToolChangeResult& tcr, const Vec2f& translation, float angle) const;
@@ -181,7 +184,7 @@ struct LayerResult {
// It is used for the pressure equalizer because it needs to buffer one layer back.
bool nop_layer_result { false };
static LayerResult make_nop_layer_result() { return {"", std::numeric_limits<coord_t>::max(), false, false, true}; }
static LayerResult make_nop_layer_result() { return {"", std::numeric_limits<size_t>::max(), false, false, true}; }
};
class GCode {
+2 -2
View File
@@ -1450,8 +1450,8 @@ void GCodeProcessor::run_post_process()
// flag) runs none of this. It is pure data construction — it only fills m_filament_blocks /
// m_extruder_blocks / m_machine_*_gcode_*_line_id and never touches the exported g-code, so even
// the enable_pre_heating fleet stays byte-identical (nothing reads the blocks until the injection
// pass). In practice it also stays empty/degenerate today because no template/code yet emits the
// MACHINE_*_GCODE_* / NOZZLE_CHANGE_* / CP_TOOLCHANGE_WIPE markers it keys off.
// pass). The wipe tower emits the NOZZLE_CHANGE_* (ramming) and CP_TOOLCHANGE_WIPE markers this
// builder keys off; the MACHINE_*_GCODE_* markers come from the machine g-code templates.
m_filament_blocks.clear();
m_extruder_blocks.clear();
m_machine_start_gcode_end_line_id = (unsigned int) (-1);
+2 -1
View File
@@ -143,7 +143,8 @@ BoundingBoxf get_wipe_tower_extrusions_extents(const Print &print, const coordf_
double wipe_tower_y = print.config().wipe_tower_y.get_at(plate_idx) + plate_origin(1);
Transform2d trafo =
Eigen::Translation2d(wipe_tower_x, wipe_tower_y) *
Eigen::Rotation2Dd(Geometry::deg2rad(print.config().wipe_tower_rotation_angle.value));
Eigen::Rotation2Dd(Geometry::deg2rad(print.config().wipe_tower_rotation_angle.value)) *
Eigen::Translation2d(print.wipe_tower_data().rib_offset.cast<double>()); // tower-local rib-wall shift, zero unless rib
BoundingBoxf bbox;
for (const std::vector<WipeTower::ToolChangeResult> &tool_changes : print.wipe_tower_data().tool_changes) {
File diff suppressed because it is too large Load Diff
+112 -112
View File
@@ -12,7 +12,7 @@
#include "libslic3r/Polyline.hpp"
#include "libslic3r/TriangleMesh.hpp"
#include <unordered_set>
#include "libslic3r/MultiNozzleUtils.hpp"
namespace Slic3r
{
@@ -20,6 +20,11 @@ class WipeTowerWriter;
class PrintConfig;
enum GCodeFlavor : unsigned char;
// Cuts the tower wall polygon open at each skip point (a toolchange's entry position)
// so the entry travel can pass through instead of crossing the printed wall. Defined in
// WipeTower.cpp, shared by WipeTower and WipeTower2.
Polylines construct_gap_for_skip_points(
const Polygon& polygon, const std::vector<Vec2f>& skip_points, float wt_width, float gap_length, Polygon& insert_skip_polygon);
class WipeTower
{
@@ -84,7 +89,6 @@ public:
bool priming;
bool is_tool_change{false};
bool is_contact{false};
Vec2f tool_change_start_pos;
// Pass a polyline so that normal G-code generator can do a wipe for us.
@@ -108,6 +112,7 @@ public:
// executing the gcode finish_layer_tcr.
bool is_finish_first = false;
bool is_contact = false;
NozzleChangeResult nozzle_change_result;
// Sum the total length of the extrusion.
@@ -122,6 +127,8 @@ public:
}
return e_length;
}
// Orca: set by WipeTower2 (non-BBL tower) to force a travel to the tower even when the
// previous position is unknown; read by WipeTowerIntegration::append_tcr2 (GCode.cpp).
bool force_travel = false;
};
@@ -162,15 +169,12 @@ public:
bool priming,
size_t old_tool,
bool is_finish,
bool is_tool_change,
float purge_volume,
bool is_contact = false) const;
bool is_tool_change, float purge_volume, bool is_contact) const;
ToolChangeResult construct_block_tcr(WipeTowerWriter& writer,
bool priming,
size_t filament_id,
bool is_finish,
float purge_volume) const;
bool is_finish, float purge_volume) const;
// x -- x coordinates of wipe tower in mm ( left bottom corner )
@@ -184,9 +188,14 @@ public:
// Set the extruder properties.
void set_extruder(size_t idx, const PrintConfig& config);
void set_shared_print_bed(const Polygons &bed) { m_shared_print_bed = bed; }
// Orca: has_filament_switcher is not a static PrintConfig member here, so it is pushed in from
// Print via a setter rather than read in the ctor. Device-set only.
void set_has_filament_switcher(bool v) { m_has_filament_switcher = v; }
// Appends into internal structure m_plan containing info about the future wipe tower
// to be used before building begins. The entries must be added ordered in z.
void plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, float wipe_volume = 0.f, float prime_volume = 0.f);
void plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, float wipe_volume_ec = 0.f, float wipe_volume_nc = 0.f, float prime_volume = 0.f);
// Iterates through prepared m_plan, generates ToolChangeResults and appends them to "result"
void generate(std::vector<std::vector<ToolChangeResult>> &result);
@@ -219,9 +228,6 @@ public:
}
}
void set_wipe_volume(std::vector<std::vector<float>>& wiping_matrix) {
wipe_volumes = wiping_matrix;
}
// Switch to a next layer.
void set_layer(
@@ -250,7 +256,6 @@ public:
// Calculate extrusion flow from desired line width, nozzle diameter, filament diameter and layer_height:
m_extrusion_flow = extrusion_flow(layer_height);
// Advance m_layer_info iterator, making sure we got it right
while (!m_plan.empty() && m_layer_info->z < print_z - WT_EPSILON && m_layer_info+1 != m_plan.end())
++m_layer_info;
@@ -309,20 +314,9 @@ public:
std::vector<float> get_used_filament() const { return m_used_filament_length; }
int get_number_of_toolchanges() const { return m_num_tool_changes; }
void set_filament_map(const std::vector<int> &filament_map) { m_filament_map = filament_map; }
// Vortek H2C: filament_id → physical nozzle_id for carousel rotation detection
void set_filament_nozzle_map(const std::vector<int> &nozzle_map) { m_filament_nozzle_map = nozzle_map; }
void set_has_tpu_filament(bool has_tpu) { m_has_tpu_filament = has_tpu; }
bool has_tpu_filament() const { return m_has_tpu_filament; }
// Orca: has_filament_switcher is not a static PrintConfig member, so it is pushed in from Print
// via a setter rather than read in the ctor. Device-set only.
void set_has_filament_switcher(bool v) { m_has_filament_switcher = v; }
// The region every extruder can reach, used to clamp the PETG pre-extrusion offset to the
// printable bed.
void set_shared_print_bed(const Polygons &bed) { m_shared_print_bed = bed; }
struct FilamentParameters {
std::string material = "PLA";
int category;
@@ -331,15 +325,15 @@ public:
bool is_support = false;
int nozzle_temperature = 0;
int nozzle_temperature_initial_layer = 0;
int interface_print_temperature = 0;
float loading_speed = 0.f;
float loading_speed_start = 0.f;
float unloading_speed = 0.f;
float unloading_speed_start = 0.f;
float delay = 0.f ;
int cooling_moves = 0;
float cooling_initial_speed = 0.f;
float cooling_final_speed = 0.f;
// BBS: remove useless config
//float loading_speed = 0.f;
//float loading_speed_start = 0.f;
//float unloading_speed = 0.f;
//float unloading_speed_start = 0.f;
//float delay = 0.f ;
//int cooling_moves = 0;
//float cooling_initial_speed = 0.f;
//float cooling_final_speed = 0.f;
float ramming_line_width_multiplicator = 1.f;
float ramming_step_multiplicator = 1.f;
float max_e_speed = std::numeric_limits<float>::max();
@@ -349,41 +343,41 @@ public:
float retract_length;
float retract_speed;
float wipe_dist;
float tower_interface_pre_extrusion_dist = 0.f;
float tower_interface_pre_extrusion_length = 0.f;
// Outward shift of the wipe start for a PETG pre-extrusion on filament-switcher devices;
// set from filament_tower_interface_pre_extrusion_dist.
float petg_pre_extrusion_offset_dist = 0.f;
float tower_ironing_area = 4.f;
float tower_interface_purge_length = 0.f;
// Distance (in mm of filament) that a hotend is allowed to pre-cool before the
// tower is reached; drives the prime-tower heating-during-wipe model (multi-nozzle only).
float filament_cooling_before_tower = 0.f;
// .first = extruder change, .second = nozzle change (carousel)
std::pair<float,float> max_e_ramming_speed{0.f, 0.f};
std::pair<float,float> ramming_travel_time{0.f, 0.f};
std::pair<int,int> precool_target_temp{0, 0};
std::pair<std::vector<float>,std::vector<float>> precool_t;
std::pair<std::vector<float>,std::vector<float>> precool_t_first_layer;
std::pair<float,float> max_e_ramming_speed;//[0]extruder change [1]nozzle change
std::pair<float, float> ramming_travel_time; // Travel time after ramming
std::pair<std::vector<float>,std::vector<float>> precool_t;//Pre-cooling time, set to 0 to ensure the ramming speed is controlled solely by ramming volumetric speed.
std::pair<std::vector<float>, std::vector<float>> precool_t_first_layer;
std::pair<int,int> precool_target_temp;
float filament_cooling_before_tower = 0.f;
float flat_iron_area;
float filament_tower_interface_print_temp;
float filament_tower_interface_pre_extrusion_dist = 0;
float filament_tower_interface_pre_extrusion_length = 0;
float filament_petg_pre_extrusion_offset_dist = 0;
};
void set_used_filament_ids(const std::vector<int> &used_filament_ids) { m_used_filament_ids = used_filament_ids; };
void set_used_filament_ids(const std::vector<int> &used_filament_ids) { m_used_filament_ids = used_filament_ids; };
void set_filament_categories(const std::vector<int> & filament_categories) { m_filament_categories = filament_categories;};
std::vector<int> m_used_filament_ids;
void set_nozzle_group_result(const MultiNozzleUtils::LayeredNozzleGroupResult &multi_nozzle_group_result) { m_multi_nozzle_group_result = &multi_nozzle_group_result; };
std::vector<int> m_used_filament_ids;
std::vector<int> m_filament_categories;
const MultiNozzleUtils::LayeredNozzleGroupResult *m_multi_nozzle_group_result{nullptr};
enum class WipeTowerLayerType : unsigned char { Normal, Contact, Solid, Contact_UP};// Contact layer should be solid and reduce feed
struct WipeTowerBlock
{
int block_id{0};
int filament_adhesiveness_category{0};
std::vector<float> layer_depths;
std::vector<bool> solid_infill;
//std::vector<bool> solid_infill;
std::vector<float> finish_depth{0}; // the start pos of finish frame for every layer
std::vector<WipeTowerLayerType> layers_type; // type of the layer, normal, Contact or Solid
float depth{0};
float start_depth{0};
float cur_depth{0};
int last_filament_change_id{-1};
int last_filament_change_id{-1};
int last_nozzle_change_id{-1};
};
@@ -403,25 +397,33 @@ public:
WipeTowerBlock* get_block_by_category(int filament_adhesiveness_category, bool create);
void add_depth_to_block(int filament_id, int filament_adhesiveness_category, float depth, bool is_nozzle_change = false);
int get_filament_category(int filament_id);
bool is_in_same_extruder(int filament_id_1, int filament_id_2);
// Vortek H2C: format BBS-compatible NOZZLE_CHANGE_START/END tag with OF/NF/ON/NN payload
std::string format_nozzle_change_tag(bool start, int old_filament_id, int new_filament_id) const;
void reset_block_status();
int get_wall_filament_for_all_layer();
// for generate new wipe tower
void generate_new(std::vector<std::vector<WipeTower::ToolChangeResult>> &result);
void plan_tower_new();
void generate_wipe_tower_blocks();
void generate_wipe_tower_blocks(bool add_solid_flag);
void update_all_layer_depth(float wipe_tower_depth);
void set_nozzle_last_layer_id();
void set_first_layer_flow_ratio(const float flow_ratio);
// Orca: default/initial-layer/travel acceleration are object-scope options here (PrintConfig
// members in BBS), so Print pushes the resolved per-variant columns in via this setter.
void set_accelerations(const std::vector<double> &normal, const std::vector<double> &first_layer_normal,
const std::vector<double> &travel, const std::vector<double> &first_layer_travel);
void calc_block_infill_gap();
ToolChangeResult tool_change_new(size_t new_tool, bool solid_change = false, bool solid_nozzlechange=false);
NozzleChangeResult nozzle_change_new(int old_filament_id, int new_filament_id, bool solid_change = false);
NozzleChangeResult ramming(int old_filament_id, int new_filament_id, bool solid_change = false, bool extruder_change = true); // extruder_chang means nozzle_change
ToolChangeResult finish_layer_new(bool extrude_perimeter = true, bool extrude_fill = true, bool extrude_fill_wall = true);
ToolChangeResult finish_block(const WipeTowerBlock &block, int filament_id, bool extrude_fill = true);
ToolChangeResult finish_block_solid(const WipeTowerBlock &block, int filament_id, bool extrude_fill = true ,bool interface_solid =false);
ToolChangeResult finish_block_solid(const WipeTowerBlock &block, int filament_id, bool extrude_fill = true, WipeTowerLayerType layer_type = WipeTowerLayerType::Normal);
void toolchange_wipe_new(WipeTowerWriter &writer, const box_coordinates &cleaning_box, float wipe_length,bool solid_toolchange=false);
Vec2f get_rib_offset() const { return m_rib_offset; }
bool is_need_ramming(int filament_id_1, int filament_id_2, int layer_id) const;
bool is_same_extruder(int filament_id_1, int filament_id_2, int layer_id) const;
bool is_same_nozzle(int filament_id_1, int filament_id_2, int layer_id) const;
int get_nozzle_id(int filament_id, int layer_id) const;
int get_extruder_id(int filament_id, int layer_id) const;
private:
enum wipe_shape // A fill-in direction
@@ -441,7 +443,6 @@ private:
bool m_enable_wrapping_detection = false;
bool m_enable_timelapse_print = false;
bool m_semm = true; // Are we using a single extruder multimaterial printer?
bool m_purge_in_prime_tower = false; // Do we purge in the prime tower?
Vec2f m_wipe_tower_pos; // Left front corner of the wipe tower in mm.
float m_wipe_tower_width; // Width of the wipe tower.
float m_wipe_tower_depth = 0.f; // Depth of the wipe tower
@@ -459,12 +460,11 @@ private:
float m_travel_speed = 0.f;
float m_first_layer_speed = 0.f;
size_t m_first_layer_idx = size_t(-1);
std::vector<double> m_filaments_change_length;
Vec2f m_origin;
std::vector<int> m_last_layer_id;
std::pair<std::vector<double>,std::vector<double>> m_filaments_change_length;//[0]extruder change [1]nozzle change
size_t m_cur_layer_id;
NozzleChangeResult m_nozzle_change_result;
std::vector<int> m_filament_map;
std::vector<int> m_filament_nozzle_map; // Vortek H2C: filament_id → physical nozzle_id
bool m_has_tpu_filament{false};
bool m_is_multi_extruder{false};
bool m_use_gap_wall{false};
@@ -475,33 +475,32 @@ private:
bool m_used_fillet{false};
Vec2f m_rib_offset{Vec2f(0.f, 0.f)};
bool m_tower_framework{false};
bool m_need_reverse_travel{false};
bool m_enable_tower_interface_features{false};
// G-code generator parameters.
float m_cooling_tube_retraction = 0.f;
float m_cooling_tube_length = 0.f;
float m_parking_pos_retraction = 0.f;
float m_extra_loading_move = 0.f;
// BBS: remove useless config
//float m_cooling_tube_retraction = 0.f;
//float m_cooling_tube_length = 0.f;
//float m_parking_pos_retraction = 0.f;
//float m_extra_loading_move = 0.f;
float m_bridging = 0.f;
bool m_no_sparse_layers = false;
bool m_set_extruder_trimpot = false;
// BBS: remove useless config
//bool m_set_extruder_trimpot = false;
bool m_adhesion = true;
GCodeFlavor m_gcode_flavor;
// Multi-nozzle prime-tower heating during wipe. m_is_multiple_nozzle gates the whole
// feature; it is false for every current (single-nozzle) printer (extruder_max_nozzle_count
// defaults to 1), so the pre-heat/pre-cool path is inert and wipe-tower g-code is unchanged.
bool m_is_multiple_nozzle = false;
std::vector<double> m_hotend_heating_rate; // config.hotend_heating_rate (deg/s per extruder)
std::vector<int> m_physical_extruder_map; // logical extruder -> physical tool number (M104 T param)
// Per-extruder printable-height clamp. m_printable_height = config.extruder_printable_height
// (per-extruder Z limit; empty for single-extruder printers, [320,325] for H2D). m_last_layer_id
// records, per extruder, the last wipe-tower layer that uses it. is_valid_last_layer() is gated on
// m_is_multi_extruder so single-extruder wipe-tower g-code is unchanged; the clamp only bites a
// multi-extruder wipe tower whose final per-extruder layer exceeds that extruder's printable
// height (near the Z limit).
std::vector<double> m_printable_height;
std::vector<int> m_last_layer_id;
bool m_is_multiple_nozzle = false;
std::vector<unsigned int> m_normal_accels;
std::vector<unsigned int> m_first_layer_normal_accels;
std::vector<unsigned int> m_travel_accels;
std::vector<unsigned int> m_first_layer_travel_accels;
unsigned int m_max_accels;
bool m_accel_to_decel_enable;
float m_accel_to_decel_factor;
bool m_enable_arc_fitting = true;
std::vector<double> m_hotend_heating_rate;
std::vector<double> m_hotend_cooling_rate;
Polygons m_shared_print_bed;
// Bed properties
enum {
@@ -512,10 +511,11 @@ private:
float m_bed_width; // width of the bed bounding box
Vec2f m_bed_bottom_left; // bottom-left corner coordinates (for rectangular beds)
float m_first_layer_flow_ratio;
float m_perimeter_width = 0.4f * Width_To_Nozzle_Ratio; // Width of an extrusion line, also a perimeter spacing for 100% infill.
float m_nozzle_change_perimeter_width = 0.4f * Width_To_Nozzle_Ratio;
float m_extrusion_flow = 0.038f; //0.029f;// Extrusion flow is derived from m_perimeter_width, layer height and filament diameter.
std::unordered_map<int, std::pair<float,float>> m_block_infill_gap_width; // categories to infill_gap: toolchange gap, nozzlechange gap
// Extruder specific parameters.
std::vector<FilamentParameters> m_filpar;
@@ -528,50 +528,52 @@ private:
// A fill-in direction (positive Y, negative Y) alternates with each layer.
wipe_shape m_current_shape = SHAPE_NORMAL;
size_t m_current_tool = 0;
// Orca: support mmu wipe tower
std::vector<std::vector<float>> wipe_volumes;
// BBS
//const std::vector<std::vector<float>> wipe_volumes;
float m_depth_traversed = 0.f; // Current y position at the wipe tower.
bool m_current_layer_finished = false;
bool m_left_to_right = true;
float m_extra_spacing = 1.f;
float m_tpu_fixed_spacing = 2;
std::vector<Vec2f> m_wall_skip_points;
float m_max_speed = 5400.f; // the maximum printing speed on the prime tower.
std::vector<std::vector<Vec2f>> m_wall_skip_points;
std::map<float,Polylines> m_outer_wall;
std::vector<double> m_printable_height;
bool is_first_layer() const { return size_t(m_layer_info - m_plan.begin()) == m_first_layer_idx; }
bool is_valid_last_layer(int tool, int layer_id, double layer_z) const;
bool m_flat_ironing=false;
bool m_enable_tower_interface_features=false;
bool m_enable_tower_interface_cooldown_during_tower=false;
// Filament-switcher device flag + shared printable bed for the PETG pre-extrusion offset.
// m_has_filament_switcher is false for the whole shipping fleet (no profile sets the key), so
// the PETG branch in get_next_pos never runs -> no change fleet-wide.
bool m_has_filament_switcher=false;
Polygons m_shared_print_bed;
bool m_prev_layer_had_interface=false;
bool m_current_layer_has_interface=false;
bool m_contact_ironing = false;
bool m_has_filament_switcher = false;
float m_contact_speed = 20 * 60.f;
std::vector<int> m_physical_extruder_map;
// Calculates length of extrusion line to extrude given volume
float volume_to_length(float volume, float line_width, float layer_height) const {
return std::max(0.f, volume / (layer_height * (line_width - layer_height * (1.f - float(M_PI) / 4.f))));
}
// Calculates volume of extrusion line
float length_to_volume(float length,float line_width, float layer_height) const
{
return std::max(0.f, length * (layer_height * (line_width - layer_height * (1.f - float(M_PI) / 4.f))));
}
// Calculates depth for all layers and propagates them downwards
void plan_tower();
// Goes through m_plan and recalculates depths and width of the WT to make it exactly square - experimental
void make_wipe_tower_square();
Vec2f get_next_pos(const WipeTower::box_coordinates &cleaning_box, float wipe_length, bool interface_layer, size_t interface_tool);
Vec2f get_next_pos(const WipeTower::box_coordinates &cleaning_box, float wipe_length, bool solid_toolchange);
// Goes through m_plan, calculates border and finish_layer extrusions and subtracts them from last wipe
void save_on_last_wipe();
bool is_tpu_filament(int filament_id) const;
bool is_petg_filament(int filament_id) const;
bool is_need_reverse_travel(int filament_id, bool extruder_change) const;
bool is_need_reverse_travel(int filament, bool extruder_change) const;
// BBS
box_coordinates align_perimeter(const box_coordinates& perimeter_box);
void set_for_wipe_tower_writer(WipeTowerWriter &writer);
// to store information about tool changes for a given layer
struct WipeTowerInfo{
@@ -584,6 +586,7 @@ private:
float wipe_volume;
float wipe_length;
float nozzle_change_depth{0};
float nozzle_change_length{0};
// BBS
float purge_volume;
ToolChange(size_t old, size_t newtool, float depth=0.f, float ramming_depth=0.f, float fwl=0.f, float wv=0.f, float wl = 0, float pv = 0)
@@ -613,7 +616,7 @@ private:
// ot -1 if there is no such toolchange.
int first_toolchange_to_nonsoluble_nonsupport(
const std::vector<WipeTowerInfo::ToolChange>& tool_changes) const;
WipeTowerInfo::ToolChange set_toolchange(int old_tool, int new_tool, float layer_height, float wipe_volume, float purge_volume,int layer_id);
void toolchange_Unload(
WipeTowerWriter &writer,
const box_coordinates &cleaning_box,
@@ -633,13 +636,10 @@ private:
WipeTowerWriter &writer,
const box_coordinates &cleaning_box,
float wipe_volume);
void get_wall_skip_points(const WipeTowerInfo &layer);
// Per-extruder printable-height clamp (see m_printable_height). is_valid_last_layer returns
// false only for a multi-extruder wipe tower's final per-extruder layer that exceeds that
// extruder's printable height; returns true (no clamp) in every other case.
bool is_valid_last_layer(int tool, int layer_id, double layer_z) const;
void set_nozzle_last_layer_id();
void get_wall_skip_points(const WipeTowerInfo &layer,int layer_id);
void get_all_wall_skip_points();
ToolChangeResult merge_tcr(ToolChangeResult &first, ToolChangeResult &second);
float get_block_gap_width(int tool, bool is_nozzlechangle = false);
};
+260 -321
View File
@@ -24,7 +24,6 @@
namespace Slic3r
{
static constexpr float flat_iron_area = 4.f;
constexpr float flat_iron_speed = 10.f * 60.f;
static const double wipe_tower_wall_infill_overlap = 0.0;
static constexpr double WIPE_TOWER_RESOLUTION = 0.1;
@@ -234,24 +233,6 @@ static Polygon rounding_rectangle(Polygon& polygon, double rounding = 2., double
return res;
}
static std::pair<bool, Vec2f> ray_intersetion_line(const Vec2f& a, const Vec2f& v1, const Vec2f& b, const Vec2f& c)
{
const Vec2f v2 = c - b;
double denom = cross2(v1, v2);
if (fabs(denom) < EPSILON)
return {false, Vec2f(0, 0)};
const Vec2f v12 = (a - b);
double nume_a = cross2(v2, v12);
double nume_b = cross2(v1, v12);
double t1 = nume_a / denom;
double t2 = nume_b / denom;
if (t1 >= 0 && t2 >= 0 && t2 <= 1.) {
// Get the intersection point.
Vec2f res = a + t1 * v1;
return std::pair<bool, Vec2f>(true, res);
}
return std::pair<bool, Vec2f>(false, Vec2f{0, 0});
}
static Polygon scale_polygon(const std::vector<Vec2f>& points)
{
Polygon res;
@@ -296,6 +277,7 @@ static Polygon generate_rectange(const Line& line, coord_t offset)
return poly;
};
// Straight or arc-fitted wall segment used by WipeTowerWriter2::generate_path().
struct Segment
{
Vec2f start;
@@ -306,234 +288,6 @@ struct Segment
bool is_valid() const { return start.y() < end.y(); }
};
static std::vector<Segment> remove_points_from_segment(const Segment& segment, const std::vector<Vec2f>& skip_points, double range)
{
std::vector<Segment> result;
result.push_back(segment);
float x = segment.start.x();
for (const Vec2f& point : skip_points) {
std::vector<Segment> newResult;
for (const auto& seg : result) {
if (point.y() + range <= seg.start.y() || point.y() - range >= seg.end.y()) {
newResult.push_back(seg);
} else {
if (point.y() - range > seg.start.y()) {
newResult.push_back(Segment(Vec2f(x, seg.start.y()), Vec2f(x, point.y() - range)));
}
if (point.y() + range < seg.end.y()) {
newResult.push_back(Segment(Vec2f(x, point.y() + range), Vec2f(x, seg.end.y())));
}
}
}
result = newResult;
}
result.erase(std::remove_if(result.begin(), result.end(), [](const Segment& seg) { return !seg.is_valid(); }), result.end());
return result;
}
struct IntersectionInfo
{
Vec2f pos;
int idx;
int pair_idx; // gap_pair idx
float dis_from_idx;
bool is_forward;
};
struct PointWithFlag
{
Vec2f pos;
int pair_idx; // gap_pair idx
bool is_forward;
};
static IntersectionInfo move_point_along_polygon(
const std::vector<Vec2f>& points, const Vec2f& startPoint, int startIdx, float offset, bool forward, int pair_idx)
{
float remainingDistance = offset;
IntersectionInfo res;
int mod = points.size();
if (forward) {
int next = (startIdx + 1) % mod;
remainingDistance -= (points[next] - startPoint).norm();
if (remainingDistance <= 0) {
res.idx = startIdx;
res.pos = startPoint + (points[next] - startPoint).normalized() * offset;
res.pair_idx = pair_idx;
res.dis_from_idx = (points[startIdx] - res.pos).norm();
return res;
} else {
for (int i = (startIdx + 1) % mod; i != startIdx; i = (i + 1) % mod) {
float segmentLength = (points[(i + 1) % mod] - points[i]).norm();
if (remainingDistance <= segmentLength) {
float ratio = remainingDistance / segmentLength;
res.idx = i;
res.pos = points[i] + ratio * (points[(i + 1) % mod] - points[i]);
res.dis_from_idx = remainingDistance;
res.pair_idx = pair_idx;
return res;
}
remainingDistance -= segmentLength;
}
res.idx = (startIdx - 1 + mod) % mod;
res.pos = points[startIdx];
res.pair_idx = pair_idx;
res.dis_from_idx = (res.pos - points[res.idx]).norm();
}
} else {
int next = (startIdx + 1) % mod;
remainingDistance -= (points[startIdx] - startPoint).norm();
if (remainingDistance <= 0) {
res.idx = startIdx;
res.pos = startPoint - (points[next] - points[startIdx]).normalized() * offset;
res.dis_from_idx = (res.pos - points[startIdx]).norm();
res.pair_idx = pair_idx;
return res;
}
for (int i = (startIdx - 1 + mod) % mod; i != startIdx; i = (i - 1 + mod) % mod) {
float segmentLength = (points[(i + 1) % mod] - points[i]).norm();
if (remainingDistance <= segmentLength) {
float ratio = remainingDistance / segmentLength;
res.idx = i;
res.pos = points[(i + 1) % mod] - ratio * (points[(i + 1) % mod] - points[i]);
res.dis_from_idx = segmentLength - remainingDistance;
res.pair_idx = pair_idx;
return res;
}
remainingDistance -= segmentLength;
}
res.idx = startIdx;
res.pos = points[res.idx];
res.pair_idx = pair_idx;
res.dis_from_idx = 0;
}
return res;
};
static void insert_points(std::vector<PointWithFlag>& pl, int idx, Vec2f pos, int pair_idx, bool is_forward)
{
int next = (idx + 1) % pl.size();
Vec2f pos1 = pl[idx].pos;
Vec2f pos2 = pl[next].pos;
if ((pos - pos1).squaredNorm() < EPSILON) {
pl[idx].pair_idx = pair_idx;
pl[idx].is_forward = is_forward;
} else if ((pos - pos2).squaredNorm() < EPSILON) {
pl[next].pair_idx = pair_idx;
pl[next].is_forward = is_forward;
} else {
pl.insert(pl.begin() + idx + 1, PointWithFlag{pos, pair_idx, is_forward});
}
}
static Polylines remove_points_from_polygon(
const Polygon& polygon, const std::vector<Vec2f>& skip_points, double range, bool is_left, Polygon& insert_skip_pg)
{
assert(polygon.size() > 2);
Polylines result;
std::vector<PointWithFlag> new_pl; // add intersection points for gaps, where bool indicates whether it's a gap point.
std::vector<IntersectionInfo> inter_info;
Vec2f ray = is_left ? Vec2f(-1, 0) : Vec2f(1, 0);
auto polygon_box = get_extents(polygon);
Point anchor_point = is_left ? Point{polygon_box.max[0], polygon_box.min[1]} : polygon_box.min; // rd:ld
std::vector<Vec2f> points;
{
points.reserve(polygon.points.size());
int idx = polygon.closest_point_index(anchor_point);
Polyline tmp_poly = polygon.split_at_index(idx);
for (auto& p : tmp_poly)
points.push_back(unscale(p).cast<float>());
points.pop_back();
}
for (int i = 0; i < skip_points.size(); i++) {
for (int j = 0; j < points.size(); j++) {
Vec2f& p1 = points[j];
Vec2f& p2 = points[(j + 1) % points.size()];
auto [is_inter, inter_pos] = ray_intersetion_line(skip_points[i], ray, p1, p2);
if (is_inter) {
IntersectionInfo forward = move_point_along_polygon(points, inter_pos, j, range, true, i);
IntersectionInfo backward = move_point_along_polygon(points, inter_pos, j, range, false, i);
backward.is_forward = false;
forward.is_forward = true;
inter_info.push_back(backward);
inter_info.push_back(forward);
break;
}
}
}
// insert point to new_pl
for (const auto& p : points)
new_pl.push_back({p, -1});
std::sort(inter_info.begin(), inter_info.end(), [](const IntersectionInfo& lhs, const IntersectionInfo& rhs) {
if (rhs.idx == lhs.idx)
return lhs.dis_from_idx < rhs.dis_from_idx;
return lhs.idx < rhs.idx;
});
for (int i = inter_info.size() - 1; i >= 0; i--) {
insert_points(new_pl, inter_info[i].idx, inter_info[i].pos, inter_info[i].pair_idx, inter_info[i].is_forward);
}
{
// set insert_pg for wipe_path
for (auto& p : new_pl)
insert_skip_pg.points.push_back(scaled(p.pos));
}
int beg = 0;
bool skip = true;
int i = beg;
Polyline pl;
do {
if (skip || new_pl[i].pair_idx == -1) {
pl.points.push_back(scaled(new_pl[i].pos));
i = (i + 1) % new_pl.size();
skip = false;
} else {
if (!pl.points.empty()) {
pl.points.push_back(scaled(new_pl[i].pos));
result.push_back(pl);
pl.points.clear();
}
int left = new_pl[i].pair_idx;
int j = (i + 1) % new_pl.size();
while (j != beg && new_pl[j].pair_idx != left) {
if (new_pl[j].pair_idx != -1 && !new_pl[j].is_forward)
left = new_pl[j].pair_idx;
j = (j + 1) % new_pl.size();
}
i = j;
skip = true;
}
} while (i != beg);
if (!pl.points.empty()) {
if (new_pl[i].pair_idx == -1)
pl.points.push_back(scaled(new_pl[i].pos));
result.push_back(pl);
}
return result;
}
static Polylines contrust_gap_for_skip_points(
const Polygon& polygon, const std::vector<Vec2f>& skip_points, float wt_width, float gap_length, Polygon& insert_skip_polygon)
{
if (skip_points.empty()) {
insert_skip_polygon = polygon;
return Polylines{to_polyline(polygon)};
}
bool is_left = false;
const auto& pt = skip_points.front();
if (abs(pt.x()) < wt_width / 2.f) {
is_left = true;
}
return remove_points_from_polygon(polygon, skip_points, gap_length, is_left, insert_skip_polygon);
};
static Polygon generate_rectange_polygon(const Vec2f& wt_box_min, const Vec2f& wt_box_max)
{
Polygon res;
@@ -1245,6 +999,12 @@ WipeTower::ToolChangeResult WipeTower2::construct_tcr(WipeTowerWriter2& writer,
bool WipeTower2::use_gap_wall(const PrintConfig& config)
{
// The cone wall has its own fully separate generator with no gap machinery.
return config.prime_tower_skip_points.value && config.wipe_tower_wall_type.value != wtwCone;
}
WipeTower2::WipeTower2(const PrintConfig& config, const PrintRegionConfig& default_region_config,int plate_idx, Vec3d plate_origin, const std::vector<std::vector<float>>& wiping_matrix, size_t initial_tool) :
m_semm(config.single_extruder_multi_material.value),
m_enable_filament_ramming(config.enable_filament_ramming.value),
@@ -1272,7 +1032,7 @@ WipeTower2::WipeTower2(const PrintConfig& config, const PrintRegionConfig& defau
m_rib_width(config.wipe_tower_rib_width),
m_extra_rib_length(config.wipe_tower_extra_rib_length),
m_wall_type((int)config.wipe_tower_wall_type),
m_flat_ironing(config.prime_tower_flat_ironing.value),
m_use_gap_wall(use_gap_wall(config)),
m_enable_tower_interface_features(config.enable_tower_interface_features.value),
m_enable_tower_interface_cooldown_during_tower(config.enable_tower_interface_cooldown_during_tower.value)
{
@@ -1342,6 +1102,7 @@ void WipeTower2::set_extruder(size_t idx, const PrintConfig& config)
m_filpar[idx].is_soluble = (idx != size_t(m_wipe_tower_filament - 1));
else
m_filpar[idx].is_soluble = config.filament_soluble.get_at(idx);
m_filpar[idx].is_support = config.filament_is_support.get_at(idx);
m_filpar[idx].temperature = config.nozzle_temperature.get_at(idx);
m_filpar[idx].first_layer_temperature = config.nozzle_temperature_initial_layer.get_at(idx);
m_filpar[idx].filament_minimal_purge_on_wipe_tower = config.filament_minimal_purge_on_wipe_tower.get_at(idx);
@@ -1478,11 +1239,11 @@ std::vector<WipeTower::ToolChangeResult> WipeTower2::prime(
toolchange_Load(writer, cleaning_box); // Prime the tool.
if (idx_tool + 1 == tools.size()) {
// Last tool should not be unloaded, but it should be wiped enough to become of a pure color.
toolchange_Wipe(writer, cleaning_box, wipe_volumes[tools[idx_tool-1]][tool], false);
toolchange_Wipe(writer, cleaning_box, wipe_volumes[tools[idx_tool-1]][tool], false, true);
} else {
// Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool.
//writer.travel(writer.x(), writer.y() + m_perimeter_width, 7200);
toolchange_Wipe(writer, cleaning_box , 20.f, false);
toolchange_Wipe(writer, cleaning_box , 20.f, false, true);
WipeTower::box_coordinates box = cleaning_box;
box.translate(0.f, writer.y() - cleaning_box.ld.y() + m_perimeter_width);
toolchange_Unload(writer, box , m_filpar[m_current_tool].material, m_filpar[m_current_tool].first_layer_temperature, m_filpar[tools[idx_tool + 1]].first_layer_temperature);
@@ -1525,8 +1286,9 @@ WipeTower::ToolChangeResult WipeTower2::tool_change(size_t tool)
float wipe_area = 0.f;
float wipe_volume = 0.f;
float ramming_depth = 0.f;
bool interface_layer = m_enable_tower_interface_features && m_current_layer_has_interface;
// Finds this toolchange info
if (tool != (unsigned int)(-1))
{
@@ -1534,6 +1296,7 @@ WipeTower::ToolChangeResult WipeTower2::tool_change(size_t tool)
if ( b.new_tool == tool ) {
wipe_volume = b.wipe_volume;
wipe_area = b.required_depth;
ramming_depth = b.ramming_depth;
break;
}
}
@@ -1571,7 +1334,9 @@ WipeTower::ToolChangeResult WipeTower2::tool_change(size_t tool)
writer.speed_override_backup();
writer.speed_override(100);
Vec2f initial_position = cleaning_box.ld + Vec2f(0.f, m_depth_traversed);
// On a boundary wipe start this enters at the wall gap on the first wipe row;
// toolchange_Unload() then climbs back up to the ram band along the box interior.
Vec2f initial_position = toolchange_entry_pos(m_depth_traversed, ramming_depth, is_first_layer());
writer.set_initial_position(initial_position, m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation);
// Increase the extruder driver current to allow fast ramming.
@@ -1580,6 +1345,11 @@ WipeTower::ToolChangeResult WipeTower2::tool_change(size_t tool)
// Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool.
if (tool != (unsigned int)-1){ // This is not the last change.
// Without a ram — or with the boundary wipe start, where the ram band is
// quantized to whole rows — the box is planned as whole wipe rows; the wipe
// then fills it completely so adjacent purge blocks stay contiguous. Uses the
// old tool (m_current_tool before toolchange_Change).
const bool fill_box = !tool_ramming_enabled(m_current_tool) || boundary_wipe_start_enabled(m_current_tool);
auto new_tool_temp = is_first_layer() ? m_filpar[tool].first_layer_temperature : m_filpar[tool].temperature;
toolchange_Unload(writer, cleaning_box, m_filpar[m_current_tool].material,
(is_first_layer() ? m_filpar[m_current_tool].first_layer_temperature : m_filpar[m_current_tool].temperature),
@@ -1602,7 +1372,7 @@ WipeTower::ToolChangeResult WipeTower2::tool_change(size_t tool)
writer.extrude_explicit(target_x, writer.y(), pre_len, 600.f);
}
}
toolchange_Wipe(writer, cleaning_box, wipe_volume, interface_layer); // Wipe the newly loaded filament until the end of the assigned wipe area.
toolchange_Wipe(writer, cleaning_box, wipe_volume, interface_layer, false, fill_box); // Wipe the newly loaded filament until the end of the assigned wipe area.
if (interface_layer) {
int interface_temp = m_filpar[tool].interface_print_temperature;
if (!m_enable_tower_interface_cooldown_during_tower && interface_temp > 0 && interface_temp != base_temp)
@@ -1657,11 +1427,20 @@ void WipeTower2::toolchange_Unload(
float remaining = xr - xl ; // keeps track of distance to the next turnaround
float e_done = 0; // measures E move done from each segment
// Orca: Do ramming when SEMM and ramming is enabled or when multi tool head when ramming is enabled on the multi tool.
const bool do_ramming = (m_semm && m_enable_filament_ramming) || m_filpar[m_current_tool].multitool_ramming;
const bool do_ramming = tool_ramming_enabled(m_current_tool);
const bool cold_ramming = m_is_mk4mmu3;
// Orca: see set_toolchange() — quantized ram band + wipe restart at the boundary.
const bool boundary_wipe_start = boundary_wipe_start_enabled(m_current_tool);
float planned_ramming_depth = 0.f;
if (boundary_wipe_start && m_layer_info != m_plan.end())
for (const auto& tch : m_layer_info->tool_changes)
if (tch.old_tool == m_current_tool) { planned_ramming_depth = tch.ramming_depth; break; }
if (do_ramming) {
if (boundary_wipe_start)
// The entry sits at the wall gap on the first wipe row past the reserved
// ram band; step inward first, then move to the band clear of the wall.
writer.travel(Vec2f(ramming_start_pos.x(), writer.y()));
writer.travel(ramming_start_pos); // move to starting position
if (! m_is_mk4mmu3)
writer.disable_linear_advance();
@@ -1672,7 +1451,8 @@ void WipeTower2::toolchange_Unload(
writer.set_position(ramming_start_pos);
// if the ending point of the ram would end up in mid air, align it with the end of the wipe tower:
if (do_ramming && (m_layer_info > m_plan.begin() && m_layer_info < m_plan.end() && (m_layer_info-1!=m_plan.begin() || !m_adhesion ))) {
// (with a boundary wipe start the band is quantized to whole rows below, so no phase alignment is needed)
if (do_ramming && !boundary_wipe_start && (m_layer_info > m_plan.begin() && m_layer_info < m_plan.end() && (m_layer_info-1!=m_plan.begin() || !m_adhesion ))) {
// this is y of the center of previous sparse infill border
float sparse_beginning_y = 0.f;
@@ -1731,6 +1511,28 @@ void WipeTower2::toolchange_Unload(
e_done = 0;
}
}
// Orca: quantize the ram band up to the whole reserved rows (BBL quantizes the
// old-tool purge the same way) so no unprinted void is left between the band and
// the wipe restarting at the boundary below it.
if (planned_ramming_depth > 0.f) {
const int reserved_rows = std::max(1, int(std::round(planned_ramming_depth / y_step)));
const float last_row_y = ramming_start_pos.y() + (reserved_rows - 1) * y_step;
// Same bead model as the ramming segments above: E per mm of ram line.
const float e_per_mm = 1.f / (volume_to_length(1.f, line_width, m_layer_height) * filament_area());
const float fill_feed = m_filpar[m_current_tool].ramming_speed.empty() ? 3000.f :
60.f * volume_to_length(m_filpar[m_current_tool].ramming_speed.back(), line_width, m_layer_height);
while (true) {
const float target_x = m_left_to_right ? xr : xl;
if (std::abs(target_x - writer.x()) > WT_EPSILON)
writer.ram(writer.x(), target_x, 0.f, 0.f, e_per_mm * std::abs(target_x - writer.x()), fill_feed);
if (writer.y() + 0.5f * y_step > last_row_y)
break;
writer.travel(writer.x(), writer.y() + y_step, 7200);
m_left_to_right = !m_left_to_right;
}
}
Vec2f end_of_ramming(writer.x(),writer.y());
writer.change_analyzer_line_width(m_perimeter_width); // so the next lines are not affected by ramming_line_width_multiplier
@@ -1838,10 +1640,27 @@ void WipeTower2::toolchange_Unload(
// this is to align ramming and future wiping extrusions, so the future y-steps can be uniform from the start:
// the perimeter_width will later be subtracted, it is there to not load while moving over just extruded material
Vec2f pos = Vec2f(end_of_ramming.x(), end_of_ramming.y() + (y_step/m_extra_spacing_ramming-m_perimeter_width) / 2.f + m_perimeter_width);
if (do_ramming)
if (planned_ramming_depth > 0.f) {
// Orca: restart the wipe at the left-edge boundary on a fresh row below the
// quantized ram band so the entry scrub always runs at the wall gap (BBL keeps
// CP_TOOLCHANGE_WIPE starting at a box corner the same way). Same lattice
// formula as the no-ram branch below, offset by the ram band.
writer.travel(Vec2f(ramming_start_pos.x(),
cleaning_box.ld.y() + m_depth_traversed +
wipe_start_offset_after_ram(planned_ramming_depth, is_first_layer()) + m_perimeter_width), 2400.f);
m_left_to_right = true;
}
else if (do_ramming)
writer.travel(pos, 2400.f);
else
writer.set_position(pos);
else {
// Orca: with no ram printed there is no ramming geometry to align with. Start the
// first wipe row so the purge row lattice continues across the block boundary
// (previous box's last row top edge sits at its box top): with the planned depth
// of rows * dy, the last row's top edge then lands exactly on this box's top and
// no blank band is left between adjacent purge blocks.
writer.set_position(Vec2f(end_of_ramming.x(),
cleaning_box.ld.y() + m_depth_traversed + wipe_start_offset_after_ram(0.f, is_first_layer()) + m_perimeter_width));
}
writer.resume_preview()
.flush_planner_queue();
@@ -1920,7 +1739,9 @@ void WipeTower2::toolchange_Wipe(
WipeTowerWriter2 &writer,
const WipeTower::box_coordinates &cleaning_box,
float wipe_volume,
bool interface_layer)
bool interface_layer,
bool priming,
bool fill_box)
{
// Increase flow on first layer, slow down print.
writer.set_extrusion_flow(m_extrusion_flow * (is_first_layer() ? 1.18f : 1.f))
@@ -1929,7 +1750,7 @@ void WipeTower2::toolchange_Wipe(
const float& xr = cleaning_box.rd.x();
writer.set_extrusion_flow(m_extrusion_flow * m_extra_flow);
const float line_width = m_perimeter_width * m_extra_flow;
const float line_width = wipe_line_width();
writer.change_analyzer_line_width(line_width);
// Variables x_to_wipe and traversed_x are here to be able to make sure it always wipes at least
@@ -1937,7 +1758,7 @@ void WipeTower2::toolchange_Wipe(
// wipe until the end of the assigned area.
float x_to_wipe = volume_to_length(wipe_volume, m_perimeter_width, m_layer_height) / m_extra_flow;
float dy = (is_first_layer() ? m_extra_flow : m_extra_spacing_wipe) * m_perimeter_width; // Don't use the extra spacing for the first layer, but do use the spacing resulting from increased flow.
float dy = wipe_row_spacing(is_first_layer()); // Don't use the extra spacing for the first layer, but do use the spacing resulting from increased flow.
// All the calculations in all other places take the spacing into account for all the layers.
// If spare layers are excluded->if 1 or less toolchange has been done, it must be sill the first layer, too.So slow down.
@@ -1950,9 +1771,6 @@ void WipeTower2::toolchange_Wipe(
m_left_to_right = !m_left_to_right;
}
const bool do_ironing = m_flat_ironing && (!interface_layer || !m_enable_tower_interface_features);
const float ironing_area = m_filpar[m_current_tool].tower_ironing_area;
// now the wiping itself:
for (int i = 0; true; ++i) {
if (i!=0) {
@@ -1963,22 +1781,45 @@ void WipeTower2::toolchange_Wipe(
}
float traversed_x = writer.x();
// BBS gap wall: iron the first few mm of the purge, then drag the retracted nozzle
// back out through the wall gap and scrub it with a dry spiral centred on the entry
// point so the toolchange start blob is not left on the wall (same sequence as the
// BBL tower's toolchange_wipe_new; the spiral self-disables when the filament's
// tower ironing area is 0). WT2's entry gap always sits at the left-edge entry
// point, so only iron when the purge actually starts there heading right (in-place
// toolchangers do; SEMM ram/cooling moves leave the nozzle mid-box, far from any gap).
if (i == 0 && m_use_gap_wall && !interface_layer && !priming && m_left_to_right &&
writer.x() - xl < 2.5f * line_width) {
float ironing_length = 3.f;
if (xr - writer.x() < ironing_length)
ironing_length = std::max(xr - writer.x(), 0.f);
const float retract_length = m_filpar[m_current_tool].retract_length;
const float retract_speed = m_filpar[m_current_tool].retract_speed * 60.f;
writer.extrude(writer.x() + ironing_length, writer.y(), wipe_speed);
writer.retract(retract_length, retract_speed);
writer.travel(writer.x() - 1.5f * ironing_length, writer.y(), 600.f);
writer.travel(writer.x() + 0.5f * ironing_length, writer.y(), 240.f);
const Vec2f iron_end(writer.x() + ironing_length, writer.y());
writer.spiral_flat_ironing(writer.pos(), m_filpar[m_current_tool].tower_ironing_area, m_perimeter_width, flat_iron_speed);
writer.travel(iron_end, wipe_speed);
writer.retract(-retract_length, retract_speed);
}
if (m_left_to_right)
writer.extrude(xr - (i % 4 == 0 ? 0 : 1.5f*line_width), writer.y(), wipe_speed);
else
writer.extrude(xl + (i % 4 == 1 ? 0 : 1.5f*line_width), writer.y(), wipe_speed);
if (i == 0 && do_ironing && ironing_area > 0.f) {
writer.travel(writer.x(), writer.y(), 600.f);
writer.spiral_flat_ironing(writer.pos(), ironing_area, m_perimeter_width, 10.f * 60.f);
}
if (writer.y()+float(EPSILON) > cleaning_box.lu.y()-0.5f*line_width)
break; // in case next line would not fit
traversed_x -= writer.x();
x_to_wipe -= std::abs(traversed_x);
if (x_to_wipe < WT_EPSILON) {
// Orca: with no ram printed the box was planned as whole wipe rows; fill it
// completely (quantizing the purge up to the planned rows) so the next block
// can start right above it without a blank band in between.
if (!fill_box && x_to_wipe < WT_EPSILON) {
writer.travel(m_left_to_right ? xl + 1.5f*line_width : xr - 1.5f*line_width, writer.y(), 7200);
break;
}
@@ -2110,7 +1951,7 @@ WipeTower::ToolChangeResult WipeTower2::finish_layer()
poly = generate_support_cone_wall(writer, wt_box, feedrate, infill_cone, spacing);
} else {
WipeTower::box_coordinates wt_box(Vec2f(0.f, 0.f), m_wipe_tower_width, m_layer_info->depth + m_perimeter_width);
poly = generate_support_rib_wall(writer, wt_box, feedrate, first_layer, m_wall_type == (int)wtwRib, true, false);
poly = generate_support_rib_wall(writer, wt_box, feedrate, first_layer, m_wall_type == (int)wtwRib, true);
}
// brim (first layer only)
@@ -2228,15 +2069,32 @@ void WipeTower2::plan_toolchange(float z_par, float layer_height_par, unsigned i
return;
// this is an actual toolchange - let's calculate depth to reserve on the wipe tower
float width = m_wipe_tower_width - 3*m_perimeter_width;
float length_to_extrude = volume_to_length(0.25f * std::accumulate(m_filpar[old_tool].ramming_speed.begin(), m_filpar[old_tool].ramming_speed.end(), 0.f),
m_perimeter_width * m_filpar[old_tool].ramming_line_width_multiplicator,
layer_height_par);
// Orca: Set ramming depth to 0 if ramming is disabled.
float ramming_depth = m_enable_filament_ramming ? ((int(length_to_extrude / width) + 1) * (m_perimeter_width * m_filpar[old_tool].ramming_line_width_multiplicator * m_filpar[old_tool].ramming_step_multiplicator) * m_extra_spacing_ramming) : 0;
float first_wipe_line = - (width*((length_to_extrude / width)-int(length_to_extrude / width)) - width);
const bool first_layer_plan = (m_plan.size() - 1) == m_first_layer_idx;
m_plan.back().tool_changes.push_back(set_toolchange(old_tool, new_tool, layer_height_par, wipe_volume, first_layer_plan));
}
float first_wipe_volume = length_to_volume(first_wipe_line, m_perimeter_width * m_extra_flow, layer_height_par);
WipeTower2::WipeTowerInfo::ToolChange WipeTower2::set_toolchange(size_t old_tool, size_t new_tool, float layer_height, float wipe_volume, bool first_layer_plan)
{
float width = m_wipe_tower_width - 3*m_perimeter_width;
float length_to_extrude = volume_to_length((m_semm ? 0.25f : m_filpar[old_tool].multitool_ramming_time) * std::accumulate(m_filpar[old_tool].ramming_speed.begin(), m_filpar[old_tool].ramming_speed.end(), 0.f),
m_perimeter_width * m_filpar[old_tool].ramming_line_width_multiplicator,
layer_height);
// Orca: Reserve ramming depth only when toolchange_Unload() will actually ram,
// otherwise the unprinted reservation leaves blank bands between the purge boxes.
const bool do_ramming = tool_ramming_enabled(old_tool);
// Orca: with the gap wall on a multi-tool printer the ram band is quantized up to
// the whole reserved rows and the wipe restarts at the left-edge boundary on a
// fresh row below it (BBL parity: the old-tool purge is whole rows and the wipe
// always starts at the box corner, where the entry scrub runs).
const bool boundary_wipe_start = boundary_wipe_start_enabled(old_tool);
float ramming_depth = do_ramming ? ((int(length_to_extrude / width) + 1) * (m_perimeter_width * m_filpar[old_tool].ramming_line_width_multiplicator * m_filpar[old_tool].ramming_step_multiplicator) * m_extra_spacing_ramming) : 0;
// first_wipe_line rides for free on the last (partially used) ramming row, which
// is already covered by ramming_depth. Without ramming that row does not exist
// (and with a boundary wipe start the ram band is quantized to whole rows), so
// the whole wipe volume needs reserved wiping depth.
float first_wipe_line = (do_ramming && !boundary_wipe_start) ? - (width*((length_to_extrude / width)-int(length_to_extrude / width)) - width) : 0.f;
float first_wipe_volume = length_to_volume(first_wipe_line, m_perimeter_width * m_extra_flow, layer_height);
// ORCA: Keep wipe-depth planning consistent with toolchange_Wipe().
// ORCA: On the first layer, toolchange_Wipe() advances purge rows using
@@ -2245,12 +2103,11 @@ void WipeTower2::plan_toolchange(float z_par, float layer_height_par, unsigned i
// ORCA: float dy = (is_first_layer() ? m_extra_flow : m_extra_spacing_wipe) * m_perimeter_width;
// ORCA: Use the same spacing here so reserved depth matches consumed depth
// ORCA: and first-layer purge segments do not leave visible gaps.
const bool first_layer_plan = (m_plan.size() - 1) == m_first_layer_idx;
const float planning_spacing = first_layer_plan ? m_extra_flow : m_extra_spacing_wipe;
float wiping_depth = get_wipe_depth(wipe_volume - first_wipe_volume, layer_height_par, m_perimeter_width, m_extra_flow, planning_spacing, width);
m_plan.back().tool_changes.push_back(WipeTowerInfo::ToolChange(old_tool, new_tool, ramming_depth + wiping_depth, ramming_depth, first_wipe_line, wipe_volume));
float wiping_depth = get_wipe_depth(wipe_volume - first_wipe_volume, layer_height, m_perimeter_width, m_extra_flow, planning_spacing, width);
return WipeTowerInfo::ToolChange(old_tool, new_tool, ramming_depth + wiping_depth, ramming_depth, first_wipe_line, wipe_volume);
}
@@ -2288,49 +2145,64 @@ void WipeTower2::save_on_last_wipe()
continue;
// Which toolchange will finish_layer extrusions be subtracted from?
int idx = first_toolchange_to_nonsoluble(m_layer_info->tool_changes);
int idx = first_toolchange_to_nonsoluble_nonsupport(m_layer_info->tool_changes);
if (idx == -1) {
// In this case, finish_layer will be called at the very beginning.
finish_layer().total_extrusion_length_in_plane();
}
const float width = m_wipe_tower_width - 3*m_perimeter_width; // width we draw into
auto recompute_toolchange = [this, width](WipeTowerInfo::ToolChange& toolchange, float volume_to_save) {
float volume_left_to_wipe = std::max(m_filpar[toolchange.new_tool].filament_minimal_purge_on_wipe_tower, toolchange.wipe_volume_total - volume_to_save);
float volume_we_need_depth_for = std::max(0.f, volume_left_to_wipe - length_to_volume(toolchange.first_wipe_line, m_perimeter_width*m_extra_flow, m_layer_info->height));
// ORCA: Keep wipe-depth planning consistent with toolchange_Wipe().
// ORCA: On the first layer, toolchange_Wipe() advances purge rows using
// ORCA: m_extra_flow * m_perimeter_width, while later layers use
// ORCA: m_extra_spacing_wipe * m_perimeter_width.
// ORCA: float dy = (is_first_layer() ? m_extra_flow : m_extra_spacing_wipe) * m_perimeter_width;
// ORCA: Use the same spacing here so reserved depth matches consumed depth
// ORCA: and first-layer purge segments do not leave visible gaps.
const bool first_layer_plan = size_t(m_layer_info - m_plan.begin()) == m_first_layer_idx;
const float planning_spacing = first_layer_plan ? m_extra_flow : m_extra_spacing_wipe;
float depth_to_wipe = get_wipe_depth(volume_we_need_depth_for, m_layer_info->height, m_perimeter_width, m_extra_flow, planning_spacing, width);
toolchange.required_depth = toolchange.ramming_depth + depth_to_wipe;
toolchange.wipe_volume = volume_left_to_wipe;
};
for (int i=0; i<int(m_layer_info->tool_changes.size()); ++i) {
auto& toolchange = m_layer_info->tool_changes[i];
tool_change(toolchange.new_tool);
if (i == idx) {
float width = m_wipe_tower_width - 3*m_perimeter_width; // width we draw into
float volume_to_save = length_to_volume(finish_layer().total_extrusion_length_in_plane(), m_perimeter_width, m_layer_info->height);
float volume_left_to_wipe = std::max(m_filpar[toolchange.new_tool].filament_minimal_purge_on_wipe_tower, toolchange.wipe_volume_total - volume_to_save);
float volume_we_need_depth_for = std::max(0.f, volume_left_to_wipe - length_to_volume(toolchange.first_wipe_line, m_perimeter_width*m_extra_flow, m_layer_info->height));
// ORCA: Keep wipe-depth planning consistent with toolchange_Wipe().
// ORCA: On the first layer, toolchange_Wipe() advances purge rows using
// ORCA: m_extra_flow * m_perimeter_width, while later layers use
// ORCA: m_extra_spacing_wipe * m_perimeter_width.
// ORCA: float dy = (is_first_layer() ? m_extra_flow : m_extra_spacing_wipe) * m_perimeter_width;
// ORCA: Use the same spacing here so reserved depth matches consumed depth
// ORCA: and first-layer purge segments do not leave visible gaps.
const bool first_layer_plan = size_t(m_layer_info - m_plan.begin()) == m_first_layer_idx;
const float planning_spacing = first_layer_plan ? m_extra_flow : m_extra_spacing_wipe;
float depth_to_wipe = get_wipe_depth(volume_we_need_depth_for, m_layer_info->height, m_perimeter_width, m_extra_flow, planning_spacing, width);
toolchange.required_depth = toolchange.ramming_depth + depth_to_wipe;
toolchange.wipe_volume = volume_left_to_wipe;
recompute_toolchange(toolchange, length_to_volume(finish_layer().total_extrusion_length_in_plane(), m_perimeter_width, m_layer_info->height));
} else if (toolchange.wipe_volume < m_filpar[toolchange.new_tool].filament_minimal_purge_on_wipe_tower) {
// Keep filament_minimal_purge_on_wipe_tower enforced for toolchanges that get
// no finish-layer saving, e.g. a support/soluble filament skipped as the
// finish filament above. Recomputing only when the clamp binds leaves all
// other toolchanges with their planned values bit-for-bit.
recompute_toolchange(toolchange, 0.f);
}
}
}
}
// Return index of first toolchange that switches to non-soluble extruder
// ot -1 if there is no such toolchange.
int WipeTower2::first_toolchange_to_nonsoluble(
// Return the index of the toolchange whose new filament should print the layer's
// finish extrusions (sparse infill + wall + brim), or -1 to print them with the
// layer's incoming filament before any toolchange happens.
// Like WipeTower::first_toolchange_to_nonsoluble_nonsupport(): support and soluble
// filaments bond poorly to the material printed on top of them, so they must not
// print the tower's shell when another filament is available on the layer.
int WipeTower2::first_toolchange_to_nonsoluble_nonsupport(
const std::vector<WipeTowerInfo::ToolChange>& tool_changes) const
{
if (tool_changes.empty())
return -1;
// If a specific wipe tower filament is forced, use it to decide where to finish the layer.
if (m_wipe_tower_filament > 0) {
for (size_t idx = 0; idx < tool_changes.size(); ++idx) {
@@ -2339,8 +2211,19 @@ int WipeTower2::first_toolchange_to_nonsoluble(
}
return -1;
}
// Orca: allow calculation of the required depth and wipe volume for soluble toolchanges as well.
return tool_changes.empty() ? -1 : 0;
auto is_wall_filament = [this](size_t tool) {
return !m_filpar[tool].is_soluble && !m_filpar[tool].is_support;
};
for (size_t idx = 0; idx < tool_changes.size(); ++idx)
if (is_wall_filament(tool_changes[idx].new_tool))
return idx;
if (is_wall_filament(tool_changes.front().old_tool))
return -1;
// Only support/soluble filaments on this layer: keep the first toolchange so the
// finish-layer saving and the minimal-purge clamp still apply to it (Orca depth
// and wipe volume accounting, see save_on_last_wipe()).
return 0;
}
static WipeTower::ToolChangeResult merge_tcr(WipeTower::ToolChangeResult& first,
@@ -2363,6 +2246,24 @@ static WipeTower::ToolChangeResult merge_tcr(WipeTower::ToolChangeResult& first,
}
// Precompute, for every plan layer, the wall openings ("skip points") at each toolchange's
// entry position, like WipeTower::get_all_wall_skip_points(). toolchange_entry_pos()
// reproduces from the finalized plan where tool_change() will start, so each gap coincides
// with the entry travel's target (tcr.start_pos, pre-rotation frame). BBL parity: the gap
// sits at the CP_TOOLCHANGE_WIPE start row, never at the ram band.
void WipeTower2::compute_wall_skip_points()
{
m_wall_skip_points.assign(m_plan.size(), std::vector<Vec2f>());
for (size_t layer_id = 0; layer_id < m_plan.size(); ++layer_id) {
float depth_traversed = 0.f;
for (const auto& toolchange : m_plan[layer_id].tool_changes) {
m_wall_skip_points[layer_id].emplace_back(
toolchange_entry_pos(depth_traversed, toolchange.ramming_depth, layer_id == m_first_layer_idx));
depth_traversed += toolchange.required_depth;
}
}
}
// Processes vector m_plan and calls respective functions to generate G-code for the wipe tower
// Resulting ToolChangeResults are appended into vector "result"
void WipeTower2::generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &result)
@@ -2378,12 +2279,41 @@ void WipeTower2::generate(std::vector<std::vector<WipeTower::ToolChangeResult>>
}
#endif
m_rib_length = std::max({m_rib_length, sqrt(m_wipe_tower_depth * m_wipe_tower_depth + m_wipe_tower_width * m_wipe_tower_width)});
if (m_wall_type == (int)wtwRib) {
// Rib wall: force a square tower like WipeTower::plan_tower_new(), ignoring the
// configured prime_tower_width (the GUI greys it out in rib mode). The planned depths
// already include the extra-spacing factors, so sqrt(depth * width) preserves the
// purge area. Replan every toolchange for the new width, then re-derive the depths.
float max_depth = 0.f;
for (const auto& current_plan : m_plan)
max_depth = std::max(max_depth, current_plan.depth);
if (max_depth > EPSILON) {
m_wipe_tower_width = align_ceil(std::sqrt(max_depth * m_wipe_tower_width), m_perimeter_width);
for (size_t idx = 0; idx < m_plan.size(); ++idx)
for (auto& toolchange : m_plan[idx].tool_changes)
toolchange = set_toolchange(toolchange.old_tool, toolchange.new_tool,
m_plan[idx].height, toolchange.wipe_volume,
idx == m_first_layer_idx);
plan_tower();
}
// Like WipeTower::plan_tower_new(): extend the ribs instead of the tower when the
// tower is smaller than the height-based stability minimum.
const float min_depth = WipeTower::get_limit_depth_by_height(m_wipe_tower_height);
if (m_wipe_tower_depth + EPSILON < min_depth)
m_rib_length = std::max(m_rib_length, min_depth * (float)std::sqrt(2.f));
}
const float diagonal = std::sqrt(m_wipe_tower_depth * m_wipe_tower_depth + m_wipe_tower_width * m_wipe_tower_width);
m_rib_length = std::max(m_rib_length, diagonal);
m_rib_length += m_extra_rib_length;
m_rib_length = std::max(0.f, m_rib_length);
m_rib_length = std::max(diagonal, m_rib_length); // a negative extra length must not shrink the ribs below the diagonal
m_rib_width = std::min(m_rib_width, std::min(m_wipe_tower_depth, m_wipe_tower_width) /
2.f); // Ensure that the rib wall of the wipetower are attached to the infill.
if (m_use_gap_wall)
compute_wall_skip_points();
m_layer_info = m_plan.begin();
m_current_height = 0.f;
@@ -2410,7 +2340,7 @@ void WipeTower2::generate(std::vector<std::vector<WipeTower::ToolChangeResult>>
if (m_layer_info->depth < m_wipe_tower_depth - m_perimeter_width)
m_y_shift = (m_wipe_tower_depth-m_layer_info->depth-m_perimeter_width)/2.f;
int idx = first_toolchange_to_nonsoluble(layer.tool_changes);
int idx = first_toolchange_to_nonsoluble_nonsupport(layer.tool_changes);
WipeTower::ToolChangeResult finish_layer_tcr;
if (idx == -1) {
@@ -2503,8 +2433,7 @@ Polygon WipeTower2::generate_support_rib_wall(WipeTowerWriter2&
double feedrate,
bool first_layer,
bool rib_wall,
bool extrude_perimeter,
bool skip_points)
bool extrude_perimeter)
{
float retract_length = m_filpar[m_current_tool].retract_length;
@@ -2524,18 +2453,28 @@ Polygon WipeTower2::generate_support_rib_wall(WipeTowerWriter2&
if (!extrude_perimeter)
return wall_polygon;
if (skip_points) {
result_wall = contrust_gap_for_skip_points(wall_polygon, std::vector<Vec2f>(), m_wipe_tower_width, 2.5 * m_perimeter_width,
if (m_use_gap_wall) {
// Cut the wall open at each toolchange's entry (see compute_wall_skip_points()).
// The vector is empty during the save_on_last_wipe planning passes, which therefore
// measure the un-gapped wall — same approximation as the BBL tower.
static const std::vector<Vec2f> no_skip_points;
const size_t layer_id = size_t(m_layer_info - m_plan.begin());
const std::vector<Vec2f>& layer_skip_points =
layer_id < m_wall_skip_points.size() ? m_wall_skip_points[layer_id] : no_skip_points;
result_wall = construct_gap_for_skip_points(wall_polygon, layer_skip_points, m_wipe_tower_width, 2.5 * m_perimeter_width,
insert_skip_polygon);
} else {
result_wall.push_back(to_polyline(wall_polygon));
insert_skip_polygon = wall_polygon;
}
writer.generate_path(result_wall, feedrate, retract_length, retract_speed, m_used_fillet);
//if (m_cur_layer_id == 0) {
// BoundingBox bbox = get_extents(result_wall);
// m_rib_offset = Vec2f(-unscaled<float>(bbox.min.x()), -unscaled<float>(bbox.min.y()));
//}
// Tower-local shift that puts the rib wall's protruding first-layer min corner at the
// configured tower position, like WipeTower::generate_support_wall_new(). Measured on
// the un-gapped outline so a wall gap cannot shift the tower.
if (rib_wall && is_first_layer()) {
BoundingBox bbox = get_extents(insert_skip_polygon);
m_rib_offset = Vec2f(-unscaled<float>(bbox.min.x()), -unscaled<float>(bbox.min.y()));
}
return insert_skip_polygon;
}
+59 -10
View File
@@ -34,6 +34,10 @@ public:
bool is_finish,
bool is_contact = false) const;
// Whether this print cuts wall openings ("skip points") at the toolchange entries.
// Shared with the entry routing in GCode.cpp so the router and the tower agree.
static bool use_gap_wall(const PrintConfig& config);
// x -- x coordinates of wipe tower in mm ( left bottom corner )
// y -- y coordinates of wipe tower in mm ( left bottom corner )
// width -- width of wipe tower in mm ( default 60 mm - leave as it is )
@@ -69,9 +73,9 @@ public:
const float brim = m_wipe_tower_brim_width_real;
return BoundingBoxf(Vec2d(-brim, -brim), Vec2d(double(m_wipe_tower_width) + brim, double(m_wipe_tower_depth) + brim));
}
// WT2 doesn't currently compute a rib-origin compensation like WipeTower (m_rib_offset),
// so expose a zero offset for consistency purposes (to maintain API parity).
Vec2f get_rib_offset() const { return Vec2f::Zero(); }
// Tower-local shift that puts the rib wall's first-layer min corner at the configured
// tower position, like WipeTower::get_rib_offset(). Zero unless the rib wall is used.
Vec2f get_rib_offset() const { return m_rib_offset; }
float get_rib_width() const { return m_rib_width; }
float get_rib_length() const { return m_rib_length; }
@@ -149,6 +153,7 @@ public:
struct FilamentParameters {
std::string material = "PLA";
bool is_soluble = false;
bool is_support = false;
int temperature = 0;
int first_layer_temperature = 0;
int interface_print_temperature = 0;
@@ -220,7 +225,6 @@ private:
float m_perimeter_speed = 0.f;
float m_first_layer_speed = 0.f;
size_t m_first_layer_idx = size_t(-1);
bool m_flat_ironing = false;
bool m_enable_tower_interface_features = false;
bool m_enable_tower_interface_cooldown_during_tower = false;
bool m_prev_layer_had_interface = false;
@@ -231,6 +235,12 @@ private:
float m_rib_width = 10;
float m_extra_rib_length = 0;
float m_rib_length = 0;
Vec2f m_rib_offset = Vec2f::Zero();
bool m_use_gap_wall = false;
// Per plan layer, each toolchange's entry position (tower-local, un-shifted frame):
// where the wall is cut open so the entry travel does not cross the printed wall.
// Filled by compute_wall_skip_points() once the plan is final.
std::vector<std::vector<Vec2f>> m_wall_skip_points;
bool m_enable_arc_fitting = false;
@@ -278,6 +288,37 @@ private:
bool is_first_layer() const { return size_t(m_layer_info - m_plan.begin()) == m_first_layer_idx; }
// Purge row lattice of toolchange_Wipe(): row pitch and extrusion width.
float wipe_row_spacing(bool first_layer) const { return (first_layer ? m_extra_flow : m_extra_spacing_wipe) * m_perimeter_width; }
float wipe_line_width() const { return m_perimeter_width * m_extra_flow; }
// Whether toolchange_Unload() rams this (old) tool out.
bool tool_ramming_enabled(size_t tool) const { return (m_semm && m_enable_filament_ramming) || m_filpar[tool].multitool_ramming; }
// Whether the wipe restarts at the box boundary on a fresh row below the quantized
// ram band after ramming this (old) tool out (multi-tool gap wall; SEMM keeps the
// stock continue-from-ram-end behavior).
bool boundary_wipe_start_enabled(size_t tool) const { return tool_ramming_enabled(tool) && !m_semm && m_use_gap_wall; }
// With a boundary wipe start the wipe begins on a fresh row below the quantized ram
// band. Y offset from the box start to that first wipe row.
float wipe_start_offset_after_ram(float ramming_depth, bool first_layer) const
{
return ramming_depth + wipe_row_spacing(first_layer) - (m_perimeter_width + wipe_line_width()) / 2.f;
}
// Tower-local entry position of a toolchange whose box starts depth_traversed into
// the layer: the box corner, moved down to the first wipe row when the plan gives
// it a boundary wipe start (ramming_depth > 0 iff the unload rams). tool_change()
// enters here and compute_wall_skip_points() cuts the wall gap here, so the routed
// entry, the gap and the wipe scrub all share one opening.
Vec2f toolchange_entry_pos(float depth_traversed, float ramming_depth, bool first_layer) const
{
Vec2f pos(m_perimeter_width / 2.f, m_perimeter_width / 2.f + depth_traversed);
if (!m_semm && m_use_gap_wall && ramming_depth > 0.f)
pos.y() += wipe_start_offset_after_ram(ramming_depth, first_layer);
return pos;
}
// Calculates extrusion flow needed to produce required line width for given layer height
float extrusion_flow(float layer_height = -1.f) const // negative layer_height - return current m_extrusion_flow
{
@@ -328,9 +369,10 @@ private:
std::vector<float> m_used_filament_length;
std::vector<std::pair<float, std::vector<float>>> m_used_filament_length_until_layer;
// Return index of first toolchange that switches to non-soluble extruder
// ot -1 if there is no such toolchange.
int first_toolchange_to_nonsoluble(
// Return the index of the toolchange whose new filament should print the layer's
// finish extrusions (sparse infill + wall + brim), or -1 to print them with the
// layer's incoming filament before any toolchange happens.
int first_toolchange_to_nonsoluble_nonsupport(
const std::vector<WipeTowerInfo::ToolChange>& tool_changes) const;
void toolchange_Unload(
@@ -353,7 +395,9 @@ private:
WipeTowerWriter2 &writer,
const WipeTower::box_coordinates &cleaning_box,
float wipe_volume,
bool interface_layer);
bool interface_layer,
bool priming = false,
bool fill_box = false);
Polygon generate_support_rib_wall(WipeTowerWriter2& writer,
@@ -361,8 +405,7 @@ private:
double feedrate,
bool first_layer,
bool rib_wall,
bool extrude_perimeter,
bool skip_points);
bool extrude_perimeter);
Polygon generate_support_cone_wall(
WipeTowerWriter2& writer,
@@ -372,6 +415,12 @@ private:
float spacing);
Polygon generate_rib_polygon(const WipeTower::box_coordinates& wt_box);
void compute_wall_skip_points();
// Computes the depth reserved for a toolchange (shared by plan_toolchange() and the
// rib-wall square-tower replanning in generate()).
WipeTowerInfo::ToolChange set_toolchange(size_t old_tool, size_t new_tool, float layer_height, float wipe_volume, bool first_layer_plan);
};
+266 -34
View File
@@ -360,6 +360,14 @@ static ClipperLib_Z::Paths clip_extrusion(const ClipperLib_Z::Path& subject, con
return clipped_paths;
}
static double clipper_z_path_length(const ClipperLib_Z::Path &path)
{
double len = 0.;
for (size_t i = 1; i < path.size(); ++ i)
len += (Vec2d(double(path[i].x()), double(path[i].y())) - Vec2d(double(path[i - 1].x()), double(path[i - 1].y()))).norm();
return len;
}
struct PerimeterGeneratorArachneExtrusion
{
Arachne::ExtrusionLine* extrusion = nullptr;
@@ -571,17 +579,156 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p
return extrusion_coll;
}
// ORCA: only_one_wall_top detects the top as "slice upper", so a feature rising from the middle of a
// top surface becomes an enclosed hole that gets ringed with extra inner walls. Fill those holes back
// into the top. Only holes that are both covered by the upper layer (excludes bridges) and backed by
// solid material (excludes voids) are filled.
static ExPolygons fill_enclosed_top_feature_holes(const ExPolygons &top, const Polygons &covered_by_upper, const ExPolygons &solid)
// ORCA: only_one_wall_top acts on top surfaces, so without a top shell there is nothing for it to act on: zero top
// shell layers retype the top surfaces as internal, see LayerRegion::prepare_fill_surfaces(). A 0% top surface
// density does leave a top surface - just an unfilled one - so it does not disable the feature.
// ConfigManipulation::toggle_print_fff_options() hides the option under the same condition, so a profile that left
// it enabled does not act behind a hidden checkbox.
static bool has_top_shell_layers(const PrintRegionConfig &config)
{
ExPolygons filled = top;
for (ExPolygon &ex : filled)
ex.holes.clear();
const ExPolygons feature_holes = intersection_ex(intersection_ex(diff_ex(filled, top), covered_by_upper), solid);
return feature_holes.empty() ? top : union_ex(top, feature_holes);
return config.top_shell_layers.value > 0;
}
// ORCA: only_one_wall_first_layer thins the first layer to a single wall, the bottom counterpart of the above and
// gated the same way: zero bottom shell layers retype the bottom surfaces as internal, so that wall would ring
// sparse infill on the bed. The bottom surface density plays no part - an unfilled bottom surface is still a bottom
// surface, exactly as for the top - and it cannot reach zero anyway, being capped at a 10% minimum.
static bool has_bottom_shell_layers(const PrintRegionConfig &config)
{
return config.bottom_shell_layers.value > 0;
}
// ORCA: the inner walls are only given up when a top fill takes their space, and it has to actually reach it -
// a 0% top surface density leaves no fill at all, and without top_surface_expansion the fill never grows over
// them. Either way the original generation is kept (re-onion the not-top region), which is what users of
// only_one_wall_top alone have always got.
static bool top_fill_replaces_inner_walls(const PrintRegionConfig &config)
{
return has_top_shell_layers(config) && config.top_surface_density.value > 0 && config.top_surface_expansion.value > 0;
}
// ORCA: only_one_wall_top - cheap per-vertex classification of a wall against the top surface. Only Partial
// needs the geometry clipped or measured; a segment crossing the top with no vertex inside is rare enough to ignore.
enum class TopOverlap { None, Partial, Full };
static bool point_over_top(const Point &p, const ExPolygons &top_region, const BoundingBox &top_region_bbox)
{
if (! top_region_bbox.contains(p))
return false;
for (const ExPolygon &ex : top_region)
if (ex.contains(p, false))
return true;
return false;
}
static TopOverlap classify_over_top(const Points &pts, const ExPolygons &top_region, const BoundingBox &top_region_bbox)
{
size_t inside = 0;
for (const Point &p : pts)
if (point_over_top(p, top_region, top_region_bbox))
++ inside;
return inside == 0 ? TopOverlap::None : inside == pts.size() ? TopOverlap::Full : TopOverlap::Partial;
}
static TopOverlap classify_over_top(const Arachne::ExtrusionLine &el, const ExPolygons &top_region, const BoundingBox &top_region_bbox)
{
size_t inside = 0;
for (const Arachne::ExtrusionJunction &j : el.junctions)
if (point_over_top(j.p, top_region, top_region_bbox))
++ inside;
return inside == 0 ? TopOverlap::None : inside == el.junctions.size() ? TopOverlap::Full : TopOverlap::Partial;
}
// ORCA: only_one_wall_top for Arachne - cut out of the already generated inner walls the parts running over the top
// surface, so geometry that continues upward keeps its walls. A wall too short over the top to be worth slitting open
// is left whole, its footprint reported in kept_over_top for the caller to withhold from the top fill.
static void clip_inner_walls_over_top(std::vector<Arachne::VariableWidthLines> &inner_perimeters, const ExPolygons &top_region, coord_t perimeter_width, Polygons &kept_over_top)
{
const BoundingBox top_region_bbox = get_extents(top_region).inflated(SCALED_EPSILON);
auto covered_by = [](const Arachne::ExtrusionLine &el) {
Polyline centerline;
centerline.points.reserve(el.junctions.size());
coord_t width = 0;
for (const Arachne::ExtrusionJunction &j : el.junctions) {
centerline.points.emplace_back(j.p);
width = std::max(width, j.w);
}
return offset(centerline, float(width) / 2.f);
};
// Pull the cut back by half a wall width: the clip severs the centerline, but the bead's rounded end
// extends half a width past its endpoint and would otherwise overlap the top fill.
ClipperLib_Z::Paths top_paths_z;
for (const Polygon &poly : to_polygons(offset_ex(top_region, float(perimeter_width) / 2.f))) {
top_paths_z.emplace_back();
ClipperLib_Z::Path &out = top_paths_z.back();
out.reserve(poly.points.size());
for (const Point &pt : poly.points)
out.emplace_back(pt.x(), pt.y(), 0);
}
for (Arachne::VariableWidthLines &inner_perimeter : inner_perimeters) {
Arachne::VariableWidthLines kept;
kept.reserve(inner_perimeter.size());
for (Arachne::ExtrusionLine &el : inner_perimeter) {
if (el.empty())
continue;
const TopOverlap overlap = classify_over_top(el, top_region, top_region_bbox);
if (overlap == TopOverlap::None) {
kept.emplace_back(std::move(el));
continue;
}
if (overlap == TopOverlap::Full)
continue; // the clip below would return nothing anyway
ClipperLib_Z::Path subject;
subject.reserve(el.size());
for (const Arachne::ExtrusionJunction &j : el.junctions)
subject.emplace_back(j.p.x(), j.p.y(), j.w);
ClipperLib_Z::Paths pieces = clip_extrusion(subject, top_paths_z, ClipperLib_Z::ctDifference);
// Clipper treats the subject as an open polyline, so it also cuts a closed loop at its (arbitrary)
// start vertex and may reverse pieces. Stitch pieces sharing an endpoint back together.
auto same_pt = [](const ClipperLib_Z::IntPoint &p, const ClipperLib_Z::IntPoint &q) {
return std::abs(p.x() - q.x()) <= SCALED_EPSILON && std::abs(p.y() - q.y()) <= SCALED_EPSILON;
};
for (size_t i = 0; i < pieces.size(); ++ i) {
for (size_t j = i + 1; j < pieces.size();) {
ClipperLib_Z::Path &a = pieces[i];
ClipperLib_Z::Path &b = pieces[j];
if (same_pt(a.front(), b.front()) || same_pt(a.front(), b.back()))
std::reverse(a.begin(), a.end());
if (same_pt(a.back(), b.back()))
std::reverse(b.begin(), b.end());
if (same_pt(a.back(), b.front())) {
a.insert(a.end(), b.begin() + 1, b.end());
pieces.erase(pieces.begin() + j);
j = i + 1; // the merged path has new endpoints, restart the scan
} else
++ j;
}
}
// If the clip removed next to nothing, keep the loop untouched instead of slitting it open. The
// half-width pull-back above already costs about one width per crossing, hence two widths.
double kept_length = 0.;
for (const ClipperLib_Z::Path &path : pieces)
kept_length += clipper_z_path_length(path);
if (clipper_z_path_length(subject) - kept_length < 2. * double(perimeter_width)) {
append(kept_over_top, covered_by(el));
kept.emplace_back(std::move(el));
continue;
}
for (const ClipperLib_Z::Path &path : pieces) {
Arachne::ExtrusionLine clipped(el.inset_idx, el.is_odd);
clipped.junctions.reserve(path.size());
for (const ClipperLib_Z::IntPoint &pt : path)
clipped.junctions.emplace_back(Point(pt.x(), pt.y()), coord_t(pt.z()), el.inset_idx);
// Discard tiny leftovers that would print as zits.
if (clipped.size() >= 2 && clipped.getLength() >= perimeter_width)
kept.emplace_back(std::move(clipped));
}
}
inner_perimeter = std::move(kept);
}
}
void PerimeterGenerator::split_top_surfaces(const ExPolygons &orig_polygons, ExPolygons &top_fills,
@@ -649,7 +796,6 @@ void PerimeterGenerator::split_top_surfaces(const ExPolygons &orig_polygons, ExP
ExPolygons delete_bridge = diff_ex(orig_polygons, bridge_checker, ApplySafetyOffset::Yes);
ExPolygons top_polygons = diff_ex(delete_bridge, upper_polygons_series_clipped, ApplySafetyOffset::Yes);
top_polygons = fill_enclosed_top_feature_holes(top_polygons, upper_polygons_series_clipped, orig_polygons);
// get the not-top surface, from the "real top" but enlarged by external_infill_margin (and the
// min_width_top_surface we removed a bit before)
@@ -1234,6 +1380,11 @@ void PerimeterGenerator::process_classic()
for (const Surface &surface : all_surfaces)
surface_exp.push_back(surface.expolygon);
std::vector<size_t> surface_order = chain_expolygons(surface_exp);
// ORCA: neither one-wall option has a surface to act on without the shell behind it, see
// has_top_shell_layers() / has_bottom_shell_layers(). Gated here so every use below - including the
// topmost and first layers - sees the same answer.
const bool only_one_wall_top = this->config->only_one_wall_top && has_top_shell_layers(*this->config);
const bool only_one_wall_first_layer = this->config->only_one_wall_first_layer && has_bottom_shell_layers(*this->config);
for (size_t order_idx = 0; order_idx < surface_order.size(); order_idx++) {
const Surface &surface = all_surfaces[surface_order[order_idx]];
// detect how many perimeters must be generated for this island
@@ -1241,16 +1392,23 @@ void PerimeterGenerator::process_classic()
int sparse_infill_density = this->config->sparse_infill_density.value;
if (this->config->alternate_extra_wall && this->layer_id % 2 == 1 && !m_spiral_vase && sparse_infill_density > 0) // add alternating extra wall
loop_number++;
if (this->layer_id == object_config->raft_layers && this->config->only_one_wall_first_layer)
if (this->layer_id == object_config->raft_layers && only_one_wall_first_layer)
loop_number = 0;
// Set the topmost layer to be one wall
if (loop_number > 0 && config->only_one_wall_top && this->upper_slices == nullptr)
if (loop_number > 0 && only_one_wall_top && this->upper_slices == nullptr)
loop_number = 0;
ExPolygons last = union_ex(surface.expolygon.simplify_p(surface_simplify_resolution));
ExPolygons gaps;
ExPolygons top_fills;
ExPolygons fill_clip;
// ORCA: only_one_wall_top, all empty unless this island has a top surface on this layer. See the
// post-onion reduction below: the region to keep clear of inner walls, the space freed by the dropped
// walls (goes to infill, not left as a void) and the space held by the kept ones (withheld from the fill).
ExPolygons one_wall_top_region;
ExPolygons one_wall_top_reclaimed;
Polygons one_wall_top_kept_bands;
bool apply_one_wall_top = false;
if (loop_number >= 0) {
// In case no perimeters are to be generated, loop_number will equal to -1.
std::vector<PerimeterGeneratorLoops> contours(loop_number+1); // depth => loops
@@ -1389,8 +1547,19 @@ void PerimeterGenerator::process_classic()
//BBS: refer to superslicer
//store surface for top infill if only_one_wall_top
if (i == 0 && i!=loop_number && config->only_one_wall_top && !surface.is_bridge() && this->upper_slices != NULL) {
this->split_top_surfaces(last, top_fills, last, fill_clip);
if (i == 0 && i!=loop_number && only_one_wall_top && !surface.is_bridge() && this->upper_slices != NULL) {
if (top_fill_replaces_inner_walls(*this->config)) {
// ORCA: take the top fill and the keep-out region but leave `last` as the real geometry,
// so the onion follows it and the walls over the top are reduced in one step below.
ExPolygons non_top_polygons;
this->split_top_surfaces(last, top_fills, non_top_polygons, fill_clip);
apply_one_wall_top = !top_fills.empty();
if (apply_one_wall_top)
one_wall_top_region = diff_ex(last, non_top_polygons);
} else {
// Onion the not-top region only, so the remaining walls stop at the top boundary.
this->split_top_surfaces(last, top_fills, last, fill_clip);
}
}
if (i == loop_number && (! has_gap_fill || this->config->sparse_infill_density.value == 0)) {
@@ -1400,6 +1569,46 @@ void PerimeterGenerator::process_classic()
}
}
// ORCA: only_one_wall_top reduction - drop the inner walls (depth > 0) running over the top surface and
// take that space back from the gaps, leaving the top with the outer wall and the top infill. Classic
// perimeters are closed loops, so a wall can only be kept or dropped whole; one that merely grazes the
// top (same tolerance as the Arachne clip) is kept and withheld from the top fill instead.
if (apply_one_wall_top) {
const BoundingBox top_region_bbox = get_extents(one_wall_top_region).inflated(SCALED_EPSILON);
const double grazing_tolerance = 2. * double(perimeter_width);
// The band a wall covers, taken around its centerline so the orientation of holes does not matter.
auto wall_band = [perimeter_spacing](const Polygon &poly) {
Polygon centerline = poly;
centerline.make_counter_clockwise();
return diff(offset(centerline, float(perimeter_spacing) / 2.f),
offset(centerline, -float(perimeter_spacing) / 2.f));
};
Polygons dropped_wall_bands;
auto reduce_over_top = [&](PerimeterGeneratorLoops &loops) {
loops.erase(std::remove_if(loops.begin(), loops.end(), [&](const PerimeterGeneratorLoop &loop) {
const TopOverlap overlap = classify_over_top(loop.polygon.points, one_wall_top_region, top_region_bbox);
if (overlap == TopOverlap::None)
return false;
// Only a wall straddling the boundary is worth measuring; a wall wholly over the top goes.
if (overlap == TopOverlap::Partial &&
total_length(intersection_pl(Polylines{ loop.polygon.split_at_first_point() }, one_wall_top_region)) < grazing_tolerance) {
append(one_wall_top_kept_bands, wall_band(loop.polygon));
return false;
}
append(dropped_wall_bands, wall_band(loop.polygon));
return true;
}), loops.end());
};
for (int d = 1; d <= loop_number; ++ d) {
reduce_over_top(contours[d]);
reduce_over_top(holes[d]);
}
if (! gaps.empty())
gaps = diff_ex(gaps, one_wall_top_region);
if (! dropped_wall_bands.empty())
one_wall_top_reclaimed = diff_ex(dropped_wall_bands, one_wall_top_region);
}
// nest loops: holes first
for (int d = 0; d <= loop_number; ++ d) {
PerimeterGeneratorLoops &holes_d = holes[d];
@@ -1634,7 +1843,10 @@ void PerimeterGenerator::process_classic()
and use zigzag). */
//FIXME Vojtech: This grows by a rounded extrusion width, not by line spacing,
// therefore it may cover the area, but no the volume.
last = diff_ex(last, gap_fill.polygons_covered_by_width(10.f));
Polygons gap_fill_covered = gap_fill.polygons_covered_by_width(10.f);
last = diff_ex(last, gap_fill_covered);
if (! one_wall_top_reclaimed.empty())
one_wall_top_reclaimed = diff_ex(one_wall_top_reclaimed, gap_fill_covered);
this->gap_fill->append(std::move(gap_fill.entities));
}
@@ -1679,9 +1891,15 @@ void PerimeterGenerator::process_classic()
// append infill areas to fill_surfaces
//if any top_fills, grow them by ext_perimeter_spacing/2 to have the real un-anchored fill
ExPolygons top_infill_exp = intersection_ex(fill_clip, offset_ex(top_fills, double(ext_perimeter_spacing / 2)));
// ORCA: only_one_wall_top - route the top fill around the walls kept despite grazing the top.
if (!one_wall_top_kept_bands.empty())
top_infill_exp = diff_ex(top_infill_exp, one_wall_top_kept_bands);
if (!top_fills.empty()) {
infill_exp = union_ex(infill_exp, offset_ex(top_infill_exp, double(top_infill_peri_overlap)));
}
// ORCA: only_one_wall_top - what the top fill does not cover of the dropped walls goes to infill.
if (!one_wall_top_reclaimed.empty())
infill_exp = union_ex(infill_exp, one_wall_top_reclaimed);
this->fill_surfaces->append(infill_exp, stInternal);
apply_extra_perimeters(infill_exp);
@@ -1700,6 +1918,8 @@ void PerimeterGenerator::process_classic()
double(-inset - infill_peri_overlap));
if (!top_fills.empty())
polyWithoutOverlap = union_ex(polyWithoutOverlap, top_infill_exp);
if (!one_wall_top_reclaimed.empty())
polyWithoutOverlap = union_ex(polyWithoutOverlap, one_wall_top_reclaimed);
this->fill_no_overlap->insert(this->fill_no_overlap->end(), polyWithoutOverlap.begin(), polyWithoutOverlap.end());
}
@@ -2138,6 +2358,11 @@ void PerimeterGenerator::process_arachne()
process_no_bridge(all_surfaces, perimeter_spacing, ext_perimeter_width);
// BBS: don't simplify too much which influence arc fitting when export gcode if arc_fitting is enabled
double surface_simplify_resolution = (print_config->enable_arc_fitting && !this->has_fuzzy_skin) ? 0.2 * m_scaled_resolution : m_scaled_resolution;
// ORCA: neither one-wall option has a surface to act on without the shell behind it, see
// has_top_shell_layers() / has_bottom_shell_layers(). Gated here so every use below - including the
// topmost and first layers - sees the same answer.
const bool only_one_wall_top = this->config->only_one_wall_top && has_top_shell_layers(*this->config);
const bool only_one_wall_first_layer = this->config->only_one_wall_first_layer && has_bottom_shell_layers(*this->config);
// we need to process each island separately because we might have different
// extra perimeters for each one
for (const Surface& surface : all_surfaces) {
@@ -2150,12 +2375,12 @@ void PerimeterGenerator::process_arachne()
// Set the bottommost layer to be one wall
const bool is_bottom_layer = (this->layer_id == object_config->raft_layers) ? true : false;
if (is_bottom_layer && this->config->only_one_wall_first_layer)
if (is_bottom_layer && only_one_wall_first_layer)
loop_number = 0;
// Orca: set the topmost layer to be one wall according to the config
const bool is_topmost_layer = (this->upper_slices == nullptr) ? true : false;
if (is_topmost_layer && loop_number > 0 && config->only_one_wall_top)
if (is_topmost_layer && loop_number > 0 && only_one_wall_top)
loop_number = 0;
auto apply_precise_outer_wall = config->precise_outer_wall && config->wall_sequence == WallSequence::InnerOuter;
@@ -2175,10 +2400,10 @@ void PerimeterGenerator::process_arachne()
//PS: One wall top surface for Arachne
ExPolygons top_expolygons;
// Calculate how many inner loops remain when TopSurfaces is selected.
const int inner_loop_number = (config->only_one_wall_top && upper_slices != nullptr) ? loop_number - 1 : -1;
const int inner_loop_number = (only_one_wall_top && upper_slices != nullptr) ? loop_number - 1 : -1;
// Set one perimeter when TopSurfaces is selected.
if (config->only_one_wall_top && loop_number > 0)
if (only_one_wall_top && loop_number > 0)
loop_number = 0;
Arachne::WallToolPathsParams input_params_tmp = input_params;
@@ -2209,7 +2434,6 @@ void PerimeterGenerator::process_arachne()
upper_slices_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*upper_slices, infill_contour_bbox);
top_expolygons = diff_ex(infill_contour, upper_slices_clipped);
top_expolygons = fill_enclosed_top_feature_holes(top_expolygons, upper_slices_clipped, infill_contour);
if (!top_expolygons.empty()) {
if (lower_slices != nullptr) {
@@ -2230,25 +2454,33 @@ void PerimeterGenerator::process_arachne()
// due to thin lines being generated
top_expolygons = offset2_ex(top_expolygons, -top_surface_min_width, top_surface_min_width + float(perimeter_width * 0.85));
// Get the not-top ExPolygons (including bridges) from current slices and expanded real top ExPolygons (without bridges).
const ExPolygons not_top_expolygons = diff_ex(infill_contour, top_expolygons);
// Get final top ExPolygons.
// Get final top ExPolygons (bridges were excluded above, so they stay walled).
top_expolygons = intersection_ex(top_expolygons, infill_contour);
const Polygons not_top_polygons = to_polygons(offset_ex(not_top_expolygons,wall_0_inset));
Arachne::WallToolPaths inner_wall_tool_paths(not_top_polygons, perimeter_spacing, perimeter_spacing, coord_t(inner_loop_number + 1), 0, layer_height, input_params_tmp);
// ORCA: onion the real region (inside the outer wall) so the remaining walls follow the actual
// geometry, then cut away the parts over the top surface. Re-onioning the non-top complement
// instead - the fallback when there is no top fill - walls the top/non-top interface and rings
// top-surface islands with inner walls that don't exist when the feature is disabled.
const bool clip_walls_over_top = top_fill_replaces_inner_walls(*this->config);
const Polygons inner_region = to_polygons(offset_ex(clip_walls_over_top ? infill_contour
: diff_ex(infill_contour, top_expolygons),
wall_0_inset));
Arachne::WallToolPaths inner_wall_tool_paths(inner_region, perimeter_spacing, perimeter_spacing, coord_t(inner_loop_number + 1), 0, layer_height, input_params_tmp);
std::vector<Arachne::VariableWidthLines> inner_perimeters = inner_wall_tool_paths.getToolPaths();
// Recalculate indexes of inner perimeters before merging them.
if (!perimeters.empty()) {
for (Arachne::VariableWidthLines &inner_perimeter : inner_perimeters) {
if (inner_perimeter.empty())
continue;
if (clip_walls_over_top) {
Polygons kept_over_top;
clip_inner_walls_over_top(inner_perimeters, top_expolygons, perimeter_width, kept_over_top);
// Route the top fill around the walls kept despite grazing the top.
if (! kept_over_top.empty())
top_expolygons = diff_ex(top_expolygons, kept_over_top);
}
// Recalculate indexes of inner perimeters before merging them: they come after the single outer wall.
if (!perimeters.empty())
for (Arachne::VariableWidthLines &inner_perimeter : inner_perimeters)
for (Arachne::ExtrusionLine &el : inner_perimeter)
++el.inset_idx;
}
}
perimeters.insert(perimeters.end(), inner_perimeters.begin(), inner_perimeters.end());
infill_contour = union_ex(top_expolygons, inner_wall_tool_paths.getInnerContour());
+2 -2
View File
@@ -129,13 +129,13 @@ public:
std::vector<PathFittingData> fitting_result;
//BBS: simplify points by arc fitting
void simplify_by_fitting_arc(double tolerance);
//BBS:
void reset_to_linear_move();
//BBS:
Polylines equally_spaced_lines(double distance) const;
private:
void append_fitting_result_after_append_points();
void append_fitting_result_after_append_polyline(const Polyline& src);
void reset_to_linear_move();
bool split_fitting_result_before_index(const size_t index, Point &new_endpoint, std::vector<PathFittingData>& data) const;
bool split_fitting_result_after_index(const size_t index, Point &new_startpoint, std::vector<PathFittingData>& data) const;
};
+48 -48
View File
@@ -3409,7 +3409,11 @@ void Print::update_filament_maps_to_config(std::vector<int> f_maps, std::vector<
}
else if ((extruder_volume_type_count > extruder_count) && (m_config.filament_volume_map.values.size() > index))
nozzle_volume_type = (NozzleVolumeType)(m_config.filament_volume_map.values[index]);
m_config.filament_map_2.values[index] = m_ori_full_print_config.get_index_for_extruder(f_maps[index], "print_extruder_id", extruder_type, nozzle_volume_type, "print_extruder_variant");
// Orca: when the process variant columns cannot be matched (degenerate
// print_extruder_id), key the override by plain extruder index like the seeding
// above instead of poisoning the map with -1.
int slot_index = m_ori_full_print_config.get_index_for_extruder(f_maps[index], "print_extruder_id", extruder_type, nozzle_volume_type, "print_extruder_variant");
m_config.filament_map_2.values[index] = slot_index >= 0 ? slot_index : f_maps[index] - 1;
}
m_full_print_config = m_ori_full_print_config;
@@ -4017,10 +4021,33 @@ void Print::_make_wipe_tower()
// in BBL machine, wipe tower is only use to prime extruder. So just use a global wipe volume.
WipeTower wipe_tower(m_config, m_plate_index, m_origin, m_wipe_tower_data.tool_ordering.first_extruder(),
m_wipe_tower_data.tool_ordering.empty() ? 0.f : m_wipe_tower_data.tool_ordering.back().print_z, m_wipe_tower_data.tool_ordering.all_extruders());
// Orca: the tower's first-layer flow follows the user's first-layer flow ratio (BBS reads
// its initial_layer_flow_ratio here — STUDIO-14254; first_layer_flow_ratio is Orca's analog,
// default 1.0 in both). Honor the set_other_flow_ratios gate that governs the option
// everywhere else.
wipe_tower.set_first_layer_flow_ratio(m_default_object_config.set_other_flow_ratios
? float(m_default_region_config.first_layer_flow_ratio)
: 1.f);
wipe_tower.set_has_tpu_filament(this->has_tpu_filament());
wipe_tower.set_filament_map(this->get_filament_maps());
// Vortek H2C: pass nozzle-level map for carousel rotation detection in tool_change_new()
wipe_tower.set_filament_nozzle_map(this->get_filament_nozzle_maps());
// Per-layer filament->nozzle grouping. sort_and_build_data() above publishes it on the Print
// for by-layer prints; by-object prints publish only later (psSkirtBrim), so fall back to the
// ToolOrdering's own copy there. set_extruder() below dereferences it, so it must be set first.
auto print_group_result = get_layered_nozzle_group_result();
const MultiNozzleUtils::LayeredNozzleGroupResult &nozzle_group_result =
print_group_result ? *print_group_result : m_wipe_tower_data.tool_ordering.get_layered_nozzle_group_result();
wipe_tower.set_nozzle_group_result(nozzle_group_result);
{
// Orca: acceleration options are object-scope (PrintConfig members in BBS), so resolve
// the per-variant columns here; initial_layer_travel_acceleration is FloatOrPercent
// over travel_acceleration and needs the full config to resolve.
std::vector<double> first_layer_travel_accels;
for (size_t i = 0; i < m_config.initial_layer_travel_acceleration.values.size(); ++i)
first_layer_travel_accels.emplace_back(m_full_print_config.get_abs_value_at("initial_layer_travel_acceleration", i));
wipe_tower.set_accelerations(m_default_object_config.default_acceleration.values,
m_default_object_config.initial_layer_acceleration.values,
m_default_object_config.travel_acceleration.values,
first_layer_travel_accels);
}
// Feed the has_filament_switcher device flag (develop-only dynamic key, read defensively from
// the full config — no shipping profile sets it) and the shared printable bed used by the PETG
// pre-extrusion offset clamp. Both are inert unless has_filament_switcher is set.
@@ -4056,27 +4083,19 @@ void Print::_make_wipe_tower()
multi_extruder_flush.emplace_back(wipe_volumes);
}
// Use NozzleStatusRecorder for per-carousel-slot tracking (BBS pattern).
// The original Orca code tracked per-extruder (2 slots), which collapsed all
// carousel filaments into one slot and caused massive redundant AMS flushing.
auto group_result = get_layered_nozzle_group_result();
// Per-carousel-slot purge tracking via NozzleStatusRecorder (BBS pattern); the layered
// group result set on the tower above resolves each filament to its nozzle slot per layer.
MultiNozzleUtils::NozzleStatusRecorder nozzle_recorder;
// Fallback (group_result == null) per-physical-nozzle tracking, matching the original
// pre-port behavior: remembers the last filament loaded in each physical nozzle slot.
std::vector<unsigned int> nozzle_cur_filament_ids(nozzle_nums, (unsigned int) -1);
std::vector<int>filament_maps = get_filament_maps();
int layer_idx = -1;
unsigned int current_filament_id = m_wipe_tower_data.tool_ordering.first_extruder();
// Initialize NozzleStatusRecorder with the first filament's carousel slot
if (group_result) {
auto nozzle = group_result->get_nozzle_for_filament(current_filament_id, layer_idx);
{
auto nozzle = nozzle_group_result.get_nozzle_for_filament(current_filament_id, layer_idx);
if (nozzle)
nozzle_recorder.set_nozzle_status(nozzle->group_id, current_filament_id, nozzle->extruder_id);
} else {
size_t cur_nozzle_id = filament_maps[current_filament_id] - 1;
nozzle_cur_filament_ids[cur_nozzle_id] = current_filament_id;
}
for (auto& layer_tools : m_wipe_tower_data.tool_ordering.layer_tools()) { // for all layers
@@ -4095,8 +4114,8 @@ void Print::_make_wipe_tower()
float volume_to_purge = 0;
// Per-carousel-slot purge tracking via NozzleStatusRecorder
if (group_result) {
auto nozzle_info = group_result->get_nozzle_for_filament(filament_id, layer_idx);
{
auto nozzle_info = nozzle_group_result.get_nozzle_for_filament(filament_id, layer_idx);
if (nozzle_info) {
int extruder_id = nozzle_info->extruder_id;
int nozzle_id = nozzle_info->group_id;
@@ -4115,22 +4134,6 @@ void Print::_make_wipe_tower()
}
nozzle_recorder.set_nozzle_status(nozzle_id, filament_id, extruder_id);
}
} else {
// Fallback: original Orca per-physical-nozzle path (non-carousel printers).
// Flush source is the last filament that occupied THIS nozzle, guarded so the
// first use of a nozzle incurs no flush.
int nozzle_id = filament_maps[filament_id] - 1;
unsigned int pre_filament_id = nozzle_cur_filament_ids[nozzle_id];
if (pre_filament_id != (unsigned int) -1 && pre_filament_id != filament_id) {
volume_to_purge = multi_extruder_flush[nozzle_id][pre_filament_id][filament_id];
float flush_multiplier = (m_config.prime_volume_mode == PrimeVolumeMode::pvmFast)
? m_config.flush_multiplier_fast.get_at(nozzle_id)
: m_config.flush_multiplier.get_at(nozzle_id);
volume_to_purge *= flush_multiplier;
volume_to_purge = layer_tools.wiping_extrusions().mark_wiping_extrusions(
*this, current_filament_id, filament_id, volume_to_purge);
}
nozzle_cur_filament_ids[nozzle_id] = filament_id;
}
//During the filament change, the extruder will extrude an extra length of grab_length for the corresponding detection, so the purge can reduce this length.
@@ -4138,29 +4141,21 @@ void Print::_make_wipe_tower()
float grab_purge_volume = m_config.grab_length.get_at(grab_extruder_id) * 2.4; //(diameter/2)^2*PI=2.4
volume_to_purge = std::max(0.f, volume_to_purge - grab_purge_volume);
// Select prime volume per-filament: nozzle change (carousel rotation) uses
// filament_prime_volume_nc, filament change (same nozzle slot) uses filament_prime_volume.
// Prime volume per-filament: the tower now picks extruder-change vs nozzle-change
// (carousel) internally per plan layer, so pass both candidates (BBS pattern).
float wipe_volume_ec = filament_id < m_config.filament_prime_volume.values.size()
? m_config.filament_prime_volume.values[filament_id]
: (float) m_config.prime_volume;
float wipe_volume_nc = filament_id < m_config.filament_prime_volume_nc.values.size()
? m_config.filament_prime_volume_nc.values[filament_id]
: (float) m_config.prime_volume;
float prime_volume = wipe_volume_ec;
if (group_result) {
bool is_nozzle_change = group_result->are_filaments_same_extruder(current_filament_id, filament_id, layer_idx) &&
!group_result->are_filaments_same_nozzle(current_filament_id, filament_id, layer_idx);
if (is_nozzle_change) {
prime_volume = wipe_volume_nc;
}
}
if (m_config.prime_volume_mode == PrimeVolumeMode::pvmSaving) {
prime_volume = 15.f;
wipe_volume_ec = 15.f;
wipe_volume_nc = 15.f;
}
wipe_tower.plan_toolchange((float)layer_tools.print_z, (float)layer_tools.wipe_tower_layer_height, current_filament_id, filament_id,
prime_volume, volume_to_purge);
wipe_volume_ec, wipe_volume_nc, volume_to_purge);
current_filament_id = filament_id;
}
layer_tools.wiping_extrusions().ensure_perimeters_infills_order(*this);
@@ -4338,7 +4333,12 @@ void Print::_make_wipe_tower()
wipe_tower.get_rib_width(), wipe_tower.get_rib_length(),
config().wipe_tower_fillet_wall.value);
const Vec3d origin = Vec3d::Zero();
m_fake_wipe_tower.set_fake_extrusion_data(wipe_tower.position(), wipe_tower.width(), wipe_tower.get_wipe_tower_height(),
// FakeWipeTower::pos is a bed-frame translation applied after rotation
// (getFakeExtrusionPathsFromWipeTower2 rotates about the local origin), so the
// tower-local rib offset must be rotated into the bed frame first.
m_fake_wipe_tower.rib_offset = Eigen::Rotation2Df(Geometry::deg2rad((float)config().wipe_tower_rotation_angle.value)) *
wipe_tower.get_rib_offset();
m_fake_wipe_tower.set_fake_extrusion_data(wipe_tower.position() + m_fake_wipe_tower.rib_offset, wipe_tower.width(), wipe_tower.get_wipe_tower_height(),
config().initial_layer_print_height, m_wipe_tower_data.depth,
m_wipe_tower_data.z_and_depth_pairs, m_wipe_tower_data.brim_width,
config().wipe_tower_rotation_angle, config().wipe_tower_cone_angle,
+15 -1
View File
@@ -1355,7 +1355,11 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
if ((extruder_volume_type_count > extruder_count) && opt_filament_volume_maps
&& opt_filament_volume_maps->values.size() == filament_maps.size())
nozzle_volume_type = (NozzleVolumeType)(opt_filament_volume_maps->values[index]);
m_config.filament_map_2.values[index] = new_full_config.get_index_for_extruder(filament_maps[index], "print_extruder_id", extruder_type, nozzle_volume_type, "print_extruder_variant");
// Orca: when the process variant columns cannot be matched (degenerate
// print_extruder_id), key the override by plain extruder index like the seeding
// above instead of poisoning the map with -1.
int slot_index = new_full_config.get_index_for_extruder(filament_maps[index], "print_extruder_id", extruder_type, nozzle_volume_type, "print_extruder_variant");
m_config.filament_map_2.values[index] = slot_index >= 0 ? slot_index : filament_maps[index] - 1;
}
// Do not use the ApplyStatus as we will use the max function when updating apply_status.
@@ -1411,6 +1415,16 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
num_extruders_changed = true;
}
}
else if (! print_diff.empty()) {
// Orca: m_config can diverge from an unchanged full config (e.g. the in-slice retract
// override recompute writing different values than the apply-time computation). The
// invalidation above already fired for print_diff, so repair m_config here as well;
// otherwise the divergence is never corrected and every subsequent apply of the same
// config invalidates the result again, forever.
m_placeholder_parser.apply_config(filament_overrides);
m_config.apply_only(new_full_config, print_diff, true);
m_config.apply(filament_overrides);
}
ModelObjectStatusDB model_object_status_db;
+40
View File
@@ -10516,6 +10516,44 @@ int DynamicPrintConfig::get_extruder_nozzle_volume_count(int extruder_count, std
return count;
}
// Orca: BBL system profiles ship full-width print_extruder_id/print_extruder_variant columns, but
// custom multi-extruder printers only ever get the machine-scope columns synthesized for them (see
// extend_extruder_variant); the process scope keeps the length-1 defaults, both in presets and in
// 3mf project configs. Expanding with that degenerate map makes every per-extruder lookup fail, and
// because both keys are themselves in print_options_with_variant, the expansion then latches a
// full-width-but-wrong [1,1,...] map that also defeats the generated_extruder_id fallback in
// get_index_for_extruder. Synthesize the process columns from the printer's extruder_variant_list
// (same token walk as extend_extruder_variant) before expanding.
static void ensure_process_variant_columns(DynamicPrintConfig &config, const DynamicPrintConfig &printer_config)
{
auto id_opt = dynamic_cast<ConfigOptionInts *>(config.option("print_extruder_id"));
auto variant_opt = dynamic_cast<ConfigOptionStrings *>(config.option("print_extruder_variant"));
auto list_opt = dynamic_cast<const ConfigOptionStrings *>(printer_config.option("extruder_variant_list"));
if (!id_opt || !variant_opt || !list_opt)
return;
if (id_opt->values.size() != 1 || variant_opt->values.size() != 1)
return;
std::vector<int> ids;
std::vector<std::string> variants;
for (int i = 0; i < int(list_opt->values.size()); ++i) {
std::vector<std::string> tokens;
boost::split(tokens, list_opt->get_at(i), boost::is_any_of(","), boost::token_compress_on);
for (std::string &token : tokens) {
boost::trim(token);
if (token.empty())
continue;
ids.push_back(i + 1);
variants.push_back(token);
}
}
// A single column is the legitimate single-extruder layout, not a degenerate one.
if (ids.size() <= 1)
return;
id_opt->values = std::move(ids);
variant_opt->values = std::move(variants);
}
std::vector<int> DynamicPrintConfig::update_values_to_printer_extruders(DynamicPrintConfig& printer_config, int extruder_count, int extruder_nozzle_volume_count, std::vector<std::vector<NozzleVolumeType>>& nv_types,
std::set<std::string>& key_set, std::string id_name, std::string variant_name, unsigned int stride, unsigned int extruder_id, NozzleVolumeType filament_nvt)
{
@@ -10557,6 +10595,8 @@ std::vector<int> DynamicPrintConfig::update_values_to_printer_extruders(DynamicP
variant_count = 1;
}
else {
if (id_name == "print_extruder_id")
ensure_process_variant_columns(*this, printer_config);
// Orca: emit the slots first, then size variant_count from what was actually
// emitted. extruder_nozzle_volume_count only equals the emitted total when every
// extruder carries per-type stats; an extruder with an empty stats entry combined
+49 -34
View File
@@ -1364,7 +1364,6 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "infill_combination_max_layer_height"
|| opt_key == "bottom_shell_thickness"
|| opt_key == "top_shell_thickness"
|| opt_key == "top_surface_expansion"
|| opt_key == "top_surface_expansion_margin"
|| opt_key == "top_surface_expansion_direction"
|| opt_key == "minimum_sparse_infill_area"
@@ -1400,7 +1399,6 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "infill_anchor"
|| opt_key == "infill_anchor_max"
|| opt_key == "top_surface_line_width"
|| opt_key == "top_surface_density"
|| opt_key == "bottom_surface_density"
|| opt_key == "center_of_surface_pattern"
|| opt_key == "separated_infills"
@@ -1434,6 +1432,24 @@ bool PrintObject::invalidate_state_by_config_options(
is_approx(new_density->value, 0.) || is_approx(new_density->value, 100.))
steps.emplace_back(posPerimeters);
steps.emplace_back(posPrepareInfill);
} else if (opt_key == "top_surface_density") {
// ORCA: 0% means no top solid fill, which switches off both the top surface expansion and the wall
// removal over top surfaces. Only crossing zero matters; posPerimeters cascades to posPrepareInfill.
const auto *old_density = old_config.option<ConfigOptionPercent>(opt_key);
const auto *new_density = new_config.option<ConfigOptionPercent>(opt_key);
assert(old_density && new_density);
if (is_approx(old_density->value, 0.) || is_approx(new_density->value, 0.))
steps.emplace_back(posPerimeters);
steps.emplace_back(posInfill);
} else if (opt_key == "top_surface_expansion") {
// ORCA: without the expansion the top fill never reaches the space freed by only_one_wall_top, so the
// walls over top surfaces are kept. Only crossing zero matters; posPerimeters cascades to posPrepareInfill.
const auto *old_expansion = old_config.option<ConfigOptionFloat>(opt_key);
const auto *new_expansion = new_config.option<ConfigOptionFloat>(opt_key);
assert(old_expansion && new_expansion);
if (old_expansion->value <= 0. || new_expansion->value <= 0.)
steps.emplace_back(posPerimeters);
steps.emplace_back(posPrepareInfill);
} else if (opt_key == "internal_solid_infill_line_width") {
// This value is used for calculating perimeter - infill overlap, thus perimeters need to be recalculated.
steps.emplace_back(posPerimeters);
@@ -1760,51 +1776,50 @@ void PrintObject::detect_surfaces_type()
}
}
// ORCA: Expand the top surfaces outward by top_surface_expansion in every direction. This
// enlarges the top solid infill and, in particular, grows it over the covered material left
// by features rising from the middle of a top surface (filling holes and joining tops so the
// features rest on it). The expansion stays inside the section it belongs to: each connected
// solid island has its own outer wall, so the top is grown within each island separately and
// clipped to it - growing one island's top across the gap into another island (which may have
// no top surface, leaving a partially filled layer) is never allowed. The top infill sits
// inside the perimeters, so the margin is measured from the walls: the island is inset by the
// band the walls consume (outer wall + inner walls) plus the configured margin, making that
// value the real clearance between the expanded top and the walls (avoiding a hull line). The
// original top is unioned back in, so where it already sits within that band it is kept as-is.
// Never claims a bottom surface.
const double top_expansion = layerm->region().config().top_surface_expansion.value;
if (top_expansion > 0. && ! top.empty()) {
const double d = scale_(top_expansion);
const auto jt = Clipper2Lib::JoinType::Miter;
const ExPolygons T = union_ex(to_expolygons(top));
const int wall_loops = layerm->region().config().wall_loops.value;
// ORCA: Grow the top surfaces by top_surface_expansion, so the top solid infill also covers the
// material left by features rising from the middle of a top surface (filling the holes and
// joining the tops, so the features rest on solid infill). Each connected island is grown and
// clipped separately: growing one island's top across a gap into another - which may have no top
// surface at all, leaving a partially filled layer - is never allowed. The original top is
// unioned back in and bottom surfaces are never claimed, so this can only add area.
const PrintRegionConfig &region_config = layerm->region().config();
const double top_expansion = region_config.top_surface_expansion.value;
// Nothing to expand without a top fill: a 0% top surface density leaves the top layer with
// walls only, and zero top shell layers retypes it as internal in prepare_fill_surfaces().
if (top_expansion > 0. && region_config.top_shell_layers.value > 0 &&
region_config.top_surface_density.value > 0. && ! top.empty()) {
const double d = scale_(top_expansion);
const ExPolygons T = union_ex(to_expolygons(top));
// Walls are laid out on spacing, not width; and only_one_wall_top leaves a single wall over
// a top surface, which is exactly the situation handled here.
const int wall_loops = region_config.only_one_wall_top.value ? std::min(region_config.wall_loops.value, 1)
: region_config.wall_loops.value;
const double wall_band = wall_loops <= 0 ? 0. :
double(layerm->flow(frExternalPerimeter).scaled_width()) +
double(layerm->flow(frPerimeter).scaled_width()) * double(wall_loops - 1);
const double margin = scale_(layerm->region().config().top_surface_expansion_margin.value);
double(layerm->flow(frPerimeter).scaled_spacing()) * double(wall_loops - 1);
const double margin = scale_(region_config.top_surface_expansion_margin.value);
// minimum real top to act on: ignore anything thinner than ~2 top-infill lines
const float min_top = float(layerm->flow(frTopSolidInfill).scaled_width());
const auto direction = layerm->region().config().top_surface_expansion_direction.value;
const auto direction = region_config.top_surface_expansion_direction.value;
ExPolygons grown;
for (const ExPolygon &island : union_ex(layerm_slices_surfaces)) {
// The top infill only exists inside the perimeters, so seed and measure from the infill
// region (the island minus the wall band), not the raw slice. A section whose only
// exposed top lies in the wall band - i.e. a layer where the top is just the walls
// themselves - has no infill here and is skipped, instead of being flooded inward by
// the expansion. Thin slivers inside the infill region are dropped by the opening too.
// region (the island minus the wall band), not the raw slice: a section whose exposed top
// is just the walls themselves is then skipped instead of being flooded inward. Clip the
// layer's tops to the island first, to keep the boolean ops proportional to the island.
const ExPolygons infill_region = wall_band > 0. ? offset_ex(island, -float(wall_band)) : ExPolygons{ island };
const ExPolygons island_top = intersection_ex(T, infill_region);
const ExPolygons island_top = intersection_ex(
ClipperUtils::clip_clipper_polygons_with_subject_bbox(T, get_extents(island).inflated(SCALED_EPSILON)),
infill_region);
if (opening_ex(island_top, min_top).empty())
continue; // no real top infill in this section - never expand into it
// grow by d, then keep only the part allowed by the configured direction: inward fills
// the holes/gaps left by features (clip the growth back to the top's own filled outline,
// which leaves the outer edge fixed), outward grows the outer edge toward the walls (drop
// the growth that fell into the original holes), and inward+outward keeps both.
ExPolygons expanded = offset_ex_2(island_top, d, jt);
// Grow, then keep only what the configured direction allows, using the top's own filled
// outline (same outer edge, holes closed) to tell the two apart.
ExPolygons expanded = offset_ex_2(island_top, d, Clipper2Lib::JoinType::Miter);
if (direction != TopSurfaceExpansionDirection::InwardAndOutward) {
ExPolygons outline; // the top with its holes filled (same outer edge)
ExPolygons outline;
outline.reserve(island_top.size());
for (const ExPolygon &ex : island_top)
outline.emplace_back(ex.contour);
+6 -3
View File
@@ -91,12 +91,15 @@ public:
//
void toggle_top_layer_only_view_range();
//
// Dim previous layers (ORCA, ported from preFlight)
// Whether the layers below the current top layer are rendered darkened while
// scrubbing below the full print, so only the current layer is shown at full brightness.
// Dim previous layers (ORCA)
// Whether the layers the layer slider is not scrubbed to are rendered darkened while
// showing less than the full print, so only the inspected layer(s) are at full brightness.
// How bright those darkened layers are rendered, 1.0 = unchanged, 0.0 = black.
//
bool is_dim_previous_layers() const;
void set_dim_previous_layers(bool value);
float get_dim_previous_layers_brightness() const;
void set_dim_previous_layers_brightness(float value);
//
// Returns true if the given option is visible.
//
+4 -2
View File
@@ -19,9 +19,11 @@ struct Settings
EViewType view_type{ EViewType::FeatureType };
ETimeMode time_mode{ ETimeMode::Normal };
bool top_layer_only_view_range{ false };
// ORCA: when enabled, all layers below the current top layer are rendered
// darkened (keeping their color) while scrubbing below the full print (ported from preFlight)
// ORCA: when enabled, every layer the layer slider is not scrubbed to is rendered
// darkened (keeping its color) while showing less than the full print
bool dim_previous_layers{ false };
// ORCA: how bright those darkened layers are rendered, 1.0 = unchanged, 0.0 = black
float dim_previous_layers_brightness{ 0.4f };
bool spiral_vase_mode{ false };
//
// Required update flags
+10
View File
@@ -82,6 +82,16 @@ void Viewer::set_dim_previous_layers(bool value)
m_impl->set_dim_previous_layers(value);
}
float Viewer::get_dim_previous_layers_brightness() const
{
return m_impl->get_dim_previous_layers_brightness();
}
void Viewer::set_dim_previous_layers_brightness(float value)
{
m_impl->set_dim_previous_layers_brightness(value);
}
bool Viewer::is_option_visible(EOptionType type) const
{
return m_impl->is_option_visible(type);
+38 -23
View File
@@ -1223,16 +1223,12 @@ static float encode_color(const Color& color) {
return static_cast<float>(i_color);
}
// ORCA: how much the layers below the current top layer are darkened when
// Settings::dim_previous_layers is enabled (ported from preFlight). 0.0 = no change, 1.0 = black.
static constexpr float PREVIOUS_LAYER_DARKEN_FACTOR = 0.60f;
// ORCA: returns the encoded color scaled towards black by 'factor', preserving its hue
static float encode_color_darkened(const Color& color, float factor) {
const float keep = 1.0f - factor;
const int r = static_cast<int>(color[0] * keep);
const int g = static_cast<int>(color[1] * keep);
const int b = static_cast<int>(color[2] * keep);
// ORCA: returns the encoded color scaled towards black by 'brightness', preserving its hue.
// 1.0 = no change, 0.0 = black.
static float encode_color_dimmed(const Color& color, float brightness) {
const int r = static_cast<int>(color[0] * brightness);
const int g = static_cast<int>(color[1] * brightness);
const int b = static_cast<int>(color[2] * brightness);
const int i_color = r << 16 | g << 8 | b;
return static_cast<float>(i_color);
}
@@ -1248,15 +1244,20 @@ void ViewerImpl::update_colors_texture()
const size_t top_layer_id = m_settings.top_layer_only_view_range ? m_layers.get_view_range()[1] : 0;
const bool color_top_layer_only = m_view_range.get_full()[1] != m_view_range.get_visible()[1];
// ORCA: when dim_previous_layers is enabled, darken every layer below the current top layer
// (keeping its color) whenever we are not rendering the whole print, so that only the layer
// being scrubbed to is shown at full brightness (ported from preFlight). This shares
// top_layer_id with the greying path, so it only applies while in top-layer-only mode - that
// way the moves slider still animates normally across all layers when that mode is disabled.
const bool dim_previous_layers = m_settings.dim_previous_layers && !m_layers.empty();
const bool full_render = (m_layers.get_view_range()[0] == 0) &&
(m_layers.get_view_range()[1] >= static_cast<uint32_t>(m_layers.count()) - 1) &&
(m_view_range.get_visible()[1] == m_view_range.get_full()[1]);
// ORCA: when dim_previous_layers is enabled, darken every layer (keeping its color) except the
// one(s) the layer slider is being scrubbed to, so that only those are shown at full brightness.
// A slider thumb marks a layer as inspected only once it is moved away from
// its end of the print: the upper one while it is below the last layer (or while the moves
// slider is not at the end of the layer), the lower one while it is above the first layer, so
// trimming the print from the bottom lights up the lowest visible layer and using the slider as
// a range lights up both ends. When neither thumb is moved the whole print is rendered normally.
// Gated on top-layer-only mode, which the greying path below also keys off of, so that the moves
// slider still animates normally across all layers when that mode is disabled.
const Interval& layers_range = m_layers.get_view_range();
const bool inspecting_top_layer = layers_range[1] + 1 < m_layers.count() || color_top_layer_only;
const bool inspecting_bottom_layer = layers_range[0] > 0;
const bool dim_previous_layers = m_settings.dim_previous_layers && m_settings.top_layer_only_view_range &&
!m_layers.empty() && (inspecting_top_layer || inspecting_bottom_layer);
// Based on current settings and slider position, we might want to render some
// vertices as dark grey (or darkened, see above). Use either that or the normal color (from the cache).
@@ -1265,9 +1266,13 @@ void ViewerImpl::update_colors_texture()
for (size_t i=0; i<m_vertices.size(); ++i) {
const PathVertex& v = m_vertices[i];
const bool keep_spiral_seam = m_settings.spiral_vase_mode && i == m_view_range.get_enabled()[0];
if (dim_previous_layers && !full_render && v.layer_id < top_layer_id && !keep_spiral_seam)
colors[i] = encode_color_darkened(get_vertex_color(v), PREVIOUS_LAYER_DARKEN_FACTOR);
else if (color_top_layer_only && v.layer_id < top_layer_id && !keep_spiral_seam)
// ORCA: layers kept at full brightness by the dimming above are excluded from the greying below too
const bool inspected_layer = dim_previous_layers &&
((inspecting_top_layer && v.layer_id == layers_range[1]) ||
(inspecting_bottom_layer && v.layer_id == layers_range[0]));
if (dim_previous_layers && !inspected_layer && !keep_spiral_seam)
colors[i] = encode_color_dimmed(get_vertex_color(v), m_settings.dim_previous_layers_brightness);
else if (!inspected_layer && color_top_layer_only && v.layer_id < top_layer_id && !keep_spiral_seam)
colors[i] = encode_color(DUMMY_COLOR);
else
colors[i] = m_vertices_colors[i];
@@ -1379,7 +1384,7 @@ void ViewerImpl::toggle_top_layer_only_view_range()
update_colors_texture();
}
// ORCA: enable/disable darkening of the layers below the current top layer (ported from preFlight)
// ORCA: enable/disable darkening of the layers the layer slider is not scrubbed to
void ViewerImpl::set_dim_previous_layers(bool value)
{
if (m_settings.dim_previous_layers == value)
@@ -1390,6 +1395,16 @@ void ViewerImpl::set_dim_previous_layers(bool value)
m_settings.update_colors = true;
}
// ORCA: set how bright the darkened layers are rendered, 1.0 = unchanged, 0.0 = black
void ViewerImpl::set_dim_previous_layers_brightness(float value)
{
value = std::clamp(value, 0.0f, 1.0f);
if (m_settings.dim_previous_layers_brightness == value)
return;
m_settings.dim_previous_layers_brightness = value;
m_settings.update_colors = true;
}
std::vector<ETimeMode> ViewerImpl::get_time_modes() const
{
std::vector<ETimeMode> ret;
+6 -1
View File
@@ -85,9 +85,14 @@ public:
bool is_top_layer_only_view_range() const { return m_settings.top_layer_only_view_range; }
void toggle_top_layer_only_view_range();
// ORCA: darken layers below the current top layer while scrubbing (ported from preFlight)
// ORCA: darken every layer the layer slider is not scrubbed to, so that only the inspected
// one(s) - the top thumb's layer, the bottom thumb's layer, or both - stay at full
// brightness. dim_previous_layers_brightness sets how dark the rest go, 1.0 = unchanged,
// 0.0 = black
bool is_dim_previous_layers() const { return m_settings.dim_previous_layers; }
void set_dim_previous_layers(bool value);
float get_dim_previous_layers_brightness() const { return m_settings.dim_previous_layers_brightness; }
void set_dim_previous_layers_brightness(float value);
bool is_spiral_vase_mode() const { return m_settings.spiral_vase_mode; }
+64 -14
View File
@@ -70,6 +70,9 @@ float FullTransparentModdifiedToFixAlpha = 0.3f;
// value like 0.18f could not because in C++ (int)(0.18f * 255) == 45 however in OpenGL it renders this as 46
// which breaks the `SelectMachineDialog::record_edge_pixels_data()` function!
float FULL_BLACK_THRESHOLD = 0.2f;
// Keep depth_tex away from texture unit 0 to avoid sampler-type aliasing with
// shadow/environment samplers when realistic view is disabled.
static constexpr int OUTLINE_DEPTH_TEX_UNIT = 5;
Slic3r::ColorRGBA adjust_color_for_rendering(const Slic3r::ColorRGBA &colors)
{
@@ -518,6 +521,37 @@ void GLVolume::render_with_outline(const GUI::Size& cnv_size)
glsafe(::glStencilMask(0xFF));
glsafe(::glDisable(GL_STENCIL_TEST));
// render the outline using depth buffer and discard the pixels that are not on the outline
// The silhouette is resolved per sample in the shader (see DetectSilho in gouraud.fs/phong.fs).
// That needs the GL 3.2 entry points and a shader that declares depth_tex as sampler2DMS, which
// only the 140 ones do and only under GL_ARB_texture_multisample - so ask the compiled program
// rather than the GL version, or a sampler2D ends up bound to a multisample texture.
// Only the Arb branch below allocates a multisample texture, so keep the target consistent with it.
const bool use_msaa_outline = framebuffers_type == GUI::OpenGLManager::EFramebufferType::Arb &&
GUI::wxGetApp().is_gl_version_greater_or_equal_to(3, 2) &&
shader->get_uniform_location("msaa_samples") >= 0;
const GLenum depth_tex_target = use_msaa_outline ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D;
// Keep the depth texture off image unit 0. The object shaders leave shadow_map (and
// environment_tex) at the default sampler value 0 whenever the shadow pass is skipped - which is
// the case with realistic view off - and GL forbids two sampler types referring to the same image
// unit. A sampler2DMS on unit 0 then makes every draw fail with INVALID_OPERATION on drivers that
// enforce it (Mesa), i.e. the model disappears entirely. Unit 5 is unused (shadow_map takes 4).
const int depth_tex_unit = OUTLINE_DEPTH_TEX_UNIT;
int aa_samples = 1;
if (use_msaa_outline) {
if (const AppConfig* app_config = GUI::wxGetApp().app_config; app_config != nullptr) {
const std::string value = app_config->get(SETTING_OPENGL_AA_SAMPLES);
if (value == "2" || value == "4" || value == "8" || value == "16")
aa_samples = ::atoi(value.c_str());
}
// Never request more samples than the driver supports for depth textures (a 1-sample texture
// is used when MSAA is disabled, keeping a single code path for the sampler2DMS shader).
GLint max_samples = 1;
glsafe(::glGetIntegerv(GL_MAX_DEPTH_TEXTURE_SAMPLES, &max_samples));
if (aa_samples > max_samples)
aa_samples = max_samples < 1 ? 1 : max_samples;
if (aa_samples < 1)
aa_samples = 1;
}
// 1st. render pass, render the model into a separate render target that has only depth buffer
GLuint depth_fbo = 0;
GLuint depth_tex = 0;
@@ -525,21 +559,26 @@ void GLVolume::render_with_outline(const GUI::Size& cnv_size)
glsafe(::glGenFramebuffers(1, &depth_fbo));
glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, depth_fbo));
glActiveTexture(GL_TEXTURE0);
glsafe(::glActiveTexture(GL_TEXTURE0 + depth_tex_unit));
glsafe(::glGenTextures(1, &depth_tex));
glsafe(::glBindTexture(GL_TEXTURE_2D, depth_tex));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, cnv_size.get_width(), cnv_size.get_height(), 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr));
glsafe(::glBindTexture(depth_tex_target, depth_tex));
if (use_msaa_outline) {
// Multisample textures do not take filter/wrap parameters.
glsafe(::glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, aa_samples, GL_DEPTH_COMPONENT32F, cnv_size.get_width(), cnv_size.get_height(), GL_TRUE));
} else {
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, cnv_size.get_width(), cnv_size.get_height(), 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr));
}
glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_tex, 0));
glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depth_tex_target, depth_tex, 0));
} else {
glsafe(::glGenFramebuffersEXT(1, &depth_fbo));
glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, depth_fbo));
glActiveTexture(GL_TEXTURE0);
glsafe(::glActiveTexture(GL_TEXTURE0 + depth_tex_unit));
glsafe(::glGenTextures(1, &depth_tex));
glsafe(::glBindTexture(GL_TEXTURE_2D, depth_tex));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
@@ -550,12 +589,15 @@ void GLVolume::render_with_outline(const GUI::Size& cnv_size)
glsafe(::glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, depth_tex, 0));
}
// Unbind before drawing: the texture is this framebuffer's depth attachment, so leaving it bound
// to a sampled unit would be a feedback loop.
glsafe(::glBindTexture(depth_tex_target, 0));
glsafe(::glActiveTexture(GL_TEXTURE0));
glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
if (tverts_range == std::make_pair<size_t, size_t>(0, -1))
model.render(shader);
else
model.render(this->tverts_range, shader);
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
// 2nd. render pass, just a normal render with the depth buffer passed as a texture
if (framebuffers_type == GUI::OpenGLManager::EFramebufferType::Arb) {
@@ -565,13 +607,17 @@ void GLVolume::render_with_outline(const GUI::Size& cnv_size)
}
shader->set_uniform("is_outline", true);
shader->set_uniform("screen_size", Vec2f{cnv_size.get_width(), cnv_size.get_height()});
glActiveTexture(GL_TEXTURE0);
glsafe(::glBindTexture(GL_TEXTURE_2D, depth_tex));
shader->set_uniform("depth_tex", 0);
shader->set_uniform("msaa_samples", aa_samples);
glsafe(::glActiveTexture(GL_TEXTURE0 + depth_tex_unit));
glsafe(::glBindTexture(depth_tex_target, depth_tex));
glsafe(::glActiveTexture(GL_TEXTURE0));
shader->set_uniform("depth_tex", depth_tex_unit);
simple_render(shader, model_objects, colors);
// Some clean up to do
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
glsafe(::glActiveTexture(GL_TEXTURE0 + depth_tex_unit));
glsafe(::glBindTexture(depth_tex_target, 0));
glsafe(::glActiveTexture(GL_TEXTURE0));
shader->set_uniform("is_outline", false);
if (framebuffers_type == GUI::OpenGLManager::EFramebufferType::Arb) {
glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, 0));
@@ -1075,6 +1121,10 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type,
const float support_normal_z = get_selection_support_normal_z();
// Prime depth_tex on every frame so non-outline draws do not keep the
// default sampler unit 0, which can conflict with other sampler types.
shader->set_uniform("depth_tex", OUTLINE_DEPTH_TEX_UNIT);
for (GLVolumeWithIdAndZ& volume : to_render) {
#if ENABLE_MODIFIERS_ALWAYS_TRANSPARENT
if (type == ERenderType::Transparent) {
+10 -5
View File
@@ -703,12 +703,13 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in
toggle_line("spiral_mode_max_xy_smoothing", has_spiral_vase && config->opt_bool("spiral_mode_smooth"));
toggle_line("spiral_starting_flow_ratio", has_spiral_vase);
toggle_line("spiral_finishing_flow_ratio", has_spiral_vase);
bool has_top_shell = config->opt_int("top_shell_layers") > 0 || (has_spiral_vase && config->opt_int("bottom_shell_layers") > 1);
bool has_top_shell_layers = config->opt_int("top_shell_layers") > 0 || (has_spiral_vase && config->opt_int("bottom_shell_layers") > 1);
bool has_top_shell = has_top_shell_layers && config->option<ConfigOptionPercent>("top_surface_density")->value > 0;
bool has_bottom_shell = config->opt_int("bottom_shell_layers") > 0;
bool has_solid_infill = has_top_shell || has_bottom_shell;
bool has_solid_infill = has_top_shell_layers || has_bottom_shell;
toggle_field("top_surface_pattern", has_top_shell);
toggle_field("bottom_surface_pattern", has_bottom_shell);
toggle_field("top_surface_density", has_top_shell);
toggle_field("top_surface_density", has_top_shell_layers);
toggle_field("bottom_surface_density", has_bottom_shell);
toggle_field("top_layer_direction", has_top_shell);
toggle_field("bottom_layer_direction", has_bottom_shell);
@@ -751,7 +752,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in
for (auto el : { "sparse_infill_speed", "bridge_speed", "internal_bridge_speed"})
toggle_field(el, have_infill || has_solid_infill, variant_index);
toggle_field("top_shell_thickness", ! has_spiral_vase && has_top_shell);
toggle_field("top_shell_thickness", ! has_spiral_vase && has_top_shell_layers);
toggle_field("bottom_shell_thickness", ! has_spiral_vase && has_bottom_shell);
// Gap fill is newly allowed in between perimeter lines even for empty infill (see GH #1476).
@@ -1008,7 +1009,11 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in
toggle_line("make_overhang_printable_angle", have_make_overhang_printable);
toggle_line("make_overhang_printable_hole_size", have_make_overhang_printable);
toggle_line("min_width_top_surface", config->opt_bool("only_one_wall_top") || ((config->opt_float("min_length_factor") > 0.5f) && have_arachne)); // 0.5 is default value
// Orca: the one-wall options act on top/bottom surfaces, which exist only with a shell. An unfilled surface
// (0% surface density) is still a surface, so these are gated on the layer counts alone.
toggle_line("only_one_wall_first_layer", has_bottom_shell);
toggle_line("only_one_wall_top", has_top_shell_layers);
toggle_line("min_width_top_surface", (has_top_shell_layers && config->opt_bool("only_one_wall_top")) || ((config->opt_float("min_length_factor") > 0.5f) && have_arachne)); // 0.5 is default value
for (auto el : { "hole_to_polyhole_threshold", "hole_to_polyhole_twisted", "hole_to_polyhole_max_edges" })
toggle_line(el, config->opt_bool("hole_to_polyhole"));
+5 -21
View File
@@ -1334,27 +1334,7 @@ void SpinCtrl::BUILD() {
if (!parsed || value < INT_MIN || value > INT_MAX)
tmp_value = UNDEF_VALUE;
else {
tmp_value = std::min(std::max((int)value, temp->GetMin()), temp->GetMax());
#ifdef __WXOSX__
#ifdef UNDEFINED__WXOSX__ // BBS
// Forcibly set the input value for SpinControl, since the value
// inserted from the keyboard or clipboard is not updated under OSX
SpinInput* spin = static_cast<SpinInput*>(window);
spin->SetValue(tmp_value);
// But in SetValue() is executed m_text_ctrl->SelectAll(), so
// discard this selection and set insertion point to the end of string
// temp->GetText()->SetInsertionPointEnd();
#endif
#else
// update value for the control only if it was changed in respect to the Min/max values
if (tmp_value != (int)value) {
temp->SetValue(tmp_value);
// But after SetValue() cursor ison the first position
// so put it to the end of string
// int pos = std::to_string(tmp_value).length();
// temp->SetSelection(pos, pos);
}
#endif
tmp_value = (int)value;
}
}), temp->GetTextCtrl()->GetId());
@@ -1375,6 +1355,10 @@ void SpinCtrl::propagate_value()
on_kill_focus();
} else {
auto ctrl = dynamic_cast<SpinInput *>(window);
tmp_value = std::min(std::max(tmp_value, ctrl->GetMin()), ctrl->GetMax());
if (ctrl->GetValue() != tmp_value)
ctrl->SetValue(tmp_value); // Clamp now when the user is done typing (kill focus / Enter / spin arrows)
if (m_value.empty()
? !ctrl->GetTextCtrl()->GetLabel().IsEmpty()
: ctrl->GetValue() != boost::any_cast<int>(m_value))
+2 -1
View File
@@ -1134,8 +1134,9 @@ void GCodeViewer::load_as_gcode(const GCodeProcessorResult& gcode_result, const
if (current_top_layer_only != required_top_layer_only)
m_viewer.toggle_top_layer_only_view_range();
// ORCA: darken layers below the current one while scrubbing the preview (ported from preFlight)
// ORCA: darken the layers the preview layer slider is not scrubbed to
m_viewer.set_dim_previous_layers(get_app_config()->get_bool("preview_dim_previous_layers"));
m_viewer.set_dim_previous_layers_brightness(0.01f * std::stoi(get_app_config()->get("preview_dim_previous_layers_brightness")));
// avoid processing if called with the same gcode_result
if (m_last_result_id == gcode_result.id && wxGetApp().is_editor()) {
+4 -1
View File
@@ -333,9 +333,12 @@ public:
libvgcode::EViewType get_view_type() const { return m_viewer.get_view_type(); }
// ORCA: darken layers below the current top layer while scrubbing the preview (ported from preFlight)
// ORCA: darken the layers not scrubbed to while using the preview layer slider
void set_dim_previous_layers(bool value) { m_viewer.set_dim_previous_layers(value); }
bool is_dim_previous_layers() const { return m_viewer.is_dim_previous_layers(); }
// ORCA: brightness of those darkened layers, 1.0 = unchanged, 0.0 = black
void set_dim_previous_layers_brightness(float value) { m_viewer.set_dim_previous_layers_brightness(value); }
float get_dim_previous_layers_brightness() const { return m_viewer.get_dim_previous_layers_brightness(); }
void set_layers_z_range(const std::array<unsigned int, 2>& layers_z_range);
+53 -1
View File
@@ -548,7 +548,7 @@ void RemoveButtonBorder(wxWindow* win)
GtkCssProvider* provider = gtk_css_provider_new();
const char* css =
"button {"
"button, button:hover, button:active, button:focus {"
" border: none;"
" outline: none;"
" box-shadow: none;"
@@ -589,6 +589,58 @@ void RemoveButtonBorder(wxWindow* win)
);
#endif
}
void RemoveInputBorder(wxWindow* win)
{
GtkWidget* widget = win->GetHandle();
if (!widget) return;
#if GTK_CHECK_VERSION(3, 0, 0)
// GTK3+: use CSS provider
GtkCssProvider* provider = gtk_css_provider_new();
// Target 'entry' and its inner subnodes (like text selection areas)
const char* css =
"entry, entry text, entry undershoot {"
" border: none;"
" outline: none;"
" box-shadow: none;"
" padding: 0px;"
" margin: 0px;"
" min-height: 0px;"
" min-width: 0px;"
" background: none;"
"}";
#if GTK_CHECK_VERSION(4, 0, 0)
// GTK4
gtk_css_provider_load_from_data(provider, css, -1);
#else
// GTK3
gtk_css_provider_load_from_data(provider, css, -1, nullptr);
#endif
GtkStyleContext* ctx = gtk_widget_get_style_context(widget);
gtk_style_context_add_provider(
ctx,
GTK_STYLE_PROVIDER(provider),
GTK_STYLE_PROVIDER_PRIORITY_USER
);
g_object_unref(provider);
#else
// GTK2: Target the x/y thickness of the entry widget
gtk_rc_parse_string(
"style \"no-padding-entry\" {"
" xthickness = 0"
" ythickness = 0"
" GtkEntry::inner-border = { 0, 0, 0, 0 }"
" GtkEntry::focus-line-width = 0"
"}"
"class \"GtkEntry\" style \"no-padding-entry\""
);
#endif
}
#endif // __WXGTK__
#ifdef __linux__
+3 -2
View File
@@ -472,8 +472,9 @@ void dataview_remove_insets(wxDataViewCtrl* dv);
void staticbox_remove_margin(wxStaticBox* sb);
#endif
#ifdef __WXGTK3__
void RemoveButtonBorder(wxWindow* win);
#ifdef __WXGTK__
void RemoveButtonBorder(wxWindow* win); // for wxButton/wxBitmapToggleButton based controls (SwitchButton, CheckBox)
void RemoveInputBorder(wxWindow* win); // for TextCtrl based controls (TextInput, ComboBox, SpinInput..)
#endif
#if defined(__WXOSX__) || defined(__linux__)
+1 -1
View File
@@ -548,7 +548,7 @@ void ArrangeJob::process(Ctl &ctl)
params.stopcondition = [&ctl]() { return ctl.was_canceled(); };
params.progressind = [this, &ctl](unsigned num_finished, std::string str = "") {
ctl.update_status(num_finished * 100 / status_range(), _u8L("Arranging") + str);
ctl.update_status(num_finished * 100 / status_range(), _u8L("Arranging ") + str);
};
{
+1
View File
@@ -2298,6 +2298,7 @@ arrangement::ArrangePolygon PartPlate::estimate_wipe_tower_polygon(const Dynamic
bool enable_wrapping = (wrapping_opt != nullptr) && wrapping_opt->value;
wt_size = estimate_wipe_tower_size(config, w, v, extruder_count, plate_extruder_size, use_global_objects, enable_wrapping);
int plate_width=m_width, plate_depth=m_depth;
w = wt_size(0); // effective width; differs from prime_tower_width when the rib wall squares the tower
float depth = wt_size(1);
float margin = WIPE_TOWER_MARGIN + tower_brim_width, wp_brim_width = 0.f;
const ConfigOption* wipe_tower_brim_width_opt = config.option("prime_tower_brim_width");
+2
View File
@@ -11,6 +11,7 @@ namespace Slic3r
// IMPORTANT: ordinal order is the Plugins dialog Source sort priority.
Mine,
Subscribed,
Orphaned,
Local
};
@@ -20,6 +21,7 @@ namespace Slic3r
{
case PluginSource::Mine: return "mine";
case PluginSource::Subscribed: return "subscribed";
case PluginSource::Orphaned: return "orphaned";
case PluginSource::Local: return "local";
}
+44 -9
View File
@@ -103,6 +103,7 @@ struct PluginDialogItem
bool loading = false;
bool is_cloud_plugin = false;
bool orphaned = false;
bool has_local_package = false;
bool unauthorized = false;
bool has_script_capability = false;
@@ -246,6 +247,7 @@ nlohmann::json build_plugin_payload_item(const PluginDialogItem& dialog_item)
payload_item["sharing_token"] = dialog_item.sharing_token;
payload_item["thumbnail_url"] = dialog_item.thumbnail_url;
payload_item["installed"] = dialog_item.has_local_package;
payload_item["orphaned"] = dialog_item.orphaned;
payload_item["installed_version"] = dialog_item.installed_version;
payload_item["latest_version"] = dialog_item.latest_version;
return payload_item;
@@ -268,7 +270,8 @@ PluginSource derive_plugin_source(const PluginDescriptor& descriptor)
const bool is_cloud = descriptor.is_cloud_plugin();
const bool is_mine = is_cloud && has_cloud_meta && descriptor.cloud->is_mine;
// Source is ownership/locality only; issue states never replace this badge.
if (is_cloud && has_cloud_meta && descriptor.cloud->orphaned)
return PluginSource::Orphaned;
if (is_mine)
return PluginSource::Mine;
if (is_cloud)
@@ -281,11 +284,12 @@ PluginAvailableActions evaluate_action_policy(const PluginDialogItem& item)
PluginAvailableActions available_actions;
const bool is_loading = item.status == PluginStatus::Loading;
const bool is_cloud = item.is_cloud_plugin;
const bool is_orphaned = item.orphaned;
const bool is_mine = item.source == PluginSource::Mine;
const bool has_local = item.has_local_package;
const bool authorized_for_install = !item.unauthorized;
available_actions.toggle_installs_cloud_plugin = is_cloud && !has_local && authorized_for_install;
available_actions.toggle_installs_cloud_plugin = is_cloud && !is_orphaned && !has_local && authorized_for_install;
available_actions.can_toggle = !is_loading && (has_local || available_actions.toggle_installs_cloud_plugin);
auto add_action = [&available_actions](const char* id, const char* label, bool enabled = true, bool danger = false) {
@@ -294,7 +298,7 @@ PluginAvailableActions evaluate_action_policy(const PluginDialogItem& item)
// Owned cloud plugins fall through to the local delete: it removes the installed package only.
// Deleting a plugin from the cloud is a plugin hub operation and is never offered here.
if (is_cloud && !is_mine) {
if (is_cloud && !is_orphaned && !is_mine) {
add_action("unsubscribe_plugin", "Unsubscribe", true, true);
} else if (has_local) {
add_action("delete_plugin", "Delete", true, true);
@@ -302,11 +306,13 @@ PluginAvailableActions evaluate_action_policy(const PluginDialogItem& item)
add_action("open_folder", "Show in folder", has_local);
if (is_cloud) {
add_action("reinstall_plugin", "Reinstall");
} else {
add_action("reload_plugin", "Reload");
add_action("clear_cache_reload_plugin", "Delete cache and reload");
if (!is_orphaned) {
if (is_cloud) {
add_action("reinstall_plugin", "Reinstall");
} else {
add_action("reload_plugin", "Reload");
add_action("clear_cache_reload_plugin", "Delete cache and reload");
}
}
return available_actions;
@@ -360,6 +366,7 @@ PluginDialogItem build_plugin_dialog_item(const PluginDescriptor& descriptor)
item.error_text = descriptor.normalized_error();
item.has_error = descriptor.has_error();
item.is_cloud_plugin = descriptor.is_cloud_plugin();
item.orphaned = descriptor.cloud.has_value() && descriptor.cloud->orphaned;
item.has_local_package = descriptor.has_local_package();
item.unauthorized = descriptor.is_unauthorized();
item.is_loaded = manager.is_plugin_loaded(descriptor.plugin_key);
@@ -592,7 +599,35 @@ bool PluginsDialog::get_descriptor(const std::string& plugin_key, PluginDescript
void PluginsDialog::refresh_plugin_metadata_async(const wxString& title, const wxString& message, bool fetch_cloud)
{
run_with_dialog([fetch_cloud]() { refresh_plugin_metadata_blocking(fetch_cloud); }, [this]() { send_plugins(); }, title, message);
run_with_dialog([fetch_cloud]() { refresh_plugin_metadata_blocking(fetch_cloud); }, [this]() {
prompt_for_missing_plugins();
send_plugins();
}, title, message);
}
void PluginsDialog::prompt_for_missing_plugins()
{
PluginManager& manager = PluginManager::instance();
const std::vector<PluginDescriptor> missing = manager.get_missing_plugin_descriptors();
if (missing.empty())
return;
wxString names;
std::vector<std::string> keys;
keys.reserve(missing.size());
for (const PluginDescriptor& plugin : missing) {
keys.push_back(plugin.plugin_key);
names += "\n- ";
names += plugin_display_name(plugin.plugin_key);
}
const int result = wxMessageBox(
wxString::Format(_L("The following installed plugins were not found on disk:\n%s\n\nRemove them from OrcaSlicer?"), names),
_L("Missing Plugins"), wxYES_NO | wxNO_DEFAULT | wxICON_WARNING, this);
restore_z_order();
if (result == wxYES)
manager.remove_missing_plugins(keys);
}
void PluginsDialog::refresh_plugins()
+1
View File
@@ -68,6 +68,7 @@ private:
bool get_descriptor(const std::string& plugin_key, Slic3r::PluginDescriptor& descriptor) const;
void refresh_plugin_metadata_async(const wxString& title, const wxString& message, bool fetch_cloud);
void prompt_for_missing_plugins();
void refresh_plugins();
void toggle_plugin(const std::string& plugin_key, bool enabled);
void toggle_plugin_capability(const std::string& plugin_key, PluginCapabilityType type, const std::string& capability_name, bool enabled);
+31 -1
View File
@@ -700,6 +700,12 @@ wxBoxSizer *PreferencesDialog::create_item_spinctrl(wxString title, wxString tit
auto input = new SpinInput(m_parent, wxEmptyString, side_label, wxDefaultPosition, DESIGN_INPUT_SIZE, wxSP_ARROW_KEYS, min, max, stoi(app_config->get(param)));
input->SetToolTip(tip);
// ORCA: this one is only meaningful while the dimming it controls is enabled
if (param == "preview_dim_previous_layers_brightness") {
m_dim_previous_layers_brightness_input = input;
input->Enable(app_config->get_bool("preview_dim_previous_layers"));
}
m_sizer->Add(input, 0, wxALIGN_CENTER_VERTICAL);
if(!title2.empty()){
@@ -1050,8 +1056,10 @@ wxBoxSizer *PreferencesDialog::create_item_checkbox(wxString title, wxString too
wxGetApp().mainframe->m_webview->SendCloudProvidersInfo();
}
}
// ORCA: apply the preview dimming change immediately to the currently loaded preview (ported from preFlight)
// ORCA: apply the preview dimming change immediately to the currently loaded preview
else if (param == "preview_dim_previous_layers") {
if (m_dim_previous_layers_brightness_input)
m_dim_previous_layers_brightness_input->Enable(app_config->get_bool(param));
if (Plater* plater = wxGetApp().plater()) {
if (GLCanvas3D* canvas = plater->get_preview_canvas3D()) {
canvas->get_gcode_viewer().set_dim_previous_layers(app_config->get_bool(param));
@@ -1914,6 +1922,28 @@ void PreferencesDialog::create_items()
);
g_sizer->Add(item_dim_previous_layers);
auto item_dim_previous_layers_brightness = create_item_spinctrl(
_L("Dimmed layer brightness"),
"",
_L("%"),
_L("How brightly the dimmed layers are rendered when \"Dim lower layers\" is enabled.\n"
"99% is barely darkened, 0% renders them black. Capped at 99% because 100% would be the same as disabling the option."),
"preview_dim_previous_layers_brightness",
0,
99,
// ORCA: apply the new brightness immediately to the currently loaded preview
[](int value) {
if (Plater* plater = wxGetApp().plater()) {
if (GLCanvas3D* canvas = plater->get_preview_canvas3D()) {
canvas->get_gcode_viewer().set_dim_previous_layers_brightness(0.01f * value);
canvas->set_as_dirty();
canvas->request_extra_frame();
}
}
}
);
g_sizer->Add(item_dim_previous_layers_brightness);
g_sizer->AddSpacer(FromDIP(10));
sizer_page->Add(g_sizer, 0, wxEXPAND);
+2
View File
@@ -13,6 +13,7 @@
#include "Widgets/ComboBox.hpp"
#include "Widgets/CheckBox.hpp"
#include "Widgets/TextInput.hpp"
#include "Widgets/SpinInput.hpp"
#include "Widgets/TabCtrl.hpp"
#include "slic3r/Utils/bambu_networking.hpp"
@@ -71,6 +72,7 @@ public:
::CheckBox * m_sync_user_preset_checkbox = {nullptr};
::CheckBox * m_bambu_cloud_checkbox = {nullptr};
::TextInput *m_backup_interval_textinput = {nullptr};
::SpinInput *m_dim_previous_layers_brightness_input = {nullptr};
::ComboBox * m_network_version_combo = {nullptr};
std::vector<NetworkLibraryVersionInfo> m_available_versions;
+3
View File
@@ -865,6 +865,9 @@ PlaterPresetComboBox::PlaterPresetComboBox(wxWindow *parent, Preset::Type preset
clr_picker = new wxBitmapButton(parent, wxID_ANY, {}, wxDefaultPosition, wxSize(FromDIP(20), FromDIP(20)), wxBU_EXACTFIT | wxBU_AUTODRAW | wxBORDER_NONE);
clr_picker->SetBackgroundColour(StateColor::darkModeColorFor(*wxWHITE));
clr_picker->SetToolTip(_L("Click to select filament color"));
#ifdef __WXGTK__
RemoveButtonBorder(clr_picker);
#endif
clr_picker->Bind(wxEVT_BUTTON, [this](wxCommandEvent& e) {
// Check if it's an official filament
auto fila_type = Preset::remove_suffix_modified(GetValue().ToUTF8().data());
+2 -2
View File
@@ -2,7 +2,7 @@
#include "../wxExtensions.hpp"
#ifdef __WXGTK3__
#ifdef __WXGTK__
#include "../GUI_Utils.hpp"
#endif
@@ -29,7 +29,7 @@ CheckBox::CheckBox(wxWindow *parent, int id)
Bind(wxEVT_LEAVE_WINDOW, &CheckBox::updateBitmap, this);
#endif
#ifdef __WXGTK3__
#ifdef __WXGTK__
Slic3r::GUI::RemoveButtonBorder(this);
#endif
+5
View File
@@ -2,6 +2,10 @@
#include "../wxExtensions.hpp"
#ifdef __WXGTK__
#include "../GUI_Utils.hpp"
#endif
namespace Slic3r {
namespace GUI {
RadioBox::RadioBox(wxWindow *parent)
@@ -15,6 +19,7 @@ RadioBox::RadioBox(wxWindow *parent)
// Bind(wxEVT_TOGGLEBUTTON, [this](auto& e) { update(); e.Skip(); });
update();
#ifdef __WXGTK__
Slic3r::GUI::RemoveButtonBorder(this);
wxSize bestSize = GetBestSize();
bestSize.IncTo(m_on.GetBmpSize());
SetSize(bestSize);
+9
View File
@@ -5,6 +5,10 @@
#include <wx/dcgraph.h>
#ifdef __WXGTK__
#include "../GUI_Utils.hpp"
#endif
BEGIN_EVENT_TABLE(SpinInput, StaticBox)
EVT_KEY_DOWN(SpinInput::keyPressed)
@@ -58,6 +62,11 @@ void SpinInput::Create(wxWindow *parent,
state_handler.attach({&label_color, &text_color});
state_handler.update_binds();
text_ctrl = new TextCtrl(this, wxID_ANY, text, {20, 4}, wxDefaultSize, style | wxBORDER_NONE | wxTE_PROCESS_ENTER, wxTextValidator(wxFILTER_DIGITS));
#ifdef __WXGTK__
Slic3r::GUI::RemoveInputBorder(text_ctrl);
#endif
text_ctrl->SetFont(Label::Body_14);
text_ctrl->SetBackgroundColour(background_color.colorForStates(state_handler.states()));
text_ctrl->SetForegroundColour(text_color.colorForStates(state_handler.states()));
+2 -2
View File
@@ -12,7 +12,7 @@
#include "libslic3r/MacUtils.hpp"
#endif
#ifdef __WXGTK3__
#ifdef __WXGTK__
#include "../GUI_Utils.hpp"
#endif
@@ -37,7 +37,7 @@ SwitchButton::SwitchButton(wxWindow* parent, wxWindowID id)
Bind(wxEVT_TOGGLEBUTTON, [this](auto& e) { update(); e.Skip(); });
SetFont(Label::Body_12);
#ifdef __WXGTK3__
#ifdef __WXGTK__
Slic3r::GUI::RemoveButtonBorder(this);
#endif
+9
View File
@@ -6,6 +6,10 @@
#include <wx/dcclient.h>
#include <wx/dcgraph.h>
#ifdef __WXGTK__
#include "../GUI_Utils.hpp"
#endif
BEGIN_EVENT_TABLE(TextInput, StaticBox)
EVT_PAINT(TextInput::paintEvent)
@@ -60,6 +64,11 @@ void TextInput::Create(wxWindow * parent,
state_handler.attach({&label_color, & text_color});
state_handler.update_binds();
text_ctrl = new TextCtrl(this, wxID_ANY, text, {4, 4}, wxDefaultSize, style | wxBORDER_NONE | wxTE_PROCESS_ENTER);
#ifdef __WXGTK__
Slic3r::GUI::RemoveInputBorder(text_ctrl);
#endif
text_ctrl->SetFont(Label::Body_14);
text_ctrl->SetInitialSize(text_ctrl->GetBestSize());
text_ctrl->SetBackgroundColour(background_color.colorForStates(state_handler.states()));
+4
View File
@@ -1022,6 +1022,10 @@ ScalableButton::ScalableButton( wxWindow * parent,
m_width = size.x * 10 / em;
m_height= size.y * 10 / em;
}
#ifdef __WXGTK__
Slic3r::GUI::RemoveButtonBorder(this);
#endif
}
+39 -57
View File
@@ -1,12 +1,14 @@
#include "PresetUpdater.hpp"
#include <algorithm>
#include <boost/filesystem/directory.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/nowide/fstream.hpp>
#include <functional>
#include <atomic>
#include <mutex>
#include <set>
#include <string>
#include <thread>
#include <unordered_map>
#include <ostream>
@@ -19,6 +21,7 @@
#include <boost/lexical_cast.hpp>
#include <boost/log/trivial.hpp>
#include <vector>
#include <wx/app.h>
#include <wx/msgdlg.h>
@@ -204,7 +207,6 @@ struct PresetUpdater::priv
// Per-vendor update checking
std::set<std::string> checked_vendors;
std::mutex vendor_check_mutex;
std::vector<std::thread> vendor_check_threads;
std::atomic<bool> vendor_check_cancel{false};
@@ -221,10 +223,10 @@ struct PresetUpdater::priv
priv();
void set_download_prefs(AppConfig *app_config);
bool get_file(const std::string &url, const fs::path &target_path) const;
//BBS: refine preset update logic
bool get_file(const std::string &url, const fs::path &target_path) const;
//BBS: refine preset update logic
bool extract_file(const fs::path &source_path, const fs::path &dest_path = {});
void prune_tmps() const;
void prune_tmp(const std::string& vendor_id) const;
void sync_version() const;
void parse_version_string(const std::string& body) const;
void sync_resources(std::string http_url, std::map<std::string, Resource> &resources, bool check_patch = false, std::string current_version="", std::string changelog_file="");
@@ -371,14 +373,14 @@ bool PresetUpdater::priv::extract_file(const fs::path &source_path, const fs::pa
return true;
}
// Remove leftover paritally downloaded files, if any.
void PresetUpdater::priv::prune_tmps() const
// Remove a leftover partial archive for the vendor about to be synchronized.
void PresetUpdater::priv::prune_tmp(const std::string& vendor_id) const
{
for (auto &dir_entry : boost::filesystem::directory_iterator(cache_path))
if (is_plain_file(dir_entry) && dir_entry.path().extension() == TMP_EXTENSION) {
BOOST_LOG_TRIVIAL(debug) << "[Orca Updater]remove old cached files: " << dir_entry.path().string();
fs::remove(dir_entry.path());
}
boost::system::error_code ec;
const fs::path tmp_path = cache_path / (vendor_id + TMP_EXTENSION);
fs::remove(tmp_path, ec);
if (ec)
BOOST_LOG_TRIVIAL(warning) << "[Orca Updater]failed to remove " << tmp_path.string() << ": " << ec.message();
}
//BBS: refine the Preset Updater logic
@@ -1060,10 +1062,9 @@ void PresetUpdater::priv::check_installed_vendor_profiles() const
Semver resource_ver = get_version_from_json(file_path);
Semver vendor_ver = get_version_from_json(path_in_vendor.string());
bool version_match = ((resource_ver.maj() == vendor_ver.maj()) && (resource_ver.min() == vendor_ver.min()));
if (!version_match || (vendor_ver < resource_ver)) {
BOOST_LOG_TRIVIAL(info) << "[Orca Updater]:found vendor "<<vendor_name<<" newer version "<<resource_ver.to_string() <<" from resource, old version "<<vendor_ver.to_string();
if (vendor_ver < resource_ver) {
BOOST_LOG_TRIVIAL(info) << "[Orca Updater]:found vendor " << vendor_name << " newer version "
<< resource_ver.to_string() << " from resource, old version " << vendor_ver.to_string();
bundles.insert(vendor_name);
}
}
@@ -1305,49 +1306,29 @@ PresetUpdater::~PresetUpdater()
//BBS: change directories by design
//BBS: refine the preset updater logic
void PresetUpdater::sync(std::string http_url, std::string language, std::string plugin_version, PresetBundle *preset_bundle)
void PresetUpdater::sync(std::string http_url, std::string language, std::string plugin_version, PresetBundle * /*preset_bundle*/)
{
//p->set_download_prefs(GUI::wxGetApp().app_config);
if (!p->enabled_version_check && !p->enabled_config_update) { return; }
// Copy the whole vendors data for use in the background thread
// Unfortunatelly as of C++11, it needs to be copied again
// into the closure (but perhaps the compiler can elide this).
VendorMap vendors = preset_bundle ? preset_bundle->vendors : VendorMap{};
// Determine active vendor before entering the thread
std::string active_vendor;
if (preset_bundle) {
const Preset& printer = preset_bundle->printers.get_edited_preset();
if (printer.vendor)
active_vendor = printer.vendor->id;
}
p->thread = std::thread([this, vendors, active_vendor, http_url, language, plugin_version]() {
this->p->prune_tmps();
if (p->cancel)
return;
this->p->sync_version();
if (p->cancel)
return;
// Per-vendor config check for the active vendor at startup
if (!active_vendor.empty() && !vendors.empty()) {
this->p->sync_vendor_config(active_vendor);
if (p->cancel)
return;
{
std::lock_guard<std::mutex> lock(this->p->vendor_check_mutex);
this->p->checked_vendors.insert(active_vendor);
}
}
if (p->cancel)
return;
this->p->sync_plugins(http_url, plugin_version);
this->p->sync_printer_config(http_url);
//if (p->cancel)
// return;
//remove the tooltip currently
//this->p->sync_tooltip(http_url, language);
p->thread = std::thread([this, http_url, language, plugin_version]() {
try {
this->p->sync_version();
if (p->cancel)
return;
// Vendor profile updates are triggered by check_vendor_update()
// after the startup printer preset has been restored.
this->p->sync_plugins(http_url, plugin_version);
this->p->sync_printer_config(http_url);
//if (p->cancel)
// return;
//remove the tooltip currently
//this->p->sync_tooltip(http_url, language);
} catch (const std::exception &e) {
BOOST_LOG_TRIVIAL(error) << "[Orca Updater] background sync failed: " << e.what();
} catch (...) {
BOOST_LOG_TRIVIAL(error) << "[Orca Updater] background sync failed with an unknown exception";
}
});
}
@@ -1356,16 +1337,17 @@ void PresetUpdater::check_vendor_update(const std::string& vendor_id)
if (!p->enabled_config_update) return;
if (vendor_id.empty()) return;
std::lock_guard<std::mutex> lock(p->vendor_check_mutex);
if (!p->checked_vendors.insert(vendor_id).second)
return;
p->vendor_check_threads.emplace_back([this, vendor_id]() {
try {
this->p->prune_tmp(vendor_id);
this->p->sync_vendor_config(vendor_id);
} catch (const std::exception& e) {
BOOST_LOG_TRIVIAL(error) << "[Orca Updater] vendor update failed for " << vendor_id << ": " << e.what();
} catch (...) {
BOOST_LOG_TRIVIAL(error) << "[Orca Updater] vendor update failed for " << vendor_id << " with an unknown exception";
}
});
}
+3
View File
@@ -20,6 +20,7 @@ struct CloudPluginState
bool update_available = false; // Cloud version > the local package version.
bool unauthorized = false; // Cloud plugin is valid locally, but cannot receive cloud updates.
bool is_mine = false; // Plugin was created (and uploaded) by the current user.
bool orphaned = false; // Cloud identity remains locally, but the plugin is no longer subscribed/available.
};
enum class PluginUpdateStatus
@@ -106,6 +107,8 @@ struct PluginDescriptor
{
if (!cloud.has_value())
return PluginUpdateStatus::Normal;
if (cloud->orphaned)
return PluginUpdateStatus::Normal;
if (cloud->unauthorized)
return PluginUpdateStatus::Unauthorized;
if (cloud->update_available)
+1 -2
View File
@@ -120,8 +120,7 @@ bool delete_plugin_root(const boost::filesystem::path& resolved_root, const std:
}
if (removed_count == 0) {
error = "Plugin folder was not found: " + resolved_root.string();
return false;
return true;
}
BOOST_LOG_TRIVIAL(info) << "Deleted plugin: " << plugin_id << " from " << resolved_root.string();
+65 -18
View File
@@ -310,6 +310,7 @@ void PluginManager::merge_discovered_plugins(std::vector<PluginDescriptor> disco
}
seen.push_back(descriptor.plugin_key);
m_missing_plugin_keys.erase(descriptor.plugin_key);
Plugin* existing = find_plugin_locked(descriptor.plugin_key);
if (existing == nullptr) {
@@ -330,12 +331,47 @@ void PluginManager::merge_discovered_plugins(std::vector<PluginDescriptor> disco
return;
}
// Unloading may call Python and lifecycle subscribers may re-enter the manager, so never do it
// while holding m_mutex. unload_and_erase_if() retries until no matching entry is loaded at the
// moment of erase, in case another caller starts a load between the initial snapshot and the
// teardown.
unload_and_erase_if(
[&seen](const Plugin& plugin) { return std::find(seen.begin(), seen.end(), plugin.descriptor.plugin_key) == seen.end(); });
// A package can be temporarily absent while an external side-loader replaces it. Keep the
// descriptor and its persisted enable state until the user explicitly removes the missing
// entry, or a later scan rediscovers it. In particular, do not unload here: the unload callback
// would turn a transient filesystem gap into enabled=false in the sidecar.
{
std::lock_guard<std::mutex> lock(m_mutex);
for (const Plugin& plugin : m_plugins) {
if (plugin.descriptor.has_local_package() &&
std::find(seen.begin(), seen.end(), plugin.descriptor.plugin_key) == seen.end())
m_missing_plugin_keys.insert(plugin.descriptor.plugin_key);
}
}
}
std::vector<PluginDescriptor> PluginManager::get_missing_plugin_descriptors() const
{
std::lock_guard<std::mutex> lock(m_mutex);
std::vector<PluginDescriptor> result;
result.reserve(m_missing_plugin_keys.size());
for (const Plugin& plugin : m_plugins)
if (m_missing_plugin_keys.count(plugin.descriptor.plugin_key) != 0)
result.push_back(plugin.descriptor);
return result;
}
void PluginManager::remove_missing_plugins(const std::vector<std::string>& plugin_keys)
{
const std::unordered_set<std::string> requested(plugin_keys.begin(), plugin_keys.end());
// The predicate is evaluated only while m_mutex is held by unload_and_erase_if(). Checking the
// current missing set here prevents a package that reappeared between the dialog and removal
// from being erased.
unload_and_erase_if([this, &requested](const Plugin& plugin) {
return requested.count(plugin.descriptor.plugin_key) != 0 &&
m_missing_plugin_keys.count(plugin.descriptor.plugin_key) != 0;
});
std::lock_guard<std::mutex> lock(m_mutex);
for (const std::string& plugin_key : requested)
m_missing_plugin_keys.erase(plugin_key);
}
void PluginManager::unload_and_erase_if(const std::function<bool(const Plugin&)>& should_remove,
@@ -1422,7 +1458,8 @@ void PluginManager::fetch_plugins_from_cloud(std::vector<std::string>* out_not_f
std::vector<PluginDescriptor> cloud_list{};
std::vector<std::string> not_found{}, unauthorized{};
if (!m_cloud_service.fetch_manifests_into_descriptors(cloud_list, not_found, unauthorized)) {
const bool cloud_fetch_succeeded = m_cloud_service.fetch_manifests_into_descriptors(cloud_list, not_found, unauthorized);
if (!cloud_fetch_succeeded) {
if (wxTheApp != nullptr) {
GUI::wxGetApp().CallAfter([] {
if (GUI::wxGetApp().is_closing())
@@ -1437,9 +1474,10 @@ void PluginManager::fetch_plugins_from_cloud(std::vector<std::string>* out_not_f
}
}
update_cloud_metadata(cloud_list);
if (cloud_fetch_succeeded)
update_cloud_metadata(cloud_list);
{
if (cloud_fetch_succeeded) {
std::lock_guard<std::mutex> lock(m_mutex);
// Clear the previous cloud verdicts before re-applying the fresh ones.
@@ -1448,19 +1486,28 @@ void PluginManager::fetch_plugins_from_cloud(std::vector<std::string>* out_not_f
if (!entry.is_cloud_plugin())
continue;
entry.set_unauthorized(false);
if (entry.cloud.has_value())
entry.cloud->orphaned = false;
if (entry.normalized_error() == CLOUD_PLUGIN_NOT_FOUND_ERROR)
entry.clear_error();
}
for (const std::string& uuid : not_found) {
for (Plugin& plugin : m_plugins) {
PluginDescriptor& entry = plugin.descriptor;
if (!entry.is_cloud_plugin() || entry.cloud_uuid() != uuid)
continue;
if (!entry.has_local_package())
entry.set_error(CLOUD_PLUGIN_NOT_FOUND_ERROR);
break;
}
// A successful subscriptions response may report missing UUIDs explicitly, or it may
// simply omit an unsubscribed plugin from `data`. Both cases leave a locally retained
// cloud package orphaned. Owned plugins are returned by the separate mine endpoint and
// must not be orphaned merely because they are not subscribed.
for (Plugin& plugin : m_plugins) {
PluginDescriptor& entry = plugin.descriptor;
if (!entry.is_cloud_plugin() || entry.cloud->is_mine)
continue;
const bool explicitly_not_found = std::find(not_found.begin(), not_found.end(), entry.cloud_uuid()) != not_found.end();
const bool returned_by_cloud = std::any_of(cloud_list.begin(), cloud_list.end(), [&entry](const PluginDescriptor& cloud_entry) {
return cloud_entry.cloud_uuid() == entry.cloud_uuid();
});
entry.cloud->orphaned = explicitly_not_found || !returned_by_cloud;
if (entry.cloud->orphaned)
entry.cloud->update_available = false;
}
for (const std::string& uuid : unauthorized) {
+6
View File
@@ -132,6 +132,11 @@ public:
bool try_get_plugin_descriptor(const std::string& plugin_key, PluginDescriptor& out) const;
// Same, but only for packages that are loadable (i.e. not an invalid package).
bool try_get_valid_plugin_descriptor(const std::string& plugin_key, PluginDescriptor& out) const;
// Packages that were present in the previous discovery pass but were not found on disk in the
// latest rescan. They are retained until the user explicitly removes them or a later scan finds
// them again.
std::vector<PluginDescriptor> get_missing_plugin_descriptors() const;
void remove_missing_plugins(const std::vector<std::string>& plugin_keys);
// Packages whose .install_state.json marks them for auto-load.
std::vector<std::string> get_enabled_plugin_keys() const;
// The package owning a loaded capability, for the by-name dispatch path.
@@ -262,6 +267,7 @@ private:
// Every discovered plugin, loaded or not. module == nullptr => not loaded.
std::vector<Plugin> m_plugins;
std::unordered_set<std::string> m_missing_plugin_keys;
std::unordered_set<std::string> m_load_in_progress;
// Keys whose in-flight load has been cancelled. Cancellation does NOT remove the key from
+10
View File
@@ -2,6 +2,7 @@
#include <libslic3r/Model.hpp>
#include <libslic3r/PresetBundle.hpp>
#include <slic3r/GUI/GUI.hpp>
#include <slic3r/GUI/GUI_App.hpp>
#include <slic3r/GUI/Plater.hpp>
@@ -55,6 +56,15 @@ void host_bindings::register_app(py::module_& host)
return current_plater()->model();
}, py::return_value_policy::reference);
host.def("preset_bundle", &current_preset_bundle, py::return_value_policy::reference);
// UI language of the running app ("en_US", "ru_RU", ...), so plugins can
// localize their own dialogs. The app config file that stores this value
// is deny-listed by the audit hook (it sits next to cloud secrets), so a
// read-only accessor is the supported way to get just the language.
host.def("app_language", []() -> std::string {
if (wxTheApp == nullptr)
throw std::runtime_error("OrcaSlicer application is not initialized");
return GUI::into_u8(GUI::wxGetApp().current_language_code_safe());
});
}
} // namespace Slic3r
+1
View File
@@ -11,6 +11,7 @@ add_executable(${_TEST_NAME}_tests
test_gcodewriter.cpp
test_model.cpp
test_multifilament.cpp
test_perimeters.cpp
test_print.cpp
test_printobject.cpp
test_skirt_brim.cpp
+257
View File
@@ -0,0 +1,257 @@
#include <catch2/catch_all.hpp>
#include "libslic3r/ExtrusionEntity.hpp"
#include "libslic3r/ExtrusionEntityCollection.hpp"
#include "libslic3r/Layer.hpp"
#include "libslic3r/Print.hpp"
#include <algorithm>
#include <cmath>
#include <vector>
#include "test_helpers.hpp"
using namespace Slic3r;
using namespace Slic3r::Test;
namespace {
// The layer at this Z is the last one of the base, so its top surface is the ledge.
const double ledge_z = 5.0;
// The first layer, at initial_layer_print_height.
const double first_layer_z = 0.2;
// TestMesh::step scaled 3x in X/Y: a 60x60x5 base carrying a 54x54 column up to z=10, leaving a 3mm
// top ledge around a feature that keeps rising. That is the geometry both only_one_wall_top and the
// top surface expansion act on. The ledge has to stay wider than the wall band plus two top-infill
// lines, or the expansion discards it as a sliver and the tests below assert nothing.
TriangleMesh step_with_ledge()
{
TriangleMesh m = Slic3r::Test::mesh(TestMesh::step);
m.scale(Vec3f(3.f, 3.f, 1.f));
return m;
}
// Every setting the assertions depend on, so none of them rests on a default.
DynamicPrintConfig base_config(const char *wall_generator)
{
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
config.set_deserialize_strict({
{ "wall_generator", wall_generator },
{ "layer_height", 0.2 }, // puts a layer boundary exactly on ledge_z
{ "initial_layer_print_height", 0.2 },
{ "wall_loops", 3 },
{ "sparse_infill_density", "15%" },
{ "top_shell_layers", 3 },
{ "bottom_shell_layers", 3 },
{ "top_surface_density", "100%" },
{ "top_surface_expansion", 0.0 },
{ "only_one_wall_top", false },
{ "only_one_wall_first_layer", false },
// Do not let the one-wall threshold discard the 3mm ledge before the feature sees it.
{ "min_width_top_surface", 0.0 },
});
return config;
}
double collection_length(const ExtrusionEntityCollection &coll)
{
double len = 0.;
for (const ExtrusionEntity *entity : coll.flatten().entities)
if (! entity->is_collection())
len += entity->length();
return len;
}
// Extruded length per layer. Two slices are compared through this rather than through their G-code,
// because the G-code carries a config block that differs whenever any setting differs.
struct SliceLengths {
std::vector<double> perimeters;
std::vector<double> fills;
};
SliceLengths slice_lengths(const Print &print)
{
SliceLengths out;
for (const Layer *layer : print.objects().front()->layers()) {
double perimeters = 0., fills = 0.;
for (const LayerRegion *region : layer->regions()) {
perimeters += collection_length(region->perimeters);
fills += collection_length(region->fills);
}
out.perimeters.push_back(perimeters);
out.fills.push_back(fills);
}
return out;
}
double perimeter_length_at(const Print &print, double print_z)
{
for (const Layer *layer : print.objects().front()->layers())
if (std::abs(layer->print_z - print_z) < 1e-4) {
double len = 0.;
for (const LayerRegion *region : layer->regions())
len += collection_length(region->perimeters);
return len;
}
return 0.;
}
// Largest per-layer difference between two series; a negative result means they are not comparable.
double max_difference(const std::vector<double> &a, const std::vector<double> &b)
{
if (a.size() != b.size() || a.empty())
return -1.;
double worst = 0.;
for (size_t i = 0; i < a.size(); ++ i)
worst = std::max(worst, std::abs(a[i] - b[i]));
return worst;
}
} // namespace
// The expansion only retypes area as top solid infill, so it can do nothing where there is no top
// fill to begin with: zero top shell layers retypes the top surfaces as internal, and a top surface
// density of 0% leaves the top layer with walls only. The last section is the control - the same
// expansion on the same model does change the slice once a top fill exists - without which the two
// equality checks above it would hold for an unrelated reason.
TEST_CASE("Top surface expansion only acts where there is a top fill", "[Perimeters]")
{
const char *wall_generator = GENERATE("classic", "arachne");
CAPTURE(wall_generator);
auto lengths_for = [wall_generator](int top_shell_layers, const char *top_surface_density, double expansion) {
DynamicPrintConfig config = base_config(wall_generator);
config.set_deserialize_strict({
{ "top_shell_layers", top_shell_layers },
{ "top_surface_density", top_surface_density },
{ "top_surface_expansion", expansion },
});
Print print;
init_and_process_print({ step_with_ledge() }, print, config);
REQUIRE_FALSE(print.objects().empty());
return slice_lengths(print);
};
SECTION("no top shell layers") {
const SliceLengths off = lengths_for(0, "100%", 0.0);
const SliceLengths on = lengths_for(0, "100%", 2.0);
REQUIRE(off.perimeters.size() == on.perimeters.size());
CHECK_THAT(max_difference(off.perimeters, on.perimeters), Catch::Matchers::WithinAbs(0., 1.0));
CHECK_THAT(max_difference(off.fills, on.fills), Catch::Matchers::WithinAbs(0., 1.0));
}
SECTION("zero top surface density") {
const SliceLengths off = lengths_for(3, "0%", 0.0);
const SliceLengths on = lengths_for(3, "0%", 2.0);
REQUIRE(off.perimeters.size() == on.perimeters.size());
CHECK_THAT(max_difference(off.perimeters, on.perimeters), Catch::Matchers::WithinAbs(0., 1.0));
CHECK_THAT(max_difference(off.fills, on.fills), Catch::Matchers::WithinAbs(0., 1.0));
}
SECTION("with a top fill the same expansion does change the slice") {
const SliceLengths off = lengths_for(3, "100%", 0.0);
const SliceLengths on = lengths_for(3, "100%", 2.0);
REQUIRE(off.fills.size() == on.fills.size());
CHECK(max_difference(off.fills, on.fills) > scale_(0.5));
}
}
// With no top shell the top surfaces are retyped as internal, so the top surface density has nothing
// left to control: there is no top fill, and only_one_wall_top - the one route from the density to the
// perimeters - is itself switched off for want of a top surface to act on.
TEST_CASE("Top surface density does not affect a slice without a top shell", "[Perimeters]")
{
const char *wall_generator = GENERATE("classic", "arachne");
CAPTURE(wall_generator);
auto lengths_for = [wall_generator](const char *top_surface_density) {
DynamicPrintConfig config = base_config(wall_generator);
config.set_deserialize_strict({
{ "top_shell_layers", 0 },
{ "only_one_wall_top", true },
{ "top_surface_density", top_surface_density },
});
Print print;
init_and_process_print({ step_with_ledge() }, print, config);
REQUIRE_FALSE(print.objects().empty());
return slice_lengths(print);
};
const SliceLengths solid = lengths_for("100%");
const SliceLengths none = lengths_for("0%");
REQUIRE(solid.perimeters.size() == none.perimeters.size());
CHECK_THAT(max_difference(solid.perimeters, none.perimeters), Catch::Matchers::WithinAbs(0., 1.0));
CHECK_THAT(max_difference(solid.fills, none.fills), Catch::Matchers::WithinAbs(0., 1.0));
}
// On the ledge layer the inner walls are given up to the top fill, so that layer loses wall length.
// The handover needs a top fill that reaches the freed space: at a top surface density of 0% there is
// no top fill at all, and without top_surface_expansion the fill never grows over the walls. Either
// way the feature still runs, through the original generation, which keeps the inner walls up to the
// top boundary - putting that layer back between the plain and the one-wall slice.
TEST_CASE("Only one wall on top surfaces drops inner walls only where a top fill replaces them", "[Perimeters]")
{
const char *wall_generator = GENERATE("classic", "arachne");
CAPTURE(wall_generator);
auto ledge_perimeters_for = [wall_generator](bool only_one_wall_top, const char *top_surface_density, double expansion) {
DynamicPrintConfig config = base_config(wall_generator);
config.set_deserialize_strict({
{ "only_one_wall_top", only_one_wall_top },
{ "top_surface_density", top_surface_density },
{ "top_surface_expansion", expansion },
});
Print print;
init_and_process_print({ step_with_ledge() }, print, config);
REQUIRE_FALSE(print.objects().empty());
return perimeter_length_at(print, ledge_z);
};
const double plain = ledge_perimeters_for(false, "100%", 2.0);
const double one_wall = ledge_perimeters_for(true, "100%", 2.0);
const double one_wall_no_fill = ledge_perimeters_for(true, "0%", 2.0);
const double one_wall_no_expand = ledge_perimeters_for(true, "100%", 0.0);
REQUIRE(plain > 0.);
CHECK(one_wall < plain);
// Both fall back to the original generation, which cuts the walls back to the top boundary but not past it.
CHECK(one_wall_no_fill > one_wall);
CHECK(one_wall_no_fill < plain);
CHECK(one_wall_no_expand > one_wall);
CHECK(one_wall_no_expand < plain);
}
// The bottom counterpart: the first layer is thinned to a single wall only where a bottom shell fills the
// space behind it. With no bottom shell layers the bottom surfaces are retyped as internal, so that wall
// would ring sparse infill on the bed - the option is switched off instead, and the GUI hides it in that
// state so a profile that left it enabled cannot act behind a hidden checkbox.
TEST_CASE("Only one wall on the first layer needs a bottom shell", "[Perimeters]")
{
const char *wall_generator = GENERATE("classic", "arachne");
CAPTURE(wall_generator);
auto first_layer_perimeters_for = [wall_generator](bool only_one_wall_first_layer, int bottom_shell_layers) {
DynamicPrintConfig config = base_config(wall_generator);
config.set_deserialize_strict({
{ "only_one_wall_first_layer", only_one_wall_first_layer },
{ "bottom_shell_layers", bottom_shell_layers },
});
Print print;
init_and_process_print({ step_with_ledge() }, print, config);
REQUIRE_FALSE(print.objects().empty());
return perimeter_length_at(print, first_layer_z);
};
const double plain = first_layer_perimeters_for(false, 3);
const double one_wall = first_layer_perimeters_for(true, 3);
// Both at zero bottom shell layers, so everything else that setting changes cancels out between them.
const double plain_no_shell = first_layer_perimeters_for(false, 0);
const double one_wall_no_shell = first_layer_perimeters_for(true, 0);
REQUIRE(plain > 0.);
CHECK(one_wall < plain);
// No bottom shell: the option is inert, down to the same walls an unchecked box gives.
CHECK_THAT(one_wall_no_shell, Catch::Matchers::WithinAbs(plain_no_shell, 1.0));
}
@@ -43,18 +43,33 @@ TEST_CASE("apply_override fills nil entries from the 0-based default index", "[C
REQUIRE(resolved.values == std::vector<double>({30., 42.}));
}
SECTION("an index past the machine slots falls back to the first slot") {
SECTION("an index past the machine slots keeps the slot's own value") {
std::vector<int> slot_index{5, 0};
ConfigOptionFloats resolved(machine);
REQUIRE(resolved.apply_override(&filament, slot_index));
REQUIRE(resolved.values == std::vector<double>({10., 42.}));
}
SECTION("a negative index (unresolved slot) falls back to the first slot") {
std::vector<int> slot_index{-1, 0};
SECTION("a negative index (unresolved slot) keeps the slot's own value") {
ConfigOptionFloatsNullable all_nil;
all_nil.values = {ConfigOptionFloatsNullable::nil_value(), ConfigOptionFloatsNullable::nil_value(),
ConfigOptionFloatsNullable::nil_value()};
std::vector<int> slot_index{2, -1, 0};
ConfigOptionFloats resolved(machine);
REQUIRE(resolved.apply_override(&filament, slot_index));
REQUIRE(resolved.values == std::vector<double>({10., 42.}));
REQUIRE(!resolved.apply_override(&all_nil, slot_index));
REQUIRE(resolved.values == std::vector<double>({30., 20., 10.}));
}
SECTION("all-nil overrides keyed by unresolved slots leave the machine values intact") {
// The failed-lookup map a degenerate print_extruder_id used to produce; the negative
// slots must not collapse the machine array to its first value.
ConfigOptionFloats per_extruder({100., 70., 70., 70., 100.});
ConfigOptionFloatsNullable all_nil;
all_nil.values.assign(5, ConfigOptionFloatsNullable::nil_value());
std::vector<int> slot_index{0, -1, -1, -1, 0};
ConfigOptionFloats resolved(per_extruder);
REQUIRE(!resolved.apply_override(&all_nil, slot_index));
REQUIRE(resolved.values == std::vector<double>({100., 70., 70., 70., 100.}));
}
}
@@ -272,6 +287,102 @@ TEST_CASE("update_values_to_printer_extruders expands one slot per (extruder x v
}
}
TEST_CASE("update_values_to_printer_extruders synthesizes degenerate process variant columns", "[Config]")
{
// Non-BBL process presets and 3mf project configs keep the length-1 defaults for
// print_extruder_id/print_extruder_variant; only BBL system presets ship full-width columns.
auto add_degenerate_print_columns = [](DynamicPrintConfig &config) {
config.option<ConfigOptionInts>("print_extruder_id", true)->values = {1};
config.option<ConfigOptionStrings>("print_extruder_variant", true)->values = {"Direct Drive Standard"};
config.option<ConfigOptionFloats>("outer_wall_speed", true)->values = {30.};
};
SECTION("a single-column pair on a multi-extruder machine expands to one column per extruder") {
DynamicPrintConfig config;
config.option<ConfigOptionEnumsGeneric>("extruder_type", true)->values = {etDirectDrive, etDirectDrive};
config.option<ConfigOptionEnumsGeneric>("nozzle_volume_type", true)->values = {nvtStandard, nvtStandard};
config.option<ConfigOptionStrings>("extruder_variant_list", true)->values = {"Direct Drive Standard", "Direct Drive Standard"};
add_degenerate_print_columns(config);
std::vector<std::vector<NozzleVolumeType>> nozzle_volume_types;
int extruder_count = 2;
int count = config.get_extruder_nozzle_volume_count(extruder_count, nozzle_volume_types);
std::vector<int> variant_index = config.update_values_to_printer_extruders(config, extruder_count, count, nozzle_volume_types,
print_options_with_variant, "print_extruder_id", "print_extruder_variant");
REQUIRE(variant_index == std::vector<int>({0, 1}));
REQUIRE(config.option<ConfigOptionInts>("print_extruder_id")->values == std::vector<int>({1, 2}));
REQUIRE(config.option<ConfigOptionStrings>("print_extruder_variant")->values ==
std::vector<std::string>({"Direct Drive Standard", "Direct Drive Standard"}));
// width-1 data arrays replicate their only column into every slot
REQUIRE(config.option<ConfigOptionFloats>("outer_wall_speed")->values == std::vector<double>({30., 30.}));
}
SECTION("a multi-variant list synthesizes one column per (extruder x variant)") {
DynamicPrintConfig config = make_hybrid_printer_config();
add_degenerate_print_columns(config);
std::vector<std::vector<NozzleVolumeType>> nozzle_volume_types;
int extruder_count = 2;
int count = config.get_extruder_nozzle_volume_count(extruder_count, nozzle_volume_types);
REQUIRE(count == 3);
std::vector<int> variant_index = config.update_values_to_printer_extruders(config, extruder_count, count, nozzle_volume_types,
print_options_with_variant, "print_extruder_id", "print_extruder_variant");
// same slot resolution as the explicit BBL-style 4-column layout
REQUIRE(variant_index == std::vector<int>({0, 2, 3}));
REQUIRE(config.option<ConfigOptionInts>("print_extruder_id")->values == std::vector<int>({1, 2, 2}));
REQUIRE(config.option<ConfigOptionStrings>("print_extruder_variant")->values ==
std::vector<std::string>({"Direct Drive Standard", "Direct Drive Standard", "Direct Drive High Flow"}));
REQUIRE(config.option<ConfigOptionFloats>("outer_wall_speed")->values == std::vector<double>({30., 30., 30.}));
}
SECTION("a single-extruder single-column layout is not treated as degenerate") {
DynamicPrintConfig config;
config.option<ConfigOptionEnumsGeneric>("extruder_type", true)->values = {etDirectDrive};
config.option<ConfigOptionEnumsGeneric>("nozzle_volume_type", true)->values = {nvtStandard};
config.option<ConfigOptionStrings>("extruder_variant_list", true)->values = {"Direct Drive Standard"};
add_degenerate_print_columns(config);
std::vector<std::vector<NozzleVolumeType>> nozzle_volume_types;
int extruder_count = 1;
int count = config.get_extruder_nozzle_volume_count(extruder_count, nozzle_volume_types);
config.update_values_to_printer_extruders(config, extruder_count, count, nozzle_volume_types,
print_options_with_variant, "print_extruder_id", "print_extruder_variant");
REQUIRE(config.option<ConfigOptionInts>("print_extruder_id")->values == std::vector<int>({1}));
REQUIRE(config.option<ConfigOptionFloats>("outer_wall_speed")->values == std::vector<double>({30.}));
}
SECTION("a second expansion leaves the synthesized layout unchanged") {
DynamicPrintConfig config;
config.option<ConfigOptionEnumsGeneric>("extruder_type", true)->values = {etDirectDrive, etDirectDrive};
config.option<ConfigOptionEnumsGeneric>("nozzle_volume_type", true)->values = {nvtStandard, nvtStandard};
config.option<ConfigOptionStrings>("extruder_variant_list", true)->values = {"Direct Drive Standard", "Direct Drive Standard"};
add_degenerate_print_columns(config);
std::vector<std::vector<NozzleVolumeType>> nozzle_volume_types;
int extruder_count = 2;
int count = config.get_extruder_nozzle_volume_count(extruder_count, nozzle_volume_types);
config.update_values_to_printer_extruders(config, extruder_count, count, nozzle_volume_types,
print_options_with_variant, "print_extruder_id", "print_extruder_variant");
DynamicPrintConfig once = config;
config.update_values_to_printer_extruders(config, extruder_count, count, nozzle_volume_types,
print_options_with_variant, "print_extruder_id", "print_extruder_variant");
REQUIRE(config.option<ConfigOptionInts>("print_extruder_id")->values ==
once.option<ConfigOptionInts>("print_extruder_id")->values);
REQUIRE(config.option<ConfigOptionStrings>("print_extruder_variant")->values ==
once.option<ConfigOptionStrings>("print_extruder_variant")->values);
REQUIRE(config.option<ConfigOptionFloats>("outer_wall_speed")->values ==
once.option<ConfigOptionFloats>("outer_wall_speed")->values);
}
}
TEST_CASE("update_values_to_printer_extruders_for_multiple_filaments resolves per-filament slots", "[Config]")
{
auto make_filament_arrays = [](DynamicPrintConfig &config) {
@@ -500,6 +500,52 @@ TEST_CASE("Re-applying an unchanged config after slicing keeps the result valid"
REQUIRE(print.is_step_done(psSlicingFinished));
}
TEST_CASE("A degenerate process variant map on a custom multi-extruder printer slices to a stable result", "[Print][Regression]")
{
// Non-BBL multi-extruder printers get machine-scope variant columns synthesized on preset
// load (extend_extruder_variant), but nothing ships process-scope print_extruder_id /
// print_extruder_variant: presets and 3mf project configs carry the length-1 defaults. The
// apply-time expansion must synthesize the process columns from extruder_variant_list;
// otherwise the failed per-extruder lookups collapse the per-extruder retract overrides
// during slicing and the post-slice re-apply invalidates every fresh result, forever.
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
config.set_num_extruders(5);
config.option<ConfigOptionFloats>("nozzle_diameter", true)->values = {0.4, 0.4, 0.4, 0.4, 0.4};
// per-extruder machine values that a first-slot collapse would destroy
config.option<ConfigOptionPercents>("retract_before_wipe", true)->values = {100., 70., 70., 70., 100.};
config.option<ConfigOptionEnumsGeneric>("z_hop_types", true)->values = {zhtSlope, zhtNormal, zhtNormal, zhtNormal, zhtSlope};
// filament presets carry the nullable override twins (all-nil = "no override"); they are what
// routes the machine values through apply_override in the in-slice override recompute
config.option<ConfigOptionPercentsNullable>("filament_retract_before_wipe", true)->values =
std::vector<double>(5, ConfigOptionPercentsNullable::nil_value());
config.option<ConfigOptionEnumsGenericNullable>("filament_z_hop_types", true)->values =
std::vector<int>(5, ConfigOptionEnumsGenericNullable::nil_value());
config.option<ConfigOptionFloats>("filament_diameter", true)->values = std::vector<double>(5, 1.75);
config.option<ConfigOptionStrings>("filament_colour", true)->values = {"#FF0000", "#00FF00", "#0000FF", "#FFFF00", "#00FFFF"};
config.option<ConfigOptionInts>("filament_map", true)->values = {1, 2, 3, 4, 1};
Model model;
model.add_object("cube", "", make_cube(20, 20, 20))->add_instance()->set_offset(Vec3d(100., 100., 0.));
Print print;
print.apply(model, config);
print.process();
REQUIRE(print.is_step_done(psSlicingFinished));
// BackgroundSlicingProcess reads the engine-computed maps back into the plate config after
// slicing; the next apply overlays that written-back state.
config.option<ConfigOptionInts>("filament_map", true)->values = print.get_filament_maps();
config.option<ConfigOptionInts>("filament_volume_map", true)->values = print.get_filament_volume_maps();
config.option<ConfigOptionInts>("filament_nozzle_map", true)->values = print.get_filament_nozzle_maps();
auto status = print.apply(model, config);
REQUIRE(status == PrintBase::APPLY_STATUS_UNCHANGED);
REQUIRE(print.is_step_done(psSlicingFinished));
// the per-extruder machine values must survive the in-slice override recompute
REQUIRE(print.config().retract_before_wipe.values == std::vector<double>({100., 70., 70., 70., 100.}));
REQUIRE(print.config().z_hop_types.values == std::vector<int>({zhtSlope, zhtNormal, zhtNormal, zhtNormal, zhtSlope}));
}
TEST_CASE("normalize_nozzle_map_per_layer makes per-filament assignments gap-free", "[MultiNozzle][H2C][Dynamic]")
{
SECTION("gaps inherit the last used nozzle, entries on used layers stay untouched") {
@@ -13,6 +13,7 @@
#include <boost/filesystem.hpp>
#include <nlohmann/json.hpp>
#include <chrono>
#include <fstream>
#include <string>
@@ -48,6 +49,18 @@ const char* const CLOUD_PLUGIN_SOURCE = R"PY(# /// script
# version = "1.0"
# ///
print('ok')
import orca
class stubscript(orca.script.ScriptPluginCapabilityBase):
def get_name(self):
return "stubscript"
def execute(self):
return orca.ExecutionResult.success("Stub orca script.")
@orca.plugin
class stubpackage(orca.base):
def register_capabilities(self):
orca.register_capability(stubscript)
)PY";
} // namespace
@@ -152,4 +165,31 @@ TEST_CASE("cloud metadata refresh preserves a plugin's stored config", "[PluginC
reloaded.load();
REQUIRE(reloaded.has_config(id));
CHECK(reloaded.get_config(id)->config == configured);
// A local package can remain after the cloud subscription disappears. The cloud identity is
// retained for diagnosis, but the orphaned state must suppress update availability until the
// plugin is returned by a later cloud refresh.
PluginDescriptor orphaned_record = cloud_record;
orphaned_record.cloud->orphaned = true;
orphaned_record.cloud->update_available = true;
manager.update_cloud_metadata({orphaned_record});
const PluginDescriptor orphaned = find_by_uuid();
REQUIRE(orphaned.cloud.has_value());
CHECK(orphaned.cloud->orphaned);
CHECK_FALSE(orphaned.has_error());
CHECK(orphaned.get_update_status() == PluginUpdateStatus::Normal);
// Orphaned is informational only: the local package must remain loadable and usable.
std::string load_error;
manager.load_plugin(uuid, /*skip_deps=*/true);
REQUIRE(manager.wait_for_plugin_load(uuid, std::chrono::seconds(120), load_error));
INFO("load error: " << load_error);
CHECK(load_error.empty());
CHECK(manager.is_plugin_loaded(uuid));
CHECK(manager.unload_plugin(uuid));
// Seeing the plugin in a subsequent cloud response clears the orphaned marker.
manager.update_cloud_metadata({cloud_record});
CHECK_FALSE(find_by_uuid().cloud->orphaned);
}