diff --git a/.github/workflows/build_all.yml b/.github/workflows/build_all.yml index b23d20e90c..f5f0834b48 100644 --- a/.github/workflows/build_all.yml +++ b/.github/workflows/build_all.yml @@ -122,54 +122,73 @@ jobs: arch: universal macos-combine-only: true secrets: inherit - unit_tests: - name: Unit Tests - # Tests are built on the aarch64 leg by default (faster GitHub arm runner), - # so run them there; self-hosted builds them on the amd64 server instead. - runs-on: ${{ vars.SELF_HOSTED && 'orca-lnx-server' || 'ubuntu-24.04-arm' }} + # One test job per built arch, on the runner that built it. + unit_tests_linux_x86_64: + name: Linux x86_64 needs: build_linux if: ${{ !cancelled() && success() }} + uses: ./.github/workflows/unit_tests.yml + with: + os: ${{ vars.SELF_HOSTED && 'orca-lnx-server' || 'ubuntu-24.04' }} + artifact: ${{ github.sha }}-tests-linux-x86_64 + unit_tests_linux_aarch64: + name: Linux aarch64 + needs: build_linux + if: ${{ !cancelled() && success() }} + uses: ./.github/workflows/unit_tests.yml + with: + os: ubuntu-24.04-arm + artifact: ${{ github.sha }}-tests-linux-aarch64 + unit_tests_windows_x64: + name: Windows x64 + needs: build_windows + if: ${{ !cancelled() && success() }} + uses: ./.github/workflows/unit_tests.yml + with: + os: ${{ vars.SELF_HOSTED && 'orca-win-server' || 'windows-latest' }} + artifact: ${{ github.sha }}-tests-windows-x64 + unit_tests_windows_arm64: + name: Windows arm64 + needs: build_windows + if: ${{ !cancelled() && success() }} + uses: ./.github/workflows/unit_tests.yml + with: + os: windows-11-arm + artifact: ${{ github.sha }}-tests-windows-arm64 + test-dir: build-arm64/tests + unit_tests_macos_arm64: + name: macOS arm64 + needs: build_macos_arch + if: ${{ !cancelled() && success() }} + uses: ./.github/workflows/unit_tests.yml + with: + os: ${{ vars.SELF_HOSTED && 'orca-macos-arm64' || 'macos-14' }} + artifact: ${{ github.sha }}-tests-macos-arm64 + test-dir: build/arm64/tests + publish_test_results: + name: Publish Test Results + needs: [unit_tests_linux_x86_64, unit_tests_linux_aarch64, unit_tests_windows_x64, unit_tests_windows_arm64, unit_tests_macos_arm64] + if: ${{ !cancelled() }} + runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout@v7 - with: - sparse-checkout: | - .github - scripts - tests - - name: Apt-Install Dependencies - if: ${{ !vars.SELF_HOSTED }} - uses: ./.github/actions/apt-install-deps - - name: Restore Test Artifact + - name: Download Test Results uses: actions/download-artifact@v8 with: - name: ${{ github.sha }}-tests - - uses: lukka/get-cmake@latest - with: - cmakeVersion: "~4.3.0" # use most recent 4.3.x version - useLocalCache: true # <--= Use the local cache (default is 'false'). - useCloudCache: true - - name: Unpackage and Run Unit Tests - timeout-minutes: 20 - run: | - tar -xvf build_tests.tar - scripts/run_unit_tests.sh - - name: Upload Test Logs - uses: actions/upload-artifact@v7 - if: ${{ failure() }} - with: - name: unit-test-logs - path: build/tests/**/*.log + pattern: test-results-* + path: test-results + # Best-effort: a read-only token (e.g. fork PRs) can't write the check, so + # don't let a publish failure fail the run. The test jobs gate correctness. - name: Publish Test Results - if: always() + continue-on-error: true uses: EnricoMi/publish-unit-test-result-action@v2 with: - files: "ctest_results.xml" - - name: Delete Test Artifact + files: "test-results/**/*.xml" + - name: Delete Test Results if: success() uses: geekyeggo/delete-artifact@v6 with: - name: ${{ github.sha }}-tests + name: test-results-* + failOnError: false flatpak: name: "Flatpak" container: diff --git a/.github/workflows/build_orca.yml b/.github/workflows/build_orca.yml index ab676895d4..faed9a8379 100644 --- a/.github/workflows/build_orca.yml +++ b/.github/workflows/build_orca.yml @@ -160,8 +160,26 @@ jobs: - name: Build slicer mac if: runner.os == 'macOS' && !inputs.macos-combine-only working-directory: ${{ github.workspace }} + # arm64 only: build the tests here; the unit_tests_macos job runs them. + env: + ORCA_TESTS_BUILD_ONLY: ${{ inputs.arch == 'arm64' && '1' || '' }} run: | - ./build_release_macos.sh -s -n -x ${{ !vars.SELF_HOSTED && '-1' || '' }} -a ${{ inputs.arch }} -t 10.15 + ./build_release_macos.sh -s -n -x ${{ !vars.SELF_HOSTED && '-1' || '' }} -a ${{ inputs.arch }} -t 10.15 ${{ inputs.arch == 'arm64' && '-T' || '' }} + + - name: Pack unit tests mac + if: runner.os == 'macOS' && !inputs.macos-combine-only && inputs.arch == 'arm64' + working-directory: ${{ github.workspace }} + run: tar -cvf build_tests.tar build/arm64/tests + + - name: Upload Test Artifact mac + if: runner.os == 'macOS' && !inputs.macos-combine-only && inputs.arch == 'arm64' + uses: actions/upload-artifact@v7 + with: + name: ${{ github.sha }}-tests-macos-arm64 + overwrite: true + path: build_tests.tar + retention-days: 5 + if-no-files-found: error - name: Pack macOS app bundle ${{ inputs.arch }} if: runner.os == 'macOS' && !inputs.macos-combine-only @@ -354,11 +372,28 @@ jobs: # env: # WindowsSdkDir: 'C:\Program Files (x86)\Windows Kits\10\' # WindowsSDKVersion: '10.0.26100.0\' + # "tests" builds the unit tests too; the unit_tests_windows_* jobs run them. run: | $arch = "${{ inputs.arch }}" - if ($arch -eq "arm64") { .\build_release_vs.bat slicer arm64 } else { .\build_release_vs.bat slicer } + if ($arch -eq "arm64") { .\build_release_vs.bat slicer arm64 tests } else { .\build_release_vs.bat slicer tests } shell: pwsh + - name: Pack unit tests Win + if: runner.os == 'Windows' + working-directory: ${{ github.workspace }} + shell: pwsh + run: tar -cvf build_tests.tar ${{ env.BUILD_DIR }}/tests + + - name: Upload Test Artifact Win + if: runner.os == 'Windows' + uses: actions/upload-artifact@v7 + with: + name: ${{ github.sha }}-tests-windows-${{ inputs.arch }} + overwrite: true + path: build_tests.tar + retention-days: 5 + if-no-files-found: error + # NSIS is x86-only; it runs (and the installer it emits runs) under ARM64's # x86 emulation, packaging the native arm64 payload from build-arm64. - name: Create installer Win @@ -471,26 +506,22 @@ jobs: if: runner.os == 'Linux' shell: bash run: | - # Build + tar the unit tests (-t) only on the leg that runs them: the - # aarch64 leg by default (faster GitHub arm runner), or amd64 when using - # self-hosted runners (no arm self-hosted server). unit_tests downloads - # this tarball. The profile validator is built with -s, so amd64 keeps it. - tests=${{ (!vars.SELF_HOSTED && inputs.arch == 'aarch64') || (vars.SELF_HOSTED && inputs.arch != 'aarch64') }} - if $tests; then flags=-istrlL; else flags=-isrlL; fi - ./build_linux.sh "$flags" + # Build + tar the unit tests (-t) on both Linux legs so each arch + # (x86_64 + aarch64) gets tested by its own unit_tests_linux_* job. + ./build_linux.sh -istrlL ./scripts/check_appimage_libs.sh ./build/package ./build/package/bin/orca-slicer appimage=./build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}${{ env.arch_suffix }}_${{ env.ver }}.AppImage mv -n ./build/OrcaSlicer_Linux_V${{ env.ver_pure }}.AppImage "$appimage" chmod +x "$appimage" - if $tests; then tar -cvpf build_tests.tar build/tests; fi + tar -cvf build_tests.tar build/tests # Use tar because upload-artifacts won't always preserve directory structure # and doesn't preserve file permissions - name: Upload Test Artifact - if: runner.os == 'Linux' && ((!vars.SELF_HOSTED && inputs.arch == 'aarch64') || (vars.SELF_HOSTED && inputs.arch != 'aarch64')) + if: runner.os == 'Linux' uses: actions/upload-artifact@v7 with: - name: ${{ github.sha }}-tests + name: ${{ github.sha }}-tests-linux-${{ inputs.arch == 'aarch64' && 'aarch64' || 'x86_64' }} overwrite: true path: build_tests.tar retention-days: 5 diff --git a/.github/workflows/dedupe-issues.yml b/.github/workflows/dedupe-issues.yml index cb0a9c0931..a8c21edd01 100644 --- a/.github/workflows/dedupe-issues.yml +++ b/.github/workflows/dedupe-issues.yml @@ -27,7 +27,7 @@ jobs: with: prompt: "/dedupe ${{ github.repository }}/issues/${{ github.event.issue.number || inputs.issue_number }}" claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} - claude_args: "--model claude-sonnet-4-5-20250929" + model: "claude-sonnet-4-5-20250929" claude_env: | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml new file mode 100644 index 0000000000..0fe91c7440 --- /dev/null +++ b/.github/workflows/unit_tests.yml @@ -0,0 +1,73 @@ +name: Unit Tests + +# Download a platform's test artifact, run ctest, and upload the JUnit +# results for aggregation. Called once per arch from build_all.yml. +on: + workflow_call: + inputs: + os: + required: true + type: string + artifact: + description: Test artifact uploaded by the build leg + required: true + type: string + test-dir: + description: Built tests dir; defaults to build/tests, override for arch-separated builds + required: false + type: string + default: build/tests + +jobs: + unit_tests: + # Static "Unit Tests"; the per-arch label is the caller's job name, so the + # graph shows e.g. "Windows x64 / Unit Tests". + name: Unit Tests + runs-on: ${{ inputs.os }} + steps: + - name: Checkout + uses: actions/checkout@v7 + with: + # tests/data is referenced by absolute path (TEST_DATA_DIR). + sparse-checkout: | + .github + scripts + tests + - name: Apt-Install Dependencies + if: runner.os == 'Linux' && !vars.SELF_HOSTED + uses: ./.github/actions/apt-install-deps + - name: Restore Test Artifact + uses: actions/download-artifact@v8 + with: + name: ${{ inputs.artifact }} + - uses: lukka/get-cmake@latest + with: + cmakeVersion: "~4.3.0" # use most recent 4.3.x version + useLocalCache: true + useCloudCache: true + - name: Unpackage and Run Unit Tests + timeout-minutes: 20 + shell: bash + run: | + tar -xvf build_tests.tar + # Multi-config generators (Windows/macOS) need a config; Linux is single-config. + scripts/run_unit_tests.sh "${{ inputs.test-dir }}" "${{ runner.os != 'Linux' && 'Release' || '' }}" + - name: Upload Test Logs + if: ${{ failure() }} + uses: actions/upload-artifact@v7 + with: + name: unit-test-logs-${{ inputs.artifact }} + path: ${{ inputs.test-dir }}/**/*.log + - name: Upload Test Results + if: always() + uses: actions/upload-artifact@v7 + with: + name: test-results-${{ inputs.artifact }} + path: ctest_results.xml + retention-days: 5 + if-no-files-found: warn + - name: Delete Test Artifact + if: success() + uses: geekyeggo/delete-artifact@v6 + with: + name: ${{ inputs.artifact }} diff --git a/build_release_macos.sh b/build_release_macos.sh index e599f2246e..ecfef5dacc 100755 --- a/build_release_macos.sh +++ b/build_release_macos.sh @@ -59,7 +59,7 @@ while getopts ":dpa:snt:xbc:i:1Tuh" opt; do echo " -c: Set CMake build configuration, default is Release" echo " -i: Add a prefix to ignore during CMake dependency discovery (repeatable), defaults to /opt/local:/usr/local:/opt/homebrew" echo " -1: Use single job for building" - echo " -T: Build and run tests" + echo " -T: Build and run tests (set ORCA_TESTS_BUILD_ONLY=1 to build without running)" exit 0 ;; * ) @@ -216,13 +216,10 @@ function build_slicer() { cmake --build . --config "$BUILD_CONFIG" --target "$SLICER_BUILD_TARGET" ) - if [ "1." == "$BUILD_TESTS". ]; then - echo "Running tests for $_ARCH..." - ( - set -x - cd "$PROJECT_BUILD_DIR" - ctest --build-config "$BUILD_CONFIG" --output-on-failure - ) + # -T also runs the tests; ORCA_TESTS_BUILD_ONLY=1 builds them without + # running, so CI can build here and run them in a dedicated job. + if [ "1." == "$BUILD_TESTS". ] && [ "1." != "$ORCA_TESTS_BUILD_ONLY". ]; then + "$PROJECT_DIR/scripts/run_unit_tests.sh" "build/$_ARCH/tests" "$BUILD_CONFIG" fi echo "Verify localization with gettext..." diff --git a/build_release_vs.bat b/build_release_vs.bat index 8fb86586a5..e9ae7ca527 100644 --- a/build_release_vs.bat +++ b/build_release_vs.bat @@ -20,6 +20,12 @@ for %%a in (%*) do ( if "%%a"=="-x" set USE_NINJA=1 ) +@REM Check for unit-tests option ("tests") +set BUILD_TESTS=OFF +for %%a in (%*) do ( + if /I "%%a"=="tests" set BUILD_TESTS=ON +) + if "%USE_NINJA%"=="1" ( echo Using Ninja Multi-Config generator set CMAKE_GENERATOR="Ninja Multi-Config" @@ -154,10 +160,10 @@ cd %build_dir% echo on set CMAKE_POLICY_VERSION_MINIMUM=3.5 if "%USE_NINJA%"=="1" ( - cmake .. -G %CMAKE_GENERATOR% -DORCA_TOOLS=ON %SIG_FLAG% -DCMAKE_BUILD_TYPE=%build_type% + cmake .. -G %CMAKE_GENERATOR% -DORCA_TOOLS=ON %SIG_FLAG% -DBUILD_TESTS=%BUILD_TESTS% -DCMAKE_BUILD_TYPE=%build_type% cmake --build . --config %build_type% --target ALL_BUILD ) else ( - cmake .. -G %CMAKE_GENERATOR% -A %arch% -DORCA_TOOLS=ON %SIG_FLAG% -DCMAKE_BUILD_TYPE=%build_type% + cmake .. -G %CMAKE_GENERATOR% -A %arch% -DORCA_TOOLS=ON %SIG_FLAG% -DBUILD_TESTS=%BUILD_TESTS% -DCMAKE_BUILD_TYPE=%build_type% cmake --build . --config %build_type% --target ALL_BUILD -- -m ) @echo off diff --git a/localization/i18n/OrcaSlicer.pot b/localization/i18n/OrcaSlicer.pot index b50789a152..79b8fb0fb0 100644 --- a/localization/i18n/OrcaSlicer.pot +++ b/localization/i18n/OrcaSlicer.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-07 10:05-0300\n" +"POT-Creation-Date: 2026-07-07 16:50-0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -325,6 +325,7 @@ msgstr "" msgid "Optimize orientation" msgstr "" +msgctxt "Verb" msgid "Scale" msgstr "" @@ -368,6 +369,9 @@ msgstr "" msgid "Object operations" msgstr "" +msgid "Scale" +msgstr "" + msgid "Volume operations" msgstr "" @@ -413,6 +417,10 @@ msgstr "" msgid "Reset current rotation to real zeros." msgstr "" +msgctxt "Noun" +msgid "Scale" +msgstr "" + #. TRN - Input label. Be short as possible msgid "Size" msgstr "" @@ -3499,6 +3507,7 @@ msgstr "" msgid "Save" msgstr "" +msgctxt "Navigation" msgid "Back" msgstr "" @@ -4781,6 +4790,7 @@ msgstr "" msgid "Color change" msgstr "" +msgctxt "Noun" msgid "Print" msgstr "" @@ -4936,11 +4946,15 @@ msgstr "" msgid "Align to Y axis" msgstr "" -msgctxt "Camera" +msgctxt "Camera View" +msgid "Back" +msgstr "" + +msgctxt "Camera View" msgid "Left" msgstr "" -msgctxt "Camera" +msgctxt "Camera View" msgid "Right" msgstr "" @@ -5257,6 +5271,10 @@ msgstr "" msgid "Export G-code file" msgstr "" +msgctxt "Verb" +msgid "Print" +msgstr "" + msgid "Export plate sliced file" msgstr "" @@ -13920,6 +13938,9 @@ msgstr "" msgid "Aligned back" msgstr "" +msgid "Back" +msgstr "" + msgid "Random" msgstr "" diff --git a/localization/i18n/ca/OrcaSlicer_ca.po b/localization/i18n/ca/OrcaSlicer_ca.po index 9f18508808..a509c7a89f 100644 --- a/localization/i18n/ca/OrcaSlicer_ca.po +++ b/localization/i18n/ca/OrcaSlicer_ca.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-07 10:05-0300\n" +"POT-Creation-Date: 2026-07-07 16:46-0300\n" "PO-Revision-Date: 2025-03-15 10:55+0100\n" "Last-Translator: \n" "Language-Team: \n" @@ -332,6 +332,7 @@ msgstr "Gizmo-Rotació" msgid "Optimize orientation" msgstr "Optimitzar l'orientació" +msgctxt "Verb" msgid "Scale" msgstr "Escalar" @@ -376,6 +377,9 @@ msgstr "Ràtios d'escala" msgid "Object operations" msgstr "Operacions amb objectes" +msgid "Scale" +msgstr "Escalar" + # TODO: Review, changed by lang refactor. PR 14254 msgid "Volume operations" msgstr "Operacions de volum" @@ -427,6 +431,10 @@ msgstr "Restableix la rotació actual al valor d'quan s'ha obert l'eina de rotac msgid "Reset current rotation to real zeros." msgstr "Restableix la rotació actual a zeros reals." +msgctxt "Noun" +msgid "Scale" +msgstr "Escalar" + #. TRN - Input label. Be short as possible msgid "Size" msgstr "Mida" @@ -3652,7 +3660,7 @@ msgstr "Calibratge completat. Trobeu la línia d'extrusió més uniforme al vost msgid "Save" msgstr "Desar" -# TODO: Review, changed by lang refactor. PR 14254 +msgctxt "Navigation" msgid "Back" msgstr "Darrera" @@ -5035,6 +5043,7 @@ msgstr "Canvis d'eina" msgid "Color change" msgstr "Canvi de color" +msgctxt "Noun" msgid "Print" msgstr "Imprimir" @@ -5198,11 +5207,15 @@ msgstr "Evitar la regió de calibratge d'extrusió" msgid "Align to Y axis" msgstr "Alinear a l'eix Y" -msgctxt "Camera" +msgctxt "Camera View" +msgid "Back" +msgstr "Darrera" + +msgctxt "Camera View" msgid "Left" msgstr "Esquerra" -msgctxt "Camera" +msgctxt "Camera View" msgid "Right" msgstr "Dreta" @@ -5531,6 +5544,10 @@ msgstr "Imprimir placa d'impressió" msgid "Export G-code file" msgstr "Exportar el fitxer del Codi-G" +msgctxt "Verb" +msgid "Print" +msgstr "Imprimir" + msgid "Export plate sliced file" msgstr "Exportar fitxer de la placa laminada" @@ -14808,6 +14825,10 @@ msgstr "Alineat" msgid "Aligned back" msgstr "Alineat al darrere" +# TODO: Review, changed by lang refactor. PR 14254 +msgid "Back" +msgstr "Darrera" + msgid "Random" msgstr "Aleatori" @@ -19647,6 +19668,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" +#~ msgstr "Imprimir" + #~ msgid "in" #~ msgstr "polç" @@ -21103,10 +21127,6 @@ msgstr "" #~ msgid "Unselect" #~ msgstr "Desmarcar" -#~ msgctxt "Verb" -#~ msgid "Scale" -#~ msgstr "Escalar" - #~ msgid "Lift Z Enforcement" #~ msgstr "Forçar elevació Z" diff --git a/localization/i18n/cs/OrcaSlicer_cs.po b/localization/i18n/cs/OrcaSlicer_cs.po index a5bdbb3ed9..7c32cff068 100644 --- a/localization/i18n/cs/OrcaSlicer_cs.po +++ b/localization/i18n/cs/OrcaSlicer_cs.po @@ -3,7 +3,7 @@ msgid "" msgstr "" "Project-Id-Version: Orca Slicer\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-07 10:05-0300\n" +"POT-Creation-Date: 2026-07-07 16:46-0300\n" "PO-Revision-Date: \n" "Last-Translator: Jakub Hencl\n" "Language-Team: \n" @@ -327,6 +327,7 @@ msgstr "Gizmo-Otočit" msgid "Optimize orientation" msgstr "Optimalizovat orientaci" +msgctxt "Verb" msgid "Scale" msgstr "Měřítko" @@ -371,6 +372,9 @@ msgstr "Poměry měřítka" msgid "Object operations" msgstr "Operace s objektem" +msgid "Scale" +msgstr "Měřítko" + # TODO: Review, changed by lang refactor. PR 14254 msgid "Volume operations" msgstr "Operace s hlasitostí" @@ -422,6 +426,10 @@ msgstr "Obnovit aktuální rotaci na hodnotu při otevření nástroje pro rotac msgid "Reset current rotation to real zeros." msgstr "Obnovit aktuální rotaci na skutečné nuly." +msgctxt "Noun" +msgid "Scale" +msgstr "Měřítko" + #. TRN - Input label. Be short as possible msgid "Size" msgstr "Velikost" @@ -3651,7 +3659,7 @@ msgstr "Kalibrace byla dokončena. Najděte na vyhřívané podložce nejrovnom msgid "Save" msgstr "Uložit" -# TODO: Review, changed by lang refactor. PR 14254 +msgctxt "Navigation" msgid "Back" msgstr "Zpět" @@ -5032,6 +5040,7 @@ msgstr "Výměny nástrojů" msgid "Color change" msgstr "Změna barvy" +msgctxt "Noun" msgid "Print" msgstr "Tisk" @@ -5195,11 +5204,15 @@ msgstr "Vyhněte se kalibrační oblasti extruze" msgid "Align to Y axis" msgstr "Zarovnat na osu Y" -msgctxt "Camera" +msgctxt "Camera View" +msgid "Back" +msgstr "Zpět" + +msgctxt "Camera View" msgid "Left" msgstr "Levý" -msgctxt "Camera" +msgctxt "Camera View" msgid "Right" msgstr "Pravý" @@ -5527,6 +5540,10 @@ msgstr "Tisková deska" msgid "Export G-code file" msgstr "Exportovat G-code soubor" +msgctxt "Verb" +msgid "Print" +msgstr "Tisk" + msgid "Export plate sliced file" msgstr "Exportovat nařezaný soubor desky" @@ -14855,6 +14872,10 @@ msgstr "Zarovnáno" msgid "Aligned back" msgstr "Zarovnat zpět" +# TODO: Review, changed by lang refactor. PR 14254 +msgid "Back" +msgstr "Zpět" + msgid "Random" msgstr "Náhodně" @@ -19681,6 +19702,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" +#~ msgstr "Tisk" + #~ msgid "in" #~ msgstr "v" diff --git a/localization/i18n/de/OrcaSlicer_de.po b/localization/i18n/de/OrcaSlicer_de.po index 3849457ddc..1fac95632e 100644 --- a/localization/i18n/de/OrcaSlicer_de.po +++ b/localization/i18n/de/OrcaSlicer_de.po @@ -3,7 +3,7 @@ msgid "" msgstr "" "Project-Id-Version: Orca Slicer\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-07 10:05-0300\n" +"POT-Creation-Date: 2026-07-07 16:46-0300\n" "PO-Revision-Date: \n" "Last-Translator: Heiko Liebscher \n" "Language-Team: \n" @@ -327,6 +327,7 @@ msgstr "Gizmo-Drehen" msgid "Optimize orientation" msgstr "Optimiere Ausrichtung" +msgctxt "Verb" msgid "Scale" msgstr "Skalieren" @@ -371,6 +372,9 @@ msgstr "Skalierungsverhältnisse" msgid "Object operations" msgstr "Objekt-Operationen" +msgid "Scale" +msgstr "Skalieren" + # TODO: Review, changed by lang refactor. PR 14254 msgid "Volume operations" msgstr "Volumen Operationen" @@ -422,6 +426,10 @@ msgstr "Setze die aktuelle Rotation auf den Wert zurück, als das Rotationswerkz msgid "Reset current rotation to real zeros." msgstr "Setze die aktuelle Rotation auf die realen Nullen zurück." +msgctxt "Noun" +msgid "Scale" +msgstr "Skalieren" + #. TRN - Input label. Be short as possible msgid "Size" msgstr "Größe" @@ -3654,7 +3662,7 @@ msgstr "Kalibrierung abgeschlossen. Bitte suchen Sie die gleichmäßigste Extrus msgid "Save" msgstr "Speichern" -# TODO: Review, changed by lang refactor. PR 14254 +msgctxt "Navigation" msgid "Back" msgstr "Zurück" @@ -5036,6 +5044,7 @@ msgstr "Werkzeugwechsel" msgid "Color change" msgstr "Farbwechsel" +msgctxt "Noun" msgid "Print" msgstr "aktuelle Platte drucken" @@ -5199,11 +5208,15 @@ msgstr "Vermeiden Sie den Bereich der Extrusionskalibrierung" msgid "Align to Y axis" msgstr "An Y-Achse ausrichten" -msgctxt "Camera" +msgctxt "Camera View" +msgid "Back" +msgstr "Zurück" + +msgctxt "Camera View" msgid "Left" msgstr "links" -msgctxt "Camera" +msgctxt "Camera View" msgid "Right" msgstr "rechts" @@ -5529,6 +5542,10 @@ msgstr "Aktuelle Platte drucken" msgid "Export G-code file" msgstr "G-Code als Datei exportieren" +msgctxt "Verb" +msgid "Print" +msgstr "aktuelle Platte drucken" + msgid "Export plate sliced file" msgstr "Exportiere aktuelle Platte als STL Datei" @@ -14962,6 +14979,10 @@ msgstr "Ausgerichtet" msgid "Aligned back" msgstr "Ausgerichtet hinten" +# TODO: Review, changed by lang refactor. PR 14254 +msgid "Back" +msgstr "Zurück" + msgid "Random" msgstr "Zufall" @@ -19822,6 +19843,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" +#~ msgstr "aktuelle Platte drucken" + #~ msgid "in" #~ msgstr "in" @@ -21730,10 +21754,6 @@ msgstr "" #~ msgid "Unselect" #~ msgstr "Abwählen" -#~ msgctxt "Verb" -#~ msgid "Scale" -#~ msgstr "Skalieren" - #~ msgid "Lift Z Enforcement" #~ msgstr "Z-Höhe einhalten" diff --git a/localization/i18n/en/OrcaSlicer_en.po b/localization/i18n/en/OrcaSlicer_en.po index 6e6628b5ce..b314b99808 100644 --- a/localization/i18n/en/OrcaSlicer_en.po +++ b/localization/i18n/en/OrcaSlicer_en.po @@ -3,7 +3,7 @@ msgid "" msgstr "" "Project-Id-Version: Orca Slicer\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-07 10:05-0300\n" +"POT-Creation-Date: 2026-07-07 16:46-0300\n" "PO-Revision-Date: 2026-06-17 15:44-0300\n" "Last-Translator: Alexandre Folle de Menezes\n" "Language-Team: \n" @@ -321,6 +321,7 @@ msgstr "" msgid "Optimize orientation" msgstr "" +msgctxt "Verb" msgid "Scale" msgstr "" @@ -364,6 +365,9 @@ msgstr "" msgid "Object operations" msgstr "" +msgid "Scale" +msgstr "" + msgid "Volume operations" msgstr "" @@ -409,6 +413,10 @@ msgstr "" msgid "Reset current rotation to real zeros." msgstr "" +msgctxt "Noun" +msgid "Scale" +msgstr "" + #. TRN - Input label. Be short as possible msgid "Size" msgstr "" @@ -3495,6 +3503,7 @@ msgstr "" msgid "Save" msgstr "" +msgctxt "Navigation" msgid "Back" msgstr "" @@ -4777,6 +4786,7 @@ msgstr "" msgid "Color change" msgstr "" +msgctxt "Noun" msgid "Print" msgstr "" @@ -4932,11 +4942,15 @@ msgstr "" msgid "Align to Y axis" msgstr "" -msgctxt "Camera" +msgctxt "Camera View" +msgid "Back" +msgstr "" + +msgctxt "Camera View" msgid "Left" msgstr "" -msgctxt "Camera" +msgctxt "Camera View" msgid "Right" msgstr "" @@ -5253,6 +5267,10 @@ msgstr "" msgid "Export G-code file" msgstr "" +msgctxt "Verb" +msgid "Print" +msgstr "" + msgid "Export plate sliced file" msgstr "" @@ -13916,6 +13934,9 @@ msgstr "" msgid "Aligned back" msgstr "" +msgid "Back" +msgstr "" + msgid "Random" msgstr "" diff --git a/localization/i18n/es/OrcaSlicer_es.po b/localization/i18n/es/OrcaSlicer_es.po index cdbe024f27..a0cdb98728 100644 --- a/localization/i18n/es/OrcaSlicer_es.po +++ b/localization/i18n/es/OrcaSlicer_es.po @@ -3,7 +3,7 @@ msgid "" msgstr "" "Project-Id-Version: Orca Slicer\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-07 10:05-0300\n" +"POT-Creation-Date: 2026-07-07 16:46-0300\n" "PO-Revision-Date: \n" "Last-Translator: Ian A. Bassi <>\n" "Language-Team: \n" @@ -321,6 +321,7 @@ msgstr "Herramienta de rotación" msgid "Optimize orientation" msgstr "Optimizar orientación" +msgctxt "Verb" msgid "Scale" msgstr "Escalar" @@ -332,7 +333,7 @@ msgstr "Error: Por favor, cierre primero todos los menús de la barra de herrami msgctxt "inches" msgid "in" -msgstr "" +msgstr "\"" msgid "mm" msgstr "mm" @@ -364,6 +365,9 @@ msgstr "Factor de escalado" msgid "Object operations" msgstr "Operaciones con objetos" +msgid "Scale" +msgstr "Escalar" + msgid "Volume operations" msgstr "Operaciones de volumen" @@ -409,6 +413,10 @@ msgstr "Restablecer la rotación actual al valor que tenía al abrir la herramie msgid "Reset current rotation to real zeros." msgstr "Restablecer la rotación actual a ceros reales." +msgctxt "Noun" +msgid "Scale" +msgstr "Escala" + #. TRN - Input label. Be short as possible msgid "Size" msgstr "Tamaño" @@ -3580,6 +3588,7 @@ msgstr "Calibración completada. Por favor, observe cual es línea de extrusión msgid "Save" msgstr "Guardar" +msgctxt "Navigation" msgid "Back" msgstr "Atrás" @@ -4709,7 +4718,7 @@ msgid "Support interface" msgstr "Interfaz de soporte" msgid "Prime tower" -msgstr "Torre de Purga" +msgstr "Torre de purga" msgid "Bottom surface" msgstr "Relleno sólido inferior" @@ -4930,8 +4939,9 @@ msgstr "Cambios de herramienta" msgid "Color change" msgstr "Cambio de color" +msgctxt "Noun" msgid "Print" -msgstr "Imprimir" +msgstr "Impresión" msgid "Printer" msgstr "Impresora" @@ -5089,11 +5099,15 @@ msgstr "Evitar la zona de calibración del extrusor" msgid "Align to Y axis" msgstr "Alinear con el eje Y" -msgctxt "Camera" +msgctxt "Camera View" +msgid "Back" +msgstr "Posterior" + +msgctxt "Camera View" msgid "Left" msgstr "Izquierda" -msgctxt "Camera" +msgctxt "Camera View" msgid "Right" msgstr "Derecha" @@ -5416,6 +5430,10 @@ msgstr "Imprimir cama" msgid "Export G-code file" msgstr "Exportar archivo G-Code" +msgctxt "Verb" +msgid "Print" +msgstr "Imprimir" + msgid "Export plate sliced file" msgstr "Exportar los objetos laminados de la cama a un archivo" @@ -5488,10 +5506,10 @@ msgid "Front View" msgstr "Vista frontal" msgid "Rear" -msgstr "Trasera" +msgstr "Posterior" msgid "Rear View" -msgstr "Vista trasera" +msgstr "Vista posterior" msgid "Left View" msgstr "Vista izquierda" @@ -9190,7 +9208,7 @@ msgstr "Característica experimental: Retraer y cortar el filamento a mayor dist msgid "" "When recording timelapse without toolhead, it is recommended to add a \"Timelapse Wipe Tower\" \n" "by right-click the empty position of build plate and choose \"Add Primitive\"->\"Timelapse Wipe Tower\"." -msgstr "Cuando se graba un timelapse sin cabezal, se recomienda añadir una \"Torre de Purga de Timelapse\" haciendo clic con el botón derecho del ratón en una posición vacía de la cama de impresión y seleccionando \"Añadir Primitivo\"->Torre de Purga de Timelapse\"." +msgstr "Cuando se graba un timelapse sin cabezal, se recomienda añadir una \"Torre de purga de Timelapse\" haciendo clic con el botón derecho del ratón en una posición vacía de la cama de impresión y seleccionando \"Añadir Primitivo\"->\"Torre de purga de Timelapse\"." msgid "A copy of the current system preset will be created, which will be detached from the system preset." msgstr "Se creará una copia de el perfil de sistema actual, que quedará separado del perfil de sistema." @@ -10896,7 +10914,7 @@ msgid " is too close to clumping detection area, there may be collisions when pr msgstr " está demasiado cerca del área de detección de aglomeraciones, puede haber colisiones durante la impresión." msgid "Prime Tower" -msgstr "Torre de Purga" +msgstr "Torre de purga" msgid " is too close to others, and collisions may be caused.\n" msgstr " está demasiado cerca de otros, y se pueden producir colisiones.\n" @@ -12750,7 +12768,7 @@ msgid "After a tool change, the exact position of the newly loaded filament insi msgstr "Tras un cambio de cabezal, es posible que no se conozca la posición exacta del filamento recién cargado dentro de la boquilla y que la presión del filamento aún no sea estable. Antes de purgar el cabezal de impresión en un relleno o en un objeto de sacrificio, OrcaSlicer siempre cebará esta cantidad de material en la torre de purga para producir sucesivas extrusiones de relleno u objetos de sacrificio de forma fiable." msgid "Wipe tower cooling" -msgstr "Refrigeración de Torre de Purga" +msgstr "Refrigeración de torre de purga" msgid "Temperature drop before entering filament tower" msgstr "Descenso de temperatura antes de entrar en la torre de filamentos" @@ -14657,6 +14675,9 @@ msgstr "Alineado" msgid "Aligned back" msgstr "Alineado atrás" +msgid "Back" +msgstr "Atrás" + msgid "Random" msgstr "Aleatorio" @@ -19429,6 +19450,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" +#~ msgstr "Imprimir" + #~ msgid "in" #~ msgstr "pulg" @@ -21012,10 +21036,6 @@ msgstr "" #~ msgid "Unselect" #~ msgstr "Deseleccionar" -#~ msgctxt "Verb" -#~ msgid "Scale" -#~ msgstr "Escala" - #~ msgid "Lift Z Enforcement" #~ msgstr "Forzar elevación Z" diff --git a/localization/i18n/fr/OrcaSlicer_fr.po b/localization/i18n/fr/OrcaSlicer_fr.po index c789268248..e3f054b7c1 100644 --- a/localization/i18n/fr/OrcaSlicer_fr.po +++ b/localization/i18n/fr/OrcaSlicer_fr.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: Orca Slicer\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-07 10:05-0300\n" +"POT-Creation-Date: 2026-07-07 16:46-0300\n" "PO-Revision-Date: \n" "Last-Translator: \n" "Language-Team: Guislain Cyril, Thomas Lété\n" @@ -324,6 +324,7 @@ msgstr "Gizmo-Pivoter" msgid "Optimize orientation" msgstr "Optimiser l'orientation" +msgctxt "Verb" msgid "Scale" msgstr "Redimensionner" @@ -367,6 +368,9 @@ msgstr "Rapports d'échelle" msgid "Object operations" msgstr "Opérations sur les objets" +msgid "Scale" +msgstr "Redimensionner" + msgid "Volume operations" msgstr "Opérations sur les volumes" @@ -412,6 +416,10 @@ msgstr "Réinitialiser la rotation actuelle à la valeur lors de l'ouverture de msgid "Reset current rotation to real zeros." msgstr "Réinitialiser la rotation actuelle à zéro." +msgctxt "Noun" +msgid "Scale" +msgstr "Redimensionner" + #. TRN - Input label. Be short as possible msgid "Size" msgstr "Taille" @@ -3569,6 +3577,7 @@ msgstr "Calibrage terminé. Veuillez trouver la ligne d'extrusion la plus unifor msgid "Save" msgstr "Enregistrer" +msgctxt "Navigation" msgid "Back" msgstr "Arrière" @@ -4916,6 +4925,7 @@ msgstr "Changements d’outil" msgid "Color change" msgstr "Changement de couleur" +msgctxt "Noun" msgid "Print" msgstr "Imprimer" @@ -5075,11 +5085,15 @@ msgstr "Éviter la région de calibration de l'extrusion" msgid "Align to Y axis" msgstr "Aligner sur l’axe Y" -msgctxt "Camera" +msgctxt "Camera View" +msgid "Back" +msgstr "Arrière" + +msgctxt "Camera View" msgid "Left" msgstr "Gauche" -msgctxt "Camera" +msgctxt "Camera View" msgid "Right" msgstr "Droite" @@ -5402,6 +5416,10 @@ msgstr "Imprimer le plateau" msgid "Export G-code file" msgstr "Exporter le fichier G-code" +msgctxt "Verb" +msgid "Print" +msgstr "Imprimer" + msgid "Export plate sliced file" msgstr "Exporter le fichier découpé du plateau" @@ -14628,6 +14646,9 @@ msgstr "Alignée" msgid "Aligned back" msgstr "Aligné à l'arrière" +msgid "Back" +msgstr "Arrière" + msgid "Random" msgstr "Aléatoire" @@ -19397,6 +19418,9 @@ msgstr "" "Éviter la déformation\n" "Saviez-vous que lors de l’impression de matériaux susceptibles de se déformer, tels que l’ABS, une augmentation appropriée de la température du plateau chauffant peut réduire la probabilité de déformation?" +#~ msgid "Print" +#~ msgstr "Imprimer" + #~ msgid "in" #~ msgstr "dans" @@ -21107,10 +21131,6 @@ msgstr "" #~ msgid "Unselect" #~ msgstr "Désélectionner" -#~ msgctxt "Verb" -#~ msgid "Scale" -#~ msgstr "Redimensionner" - #~ msgid "Lift Z Enforcement" #~ msgstr "Exécution du décalage en Z" diff --git a/localization/i18n/hu/OrcaSlicer_hu.po b/localization/i18n/hu/OrcaSlicer_hu.po index 497f0ebf29..1cf18316e2 100644 --- a/localization/i18n/hu/OrcaSlicer_hu.po +++ b/localization/i18n/hu/OrcaSlicer_hu.po @@ -3,7 +3,7 @@ msgid "" msgstr "" "Project-Id-Version: Orca Slicer\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-07 10:05-0300\n" +"POT-Creation-Date: 2026-07-07 16:46-0300\n" "Language: hu\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -324,6 +324,7 @@ msgstr "Gizmo-Forgatás" msgid "Optimize orientation" msgstr "Orientáció optimalizálása" +msgctxt "Verb" msgid "Scale" msgstr "Átméretezés" @@ -368,6 +369,9 @@ msgstr "Méretarányok" msgid "Object operations" msgstr "Objektum műveletek" +msgid "Scale" +msgstr "Átméretezés" + # TODO: Review, changed by lang refactor. PR 14254 msgid "Volume operations" msgstr "Térfogat műveletek" @@ -419,6 +423,10 @@ msgstr "Az aktuális forgatás visszaállítása a forgatás eszköz megnyitása msgid "Reset current rotation to real zeros." msgstr "Az aktuális forgatás visszaállítása valódi nullára." +msgctxt "Noun" +msgid "Scale" +msgstr "Átméretezés" + #. TRN - Input label. Be short as possible msgid "Size" msgstr "Méret" @@ -3645,7 +3653,7 @@ msgstr "A kalibrálás befejeződött. Kérlek, válaszd ki az alábbi képen l msgid "Save" msgstr "Mentés" -# TODO: Review, changed by lang refactor. PR 14254 +msgctxt "Navigation" msgid "Back" msgstr "Hátul" @@ -5026,6 +5034,7 @@ msgstr "Szerszámváltások" msgid "Color change" msgstr "Színváltás" +msgctxt "Noun" msgid "Print" msgstr "Nyomtatás" @@ -5189,11 +5198,15 @@ msgstr "Extrudáláskalibráció környékének elkerülése" msgid "Align to Y axis" msgstr "Igazítás az Y-tengelyhez" -msgctxt "Camera" +msgctxt "Camera View" +msgid "Back" +msgstr "Hátul" + +msgctxt "Camera View" msgid "Left" msgstr "Bal" -msgctxt "Camera" +msgctxt "Camera View" msgid "Right" msgstr "Jobb" @@ -5521,6 +5534,10 @@ msgstr "Nyomtatótálca" msgid "Export G-code file" msgstr "G-kód fájl exportálása" +msgctxt "Verb" +msgid "Print" +msgstr "Nyomtatás" + msgid "Export plate sliced file" msgstr "Szeletelt tálca exportálása" @@ -14880,6 +14897,10 @@ msgstr "Igazított" msgid "Aligned back" msgstr "Hátulra igazítva" +# TODO: Review, changed by lang refactor. PR 14254 +msgid "Back" +msgstr "Hátul" + msgid "Random" msgstr "Véletlenszerû" @@ -19712,6 +19733,9 @@ msgstr "" "Kunkorodás elkerülése\n" "Tudtad, hogy a kunkorodásra hajlamos anyagok (például ABS) nyomtatásakor a tárgyasztal hőmérsékletének növelése csökkentheti a kunkorodás valószínűségét?" +#~ msgid "Print" +#~ msgstr "Nyomtatás" + #~ msgid "in" #~ msgstr "in" diff --git a/localization/i18n/it/OrcaSlicer_it.po b/localization/i18n/it/OrcaSlicer_it.po index 6aa547173a..70ee57c0e9 100644 --- a/localization/i18n/it/OrcaSlicer_it.po +++ b/localization/i18n/it/OrcaSlicer_it.po @@ -3,7 +3,7 @@ msgid "" msgstr "" "Project-Id-Version: Orca Slicer\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-07 10:05-0300\n" +"POT-Creation-Date: 2026-07-07 16:46-0300\n" "PO-Revision-Date: \n" "Last-Translator: \n" "Language-Team: \n" @@ -327,6 +327,7 @@ msgstr "Strumento di rotazione" msgid "Optimize orientation" msgstr "Ottimizza orientamento" +msgctxt "Verb" msgid "Scale" msgstr "Ridimensiona" @@ -371,6 +372,9 @@ msgstr "Rapporti di scala" msgid "Object operations" msgstr "Operazioni sugli oggetti" +msgid "Scale" +msgstr "Ridimensiona" + # TODO: Review, changed by lang refactor. PR 14254 msgid "Volume operations" msgstr "Operazioni volume" @@ -422,6 +426,10 @@ msgstr "Reimposta la rotazione corrente al valore di apertura dello strumento." msgid "Reset current rotation to real zeros." msgstr "Reimposta la rotazione corrente a zero reale." +msgctxt "Noun" +msgid "Scale" +msgstr "Ridimensiona" + #. TRN - Input label. Be short as possible msgid "Size" msgstr "Dimensioni" @@ -3646,7 +3654,7 @@ msgstr "Calibrazione completata. Trova la linea di estrusione più uniforme sul msgid "Save" msgstr "Salva" -# TODO: Review, changed by lang refactor. PR 14254 +msgctxt "Navigation" msgid "Back" msgstr "Posteriore" @@ -5027,6 +5035,7 @@ msgstr "Cambi testina" msgid "Color change" msgstr "Cambio colore" +msgctxt "Noun" msgid "Print" msgstr "Stampa" @@ -5190,11 +5199,15 @@ msgstr "Evitare la regione di calibrazione dell'estrusione" msgid "Align to Y axis" msgstr "Allinea all'asse Y" -msgctxt "Camera" +msgctxt "Camera View" +msgid "Back" +msgstr "Posteriore" + +msgctxt "Camera View" msgid "Left" msgstr "Sinistra" -msgctxt "Camera" +msgctxt "Camera View" msgid "Right" msgstr "Destra" @@ -5522,6 +5535,10 @@ msgstr "Piatto di stampa" msgid "Export G-code file" msgstr "Esporta file G-code" +msgctxt "Verb" +msgid "Print" +msgstr "Stampa" + msgid "Export plate sliced file" msgstr "Esporta il file del piatto elaborato" @@ -14854,6 +14871,10 @@ msgstr "Allineato" msgid "Aligned back" msgstr "Allineato dietro" +# TODO: Review, changed by lang refactor. PR 14254 +msgid "Back" +msgstr "Posteriore" + msgid "Random" msgstr "Casuale" @@ -19692,6 +19713,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" +#~ msgstr "Stampa" + #~ msgid "in" #~ msgstr "in" @@ -21208,10 +21232,6 @@ msgstr "" #~ msgid "Unselect" #~ msgstr "Deseleziona" -#~ msgctxt "Verb" -#~ msgid "Scale" -#~ msgstr "Ridimensiona" - #~ msgid "Lift Z Enforcement" #~ msgstr "Applicazione dell'ascensore Z" diff --git a/localization/i18n/ja/OrcaSlicer_ja.po b/localization/i18n/ja/OrcaSlicer_ja.po index 3faef352c0..03a498fd2a 100644 --- a/localization/i18n/ja/OrcaSlicer_ja.po +++ b/localization/i18n/ja/OrcaSlicer_ja.po @@ -3,7 +3,7 @@ msgid "" msgstr "" "Project-Id-Version: Orca Slicer\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-07 10:05-0300\n" +"POT-Creation-Date: 2026-07-07 16:46-0300\n" "PO-Revision-Date: \n" "Last-Translator: \n" "Language-Team: \n" @@ -327,6 +327,7 @@ msgstr "ギズモ-回転" msgid "Optimize orientation" msgstr "向きを最適化" +msgctxt "Verb" msgid "Scale" msgstr "スケール" @@ -371,6 +372,9 @@ msgstr "倍率" msgid "Object operations" msgstr "オブジェクト操作" +msgid "Scale" +msgstr "スケール" + # TODO: Review, changed by lang refactor. PR 14254 msgid "Volume operations" msgstr "操作" @@ -422,6 +426,10 @@ msgstr "回転ツールを開いた時の値にリセットします。" msgid "Reset current rotation to real zeros." msgstr "現在の回転を0にリセットします。" +msgctxt "Noun" +msgid "Scale" +msgstr "スケール" + #. TRN - Input label. Be short as possible msgid "Size" msgstr "サイズ" @@ -3629,7 +3637,7 @@ msgstr "キャリブレーションが完了しました。下の写真のよう msgid "Save" msgstr "保存" -# TODO: Review, changed by lang refactor. PR 14254 +msgctxt "Navigation" msgid "Back" msgstr "背面" @@ -5000,6 +5008,7 @@ msgstr "ツールチェンジ" msgid "Color change" msgstr "色変更" +msgctxt "Noun" msgid "Print" msgstr "造形する" @@ -5163,11 +5172,15 @@ msgstr "押出しキャリブレーション領域を避ける" msgid "Align to Y axis" msgstr "Y軸に整列" -msgctxt "Camera" +msgctxt "Camera View" +msgid "Back" +msgstr "背面" + +msgctxt "Camera View" msgid "Left" msgstr "左" -msgctxt "Camera" +msgctxt "Camera View" msgid "Right" msgstr "右" @@ -5489,6 +5502,10 @@ msgstr "造形開始" msgid "Export G-code file" msgstr "G-codeをエクスポート" +msgctxt "Verb" +msgid "Print" +msgstr "造形する" + msgid "Export plate sliced file" msgstr "エクスポート" @@ -14502,6 +14519,10 @@ msgstr "整列" msgid "Aligned back" msgstr "" +# TODO: Review, changed by lang refactor. PR 14254 +msgid "Back" +msgstr "背面" + msgid "Random" msgstr "ランダム" @@ -19160,6 +19181,9 @@ msgstr "" "反りを避ける\n" "ABSのような反りやすい素材を印刷する場合、ヒートベッドの温度を適切に上げることで、反りが発生する確率を下げることができることをご存知ですか?" +#~ msgid "Print" +#~ msgstr "造形する" + #~ msgid "in" #~ msgstr "に" @@ -20106,10 +20130,6 @@ msgstr "" #~ msgid "Unselect" #~ msgstr "選択解除" -#~ msgctxt "Verb" -#~ msgid "Scale" -#~ msgstr "スケール" - #~ msgid "Z-hop when retract" #~ msgstr "リトラクト時にZ方向調整" diff --git a/localization/i18n/ko/OrcaSlicer_ko.po b/localization/i18n/ko/OrcaSlicer_ko.po index 769694b8d6..3dae5ed37e 100644 --- a/localization/i18n/ko/OrcaSlicer_ko.po +++ b/localization/i18n/ko/OrcaSlicer_ko.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Orca Slicer\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-07 10:05-0300\n" +"POT-Creation-Date: 2026-07-07 16:46-0300\n" "PO-Revision-Date: 2025-06-02 17:12+0900\n" "Last-Translator: crwusiz \n" "Language-Team: \n" @@ -331,6 +331,7 @@ msgstr "변형도구 - 회전" msgid "Optimize orientation" msgstr "방향 최적화" +msgctxt "Verb" msgid "Scale" msgstr "배율" @@ -375,6 +376,9 @@ msgstr "배율비" msgid "Object operations" msgstr "객체 작업" +msgid "Scale" +msgstr "배율" + # TODO: Review, changed by lang refactor. PR 14254 msgid "Volume operations" msgstr "용량 작업" @@ -426,6 +430,10 @@ msgstr "회전 도구를 열었을 때의 값으로 현재 회전을 초기화 msgid "Reset current rotation to real zeros." msgstr "현재 회전을 0으로 초기화합니다." +msgctxt "Noun" +msgid "Scale" +msgstr "배율" + #. TRN - Input label. Be short as possible msgid "Size" msgstr "크기" @@ -3644,7 +3652,7 @@ msgstr "교정이 완료되었습니다. 당신의 고온 베드에서 아래 msgid "Save" msgstr "저장" -# TODO: Review, changed by lang refactor. PR 14254 +msgctxt "Navigation" msgid "Back" msgstr "뒷면" @@ -5026,6 +5034,7 @@ msgstr "" msgid "Color change" msgstr "색 변경" +msgctxt "Noun" msgid "Print" msgstr "출력" @@ -5189,11 +5198,15 @@ msgstr "압출 교정 영역을 피하세요" msgid "Align to Y axis" msgstr "Y축에 정렬" -msgctxt "Camera" +msgctxt "Camera View" +msgid "Back" +msgstr "뒷면" + +msgctxt "Camera View" msgid "Left" msgstr "왼쪽" -msgctxt "Camera" +msgctxt "Camera View" msgid "Right" msgstr "오른쪽" @@ -5517,6 +5530,10 @@ msgstr "플레이트 출력" msgid "Export G-code file" msgstr "Gcode 파일 내보내기" +msgctxt "Verb" +msgid "Print" +msgstr "출력" + msgid "Export plate sliced file" msgstr "플레이트 슬라이스 파일 내보내기" @@ -14720,6 +14737,10 @@ msgstr "정렬" msgid "Aligned back" msgstr "" +# TODO: Review, changed by lang refactor. PR 14254 +msgid "Back" +msgstr "뒷면" + msgid "Random" msgstr "무작위" @@ -19528,6 +19549,9 @@ msgstr "" "뒤틀림 방지\n" "ABS와 같이 뒤틀림이 발생하기 쉬운 소재를 출력할 때, 히트베드 온도를 적절하게 높이면 뒤틀림 가능성을 줄일 수 있다는 사실을 알고 계셨나요?" +#~ msgid "Print" +#~ msgstr "출력" + #~ msgid "in" #~ msgstr "인치" @@ -21019,10 +21043,6 @@ msgstr "" #~ msgid "Unselect" #~ msgstr "선택 취소" -#~ msgctxt "Verb" -#~ msgid "Scale" -#~ msgstr "규모" - #~ msgid "Lift Z Enforcement" #~ msgstr "강제 Z 올리기" diff --git a/localization/i18n/lt/OrcaSlicer_lt.po b/localization/i18n/lt/OrcaSlicer_lt.po index 69de514c68..99cb03a620 100644 --- a/localization/i18n/lt/OrcaSlicer_lt.po +++ b/localization/i18n/lt/OrcaSlicer_lt.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-07 10:05-0300\n" +"POT-Creation-Date: 2026-07-07 16:46-0300\n" "PO-Revision-Date: 2026-07-02 14:13+0300\n" "Last-Translator: Gintaras Kučinskas \n" "Language-Team: \n" @@ -326,6 +326,7 @@ msgstr "Manipuliatorius – Pasukimas" msgid "Optimize orientation" msgstr "Optimizuoti orientaciją" +msgctxt "Verb" msgid "Scale" msgstr "Mastelis" @@ -369,6 +370,9 @@ msgstr "Mastelio koeficientai" msgid "Object operations" msgstr "" +msgid "Scale" +msgstr "Mastelis" + msgid "Volume operations" msgstr "" @@ -414,6 +418,10 @@ msgstr "Atkurti dabartinį pasukimą iki vertės, buvusios atidarius pasukimo į msgid "Reset current rotation to real zeros." msgstr "Atstatyti dabartinį sukimąsi į tikrąsias nulines vertes." +msgctxt "Noun" +msgid "Scale" +msgstr "Mastelis" + #. TRN - Input label. Be short as possible msgid "Size" msgstr "Dydis" @@ -3576,6 +3584,7 @@ msgstr "Kalibravimas baigtas. Raskite tolygiausią ekstruzijos liniją ant kaiti msgid "Save" msgstr "Išsaugoti" +msgctxt "Navigation" msgid "Back" msgstr "Atgal" @@ -4906,6 +4915,7 @@ msgstr "Įrankio keitimai" msgid "Color change" msgstr "Spalvos keitimas" +msgctxt "Noun" msgid "Print" msgstr "Spausdinti" @@ -5069,11 +5079,15 @@ msgstr "Vengti ekstruzijos kalibravimo srities" msgid "Align to Y axis" msgstr "Išlygiuoti pagal Y ašį" -msgctxt "Camera" +msgctxt "Camera View" +msgid "Back" +msgstr "Atgal" + +msgctxt "Camera View" msgid "Left" msgstr "Kairė" -msgctxt "Camera" +msgctxt "Camera View" msgid "Right" msgstr "Dešinė" @@ -5396,6 +5410,10 @@ msgstr "Spausdinti plokštę" msgid "Export G-code file" msgstr "Eksportuoti G-kodo failą" +msgctxt "Verb" +msgid "Print" +msgstr "Spausdinti" + msgid "Export plate sliced file" msgstr "Eksportuoti supjaustytos plokštės failą" @@ -14604,6 +14622,9 @@ msgstr "Sulygiuota" msgid "Aligned back" msgstr "Sulygiuota gale" +msgid "Back" +msgstr "Atgal" + msgid "Random" msgstr "Atsitiktinė" @@ -19346,6 +19367,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" +#~ msgstr "Spausdinti" + #~ msgid "in" #~ msgstr "col." diff --git a/localization/i18n/nl/OrcaSlicer_nl.po b/localization/i18n/nl/OrcaSlicer_nl.po index 4a264ad25a..03f731c243 100644 --- a/localization/i18n/nl/OrcaSlicer_nl.po +++ b/localization/i18n/nl/OrcaSlicer_nl.po @@ -3,7 +3,7 @@ msgid "" msgstr "" "Project-Id-Version: Orca Slicer\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-07 10:05-0300\n" +"POT-Creation-Date: 2026-07-07 16:46-0300\n" "PO-Revision-Date: \n" "Last-Translator: \n" "Language-Team: \n" @@ -327,6 +327,7 @@ msgstr "Roteren" msgid "Optimize orientation" msgstr "Oriëntatie optimaliseren" +msgctxt "Verb" msgid "Scale" msgstr "Schalen" @@ -371,6 +372,9 @@ msgstr "Schaalverhoudingen" msgid "Object operations" msgstr "Objectbewerkingen" +msgid "Scale" +msgstr "Schalen" + # TODO: Review, changed by lang refactor. PR 14254 msgid "Volume operations" msgstr "Volumebewerkingen" @@ -422,6 +426,10 @@ msgstr "" msgid "Reset current rotation to real zeros." msgstr "" +msgctxt "Noun" +msgid "Scale" +msgstr "Schalen" + #. TRN - Input label. Be short as possible msgid "Size" msgstr "Maat" @@ -3611,7 +3619,7 @@ msgstr "Kalibratie voltooid. Zoek de meest uniforme extrusielijn op uw hotbed, z msgid "Save" msgstr "Bewaar" -# TODO: Review, changed by lang refactor. PR 14254 +msgctxt "Navigation" msgid "Back" msgstr "Achterzijde" @@ -4971,6 +4979,7 @@ msgstr "Toolwisselingen" msgid "Color change" msgstr "Kleur veranderen" +msgctxt "Noun" msgid "Print" msgstr "" @@ -5129,11 +5138,15 @@ msgstr "Vermijd het extrusie kalibratie gebied" msgid "Align to Y axis" msgstr "Uitlijnen op Y-as" -msgctxt "Camera" +msgctxt "Camera View" +msgid "Back" +msgstr "Achterzijde" + +msgctxt "Camera View" msgid "Left" msgstr "Links" -msgctxt "Camera" +msgctxt "Camera View" msgid "Right" msgstr "Rechts" @@ -5457,6 +5470,10 @@ msgstr "Printplaat" msgid "Export G-code file" msgstr "G-codebestand exporteren" +msgctxt "Verb" +msgid "Print" +msgstr "" + msgid "Export plate sliced file" msgstr "Exporteer plate sliced bestand" @@ -14427,6 +14444,10 @@ msgstr "Uitgelijnd" msgid "Aligned back" msgstr "" +# TODO: Review, changed by lang refactor. PR 14254 +msgid "Back" +msgstr "Achterzijde" + msgid "Random" msgstr "Willekeurig" @@ -20321,10 +20342,6 @@ msgstr "" #~ msgid "Thick bridges" #~ msgstr "Dikke bruggen" -#~ msgctxt "Verb" -#~ msgid "Scale" -#~ msgstr "Schalen" - #~ msgid "Z-hop when retract" #~ msgstr "Z-hop tijdens terugtrekken (retraction)" diff --git a/localization/i18n/pl/OrcaSlicer_pl.po b/localization/i18n/pl/OrcaSlicer_pl.po index 63ea040cc2..1810dc6092 100644 --- a/localization/i18n/pl/OrcaSlicer_pl.po +++ b/localization/i18n/pl/OrcaSlicer_pl.po @@ -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-07 10:05-0300\n" +"POT-Creation-Date: 2026-07-07 16:46-0300\n" "PO-Revision-Date: \n" "Last-Translator: Krzysztof Morga <>\n" "Language-Team: \n" @@ -329,6 +329,7 @@ msgstr "Uchwyt-Obróć" msgid "Optimize orientation" msgstr "Optymalizuj orientację" +msgctxt "Verb" msgid "Scale" msgstr "Skaluj" @@ -373,6 +374,9 @@ msgstr "Współczynniki skali" msgid "Object operations" msgstr "Operacje na obiekcie" +msgid "Scale" +msgstr "Skaluj" + # TODO: Review, changed by lang refactor. PR 14254 msgid "Volume operations" msgstr "Operacje na objętości" @@ -424,6 +428,10 @@ msgstr "Zresetuj bieżący obrót do wartości ustawionej przy otwarciu narzędz msgid "Reset current rotation to real zeros." msgstr "Zresetuj bieżący obrót do wartości zerowej." +msgctxt "Noun" +msgid "Scale" +msgstr "Skaluj" + #. TRN - Input label. Be short as possible msgid "Size" msgstr "Rozmiar" @@ -3649,7 +3657,7 @@ msgstr "Kalibracja zakończona. Proszę znaleźć na płycie roboczej, linie eks msgid "Save" msgstr "Zapisz" -# TODO: Review, changed by lang refactor. PR 14254 +msgctxt "Navigation" msgid "Back" msgstr "Tył" @@ -5023,6 +5031,7 @@ msgstr "Zmiany narzędzi" msgid "Color change" msgstr "Zmiana koloru" +msgctxt "Noun" msgid "Print" msgstr "Drukuj" @@ -5186,11 +5195,15 @@ msgstr "Unikaj obszaru kalibracji ekstruzji" msgid "Align to Y axis" msgstr "Wyrównaj do osi Y" -msgctxt "Camera" +msgctxt "Camera View" +msgid "Back" +msgstr "Tył" + +msgctxt "Camera View" msgid "Left" msgstr "Lewy" -msgctxt "Camera" +msgctxt "Camera View" msgid "Right" msgstr "Prawy" @@ -5518,6 +5531,10 @@ msgstr "Drukuj aktualną płytę" msgid "Export G-code file" msgstr "Eksportuj plik G-code" +msgctxt "Verb" +msgid "Print" +msgstr "Drukuj" + msgid "Export plate sliced file" msgstr "Eksportuj plik pociętej płyty" @@ -14738,6 +14755,10 @@ msgstr "Wyrównany" msgid "Aligned back" msgstr "" +# TODO: Review, changed by lang refactor. PR 14254 +msgid "Back" +msgstr "Tył" + msgid "Random" msgstr "Losowo" @@ -19542,6 +19563,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" +#~ msgstr "Drukuj" + #~ msgid "in" #~ msgstr "cal" @@ -21000,10 +21024,6 @@ msgstr "" #~ msgid "Unselect" #~ msgstr "Odznacz" -#~ msgctxt "Verb" -#~ msgid "Scale" -#~ msgstr "Skala" - #~ msgid "Lift Z Enforcement" #~ msgstr "Wymuszenie podniesienia osi Z" diff --git a/localization/i18n/pt_BR/OrcaSlicer_pt_BR.po b/localization/i18n/pt_BR/OrcaSlicer_pt_BR.po index 9b40d74f18..a1e9a39ce2 100644 --- a/localization/i18n/pt_BR/OrcaSlicer_pt_BR.po +++ b/localization/i18n/pt_BR/OrcaSlicer_pt_BR.po @@ -3,7 +3,7 @@ msgid "" msgstr "" "Project-Id-Version: Orca Slicer\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-07 10:05-0300\n" +"POT-Creation-Date: 2026-07-07 16:46-0300\n" "PO-Revision-Date: 2026-07-04 11:51-0300\n" "Last-Translator: Alexandre Folle de Menezes\n" "Language-Team: Portuguese, Brazilian\n" @@ -326,6 +326,7 @@ msgstr "Gizmo-Rotacionar" msgid "Optimize orientation" msgstr "Otimizar orientação" +msgctxt "Verb" msgid "Scale" msgstr "Escala" @@ -369,6 +370,9 @@ msgstr "Proporções de escala" msgid "Object operations" msgstr "Operações de objeto" +msgid "Scale" +msgstr "Escala" + msgid "Volume operations" msgstr "Operações de volume" @@ -414,6 +418,10 @@ msgstr "Redefinir rotação para o valor de abertura da ferramenta de rotação. msgid "Reset current rotation to real zeros." msgstr "Redefinir rotação atual para zeros reais." +msgctxt "Noun" +msgid "Scale" +msgstr "Escala" + #. TRN - Input label. Be short as possible msgid "Size" msgstr "Tamanho" @@ -3580,6 +3588,7 @@ msgstr "Calibração concluída. Por favor, encontre a linha de extrusão mais u msgid "Save" msgstr "Salvar" +msgctxt "Navigation" msgid "Back" msgstr "Atrás" @@ -4940,6 +4949,7 @@ msgstr "Trocas de ferramenta" msgid "Color change" msgstr "Mudança de cor" +msgctxt "Noun" msgid "Print" msgstr "Imprimir" @@ -5102,11 +5112,15 @@ msgstr "Evitar a região de calibração da extrusão" msgid "Align to Y axis" msgstr "Alinhar com o eixo Y" -msgctxt "Camera" +msgctxt "Camera View" +msgid "Back" +msgstr "Atrás" + +msgctxt "Camera View" msgid "Left" msgstr "Esquerda" -msgctxt "Camera" +msgctxt "Camera View" msgid "Right" msgstr "Direita" @@ -5433,6 +5447,10 @@ msgstr "Imprimir placa" msgid "Export G-code file" msgstr "Exportar arquivo G-code" +msgctxt "Verb" +msgid "Print" +msgstr "Imprimir" + msgid "Export plate sliced file" msgstr "Exportar arquivo de placa fatiada" @@ -14720,6 +14738,9 @@ msgstr "Alinhada" msgid "Aligned back" msgstr "Alinhada atrás" +msgid "Back" +msgstr "Atrás" + msgid "Random" msgstr "Aleatória" @@ -19489,6 +19510,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" +#~ msgstr "Imprimir" + #~ msgid "in" #~ msgstr "pol" diff --git a/localization/i18n/ru/OrcaSlicer_ru.po b/localization/i18n/ru/OrcaSlicer_ru.po index 86110c89ee..712f9bcbec 100644 --- a/localization/i18n/ru/OrcaSlicer_ru.po +++ b/localization/i18n/ru/OrcaSlicer_ru.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: OrcaSlicer V2.5.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-07 10:05-0300\n" +"POT-Creation-Date: 2026-07-07 16:46-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 \n" @@ -337,6 +337,7 @@ msgstr "Инструмент вращения" msgid "Optimize orientation" msgstr "Оптимизация положения модели" +msgctxt "Verb" msgid "Scale" msgstr "Масштаб" @@ -380,6 +381,9 @@ msgstr "Коэф. масштаба" msgid "Object operations" msgstr "Операции с моделями" +msgid "Scale" +msgstr "Масштаб" + msgid "Volume operations" msgstr "Булевы операции с телами" @@ -425,6 +429,10 @@ msgstr "Сбросить последние изменения ориентац msgid "Reset current rotation to real zeros." msgstr "Сбросить ориентацию до изначальной" +msgctxt "Noun" +msgid "Scale" +msgstr "Масштаб" + #. TRN - Input label. Be short as possible msgid "Size" msgstr "Размер" @@ -3657,9 +3665,9 @@ msgstr "Калибровка завершена. Теперь найдите н msgid "Save" msgstr "Сохранить" -# Используется в нескольких местах: в 2 местах в значении расположения (шва и камеры), в одном – на кнопке "назад" (калибровка бамбу) +msgctxt "Navigation" msgid "Back" -msgstr "Сзади" +msgstr "Назад" msgid "Example" msgstr "Пример" @@ -5065,6 +5073,7 @@ msgstr "Смена инструмента" msgid "Color change" msgstr "Смена цвета" +msgctxt "Noun" msgid "Print" msgstr "Печать" @@ -5227,11 +5236,15 @@ msgstr "Избегать зону калибровки экструзии" msgid "Align to Y axis" msgstr "Выравнивать по оси Y" -msgctxt "Camera" +msgctxt "Camera View" +msgid "Back" +msgstr "Сзади" + +msgctxt "Camera View" msgid "Left" msgstr "Слева" -msgctxt "Camera" +msgctxt "Camera View" msgid "Right" msgstr "Справа" @@ -5589,6 +5602,10 @@ msgstr "Распечатать стол" msgid "Export G-code file" msgstr "Экспорт в G-код" +msgctxt "Verb" +msgid "Print" +msgstr "Печать" + msgid "Export plate sliced file" msgstr "Экспорт стола в файл проекта" @@ -15086,6 +15103,10 @@ msgstr "В углах" msgid "Aligned back" msgstr "В углах сзади" +# Используется в нескольких местах: в 2 местах в значении расположения (шва и камеры), в одном – на кнопке "назад" (калибровка бамбу) +msgid "Back" +msgstr "Сзади" + msgid "Random" msgstr "Случайная" @@ -20096,6 +20117,9 @@ msgstr "" "Предотвращение коробления материала\n" "Знаете ли вы, что при печати материалами, склонными к короблению, таких как ABS, повышение температуры подогреваемого стола может снизить эту вероятность?" +#~ msgid "Print" +#~ msgstr "Печать" + #~ msgid "in" #~ msgstr "дюйм" diff --git a/localization/i18n/sv/OrcaSlicer_sv.po b/localization/i18n/sv/OrcaSlicer_sv.po index 1f6843c487..0bbb793d5b 100644 --- a/localization/i18n/sv/OrcaSlicer_sv.po +++ b/localization/i18n/sv/OrcaSlicer_sv.po @@ -3,7 +3,7 @@ msgid "" msgstr "" "Project-Id-Version: Orca Slicer\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-07 10:05-0300\n" +"POT-Creation-Date: 2026-07-07 16:46-0300\n" "Language: sv\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -324,6 +324,7 @@ msgstr "" msgid "Optimize orientation" msgstr "Optimisera placering" +msgctxt "Verb" msgid "Scale" msgstr "Skala" @@ -368,6 +369,9 @@ msgstr "Skalnings förhållande" msgid "Object operations" msgstr "Objekt Åtgärder" +msgid "Scale" +msgstr "Skala" + # TODO: Review, changed by lang refactor. PR 14254 msgid "Volume operations" msgstr "Volym Åtgärder" @@ -418,6 +422,10 @@ msgstr "" msgid "Reset current rotation to real zeros." msgstr "" +msgctxt "Noun" +msgid "Scale" +msgstr "Skala" + #. TRN - Input label. Be short as possible msgid "Size" msgstr "Storlek" @@ -3603,7 +3611,7 @@ msgstr "Kalibreringen klar. Vänligen hitta den mest enhetliga extruderingslinje msgid "Save" msgstr "Spara" -# TODO: Review, changed by lang refactor. PR 14254 +msgctxt "Navigation" msgid "Back" msgstr "Tillbaka" @@ -4961,6 +4969,7 @@ msgstr "" msgid "Color change" msgstr "Färg byte" +msgctxt "Noun" msgid "Print" msgstr "Skriv ut" @@ -5120,11 +5129,15 @@ msgstr "Undvik kalibrerings området" msgid "Align to Y axis" msgstr "Justera mot Y-axeln" -msgctxt "Camera" +msgctxt "Camera View" +msgid "Back" +msgstr "Tillbaka" + +msgctxt "Camera View" msgid "Left" msgstr "Vänster" -msgctxt "Camera" +msgctxt "Camera View" msgid "Right" msgstr "Höger" @@ -5448,6 +5461,10 @@ msgstr "Skriv ut byggplattan" msgid "Export G-code file" msgstr "Exportera G-kod filen" +msgctxt "Verb" +msgid "Print" +msgstr "Skriv ut" + msgid "Export plate sliced file" msgstr "Exportera byggplattans beredda fil" @@ -14400,6 +14417,10 @@ msgstr "Linjerad" msgid "Aligned back" msgstr "" +# TODO: Review, changed by lang refactor. PR 14254 +msgid "Back" +msgstr "Tillbaka" + msgid "Random" msgstr "Slumpmässig" @@ -19055,6 +19076,9 @@ 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?" +#~ msgid "Print" +#~ msgstr "Skriv ut" + #~ msgid "in" #~ msgstr "i" @@ -20228,10 +20252,6 @@ msgstr "" #~ msgid "Thick bridges" #~ msgstr "Tjocka bridges" -#~ msgctxt "Verb" -#~ msgid "Scale" -#~ msgstr "Skala" - #~ msgid "Z-hop when retract" #~ msgstr "Z-hopp vid retraktion" diff --git a/localization/i18n/th/OrcaSlicer_th.po b/localization/i18n/th/OrcaSlicer_th.po index 3a4d12dd6b..0e1b189984 100644 --- a/localization/i18n/th/OrcaSlicer_th.po +++ b/localization/i18n/th/OrcaSlicer_th.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Orca Slicer\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-07 10:05-0300\n" +"POT-Creation-Date: 2026-07-07 16:46-0300\n" "PO-Revision-Date: 2026-06-19 13:40+0700\n" "Last-Translator: Icezaza\n" "Language-Team: Thai\n" @@ -324,6 +324,7 @@ msgstr "Gizmo-หมุน" msgid "Optimize orientation" msgstr "ปรับการวางแนวให้เหมาะสม" +msgctxt "Verb" msgid "Scale" msgstr "ปรับขนาด" @@ -367,6 +368,9 @@ msgstr "อัตราส่วนการปรับขนาด" msgid "Object operations" msgstr "การทำงานกับวัตถุ" +msgid "Scale" +msgstr "ปรับขนาด" + msgid "Volume operations" msgstr "การทำงานกับวอลลุ่ม" @@ -412,6 +416,10 @@ msgstr "รีเซ็ตการหมุนปัจจุบันเป็ msgid "Reset current rotation to real zeros." msgstr "รีเซ็ตการหมุนปัจจุบันให้เป็นศูนย์จริง" +msgctxt "Noun" +msgid "Scale" +msgstr "ปรับขนาด" + #. TRN - Input label. Be short as possible msgid "Size" msgstr "ขนาด" @@ -3559,6 +3567,7 @@ msgstr "การสอบเทียบเสร็จสิ้น โปร msgid "Save" msgstr "บันทึก" +msgctxt "Navigation" msgid "Back" msgstr "กลับ" @@ -4907,6 +4916,7 @@ msgstr "การเปลี่ยนแปลงเครื่องมือ msgid "Color change" msgstr "เปลี่ยนสี" +msgctxt "Noun" msgid "Print" msgstr "พิมพ์" @@ -5066,11 +5076,15 @@ msgstr "หลีกเลี่ยงบริเวณการสอบเท msgid "Align to Y axis" msgstr "จัดแนวตามแกน Y" -msgctxt "Camera" +msgctxt "Camera View" +msgid "Back" +msgstr "กลับ" + +msgctxt "Camera View" msgid "Left" msgstr "ซ้าย" -msgctxt "Camera" +msgctxt "Camera View" msgid "Right" msgstr "ขวา" @@ -5393,6 +5407,10 @@ msgstr "พิมพ์ฐานพิมพ์" msgid "Export G-code file" msgstr "ส่งออกไฟล์ G-code" +msgctxt "Verb" +msgid "Print" +msgstr "พิมพ์" + msgid "Export plate sliced file" msgstr "ส่งออกไฟล์แผ่นสไลซ์บาง ๆ" @@ -14591,6 +14609,9 @@ msgstr "จัดตำแหน่ง" msgid "Aligned back" msgstr "จัดแนวกลับ" +msgid "Back" +msgstr "กลับ" + msgid "Random" msgstr "สุ่ม" @@ -19345,6 +19366,9 @@ msgstr "" "หลีกเลี่ยงการบิดเบี้ยว\n" "คุณรู้หรือไม่ว่าเมื่อพิมพ์วัสดุที่มีแนวโน้มที่จะเกิดการบิดเบี้ยว เช่น ABS การเพิ่มอุณหภูมิฐานพิมพ์อย่างเหมาะสมสามารถลดความน่าจะเป็นของการบิดเบี้ยวได้" +#~ msgid "Print" +#~ msgstr "พิมพ์" + #~ msgid "in" #~ msgstr "นิ้ว" diff --git a/localization/i18n/tr/OrcaSlicer_tr.po b/localization/i18n/tr/OrcaSlicer_tr.po index d879d550c5..163a45e2f4 100644 --- a/localization/i18n/tr/OrcaSlicer_tr.po +++ b/localization/i18n/tr/OrcaSlicer_tr.po @@ -3,7 +3,7 @@ msgid "" msgstr "" "Project-Id-Version: Orca Slicer\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-07 10:05-0300\n" +"POT-Creation-Date: 2026-07-07 16:46-0300\n" "PO-Revision-Date: 2026-04-08 23:59+0300\n" "Last-Translator: GlauTech\n" "Language-Team: \n" @@ -327,6 +327,7 @@ msgstr "Gizmo-Döndür" msgid "Optimize orientation" msgstr "Yönü optimize edin" +msgctxt "Verb" msgid "Scale" msgstr "Ölçeklendir" @@ -371,6 +372,9 @@ msgstr "Ölçek oranları" msgid "Object operations" msgstr "Nesne İşlemleri" +msgid "Scale" +msgstr "Ölçeklendir" + # TODO: Review, changed by lang refactor. PR 14254 msgid "Volume operations" msgstr "Hacim İşlemleri" @@ -422,6 +426,10 @@ msgstr "Döndürme aracını açtığınızda mevcut döndürme değerini sıfı msgid "Reset current rotation to real zeros." msgstr "Mevcut dönüşü gerçek sıfırlara sıfırla." +msgctxt "Noun" +msgid "Scale" +msgstr "Ölçeklendir" + #. TRN - Input label. Be short as possible msgid "Size" msgstr "Boyut" @@ -3644,7 +3652,7 @@ msgstr "Kalibrasyon tamamlandı. Lütfen sıcak yatağınızdaki en düzgün eks msgid "Save" msgstr "Kaydet" -# TODO: Review, changed by lang refactor. PR 14254 +msgctxt "Navigation" msgid "Back" msgstr "Arka" @@ -5027,6 +5035,7 @@ msgstr "Takım değişiklikleri" msgid "Color change" msgstr "Renk değişimi" +msgctxt "Noun" msgid "Print" msgstr "Yazdır" @@ -5190,11 +5199,15 @@ msgstr "Ekstrüzyon kalibrasyon bölgesinden kaçın" msgid "Align to Y axis" msgstr "Y eksenine hizala" -msgctxt "Camera" +msgctxt "Camera View" +msgid "Back" +msgstr "Arka" + +msgctxt "Camera View" msgid "Left" msgstr "Sol" -msgctxt "Camera" +msgctxt "Camera View" msgid "Right" msgstr "Sağ" @@ -5522,6 +5535,10 @@ msgstr "Plakayı Yazdır" msgid "Export G-code file" msgstr "G-kod dosyasını dışa aktar" +msgctxt "Verb" +msgid "Print" +msgstr "Yazdır" + msgid "Export plate sliced file" msgstr "Dilimlenmiş plaka dosyasını dışa aktar" @@ -14776,6 +14793,10 @@ msgstr "Hizalı" msgid "Aligned back" msgstr "Arkaya hizalı" +# TODO: Review, changed by lang refactor. PR 14254 +msgid "Back" +msgstr "Arka" + msgid "Random" msgstr "Rastgele" @@ -19610,6 +19631,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" +#~ msgstr "Yazdır" + #~ msgid "in" #~ msgstr "in" diff --git a/localization/i18n/uk/OrcaSlicer_uk.po b/localization/i18n/uk/OrcaSlicer_uk.po index ab876dab5e..7de6f123b9 100644 --- a/localization/i18n/uk/OrcaSlicer_uk.po +++ b/localization/i18n/uk/OrcaSlicer_uk.po @@ -3,7 +3,7 @@ msgid "" msgstr "" "Project-Id-Version: orcaslicerua\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-07 10:05-0300\n" +"POT-Creation-Date: 2026-07-07 16:46-0300\n" "PO-Revision-Date: 2025-03-07 09:30+0200\n" "Last-Translator: \n" "Language-Team: Ukrainian\n" @@ -332,6 +332,7 @@ msgstr "Gizmo обертання" msgid "Optimize orientation" msgstr "Оптимізувати орієнтацію" +msgctxt "Verb" msgid "Scale" msgstr "Масштаб" @@ -376,6 +377,9 @@ msgstr "Коефіцієнти масштабування" msgid "Object operations" msgstr "Операції з об'єктами" +msgid "Scale" +msgstr "Масштаб" + # TODO: Review, changed by lang refactor. PR 14254 msgid "Volume operations" msgstr "Операції з об’ємом" @@ -427,6 +431,10 @@ msgstr "Скинути поточне обертання до значення msgid "Reset current rotation to real zeros." msgstr "Скинути поточне обертання до нульових значень." +msgctxt "Noun" +msgid "Scale" +msgstr "Масштаб" + #. TRN - Input label. Be short as possible msgid "Size" msgstr "Розмір" @@ -3660,7 +3668,7 @@ msgstr "Калібрування завершено. Тепер знайдіть msgid "Save" msgstr "Зберегти" -# TODO: Review, changed by lang refactor. PR 14254 +msgctxt "Navigation" msgid "Back" msgstr "Ззаду" @@ -5030,6 +5038,7 @@ msgstr "Зміна інструменту" msgid "Color change" msgstr "Зміна кольору" +msgctxt "Noun" msgid "Print" msgstr "Друк" @@ -5191,11 +5200,15 @@ msgstr "Уникайте області калібрування екструз msgid "Align to Y axis" msgstr "Розташувати вздовж осі Y" -msgctxt "Camera" +msgctxt "Camera View" +msgid "Back" +msgstr "Ззаду" + +msgctxt "Camera View" msgid "Left" msgstr "Ліво" -msgctxt "Camera" +msgctxt "Camera View" msgid "Right" msgstr "Право" @@ -5523,6 +5536,10 @@ msgstr "Друкувати пластину" msgid "Export G-code file" msgstr "Експорт файлу G-коду" +msgctxt "Verb" +msgid "Print" +msgstr "Друк" + msgid "Export plate sliced file" msgstr "Експортувати файл нарізки пластини" @@ -14753,6 +14770,10 @@ msgstr "Вирівняне" msgid "Aligned back" msgstr "" +# TODO: Review, changed by lang refactor. PR 14254 +msgid "Back" +msgstr "Ззаду" + msgid "Random" msgstr "Випадкове" @@ -19554,6 +19575,9 @@ msgstr "" "Уникнення деформації\n" "Чи знаєте ви, що при друку матеріалами, схильними до деформації, такими як ABS, відповідне підвищення температури гарячого ліжка може зменшити ймовірність деформації?" +#~ msgid "Print" +#~ msgstr "Друк" + #~ msgid "in" #~ msgstr "в" diff --git a/localization/i18n/vi/OrcaSlicer_vi.po b/localization/i18n/vi/OrcaSlicer_vi.po index 42be9867cf..a25cc186c6 100644 --- a/localization/i18n/vi/OrcaSlicer_vi.po +++ b/localization/i18n/vi/OrcaSlicer_vi.po @@ -3,7 +3,7 @@ msgid "" msgstr "" "Project-Id-Version: Orca Slicer\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-07 10:05-0300\n" +"POT-Creation-Date: 2026-07-07 16:46-0300\n" "PO-Revision-Date: 2025-10-02 17:43+0700\n" "Last-Translator: \n" "Language-Team: hainguyen.ts13@gmail.com\n" @@ -328,6 +328,7 @@ msgstr "Gizmo - Xoay" msgid "Optimize orientation" msgstr "Tối ưu định hướng" +msgctxt "Verb" msgid "Scale" msgstr "Tỷ lệ" @@ -372,6 +373,9 @@ msgstr "Tỷ lệ co giãn" msgid "Object operations" msgstr "Thao tác vật thể" +msgid "Scale" +msgstr "Tỷ lệ" + # TODO: Review, changed by lang refactor. PR 14254 msgid "Volume operations" msgstr "Thao tác thể tích" @@ -423,6 +427,10 @@ msgstr "Đặt lại xoay hiện tại về giá trị khi mở công cụ xoay. msgid "Reset current rotation to real zeros." msgstr "Đặt lại xoay hiện tại về số không thực." +msgctxt "Noun" +msgid "Scale" +msgstr "Tỷ lệ" + #. TRN - Input label. Be short as possible msgid "Size" msgstr "Kích thước" @@ -3630,7 +3638,7 @@ msgstr "Hiệu chỉnh hoàn tất. Vui lòng tìm đường đùn đồng đề msgid "Save" msgstr "Lưu" -# TODO: Review, changed by lang refactor. PR 14254 +msgctxt "Navigation" msgid "Back" msgstr "Sau" @@ -5001,6 +5009,7 @@ msgstr "" msgid "Color change" msgstr "Đổi màu" +msgctxt "Noun" msgid "Print" msgstr "In" @@ -5162,11 +5171,15 @@ msgstr "Tránh vùng hiệu chỉnh đùn" msgid "Align to Y axis" msgstr "Căn theo trục Y" -msgctxt "Camera" +msgctxt "Camera View" +msgid "Back" +msgstr "Sau" + +msgctxt "Camera View" msgid "Left" msgstr "Trái" -msgctxt "Camera" +msgctxt "Camera View" msgid "Right" msgstr "Phải" @@ -5490,6 +5503,10 @@ msgstr "In plate" msgid "Export G-code file" msgstr "Xuất file G-code" +msgctxt "Verb" +msgid "Print" +msgstr "In" + msgid "Export plate sliced file" msgstr "Xuất file plate đã slice" @@ -14668,6 +14685,10 @@ msgstr "Căn chỉnh" msgid "Aligned back" msgstr "Căn chỉnh sau" +# TODO: Review, changed by lang refactor. PR 14254 +msgid "Back" +msgstr "Sau" + msgid "Random" msgstr "Ngẫu nhiên" @@ -19475,6 +19496,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" +#~ msgstr "In" + #~ msgid "in" #~ msgstr "in" diff --git a/localization/i18n/zh_CN/OrcaSlicer_zh_CN.po b/localization/i18n/zh_CN/OrcaSlicer_zh_CN.po index eccc5752a3..d40c72b758 100644 --- a/localization/i18n/zh_CN/OrcaSlicer_zh_CN.po +++ b/localization/i18n/zh_CN/OrcaSlicer_zh_CN.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: Slic3rPE\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-07 10:05-0300\n" +"POT-Creation-Date: 2026-07-07 16:46-0300\n" "PO-Revision-Date: 2026-06-11 12:37-0300\n" "Last-Translator: Handle \n" "Language-Team: \n" @@ -330,6 +330,7 @@ msgstr "Gizmo-旋转" msgid "Optimize orientation" msgstr "优化朝向" +msgctxt "Verb" msgid "Scale" msgstr "缩放" @@ -374,6 +375,9 @@ msgstr "缩放比例" msgid "Object operations" msgstr "对象操作" +msgid "Scale" +msgstr "缩放" + # TODO: Review, changed by lang refactor. PR 14254 msgid "Volume operations" msgstr "零件操作" @@ -425,6 +429,10 @@ msgstr "重置当前旋转为打开旋转工具时的值" msgid "Reset current rotation to real zeros." msgstr "重置当前旋转为真实零位" +msgctxt "Noun" +msgid "Scale" +msgstr "缩放" + #. TRN - Input label. Be short as possible msgid "Size" msgstr "大小" @@ -3639,7 +3647,7 @@ msgstr "校准完成。如下图中的示例,请在您的热床上找到最均 msgid "Save" msgstr "保存" -# TODO: Review, changed by lang refactor. PR 14254 +msgctxt "Navigation" msgid "Back" msgstr "背面" @@ -5014,6 +5022,7 @@ msgstr "工具更换" msgid "Color change" msgstr "颜色更换" +msgctxt "Noun" msgid "Print" msgstr "打印" @@ -5178,11 +5187,15 @@ msgstr "避开挤出校准区域" msgid "Align to Y axis" msgstr "对齐到Y轴" -msgctxt "Camera" +msgctxt "Camera View" +msgid "Back" +msgstr "背面" + +msgctxt "Camera View" msgid "Left" msgstr "左" -msgctxt "Camera" +msgctxt "Camera View" msgid "Right" msgstr "右" @@ -5510,6 +5523,10 @@ msgstr "打印单盘" msgid "Export G-code file" msgstr "导出G-code文件" +msgctxt "Verb" +msgid "Print" +msgstr "打印" + msgid "Export plate sliced file" msgstr "导出单盘切片文件" @@ -14896,6 +14913,10 @@ msgstr "对齐" msgid "Aligned back" msgstr "背部对齐" +# TODO: Review, changed by lang refactor. PR 14254 +msgid "Back" +msgstr "背面" + msgid "Random" msgstr "随机" @@ -19737,6 +19758,9 @@ msgstr "" "避免翘曲\n" "您知道吗?打印ABS这类易翘曲材料时,适当提高热床温度可以降低翘曲的概率。" +#~ msgid "Print" +#~ msgstr "打印" + #~ msgid "in" #~ msgstr "在" diff --git a/localization/i18n/zh_TW/OrcaSlicer_zh_TW.po b/localization/i18n/zh_TW/OrcaSlicer_zh_TW.po index 1c8f7b256a..2dad906eff 100644 --- a/localization/i18n/zh_TW/OrcaSlicer_zh_TW.po +++ b/localization/i18n/zh_TW/OrcaSlicer_zh_TW.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: Orca Slicer\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-07-07 10:05-0300\n" +"POT-Creation-Date: 2026-07-07 16:46-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" @@ -333,6 +333,7 @@ msgstr "Gizmo-旋轉" msgid "Optimize orientation" msgstr "最佳化方向" +msgctxt "Verb" msgid "Scale" msgstr "縮放" @@ -377,6 +378,9 @@ msgstr "縮放比例" msgid "Object operations" msgstr "物件操作" +msgid "Scale" +msgstr "縮放" + # TODO: Review, changed by lang refactor. PR 14254 msgid "Volume operations" msgstr "零件操作" @@ -428,6 +432,10 @@ msgstr "重設旋轉角度為開啟旋轉工具時的狀態。" msgid "Reset current rotation to real zeros." msgstr "將旋轉角度歸零" +msgctxt "Noun" +msgid "Scale" +msgstr "縮放" + #. TRN - Input label. Be short as possible msgid "Size" msgstr "尺寸" @@ -3650,7 +3658,7 @@ msgstr "校正完成。如下圖中的範例,請在您的熱床上找到最均 msgid "Save" msgstr "儲存" -# TODO: Review, changed by lang refactor. PR 14254 +msgctxt "Navigation" msgid "Back" msgstr "背面" @@ -5056,6 +5064,7 @@ msgstr "取代工具" msgid "Color change" msgstr "顏色更換" +msgctxt "Noun" msgid "Print" msgstr "列印" @@ -5220,11 +5229,15 @@ msgstr "避開擠出校正區域" msgid "Align to Y axis" msgstr "與 Y 軸對齊" -msgctxt "Camera" +msgctxt "Camera View" +msgid "Back" +msgstr "背面" + +msgctxt "Camera View" msgid "Left" msgstr "左" -msgctxt "Camera" +msgctxt "Camera View" msgid "Right" msgstr "右" @@ -5553,6 +5566,10 @@ msgstr "列印單一列印板" msgid "Export G-code file" msgstr "匯出 G-code 檔案" +msgctxt "Verb" +msgid "Print" +msgstr "列印" + msgid "Export plate sliced file" msgstr "匯出單一列印板切片檔案" @@ -14957,6 +14974,10 @@ msgstr "對齊" msgid "Aligned back" msgstr "背部對齊" +# TODO: Review, changed by lang refactor. PR 14254 +msgid "Back" +msgstr "背面" + msgid "Random" msgstr "隨機" @@ -19808,6 +19829,9 @@ msgstr "" "您知道嗎?當列印容易翹曲的材料(如 ABS)時,適當提高熱床溫度\n" "可以降低翹曲的機率。" +#~ msgid "Print" +#~ msgstr "列印" + #~ msgid "in" #~ msgstr "在" diff --git a/scripts/run_unit_tests.sh b/scripts/run_unit_tests.sh index bd9e969227..f6a01dd3e0 100755 --- a/scripts/run_unit_tests.sh +++ b/scripts/run_unit_tests.sh @@ -4,11 +4,21 @@ # It should only require the directories build/tests, scripts/, and tests/ to function, # and cmake (with ctest) installed. # (otherwise, update the workflow too, but try to avoid to keep things self-contained) +# +# Usage: run_unit_tests.sh [TEST_DIR] [BUILD_CONFIG] +# TEST_DIR directory containing the built tests (default: build/tests) +# BUILD_CONFIG configuration to run; required for multi-config generators +# (Windows/macOS), harmless/omitted for single-config (Linux). ROOT_DIR="$(dirname "$0")/.." cd "${ROOT_DIR}" || exit 1 +TEST_DIR="${1:-build/tests}" +BUILD_CONFIG="${2:-}" + # Run the whole suite, excluding tests tagged [NotWorking]. # --no-tests=error fails the job if the filter matches nothing (instead of passing green). -ctest --test-dir build/tests -LE "NotWorking" --no-tests=error --output-junit "$(pwd)/ctest_results.xml" --output-on-failure -j +args=(--test-dir "${TEST_DIR}" -LE "NotWorking" --no-tests=error --output-junit "$(pwd)/ctest_results.xml" --output-on-failure -j) +[ -n "${BUILD_CONFIG}" ] && args+=(--build-config "${BUILD_CONFIG}") +ctest "${args[@]}" diff --git a/src/OrcaSlicer.cpp b/src/OrcaSlicer.cpp index 7e1341ac18..311451f774 100644 --- a/src/OrcaSlicer.cpp +++ b/src/OrcaSlicer.cpp @@ -7359,7 +7359,7 @@ bool CLI::export_project(Model *model, std::string& path, PlateDataPtrs &partpla bool success = false; StoreParams store_params; - store_params.path = path.c_str(); + store_params.path = path; store_params.model = model; store_params.plate_data_list = partplate_data; store_params.project_presets = project_presets; diff --git a/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp b/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp index 2d2cf9896d..da5c7357c1 100644 --- a/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp +++ b/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp @@ -1660,7 +1660,7 @@ void SkeletalTrapezoidation::propagateBeadingsDownward(edge_t* edge_to_peak, ptr } -SkeletalTrapezoidation::Beading SkeletalTrapezoidation::interpolate(const Beading& left, double ratio_left_to_whole, const Beading& right, coord_t switching_radius) const +SkeletalTrapezoidation::Beading SkeletalTrapezoidation::interpolate(const Beading& left, double ratio_left_to_whole, const Beading& right, coord_t switching_radius) { assert(ratio_left_to_whole >= 0.0 && ratio_left_to_whole <= 1.0); Beading ret = interpolate(left, ratio_left_to_whole, right); @@ -1684,6 +1684,12 @@ SkeletalTrapezoidation::Beading SkeletalTrapezoidation::interpolate(const Beadin { // We cant adjust to fit the next edge because there is no previous one?! return ret; } + // ret follows the thicker of left/right, which can hold fewer insets than left when bead + // count and thickness disagree; skip the adjustment rather than index ret past its end. + if (next_inset_idx >= coord_t(ret.toolpath_locations.size())) + { + return ret; + } assert(next_inset_idx < coord_t(left.toolpath_locations.size())); assert(left.toolpath_locations[next_inset_idx] <= switching_radius); assert(left.toolpath_locations[next_inset_idx + 1] >= switching_radius); @@ -1703,7 +1709,7 @@ SkeletalTrapezoidation::Beading SkeletalTrapezoidation::interpolate(const Beadin } -SkeletalTrapezoidation::Beading SkeletalTrapezoidation::interpolate(const Beading& left, double ratio_left_to_whole, const Beading& right) const +SkeletalTrapezoidation::Beading SkeletalTrapezoidation::interpolate(const Beading& left, double ratio_left_to_whole, const Beading& right) { assert(ratio_left_to_whole >= 0.0 && ratio_left_to_whole <= 1.0); float ratio_right_to_whole = 1.0 - ratio_left_to_whole; diff --git a/src/libslic3r/Arachne/SkeletalTrapezoidation.hpp b/src/libslic3r/Arachne/SkeletalTrapezoidation.hpp index 70c2a7d9a3..d87cd74728 100644 --- a/src/libslic3r/Arachne/SkeletalTrapezoidation.hpp +++ b/src/libslic3r/Arachne/SkeletalTrapezoidation.hpp @@ -488,7 +488,7 @@ protected: * beads. * \return The beading at the interpolated location. */ - Beading interpolate(const Beading& left, double ratio_left_to_whole, const Beading& right, coord_t switching_radius) const; + static Beading interpolate(const Beading& left, double ratio_left_to_whole, const Beading& right, coord_t switching_radius); /*! * Subroutine of \ref interpolate(const Beading&, Ratio, const Beading&, coord_t) @@ -501,7 +501,7 @@ protected: * \param right One of the beadings to interpolate between. * \return The beading at the interpolated location. */ - Beading interpolate(const Beading& left, double ratio_left_to_whole, const Beading& right) const; + static Beading interpolate(const Beading& left, double ratio_left_to_whole, const Beading& right); /*! * Get the beading at a certain node of the skeletal graph, or create one if diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index ce531b8b4c..bda92deba6 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -221,6 +221,7 @@ set(lisbslic3r_sources GCode/FanMover.hpp GCode/GCodeProcessor.cpp GCode/GCodeProcessor.hpp + GCode/ElegooGCodeProcessorHelper.cpp GCode.hpp GCode/PchipInterpolatorHelper.cpp GCode/PchipInterpolatorHelper.hpp diff --git a/src/libslic3r/Clipper2Utils.cpp b/src/libslic3r/Clipper2Utils.cpp index 12fd867500..d389d25209 100644 --- a/src/libslic3r/Clipper2Utils.cpp +++ b/src/libslic3r/Clipper2Utils.cpp @@ -188,6 +188,18 @@ ExPolygons offset_ex_2(const ExPolygons &expolygons, double delta) return results; } +ExPolygons offset_ex_2(const ExPolygons &expolygons, double delta, Clipper2Lib::JoinType joinType) +{ + Clipper2Lib::Paths64 subject = Slic3rExPolygons_to_Paths64(expolygons); + Clipper2Lib::ClipperOffset offsetter; + offsetter.AddPaths(subject, joinType, Clipper2Lib::EndType::Polygon); + Clipper2Lib::PolyPath64 polytree; + offsetter.Execute(delta, polytree); + ExPolygons results = PolyTreeToExPolygons(std::move(polytree)); + + return results; +} + ExPolygons offset2_ex_2(const ExPolygons& expolygons, double delta1, double delta2) { // 1st offset diff --git a/src/libslic3r/Clipper2Utils.hpp b/src/libslic3r/Clipper2Utils.hpp index 54b48d6bd7..2b4b0fb2ef 100644 --- a/src/libslic3r/Clipper2Utils.hpp +++ b/src/libslic3r/Clipper2Utils.hpp @@ -15,6 +15,7 @@ Slic3r::Polylines diff_pl_2(const Slic3r::Polylines& subject, const Slic3r::Pol ExPolygons union_ex_2(const Polygons &expolygons); ExPolygons union_ex_2(const ExPolygons &expolygons); ExPolygons offset_ex_2(const ExPolygons &expolygons, double delta); +ExPolygons offset_ex_2(const ExPolygons &expolygons, double delta, Clipper2Lib::JoinType joinType); ExPolygons offset2_ex_2(const ExPolygons &expolygons, double delta1, double delta2); } diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index bdb21124d4..68180cd2f7 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -272,6 +272,10 @@ struct SurfaceFillParams // For Gyroid: when true, use the parameterized "optimized" wave. bool gyroid_optimized = false; + bool anisotropic_surfaces{false}; + CenterOfSurfacePattern center_of_surface_pattern{CenterOfSurfacePattern::Each_Surface}; + bool separated_infills{false}; + bool operator<(const SurfaceFillParams &rhs) const { #define RETURN_COMPARE_NON_EQUAL(KEY) if (this->KEY < rhs.KEY) return true; if (this->KEY > rhs.KEY) return false; #define RETURN_COMPARE_NON_EQUAL_TYPED(TYPE, KEY) if (TYPE(this->KEY) < TYPE(rhs.KEY)) return true; if (TYPE(this->KEY) > TYPE(rhs.KEY)) return false; @@ -301,8 +305,12 @@ struct SurfaceFillParams RETURN_COMPARE_NON_EQUAL(lateral_lattice_angle_2); RETURN_COMPARE_NON_EQUAL(symmetric_infill_y_axis); RETURN_COMPARE_NON_EQUAL(infill_lock_depth); - RETURN_COMPARE_NON_EQUAL(skin_infill_depth); RETURN_COMPARE_NON_EQUAL(infill_overhang_angle); + RETURN_COMPARE_NON_EQUAL(skin_infill_depth); + RETURN_COMPARE_NON_EQUAL(infill_overhang_angle); RETURN_COMPARE_NON_EQUAL(gyroid_optimized); + RETURN_COMPARE_NON_EQUAL(anisotropic_surfaces); + RETURN_COMPARE_NON_EQUAL(center_of_surface_pattern); + RETURN_COMPARE_NON_EQUAL(separated_infills); return false; } @@ -329,6 +337,9 @@ struct SurfaceFillParams this->infill_lock_depth == rhs.infill_lock_depth && this->skin_infill_depth == rhs.skin_infill_depth && this->infill_overhang_angle == rhs.infill_overhang_angle && + this->anisotropic_surfaces == rhs.anisotropic_surfaces && + this->center_of_surface_pattern == rhs.center_of_surface_pattern && + this->separated_infills == rhs.separated_infills && this->gyroid_optimized == rhs.gyroid_optimized; } }; @@ -868,6 +879,9 @@ std::vector group_fills(const Layer &layer, LockRegionParam &lock_p params.lateral_lattice_angle_1 = region_config.lateral_lattice_angle_1; params.lateral_lattice_angle_2 = region_config.lateral_lattice_angle_2; params.infill_overhang_angle = region_config.infill_overhang_angle; + params.anisotropic_surfaces = region_config.anisotropic_surfaces; + params.center_of_surface_pattern = region_config.center_of_surface_pattern; + params.separated_infills = region_config.separated_infills; if (params.pattern == ipLockedZag) { params.infill_lock_depth = scale_(region_config.infill_lock_depth); params.skin_infill_depth = scale_(region_config.skin_infill_depth); @@ -1309,6 +1323,22 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: params.config = ®ion_config; params.pattern = surface_fill.params.pattern; + // Orca: Checking the filling of a centered surface by drawing for each model parts + bool is_top_or_bottom = params.extrusion_role == erTopSolidInfill || params.extrusion_role == erBottomSurface; + bool is_centered_infill = surface_fill.params.pattern == ipArchimedeanChords || surface_fill.params.pattern == ipOctagramSpiral; + if (is_top_or_bottom) { + params.is_anisotropic = surface_fill.params.anisotropic_surfaces; // Orca: anisotropic surfaces + params.center_of_surface_pattern = surface_fill.params.center_of_surface_pattern; // Orca: center of surface pattern + } + // Orca: Each_Model centers the pattern on each model part's bbox; Each_Surface / Each_Assembly + // fall through to the default (whole-object) bounding box below. + bool is_per_model_center = is_top_or_bottom && params.center_of_surface_pattern == CenterOfSurfacePattern::Each_Model && is_centered_infill; + bool is_separate_infill = !is_top_or_bottom && surface_fill.params.separated_infills && + ( + is_centered_infill || + params.config->solid_infill_rotate_template != "" || + params.config->sparse_infill_rotate_template != "" ); + if( surface_fill.params.pattern == ipLockedZag ) { params.locked_zag = true; params.infill_lock_depth = surface_fill.params.infill_lock_depth; @@ -1332,7 +1362,65 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: params.can_reverse = false; for (ExPolygon& expoly : surface_fill.expolygons) { - f->no_overlap_expolygons = intersection_ex(surface_fill.no_overlap_expolygons, ExPolygons() = {expoly}, ApplySafetyOffset::Yes); + // Orca: separate infill / per-model pattern centering. + // + // First assign this fill region to the model part whose slice at this layer overlaps it + // the most. A strict "contains" test is ambiguous for assemblies whose parts overlap (a + // region may sit inside several parts, or straddle a boundary and be inside none), so we + // pick by intersection area instead. + // + // The center must belong to an *overlap group*, not a single part: parts that + // touch/overlap form one connected physical body that shares a single center, while a + // part detached from the rest of the assembly gets its own. This holds for both + // separated infills and Each_Model surface centering (Each_Model == per connected body). + // firstLayerObjGroups() already holds these connected components, so we widen the chosen + // part's bbox to the whole group it belongs to. + if (is_per_model_center || is_separate_infill) { + double best_overlap = 0.; + ObjectID best_vol_id; + const PrintInstance* best_instance = nullptr; + for (const auto& instance : this->object()->instances()) { + for (const auto& volume : instance.print_object->firstLayerObjSlice()) { + if (f->layer_id >= volume.slices.size()) + continue; + const double overlap = area(intersection_ex(volume.slices[f->layer_id], ExPolygons{expoly})); + if (overlap > best_overlap) { + best_overlap = overlap; + best_vol_id = volume.volume_id; + best_instance = &instance; + } + } + } + if (best_instance) { + const Transform3d matrix = best_instance->model_instance->get_matrix(); + Point shift = best_instance->shift; // get_volume_bbox takes a non-const ref + auto& volumes = best_instance->model_instance->get_object()->volumes; + + // Volume ids to center on: the whole overlap group the winning part belongs to, + // falling back to just that part if it isn't part of any group. + std::vector center_ids; + for (const auto& group : best_instance->print_object->firstLayerObjGroups()) { + bool in_group = false; + for (const ObjectID& vid : group.volume_ids) + if (vid == best_vol_id) { in_group = true; break; } + if (in_group) { center_ids = group.volume_ids; break; } + } + if (center_ids.empty()) + center_ids.push_back(best_vol_id); + + BoundingBox bbox; + for (const ObjectID& vid : center_ids) + for (auto model_volume : volumes) + if (vid.id == model_volume->id().id) { + bbox.merge(model_volume->get_volume_bbox(matrix, shift, true)); + break; + } + if (bbox.defined) + f->set_bounding_box(bbox); + } + } // - End: separate infill / per-model pattern centering + + f->no_overlap_expolygons = intersection_ex(surface_fill.no_overlap_expolygons, ExPolygons() = {expoly}, ApplySafetyOffset::Yes); if (params.symmetric_infill_y_axis) { params.symmetric_y_axis = f->extended_object_bounding_box().center().x(); expoly.symmetric_y(params.symmetric_y_axis); diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp index 1d15614ccb..cf74606dd4 100644 --- a/src/libslic3r/Fill/FillBase.cpp +++ b/src/libslic3r/Fill/FillBase.cpp @@ -165,7 +165,7 @@ void Fill::fill_surface_extrusion(const Surface* surface, const FillParams& para // ORCA: special flag for flow rate calibration auto is_flow_calib = params.extrusion_role == erTopSolidInfill && this->print_object_config->has("calib_flowrate_topinfill_special_order") && this->print_object_config->option("calib_flowrate_topinfill_special_order")->getBool(); - if (is_flow_calib) { + if (is_flow_calib || params.is_anisotropic) { // Orca: disable sorting while anisotropic surfaces eec->no_sort = true; } size_t idx = eec->entities.size(); @@ -186,7 +186,8 @@ void Fill::fill_surface_extrusion(const Surface* surface, const FillParams& para } // Orca: run gap fill - this->_create_gap_fill(surface, params, eec); + if (!(params.is_anisotropic)) // Orca: Disable gap filling while anisotropic + this->_create_gap_fill(surface, params, eec); } } diff --git a/src/libslic3r/Fill/FillBase.hpp b/src/libslic3r/Fill/FillBase.hpp index ef6a6bc804..ffeed3045b 100644 --- a/src/libslic3r/Fill/FillBase.hpp +++ b/src/libslic3r/Fill/FillBase.hpp @@ -106,6 +106,8 @@ struct FillParams bool locked_zag{false}; float infill_lock_depth{0.0}; float skin_infill_depth{0.0}; + bool is_anisotropic{false}; + CenterOfSurfacePattern center_of_surface_pattern{CenterOfSurfacePattern::Each_Surface}; }; static_assert(IsTriviallyCopyable::value, "FillParams class is not POD (and it should be - see constructor)."); diff --git a/src/libslic3r/Fill/FillPlanePath.cpp b/src/libslic3r/Fill/FillPlanePath.cpp index b4681d05f9..01f4ca00da 100644 --- a/src/libslic3r/Fill/FillPlanePath.cpp +++ b/src/libslic3r/Fill/FillPlanePath.cpp @@ -77,20 +77,24 @@ void FillPlanePath::_fill_surface_single( //FIXME Vojtech: We are not sure whether the user expects the fill patterns on visible surfaces to be aligned across all the islands of a single layer. // One may align for this->centered() to align the patterns for Archimedean Chords and Octagram Spiral patterns. - const bool align = params.density < 0.995; - + // Orca: the old implementation became obsolete when it became possible to change the density of the top and bottom surfaces + bool align = params.extrusion_role == ExtrusionRole::erInternalInfill; + BoundingBox bounding_box; BoundingBox snug_bounding_box = get_extents(expolygon).inflated(SCALED_EPSILON); // Expand the bounding box to avoid artifacts at the edges - snug_bounding_box.offset(scale_(this->spacing)*params.multiline); + snug_bounding_box.offset(scale_(this->spacing)*params.multiline); - // Rotated bounding box of the area to fill in with the pattern. - BoundingBox bounding_box = align ? - // Sparse infill needs to be aligned across layers. Align infill across layers using the object's bounding box. - this->bounding_box.rotated(-direction.first) : - // Solid infill does not need to be aligned across layers, generate the infill pattern - // around the clipping expolygon only. - snug_bounding_box; + // Sparse infill (or Internal where align == true) needs to be aligned across layers. Align infill across layers using the object's bounding box. + // Solid infill does not need to be aligned across layers, generate the infill pattern around the clipping expolygon only. + if (align) + bounding_box = this->bounding_box.rotated(-direction.first); + else if (params.center_of_surface_pattern == CenterOfSurfacePattern::Each_Surface) + bounding_box = snug_bounding_box; + else if (params.center_of_surface_pattern == CenterOfSurfacePattern::Each_Model) + bounding_box = this->bounding_box.rotated(-direction.first); + else + bounding_box = extended_object_bounding_box(); Point shift = this->centered() ? bounding_box.center() : @@ -129,35 +133,49 @@ void FillPlanePath::_fill_surface_single( polylines = intersection_pl(std::move(polylines), expolygon); if (!polylines.empty()) { Polylines chained; - if (params.dont_connect() || params.density > 0.5) { - // ORCA: special flag for flow rate calibration - auto is_flow_calib = params.extrusion_role == erTopSolidInfill && - this->print_object_config->has("calib_flowrate_topinfill_special_order") && - this->print_object_config->option("calib_flowrate_topinfill_special_order")->getBool() && - dynamic_cast(this); - if (is_flow_calib) { - // We want the spiral part to be printed inside-out - // Find the center spiral line first, by looking for the longest one - auto it = std::max_element(polylines.begin(), polylines.end(), - [](const Polyline& a, const Polyline& b) { return a.length() < b.length(); }); - Polyline center_spiral = std::move(*it); + if (!params.is_anisotropic) { // Orca: not anisotropic surface + if ((params.dont_connect() || params.density > 0.5)) { + // ORCA: special flag for flow rate calibration + auto is_flow_calib = params.extrusion_role == erTopSolidInfill && + this->print_object_config->has("calib_flowrate_topinfill_special_order") && + this->print_object_config->option("calib_flowrate_topinfill_special_order")->getBool() && + dynamic_cast(this); + if (is_flow_calib) { + // We want the spiral part to be printed inside-out + // Find the center spiral line first, by looking for the longest one + auto it = std::max_element(polylines.begin(), polylines.end(), + [](const Polyline& a, const Polyline& b) { return a.length() < b.length(); }); + Polyline center_spiral = std::move(*it); - // Ensure the spiral is printed from inside to out - if (center_spiral.first_point().squaredNorm() > center_spiral.last_point().squaredNorm()) { - center_spiral.reverse(); + // Ensure the spiral is printed from inside to out + if ((center_spiral.first_point().squaredNorm() > center_spiral.last_point().squaredNorm())) { + center_spiral.reverse(); + } + + // Chain the other polylines + polylines.erase(it); + chained = chain_polylines(std::move(polylines), nullptr); + + // Then add the center spiral back + chained.push_back(std::move(center_spiral)); + } else { + chained = chain_polylines(std::move(polylines), nullptr); } - - // Chain the other polylines - polylines.erase(it); - chained = chain_polylines(std::move(polylines)); - - // Then add the center spiral back - chained.push_back(std::move(center_spiral)); - } else { - chained = chain_polylines(std::move(polylines)); + } else + connect_infill(std::move(polylines), expolygon, chained, this->spacing, params); + } else { // Orca: anisotropic surface + const Point _center(0., 0.); + for (Polyline& segment : polylines) { // sort paths by its direction + if (segment.size() > 1) { // need at least two points to evaluate direction + if (segment.first_point().ccw(segment.points[1], _center) < 0) + segment.reverse(); + } + chained.emplace_back(std::move(segment)); } - } else - connect_infill(std::move(polylines), expolygon, chained, this->spacing, params); + std::sort(chained.begin(), chained.end(), [&_center](const Polyline& a, const Polyline& b) { // just sort polylines from center to outside + return a.distance_to(_center) < b.distance_to(_center); + }); + } // paths must be repositioned and rotated back for (Polyline& pl : chained) { pl.translate(shift.x(), shift.y()); diff --git a/src/libslic3r/Format/bbs_3mf.cpp b/src/libslic3r/Format/bbs_3mf.cpp index 31519e4fbe..fd395135ae 100644 --- a/src/libslic3r/Format/bbs_3mf.cpp +++ b/src/libslic3r/Format/bbs_3mf.cpp @@ -5949,7 +5949,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) m_thumbnail_middle = iter->second; } boost::system::error_code ec; - std::string filename = std::string(store_params.path); + std::string filename = store_params.path; boost::filesystem::remove(filename + ".tmp", ec); bool result = _save_model_to_file(filename + ".tmp", *store_params.model, store_params.plate_data_list, store_params.project_presets, store_params.config, @@ -8988,7 +8988,7 @@ bool store_bbs_3mf(StoreParams& store_params) // All export should use "C" locales for number formatting. CNumericLocalesSetter locales_setter; - if (store_params.path == nullptr || store_params.model == nullptr) + if (store_params.path.empty() || store_params.model == nullptr) return false; _BBS_3MF_Exporter exporter; diff --git a/src/libslic3r/Format/bbs_3mf.hpp b/src/libslic3r/Format/bbs_3mf.hpp index df4f91f6e9..c16c423f66 100644 --- a/src/libslic3r/Format/bbs_3mf.hpp +++ b/src/libslic3r/Format/bbs_3mf.hpp @@ -226,7 +226,7 @@ typedef std::map PlateDataMaps; struct StoreParams { - const char* path; + std::string path; Model* model = nullptr; PlateDataPtrs plate_data_list; int export_plate_idx = -1; diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 0b62fd0d6f..8238f5be85 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -6215,7 +6215,8 @@ std::string GCode::extrude_support(const ExtrusionEntityCollection &support_fill static constexpr const char* support_transition_label = "support transition"; static constexpr const char* support_ironing_label = "support ironing"; - static const auto speed_for_path = [&](double length, ExtrusionRole role, double default_speed = -1.0) { + // Not static: it captures `this` by reference. + const auto speed_for_path = [&](double length, ExtrusionRole role, double default_speed = -1.0) { if (!is_support(role) || length > SMALL_PERIMETER_LENGTH(NOZZLE_CONFIG(small_support_perimeter_threshold))) return default_speed; diff --git a/src/libslic3r/GCode/ElegooGCodeProcessorHelper.cpp b/src/libslic3r/GCode/ElegooGCodeProcessorHelper.cpp new file mode 100644 index 0000000000..cac53637a5 --- /dev/null +++ b/src/libslic3r/GCode/ElegooGCodeProcessorHelper.cpp @@ -0,0 +1,190 @@ +#include "GCodeProcessor.hpp" + +#include "libslic3r/libslic3r.h" + +#include +#include +#include +#include + +namespace Slic3r { +namespace { + +bool equals_case_insensitive(std::string_view lhs, std::string_view rhs) +{ + return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin(), [](unsigned char l, unsigned char r) { + return std::tolower(l) == std::tolower(r); + }); +} + +float get_clamped_param(const GCodeReader::GCodeLine& line, char axis, float default_value, float min_value, float max_value) +{ + float value = default_value; + line.has_value(axis, value); + return std::clamp(value, min_value, max_value); +} + +float extrusion_time(float e_length, float feedrate) +{ + return feedrate > 0.0f && e_length > 0.0f ? e_length / feedrate * 60.0f : 0.0f; +} + +float retract_time(float e_length) +{ + static constexpr float retract_feedrate = 1800.0f; + return extrusion_time(std::max(e_length, 0.0f), retract_feedrate); +} + +float s819_time(float e_length, float feedrate) +{ + static constexpr float s819_tail_flush_length = 10.0f; + static constexpr float s819_tail_feedrate = 400.0f; + + const float tail_length = std::min(std::max(e_length, 0.0f), s819_tail_flush_length); + const float main_length = std::max(e_length - tail_length, 0.0f); + return extrusion_time(main_length, feedrate) + extrusion_time(tail_length, s819_tail_feedrate); +} + +float estimate_M6211_time_for_centauri_carbon(const GCodeReader::GCodeLine& line, float length, double current_x, + double current_y) +{ + static constexpr float max_segment_length = 73.0f; + static constexpr float wipe_after_flush_time = 2.8f; + static constexpr float main_feedrate = 500.0f; + static constexpr float tail_feedrate = 400.0f; + static constexpr float travel_feedrate = 5000.0f; + static constexpr double parking_x = 256.0; + static constexpr double parking_y = 0.0; + + const float flush_length = std::clamp(length, 10.0f, 1000.0f); + const float cool_time = get_clamped_param(line, 'P', 5000.0f, 0.0f, 20000.0f) * 0.001f; + const float travel_time = static_cast(std::abs(current_y - parking_y) + std::abs(current_x - parking_x)) / + travel_feedrate * 60.0f; + + // Initial time, including: material change, heating, etc. + float m6211_time = 18.2f + travel_time; + + float remaining_flush_length = std::max(flush_length, 0.0f); + while (remaining_flush_length > 0.0f) { + const float segment_length = std::min(remaining_flush_length, max_segment_length); + remaining_flush_length -= segment_length; + if (segment_length >= max_segment_length) { + // Full segment: 3-phase extrusion (30+35+10=75mm) + retract + m6211_time += extrusion_time(30.0f, main_feedrate) + extrusion_time(35.0f, main_feedrate) + + extrusion_time(10.0f, tail_feedrate) + extrusion_time(2.0f, tail_feedrate) + cool_time + + wipe_after_flush_time; + } else { + // Partial last segment: simple extrude at F500 + retract at F400 + m6211_time += extrusion_time(segment_length, main_feedrate) + extrusion_time(2.0f, tail_feedrate) + cool_time + + wipe_after_flush_time; + } + } + + return m6211_time; +} + +float estimate_M6211_time_for_centauri_carbon_2(const GCodeReader::GCodeLine& line, float length, float new_extruder_temp) +{ + const float flush_length = std::clamp(length, 10.0f, 1000.0f); + const float flush_length_single = get_clamped_param(line, 'K', 75.0f, 10.0f, 300.0f); + const float old_filament_e_feedrate = get_clamped_param(line, 'M', 300.0f, 10.0f, 600.0f); + const float new_filament_e_feedrate = get_clamped_param(line, 'N', 300.0f, 10.0f, 600.0f); + const float cool_time = get_clamped_param(line, 'P', 3000.0f, 0.0f, 20000.0f) * 0.001f; + + // The flush length of the old material, unit: mm + static constexpr float e_flush_dist = 15.0f; + // Wipe time after flush, in seconds + static constexpr float wipe_after_flush_time = 5.0f; + + const float flush_length_after_start = std::max(flush_length - e_flush_dist, 0.0f); + const int flush_times = std::max(1, static_cast(std::ceil(flush_length_after_start / flush_length_single))); + const float flush_length_actual = flush_length_single; + + // Initial time, including: material change, heating, moving, etc. + float m6211_time = 31.0f; + m6211_time += extrusion_time(std::min(e_flush_dist, flush_length), old_filament_e_feedrate); + + const int intermediate_flush_times = flush_times - 1; + const float intermediate_flush_time = s819_time(flush_length_actual, new_filament_e_feedrate) + retract_time(6.0f) + cool_time + + wipe_after_flush_time; + m6211_time += static_cast(intermediate_flush_times) * intermediate_flush_time; + m6211_time += s819_time(flush_length_actual, new_filament_e_feedrate * 0.8f) + retract_time(4.0f) + cool_time + wipe_after_flush_time; + + static constexpr float cooling_rate = 1.36f; + const float r_temp = get_clamped_param(line, 'R', new_extruder_temp + 20.0f, 185.0f, 350.0f); + const float s_temp = get_clamped_param(line, 'S', 250.0f, 185.0f, 350.0f); + + if (s_temp < r_temp) + m6211_time += (r_temp - s_temp) / cooling_rate; + + return m6211_time; +} + +float estimate_M6211_time(const GCodeReader::GCodeLine& line, std::string_view printer_model, float length, float new_extruder_temp, double current_x, double current_y) +{ + if (equals_case_insensitive(printer_model, "Elegoo Centauri Carbon") || equals_case_insensitive(printer_model, "Elegoo Centauri")) { + return estimate_M6211_time_for_centauri_carbon(line, length, current_x, current_y); + } else if (equals_case_insensitive(printer_model, "Elegoo Centauri Carbon 2") || + equals_case_insensitive(printer_model, "Elegoo Centauri 2")) { + return estimate_M6211_time_for_centauri_carbon_2(line, length, new_extruder_temp); + } + return 0.0f; +} + +} // namespace + +void GCodeProcessor::process_elegoo_M6211(const GCodeReader::GCodeLine& line) +{ + float length = 0.0f; + if (!line.has_value('L', length) || length <= 0.0f) + return; + + float t = -1.0f; + if (!line.has_value('T', t) || t < 0.0f) + return; + + const int filament_id = static_cast(std::round(t)); + if (filament_id < 0 || filament_id >= m_result.filaments_count) + return; + + const int extruder_id = m_filament_maps[filament_id]; + + float new_extruder_temp = 0.0f; + if (line.has_value('S', new_extruder_temp)) { + if (extruder_id >= 0 && static_cast(extruder_id) < m_extruder_temps.size()) + m_extruder_temps[static_cast(extruder_id)] = new_extruder_temp; + } + + const float m6211_time = estimate_M6211_time(line, m_printer_model, length, new_extruder_temp, + m_start_position[X], m_start_position[Y]); + const int curr_filament_id = get_filament_id(false); + const bool is_first_extrusion = (curr_filament_id == -1) || (filament_id == curr_filament_id); + + m_time_processor.filament_unload_times = 0; + m_time_processor.filament_load_times = m6211_time; + process_filament_change(filament_id); + + if (extruder_id >= 0 && static_cast(extruder_id) < m_remaining_volume.size()) { + const float remaining_volume = static_cast(extruder_id) < m_nozzle_volume.size() ? + m_nozzle_volume[extruder_id] : + 0.0f; + const float filament_diameter = static_cast(filament_id) < m_result.filament_diameters.size() ? + m_result.filament_diameters[filament_id] : + m_result.filament_diameters.back(); + const float area_filament_cross_section = static_cast(M_PI) * sqr(0.5f * filament_diameter); + const float volume_flushed_filament = area_filament_cross_section * length; + + if (volume_flushed_filament >= remaining_volume) { + if (!is_first_extrusion) + m_used_filaments.update_flush_per_filament(curr_filament_id, remaining_volume); + + m_used_filaments.update_flush_per_filament(filament_id, volume_flushed_filament - remaining_volume); + m_remaining_volume[extruder_id] = 0.0f; + } else { + m_used_filaments.update_flush_per_filament(filament_id, volume_flushed_filament); + m_remaining_volume[extruder_id] -= volume_flushed_filament; + } + } +} + +} // namespace Slic3r diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 44cb185aec..c0a92780c4 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -1839,7 +1839,8 @@ void GCodeProcessor::register_commands() {"VM104", [this](const GCodeReader::GCodeLine& line) { process_VM104(line); }}, {"VM109", [this](const GCodeReader::GCodeLine& line) { process_VM109(line); }}, {"M622", [this](const GCodeReader::GCodeLine& line) { process_M622(line);}}, - {"M623", [this](const GCodeReader::GCodeLine& line) { process_M623(line);}} + {"M623", [this](const GCodeReader::GCodeLine& line) { process_M623(line);}}, + {"M6211", [this](const GCodeReader::GCodeLine& line) { process_M6211(line); }} }; std::unordered_setearly_quit_commands = { @@ -1863,6 +1864,12 @@ void GCodeProcessor::register_commands() } } +void GCodeProcessor::process_M6211(const GCodeReader::GCodeLine& line) +{ + if (boost::algorithm::istarts_with(m_printer_model, "elegoo")) + process_elegoo_M6211(line); +} + bool GCodeProcessor::check_multi_extruder_gcode_valid(const int extruder_size, const Pointfs plate_printable_area, const double plate_printable_height, @@ -2027,6 +2034,7 @@ void GCodeProcessor::apply_config(const PrintConfig& config) m_parser.apply_config(config); m_flavor = config.gcode_flavor; + m_printer_model = config.printer_model.value; m_single_extruder_multi_material = config.single_extruder_multi_material; @@ -2213,6 +2221,10 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) if (printer_settings_id != nullptr) m_result.settings_ids.printer = printer_settings_id->value; + const ConfigOptionString* printer_model = config.option("printer_model"); + if (printer_model != nullptr) + m_printer_model = printer_model->value; + // BBS m_result.filaments_count = config.option("filament_diameter")->values.size(); diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 1d2e0890da..fcefd1a9a7 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -846,6 +846,7 @@ class Print; float m_preheat_time; int m_preheat_steps; bool m_disable_m73; + std::string m_printer_model; enum class EProducer { @@ -1084,6 +1085,10 @@ class Print; // Unload the current filament into the MK3 MMU2 unit at the end of print. void process_M702(const GCodeReader::GCodeLine& line); + //Used for Elegoo printer to change tool head + void process_M6211(const GCodeReader::GCodeLine& line); + void process_elegoo_M6211(const GCodeReader::GCodeLine& line); + void process_SYNC(const GCodeReader::GCodeLine& line); // Processes T line (Select Tool) diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index 5cb66ba5e5..ee03d6e959 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -187,6 +187,10 @@ static void apply_first_layer_order(const DynamicPrintConfig* config, std::vecto void ToolOrdering::handle_dontcare_extruder(const std::vector& tool_order_layer0) { + const PrintConfig* print_config = m_print_config_ptr; + if (!print_config && m_print_object_ptr) + print_config = &m_print_object_ptr->print()->config(); + if(m_layer_tools.empty() || tool_order_layer0.empty()) return; @@ -221,6 +225,8 @@ void ToolOrdering::handle_dontcare_extruder(const std::vector& too for (int i = 1; i < m_layer_tools.size(); i++) { LayerTools& lt = m_layer_tools[i]; + // Extruders in lt.extruders are already sorted. + if (lt.extruders.empty()) continue; if (lt.extruders.size() == 1 && lt.extruders.front() == 0) @@ -229,14 +235,23 @@ void ToolOrdering::handle_dontcare_extruder(const std::vector& too if (lt.extruders.front() == 0) // Pop the "don't care" extruder, the "don't care" region will be merged with the next one. lt.extruders.erase(lt.extruders.begin()); - // Reorder the extruders to start with the last one. - for (size_t i = 1; i < lt.extruders.size(); ++i) - if (lt.extruders[i] == last_extruder_id) { - // Move the last extruder to the front. - memmove(lt.extruders.data() + 1, lt.extruders.data(), i * sizeof(unsigned int)); - lt.extruders.front() = last_extruder_id; - break; + + if (print_config == nullptr + || print_config->toolchange_ordering == ToolChangeOrderingType::Default) + { + // Reorder the extruders to start with the last one. + for (size_t i = 1; i < lt.extruders.size(); ++i) { + if (lt.extruders[i] == last_extruder_id) { + // Move the last extruder to the front. + std::rotate( + lt.extruders.begin(), + lt.extruders.begin() + i, + lt.extruders.begin() + i + 1 + ); + break; + } } + } } last_extruder_id = lt.extruders.back(); } @@ -252,6 +267,10 @@ void ToolOrdering::handle_dontcare_extruder(const std::vector& too void ToolOrdering::handle_dontcare_extruder(unsigned int last_extruder_id) { + const PrintConfig* print_config = m_print_config_ptr; + if (!print_config && m_print_object_ptr) + print_config = &m_print_object_ptr->print()->config(); + if(m_layer_tools.empty()) return; if(last_extruder_id == (unsigned int)-1){ @@ -275,6 +294,8 @@ void ToolOrdering::handle_dontcare_extruder(unsigned int last_extruder_id) } for (LayerTools < : m_layer_tools) { + // Extruders in lt.extruders are already sorted. + if (lt.extruders.empty()) continue; if (lt.extruders.size() == 1 && lt.extruders.front() == 0) @@ -283,21 +304,30 @@ void ToolOrdering::handle_dontcare_extruder(unsigned int last_extruder_id) if (lt.extruders.front() == 0) // Pop the "don't care" extruder, the "don't care" region will be merged with the next one. lt.extruders.erase(lt.extruders.begin()); - // Reorder the extruders to start with the last one. - for (size_t i = 1; i < lt.extruders.size(); ++ i) - if (lt.extruders[i] == last_extruder_id) { - // Move the last extruder to the front. - memmove(lt.extruders.data() + 1, lt.extruders.data(), i * sizeof(unsigned int)); - lt.extruders.front() = last_extruder_id; - break; + + if (print_config == nullptr + || print_config->toolchange_ordering == ToolChangeOrderingType::Default) + { + // Reorder the extruders to start with the last one. + for (size_t i = 1; i < lt.extruders.size(); ++i) { + if (lt.extruders[i] == last_extruder_id) { + // Move the last extruder to the front. + std::rotate( + lt.extruders.begin(), + lt.extruders.begin() + i, + lt.extruders.begin() + i + 1 + ); + break; + } } + } if (lt == m_layer_tools[0]) { // On first layer with wipe tower, prefer a soluble extruder // at the beginning, so it is not wiped on the first layer. - if (m_print_config_ptr && m_print_config_ptr->enable_prime_tower) { + if (print_config && print_config->enable_prime_tower) { for (size_t i = 0; ifilament_soluble.get_at(lt.extruders[i]-1)) { // 1-based... + if (print_config->filament_soluble.get_at(lt.extruders[i]-1)) { // 1-based... std::swap(lt.extruders[i], lt.extruders.front()); break; } @@ -396,6 +426,7 @@ void ToolOrdering::sort_and_build_data(const PrintObject& object , unsigned int ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extruder, bool prime_multi_material) { m_print_full_config = &object.print()->full_print_config(); + m_print_config_ptr = &object.print()->config(); m_print_object_ptr = &object; m_print = const_cast(object.print()); if (object.layers().empty()) @@ -1329,8 +1360,11 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume(bool reorder_first if (!m_layer_tools.empty()) first_layer_filaments = m_layer_tools[0].extruders; + const bool use_cyclic_ordering = + (print_config->toolchange_ordering == ToolChangeOrderingType::Cyclic); + // other_layers_seq: the layer_idx and extruder_idx are base on 1 - auto get_custom_seq = [&other_layers_seqs, &reorder_first_layer, &first_layer_filaments](int layer_idx, std::vector& out_seq) -> bool { + auto get_custom_seq = [&other_layers_seqs, &reorder_first_layer, &first_layer_filaments, &layer_filaments, use_cyclic_ordering](int layer_idx, std::vector& out_seq) -> bool { if (!reorder_first_layer && layer_idx == 0) { out_seq.resize(first_layer_filaments.size()); std::transform(first_layer_filaments.begin(), first_layer_filaments.end(), out_seq.begin(), [](auto item) {return item + 1; }); @@ -1343,6 +1377,15 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume(bool reorder_first return true; } } + + if (use_cyclic_ordering && layer_idx >= 0 && size_t(layer_idx) < layer_filaments.size()) { + std::vector ordered = layer_filaments[size_t(layer_idx)]; + std::sort(ordered.begin(), ordered.end()); + out_seq.resize(ordered.size()); + std::transform(ordered.begin(), ordered.end(), out_seq.begin(), [](auto item) { return int(item) + 1; }); + return true; + } + return false; }; diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 1177a5227d..eb9153a661 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -2708,6 +2708,23 @@ const TriangleMesh& ModelVolume::get_convex_hull() const return *m_convex_hull.get(); } +// Orca: get volume bbox for separate infill +static std::mutex mtx_model; +BoundingBox ModelVolume::get_volume_bbox(const Transform3d &matrix, Point &shift, bool apply_cache = false) { + std::unique_lock l(mtx_model); // locks function here + // Orca: the cache is keyed by the instance transform/shift; a ModelVolume is shared + // across instances, so returning the cache blindly would hand back another instance's bbox. + if (m_cached_volume_bbox.defined && apply_cache + && matrix.isApprox(m_cached_volume_bbox_matrix) + && shift == m_cached_volume_bbox_shift) + return m_cached_volume_bbox; + auto hull = get_convex_hull_2d(matrix); + hull.translate(-shift); + m_cached_volume_bbox_matrix = matrix; + m_cached_volume_bbox_shift = shift; + return m_cached_volume_bbox = hull.bounding_box().polygon().bounding_box(); +} + //BBS: refine the model part names ModelVolumeType ModelVolume::type_from_string(const std::string &s) { diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index b15124d9ed..518fbbaa1c 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -920,6 +920,14 @@ public: // Extruder ID is only valid for FFF. Returns -1 for SLA or if the extruder ID is not applicable (support volumes). int extruder_id() const; + //Orca: cache clearing procedure to ensure that the shape is positioned accurately when manipulating it + void clear_cache() { + m_cached_trans_matrix = Transform3d::Identity().inverse(); // get unvelivable matrix + m_cached_volume_bbox.reset(); + m_convex_hull_2d.clear(); + m_cached_2d_polygon.clear(); + }; + bool is_splittable() const; // BBS @@ -961,39 +969,42 @@ public: // Get count of errors in the mesh int get_repaired_errors_count() const; + BoundingBox get_volume_bbox(const Transform3d &matrix, Point &shift, bool apply_cache); + void reset_volume_bbox() { m_cached_volume_bbox.reset(); }; + // Helpers for loading / storing into AMF / 3MF files. static ModelVolumeType type_from_string(const std::string &s); static std::string type_to_string(const ModelVolumeType t); const Geometry::Transformation& get_transformation() const { return m_transformation; } - void set_transformation(const Geometry::Transformation& transformation) { m_transformation = transformation; } - void set_transformation(const Transform3d& trafo) { m_transformation.set_matrix(trafo); } + void set_transformation(const Geometry::Transformation& transformation) { clear_cache(); m_transformation = transformation; } + void set_transformation(const Transform3d& trafo) { clear_cache(); m_transformation.set_matrix(trafo); } Vec3d get_offset() const { return m_transformation.get_offset(); } double get_offset(Axis axis) const { return m_transformation.get_offset(axis); } - void set_offset(const Vec3d& offset) { m_transformation.set_offset(offset); } - void set_offset(Axis axis, double offset) { m_transformation.set_offset(axis, offset); } + void set_offset(const Vec3d& offset) { clear_cache(); m_transformation.set_offset(offset); } + void set_offset(Axis axis, double offset) { clear_cache(); m_transformation.set_offset(axis, offset); } Vec3d get_rotation() const { return m_transformation.get_rotation(); } double get_rotation(Axis axis) const { return m_transformation.get_rotation(axis); } - void set_rotation(const Vec3d& rotation) { m_transformation.set_rotation(rotation); } - void set_rotation(Axis axis, double rotation) { m_transformation.set_rotation(axis, rotation); } + void set_rotation(const Vec3d& rotation) { clear_cache(); m_transformation.set_rotation(rotation); } + void set_rotation(Axis axis, double rotation) { clear_cache(); m_transformation.set_rotation(axis, rotation); } Vec3d get_scaling_factor() const { return m_transformation.get_scaling_factor(); } double get_scaling_factor(Axis axis) const { return m_transformation.get_scaling_factor(axis); } - void set_scaling_factor(const Vec3d& scaling_factor) { m_transformation.set_scaling_factor(scaling_factor); } - void set_scaling_factor(Axis axis, double scaling_factor) { m_transformation.set_scaling_factor(axis, scaling_factor); } + void set_scaling_factor(const Vec3d& scaling_factor) { clear_cache(); m_transformation.set_scaling_factor(scaling_factor); } + void set_scaling_factor(Axis axis, double scaling_factor) {clear_cache(); m_transformation.set_scaling_factor(axis, scaling_factor); } Vec3d get_mirror() const { return m_transformation.get_mirror(); } double get_mirror(Axis axis) const { return m_transformation.get_mirror(axis); } bool is_left_handed() const { return m_transformation.is_left_handed(); } - void set_mirror(const Vec3d& mirror) { m_transformation.set_mirror(mirror); } - void set_mirror(Axis axis, double mirror) { m_transformation.set_mirror(axis, mirror); } + void set_mirror(const Vec3d& mirror) { clear_cache(); m_transformation.set_mirror(mirror); } + void set_mirror(Axis axis, double mirror) { clear_cache(); m_transformation.set_mirror(axis, mirror); } void convert_from_imperial_units(); void convert_from_meters(); @@ -1048,6 +1059,9 @@ private: mutable Transform3d m_cached_trans_matrix; //BBS, used for convex_hell_2d acceleration mutable Polygon m_cached_2d_polygon; //BBS, used for convex_hell_2d acceleration Geometry::Transformation m_transformation; + mutable BoundingBox m_cached_volume_bbox; //Orca: used for separated infills + mutable Transform3d m_cached_volume_bbox_matrix{Transform3d::Identity()}; //Orca: cache key for m_cached_volume_bbox + mutable Point m_cached_volume_bbox_shift{Point(0, 0)}; //Orca: cache key for m_cached_volume_bbox //BBS: add convex_hell_2d related logic void calculate_convex_hull_2d(const Geometry::Transformation &transformation) const; diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 6c29367c01..1c785c3184 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -571,6 +571,19 @@ 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) +{ + 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); +} + void PerimeterGenerator::split_top_surfaces(const ExPolygons &orig_polygons, ExPolygons &top_fills, ExPolygons &non_top_polygons, ExPolygons &fill_clip) const { // other perimeters @@ -636,6 +649,8 @@ 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) ExPolygons temp_gap = diff_ex(top_polygons, fill_clip); @@ -2194,6 +2209,7 @@ 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) { diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 94601865b7..0e5970a5e9 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -188,6 +188,12 @@ static t_config_enum_values s_keys_map_PowerLossRecoveryMode { }; CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(PowerLossRecoveryMode) +static t_config_enum_values s_keys_map_CenterOfSurfacePattern{ + {"each_surface", int(CenterOfSurfacePattern::Each_Surface)}, + {"each_model", int(CenterOfSurfacePattern::Each_Model)}, + {"each_assembly", int(CenterOfSurfacePattern::Each_Assembly)}}; +CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(CenterOfSurfacePattern) + static t_config_enum_values s_keys_map_FuzzySkinType { { "none", int(FuzzySkinType::None) }, { "external", int(FuzzySkinType::External) }, @@ -221,6 +227,13 @@ static t_config_enum_values s_keys_map_FuzzySkinMode { }; CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(FuzzySkinMode) +static t_config_enum_values s_keys_map_TopSurfaceExpansionDirection { + { "inward_and_outward", int(TopSurfaceExpansionDirection::InwardAndOutward) }, + { "inward", int(TopSurfaceExpansionDirection::Inward) }, + { "outward", int(TopSurfaceExpansionDirection::Outward) } +}; +CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(TopSurfaceExpansionDirection) + static t_config_enum_values s_keys_map_InfillPattern { { "monotonic", ipMonotonic }, { "monotonicline", ipMonotonicLine }, @@ -522,6 +535,12 @@ static t_config_enum_values s_keys_map_PerimeterGeneratorType{ }; CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(PerimeterGeneratorType) +static t_config_enum_values s_keys_map_ToolChangeOrderingType { + { "default", int(ToolChangeOrderingType::Default) }, + { "cyclic", int(ToolChangeOrderingType::Cyclic) } +}; +CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(ToolChangeOrderingType) + static const t_config_enum_values s_keys_map_ZHopType = { { "Auto Lift", zhtAuto }, { "Normal Lift", zhtNormal }, diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 3b8b809c78..f36076da45 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -62,6 +62,19 @@ enum class FuzzySkinMode { Combined, }; +// ORCA: direction in which top_surface_expansion grows the top surfaces. +enum class TopSurfaceExpansionDirection { + InwardAndOutward, + Inward, + Outward, +}; + +enum class CenterOfSurfacePattern { + Each_Surface, + Each_Model, + Each_Assembly, +}; + enum class NoiseType { Classic, Perlin, @@ -300,6 +313,12 @@ enum class PerimeterGeneratorType Arachne }; +enum class ToolChangeOrderingType +{ + Default, + Cyclic, +}; + // BBS enum OverhangFanThreshold { Overhang_threshold_none = 0, @@ -534,6 +553,7 @@ CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(PrinterTechnology) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(GCodeFlavor) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(FuzzySkinType) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(FuzzySkinMode) +CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(TopSurfaceExpansionDirection) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(WipeTowerType) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(NoiseType) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(InfillPattern) @@ -561,6 +581,7 @@ CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(PrintHostType) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(AuthorizationType) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(WipeTowerWallType) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(PerimeterGeneratorType) +CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(ToolChangeOrderingType) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(PowerLossRecoveryMode) #undef CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS @@ -1122,6 +1143,9 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloat, lightning_prune_angle)) ((ConfigOptionFloat, lightning_straightening_angle)) ((ConfigOptionBool, align_infill_direction_to_model)) + ((ConfigOptionBool, anisotropic_surfaces)) + ((ConfigOptionEnum, center_of_surface_pattern)) + ((ConfigOptionBool, separated_infills)) ((ConfigOptionString, extra_solid_infills)) ((ConfigOptionEnum, fuzzy_skin)) ((ConfigOptionFloat, fuzzy_skin_thickness)) @@ -1187,6 +1211,9 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloatOrPercent, top_surface_line_width)) ((ConfigOptionInt, top_shell_layers)) ((ConfigOptionFloat, top_shell_thickness)) + ((ConfigOptionFloat, top_surface_expansion)) + ((ConfigOptionFloat, top_surface_expansion_margin)) + ((ConfigOptionEnum, top_surface_expansion_direction)) ((ConfigOptionFloatsNullable, top_surface_speed)) //BBS ((ConfigOptionBoolsNullable, enable_overhang_speed)) @@ -1413,6 +1440,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionBool, single_extruder_multi_material)) ((ConfigOptionBool, manual_filament_change)) ((ConfigOptionBool, single_extruder_multi_material_priming)) + ((ConfigOptionEnum, toolchange_ordering)) ((ConfigOptionBool, wipe_tower_no_sparse_layers)) ((ConfigOptionString, change_filament_gcode)) ((ConfigOptionString, change_extrusion_role_gcode)) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 13d7297f93..d977a5419b 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -4,6 +4,7 @@ #include "Print.hpp" #include "BoundingBox.hpp" #include "ClipperUtils.hpp" +#include "Clipper2Utils.hpp" #include "ElephantFootCompensation.hpp" #include "Geometry.hpp" #include "I18N.hpp" @@ -562,6 +563,11 @@ void PrintObject::prepare_infill() { if (! this->set_started(posPrepareInfill)) return; + + // Orca: clear all volume bbox caches + for (auto volume : this->model_object()->volumes) + volume->reset_volume_bbox(); + m_print->set_status(25, L("Generating infill regions")); if (m_typed_slices) { // To improve robustness of detect_surfaces_type() when reslicing (working with typed slices), see GH issue #7442. @@ -1297,6 +1303,9 @@ 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" || opt_key == "sparse_infill_filament_id" || opt_key == "internal_solid_filament_id" @@ -1330,6 +1339,9 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "top_surface_line_width" || opt_key == "top_surface_density" || opt_key == "bottom_surface_density" + || opt_key == "anisotropic_surfaces" + || opt_key == "center_of_surface_pattern" + || opt_key == "separated_infills" || opt_key == "initial_layer_line_width" || opt_key == "small_area_infill_flow_compensation" || opt_key == "lateral_lattice_angle_1" @@ -1686,6 +1698,69 @@ 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; + 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); + // 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; + + 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. + const ExPolygons infill_region = wall_band > 0. ? offset_ex(island, -float(wall_band)) : ExPolygons{ island }; + const ExPolygons island_top = intersection_ex(T, 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); + if (direction != TopSurfaceExpansionDirection::InwardAndOutward) { + ExPolygons outline; // the top with its holes filled (same outer edge) + outline.reserve(island_top.size()); + for (const ExPolygon &ex : island_top) + outline.emplace_back(ex.contour); + outline = union_ex(outline); + expanded = direction == TopSurfaceExpansionDirection::Inward ? + intersection_ex(expanded, outline) : // only growth into the holes + diff_ex(expanded, diff_ex(outline, island_top)); // only growth past the outer edge + } + // hold the expansion clear of the walls by the configured margin + const ExPolygons allowed = margin > 0. ? offset_ex(infill_region, -float(margin)) : infill_region; + append(grown, intersection_ex(expanded, allowed)); + } + + ExPolygons new_top = diff_ex(union_ex(T, grown), to_expolygons(bottom)); + top.clear(); + surfaces_append(top, std::move(new_top), stTop); + } + #ifdef SLIC3R_DEBUG_SLICE_PROCESSING { static int iRun = 0; diff --git a/src/libslic3r/miniz_extension.cpp b/src/libslic3r/miniz_extension.cpp index 424e76462c..53a4a8e9de 100644 --- a/src/libslic3r/miniz_extension.cpp +++ b/src/libslic3r/miniz_extension.cpp @@ -1,6 +1,8 @@ #include +#include #include "miniz_extension.hpp" +#include "Utils.hpp" #if defined(_MSC_VER) || defined(__MINGW64__) #include "boost/nowide/cstdio.hpp" @@ -15,6 +17,33 @@ namespace Slic3r { namespace { +std::string decode_zip_unicode_path_extra_field(const std::string& extra, const std::string& path) +{ + size_t offset = 0; + const mz_uint32 path_crc = mz_crc32(0, reinterpret_cast(path.data()), path.size()); + + while (offset + 4 <= extra.size()) { + const unsigned char* field = reinterpret_cast(extra.data() + offset); + const std::uint16_t len = field[2] | (static_cast(field[3]) << 8); + if (offset + 4 + len > extra.size()) + break; + + if (field[0] == 0x75 && field[1] == 0x70 && len >= 5 && field[4] == 0x01) { + const mz_uint32 stored_crc = + static_cast(field[5]) | + (static_cast(field[6]) << 8) | + (static_cast(field[7]) << 16) | + (static_cast(field[8]) << 24); + if (stored_crc == path_crc) + return std::string(extra.data() + offset + 9, extra.data() + offset + 4 + len); + } + + offset += 4 + len; + } + + return Slic3r::decode_path(path.c_str()); +} + bool open_zip(mz_zip_archive *zip, const char *fname, bool isread) { if (!zip) return false; @@ -76,6 +105,16 @@ bool open_zip_writer(mz_zip_archive *zip, const std::string &fname) bool close_zip_reader(mz_zip_archive *zip) { return close_zip(zip, true); } bool close_zip_writer(mz_zip_archive *zip) { return close_zip(zip, false); } +std::string decode_archive_entry_path(mz_zip_archive *zip, const mz_zip_archive_file_stat &stat) +{ + if (stat.m_is_utf8) + return stat.m_filename; + + std::string extra(1024, 0); + const size_t extra_size = mz_zip_reader_get_extra(zip, stat.m_file_index, extra.data(), extra.size()); + return decode_zip_unicode_path_extra_field(extra.substr(0, extra_size > 0 ? extra_size - 1 : 0), stat.m_filename); +} + MZ_Archive::MZ_Archive() { mz_zip_zero_struct(&arch); diff --git a/src/libslic3r/miniz_extension.hpp b/src/libslic3r/miniz_extension.hpp index 006226bf24..1a1c96689f 100644 --- a/src/libslic3r/miniz_extension.hpp +++ b/src/libslic3r/miniz_extension.hpp @@ -10,6 +10,7 @@ bool open_zip_reader(mz_zip_archive *zip, const std::string &fname_utf8); bool open_zip_writer(mz_zip_archive *zip, const std::string &fname_utf8); bool close_zip_reader(mz_zip_archive *zip); bool close_zip_writer(mz_zip_archive *zip); +std::string decode_archive_entry_path(mz_zip_archive *zip, const mz_zip_archive_file_stat &stat); class MZ_Archive { public: diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 46758e3e64..00e2cebc44 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -713,6 +713,36 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in toggle_field("top_layer_direction", has_top_shell); toggle_field("bottom_layer_direction", has_bottom_shell); + toggle_line("top_surface_expansion", has_top_shell); + toggle_line("top_surface_expansion_margin", has_top_shell); + bool has_top_surface_expansion = config->opt_float("top_surface_expansion") > 0; + toggle_field("top_surface_expansion_margin", has_top_surface_expansion); + toggle_line("top_surface_expansion_direction", has_top_shell); + toggle_field("top_surface_expansion_direction", has_top_surface_expansion); + + // Orca: Archimedean Chords and Octagram Spiral are the centered surface patterns that the + // pattern-centering, anisotropic-surface and separated-infill features act on. + auto is_centered_pattern = [](InfillPattern p) { + return p == InfillPattern::ipArchimedeanChords || p == InfillPattern::ipOctagramSpiral; + }; + bool is_top_centered = is_centered_pattern(config->option>("top_surface_pattern")->value); + bool is_bottom_centered = is_centered_pattern(config->option>("bottom_surface_pattern")->value); + bool has_centered_surface = (has_top_shell && is_top_centered) || (has_bottom_shell && is_bottom_centered); + + // Orca: center of surface pattern / anisotropic surfaces + toggle_line("center_of_surface_pattern", has_centered_surface); + toggle_line("anisotropic_surfaces", has_centered_surface); + + // Orca: separate infills + bool is_internal_infill_centered = is_centered_pattern(config->option>("sparse_infill_pattern")->value) || + config->opt_string("sparse_infill_rotate_template") != "" || + config->opt_string("solid_infill_rotate_template") != ""; + toggle_line("separated_infills", is_internal_infill_centered); + + // Orca: no need gaps + for (auto el : {"gap_fill_target", "filter_out_gap_fill"}) + toggle_field(el, !config->opt_bool("anisotropic_surfaces")); + for (auto el : { "infill_direction", "sparse_infill_line_width", "gap_fill_target","filter_out_gap_fill","infill_wall_overlap", "bridge_angle", "internal_bridge_angle", "relative_bridge_angle", "solid_infill_direction", "solid_infill_rotate_template", "internal_solid_infill_pattern", "internal_solid_filament_id", "top_surface_filament_id", "bottom_surface_filament_id", diff --git a/src/slic3r/GUI/ExtrusionCalibration.cpp b/src/slic3r/GUI/ExtrusionCalibration.cpp index 28255f37e4..cd3f49e977 100644 --- a/src/slic3r/GUI/ExtrusionCalibration.cpp +++ b/src/slic3r/GUI/ExtrusionCalibration.cpp @@ -239,7 +239,7 @@ void ExtrusionCalibration::create() m_button_save_result->SetStyle(ButtonStyle::Confirm, ButtonType::Choice); m_button_save_result->Bind(wxEVT_BUTTON, &ExtrusionCalibration::on_click_save, this); - m_button_last_step = new Button(m_step_2_panel, _L("Back")); // Back for english + m_button_last_step = new Button(m_step_2_panel, _CTX("Back", "Navigation")); // Back for english m_button_last_step->SetStyle(ButtonStyle::Regular, ButtonType::Choice); m_button_last_step->Bind(wxEVT_BUTTON, &ExtrusionCalibration::on_click_last, this); diff --git a/src/slic3r/GUI/FileArchiveDialog.cpp b/src/slic3r/GUI/FileArchiveDialog.cpp index 18c804196f..982187211c 100644 --- a/src/slic3r/GUI/FileArchiveDialog.cpp +++ b/src/slic3r/GUI/FileArchiveDialog.cpp @@ -221,16 +221,7 @@ FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* ar std::vector> filtered_entries; // second is unzipped size for (mz_uint i = 0; i < num_entries; ++i) { if (mz_zip_reader_file_stat(archive, i, &stat)) { - std::string extra(1024, 0); - boost::filesystem::path path; - size_t extra_size = mz_zip_reader_get_filename_from_extra(archive, i, extra.data(), extra.size()); - if (extra_size > 0) { - path = boost::filesystem::path(extra.substr(0, extra_size)); - } else { - wxString wname = boost::nowide::widen(stat.m_filename); - std::string name = into_u8(wname); - path = boost::filesystem::path(name); - } + boost::filesystem::path path = Slic3r::decode_archive_entry_path(archive, stat); assert(!path.empty()); if (!path.has_extension()) continue; diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 150a40dc0d..bb9de4976f 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -4258,7 +4258,7 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv }; auto append_print = [&imgui, imperial_units](const ColorRGBA& color, const std::array& offsets, const Times& times, std::pair used_filament) { - imgui.text(_u8L("Print")); + imgui.text(_CTX_utf8("Print", "Noun")); ImGui::SameLine(); float icon_size = ImGui::GetTextLineHeight(); @@ -4292,7 +4292,7 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv for (const PartialTime& item : partial_times) { switch (item.type) { - case PartialTime::EType::Print: { labels.push_back(_u8L("Print")); break; } + case PartialTime::EType::Print: { labels.push_back(_CTX_utf8("Print", "Noun")); break; } case PartialTime::EType::Pause: { labels.push_back(_u8L("Pause")); break; } case PartialTime::EType::ColorChange: { labels.push_back(_u8L("Color change")); break; } } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 4237632393..c250d96f44 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -6036,11 +6036,11 @@ void GLCanvas3D::_render_3d_navigator() strcpy(style.AxisLabels[ImGuizmo::Axis::Axis_Y], "Z"); // ORCA use uppercase to match text on tranform widgets strcpy(style.AxisLabels[ImGuizmo::Axis::Axis_Z], "X"); // ORCA use uppercase to match text on tranform widgets strcpy(style.FaceLabels[ImGuizmo::FACES::FACE_FRONT], _utf8("Front").c_str()); - strcpy(style.FaceLabels[ImGuizmo::FACES::FACE_BACK], _utf8("Back").c_str()); + strcpy(style.FaceLabels[ImGuizmo::FACES::FACE_BACK], _CTX_utf8("Back", "Camera View").c_str()); strcpy(style.FaceLabels[ImGuizmo::FACES::FACE_TOP], _utf8("Top").c_str()); strcpy(style.FaceLabels[ImGuizmo::FACES::FACE_BOTTOM], _utf8("Bottom").c_str()); - strcpy(style.FaceLabels[ImGuizmo::FACES::FACE_LEFT], _CTX_utf8(L_CONTEXT("Left", "Camera"), "Camera").c_str()); - strcpy(style.FaceLabels[ImGuizmo::FACES::FACE_RIGHT], _CTX_utf8(L_CONTEXT("Right", "Camera"), "Camera").c_str()); + strcpy(style.FaceLabels[ImGuizmo::FACES::FACE_LEFT], _CTX_utf8("Left", "Camera View").c_str()); + strcpy(style.FaceLabels[ImGuizmo::FACES::FACE_RIGHT], _CTX_utf8("Right", "Camera View").c_str()); float sc = get_scale(); #ifdef WIN32 diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index ccb6dedd29..75dc44add6 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -132,6 +132,9 @@ std::map> SettingsFactory::PART_CATE {"infill_anchor", "", 1}, {"infill_anchor_max", "", 1}, {"top_surface_pattern", "", 1}, + {"top_surface_expansion", "", 1}, + {"top_surface_expansion_margin", "", 1}, + {"top_surface_expansion_direction", "", 1}, {"bottom_surface_pattern", "", 1}, {"internal_solid_infill_pattern", "", 1}, {"align_infill_direction_to_model", "", 1}, @@ -187,7 +190,7 @@ std::vector SettingsFactory::get_visible_options(const std::s //Quality "wall_infill_order", "ironing_type", "inner_wall_line_width", "outer_wall_line_width", "top_surface_line_width", //Shell - "wall_loops", "top_shell_layers", "bottom_shell_layers", "top_shell_thickness", "bottom_shell_thickness", + "wall_loops", "top_shell_layers", "bottom_shell_layers", "top_shell_thickness", "bottom_shell_thickness", "top_surface_expansion", "top_surface_expansion_margin", "top_surface_expansion_direction", //Infill "sparse_infill_density", "sparse_infill_pattern", "top_surface_pattern", "bottom_surface_pattern", "infill_combination", "infill_direction", "infill_wall_overlap", //speed diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 218a67890b..a99b68340d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -1303,7 +1303,7 @@ void GLGizmoMeasure::render_dimensioning() return; const double ratio = new_value / old_value; - wxGetApp().plater()->take_snapshot(_u8L("Scale"), UndoRedo::SnapshotType::GizmoAction); + wxGetApp().plater()->take_snapshot(_CTX_utf8("Scale", "Verb"), UndoRedo::SnapshotType::GizmoAction); // apply scale TransformationType type; type.set_world(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index 77fc7ef46c..ebbb76f348 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -143,9 +143,9 @@ bool GLGizmoScale3D::on_init() std::string GLGizmoScale3D::on_get_name() const { if (!on_is_activable() && m_state == EState::Off) { - return _u8L("Scale") + ":\n" + _u8L("Please select at least one object."); + return _CTX_utf8("Scale", "Verb") + ":\n" + _u8L("Please select at least one object."); } else { - return _u8L("Scale"); + return _CTX_utf8("Scale", "Verb"); } } diff --git a/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp b/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp index 5297f95c82..3f82777a2a 100644 --- a/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp +++ b/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp @@ -1161,7 +1161,7 @@ void GizmoObjectManipulation::do_render_scale_input_window(ImGuiWrapper* imgui_w imgui_wrapper->calc_text_size(_L("Part")).x }) + imgui_wrapper->calc_text_size("xxx"sv).x + imgui_wrapper->scaled(3.5f); float label_max = std::max({ - imgui_wrapper->calc_text_size(_L("Scale")).x, + imgui_wrapper->calc_text_size(_CTX("Scale", "Noun")).x, imgui_wrapper->calc_text_size(_L("Size")).x }); float caption_max = std::max(label_max, coord_combo_width - 3 * space_size); @@ -1218,7 +1218,7 @@ void GizmoObjectManipulation::do_render_scale_input_window(ImGuiWrapper* imgui_w //ImGui::PushItemWidth(unit_size * 2); ImGui::AlignTextToFramePadding(); - imgui_wrapper->text(_L("Scale")); + imgui_wrapper->text(_CTX("Scale", "Noun")); ImGui::SameLine(caption_max + space_size); ImGui::PushItemWidth(unit_size); ImGui::BBLInputDouble(label_scale_values[0][0], &scale[0], 0.0f, 0.0f, "%.2f"); diff --git a/src/slic3r/GUI/ImageGrid.cpp b/src/slic3r/GUI/ImageGrid.cpp index 77fd7b132e..3c333c93fb 100644 --- a/src/slic3r/GUI/ImageGrid.cpp +++ b/src/slic3r/GUI/ImageGrid.cpp @@ -644,7 +644,7 @@ void Slic3r::GUI::ImageGrid::renderContent1(wxDC &dc, wxPoint const &pt, int ind if (m_file_sys->GetFileType() == PrinterFileSystem::F_MODEL) { if (secondAction != _L("Play")) thirdAction = secondAction; - secondAction = _L("Print"); + secondAction = _CTX("Print", "Verb"); } // Draw buttons on hovered item wxRect rect{pt.x, pt.y + m_content_rect.GetBottom() - m_buttons_background.GetHeight(), m_content_rect.GetWidth(), m_buttons_background.GetHeight()}; diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 66d7d87835..c2753f0bb1 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -2013,10 +2013,10 @@ wxBoxSizer* MainFrame::create_side_tools() }); // upload and print - SideButton* send_gcode_btn = new SideButton(p, _L("Print"), ""); + SideButton* send_gcode_btn = new SideButton(p, _CTX("Print", "Verb"), ""); send_gcode_btn->SetCornerRadius(0); send_gcode_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent&) { - m_print_btn->SetLabel(_L("Print")); + m_print_btn->SetLabel(_CTX("Print", "Verb")); m_print_select = eSendGcode; m_print_enable = get_enable_print_status(); m_print_btn->Enable(m_print_enable); @@ -2662,9 +2662,9 @@ static void add_common_view_menu_items(wxMenu* view_menu, MainFrame* mainFrame, "", nullptr, [can_change_view]() { return can_change_view(); }, mainFrame); append_menu_item(view_menu, wxID_ANY, _L("Rear") + "\t" + ctrl + "4", _L("Rear View"), [mainFrame](wxCommandEvent&) { mainFrame->select_view("rear"); }, "", nullptr, [can_change_view]() { return can_change_view(); }, mainFrame); - append_menu_item(view_menu, wxID_ANY, _CTX(L_CONTEXT("Left", "Camera"), "Camera") + "\t" + ctrl + "5", _L("Left View"),[mainFrame](wxCommandEvent &) {mainFrame->select_view("left"); }, + append_menu_item(view_menu, wxID_ANY, _CTX("Left", "Camera View") + "\t" + ctrl + "5", _L("Left View"),[mainFrame](wxCommandEvent &) {mainFrame->select_view("left"); }, "", nullptr, [can_change_view]() { return can_change_view(); }, mainFrame); - append_menu_item(view_menu, wxID_ANY, _CTX(L_CONTEXT("Right", "Camera"), "Camera") + "\t" + ctrl + "6", _L("Right View"),[mainFrame](wxCommandEvent &) { mainFrame->select_view("right"); }, + append_menu_item(view_menu, wxID_ANY, _CTX("Right", "Camera View") + "\t" + ctrl + "6", _L("Right View"),[mainFrame](wxCommandEvent &) { mainFrame->select_view("right"); }, "", nullptr, [can_change_view]() { return can_change_view(); }, mainFrame); } @@ -4040,7 +4040,7 @@ void MainFrame::set_print_button_to_default(PrintSelectType select_type) m_print_btn->Enable(m_print_enable); this->Layout(); } else if (select_type == PrintSelectType::eSendGcode) { - m_print_btn->SetLabel(_L("Print")); + m_print_btn->SetLabel(_CTX("Print", "Verb")); m_print_select = eSendGcode; if (m_print_enable) m_print_enable = get_enable_print_status() && can_send_gcode(); diff --git a/src/slic3r/GUI/MediaFilePanel.cpp b/src/slic3r/GUI/MediaFilePanel.cpp index 1fca47e7c4..84ba61d9d4 100644 --- a/src/slic3r/GUI/MediaFilePanel.cpp +++ b/src/slic3r/GUI/MediaFilePanel.cpp @@ -556,7 +556,7 @@ void MediaFilePanel::doAction(size_t index, int action) } else if (action == 1) { if (fs->GetFileType() == PrinterFileSystem::F_MODEL) { if (index != -1) { - auto dlg = new MediaProgressDialog(_L("Print"), this, [fs] { fs->FetchModelCancel(); }); + auto dlg = new MediaProgressDialog(_CTX("Print", "Verb"), this, [fs] { fs->FetchModelCancel(); }); dlg->Update(0, _L("Fetching model information...")); fs->FetchModel(index, [this, fs, dlg, index](int result, std::string const &data) { dlg->Destroy(); @@ -566,7 +566,7 @@ void MediaFilePanel::doAction(size_t index, int action) wxString msg = data.empty() ? _L("Failed to fetch model information from printer.") : from_u8(data); CallAfter([this, msg] { - MessageDialog(this, msg, _L("Print"), wxOK).ShowModal(); + MessageDialog(this, msg, _CTX("Print", "Verb"), wxOK).ShowModal(); }); return; } @@ -579,7 +579,7 @@ void MediaFilePanel::doAction(size_t index, int action) || plate_data_list.empty()) { MessageDialog(this, _L("Failed to parse model information."), - _L("Print"), wxOK).ShowModal(); + _CTX("Print", "Verb"), wxOK).ShowModal(); return; } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 26b9217b3f..aecea8f3b8 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -13113,6 +13113,9 @@ void adjust_settings_for_flowrate_calib(ModelObjectPtrs& objects, bool linear, i _obj->config.set_key_value("top_solid_infill_flow_ratio", new ConfigOptionFloat(1.0f)); _obj->config.set_key_value("infill_direction", new ConfigOptionFloat(45)); _obj->config.set_key_value("solid_infill_direction", new ConfigOptionFloat(135)); + _obj->config.set_key_value("anisotropic_surfaces", new ConfigOptionBool(false)); + _obj->config.set_key_value("center_of_surface_pattern", new ConfigOptionEnum(CenterOfSurfacePattern::Each_Surface)); + _obj->config.set_key_value("separated_infills", new ConfigOptionBool(false)); _obj->config.set_key_value("align_infill_direction_to_model", new ConfigOptionBool(true)); _obj->config.set_key_value("ironing_type", new ConfigOptionEnum(IroningType::NoIroning)); _obj->config.set_key_value("internal_solid_infill_speed", new ConfigOptionFloatsNullable(internal_solid_speeds)); @@ -13923,17 +13926,9 @@ bool Plater::preview_zip_archive(const boost::filesystem::path& archive_path) if (mz_zip_reader_file_stat(&archive, i, &stat)) { if (size != stat.m_uncomp_size) // size must fit continue; - wxString wname = boost::nowide::widen(stat.m_filename); - std::string name = into_u8(wname); + std::string name = Slic3r::decode_archive_entry_path(&archive, stat); fs::path archive_path(name); - std::string extra(1024, 0); - size_t extra_size = mz_zip_reader_get_filename_from_extra(&archive, i, extra.data(), extra.size()); - if (extra_size > 0) { - archive_path = fs::path(extra.substr(0, extra_size)); - name = archive_path.string(); - } - if (archive_path.empty()) continue; if (path != archive_path) @@ -15750,7 +15745,7 @@ int Plater::export_3mf(const boost::filesystem::path& output_path, SaveStrategy std::vector project_presets = preset_bundle.get_current_project_embedded_presets(); StoreParams store_params; - store_params.path = path_u8.c_str(); + store_params.path = path_u8; store_params.model = &p->model; store_params.plate_data_list = plate_data_list; store_params.export_plate_idx = export_plate_idx; diff --git a/src/slic3r/GUI/SelectMachine.cpp b/src/slic3r/GUI/SelectMachine.cpp index 09314d3b49..9fd0a65087 100644 --- a/src/slic3r/GUI/SelectMachine.cpp +++ b/src/slic3r/GUI/SelectMachine.cpp @@ -138,7 +138,7 @@ SelectMachineDialog::SelectMachineDialog(Plater *plater) /*mode switch*/ /*auto m_sizer_mode_switch = new wxBoxSizer(wxHORIZONTAL); - m_mode_print = new SendModeSwitchButton(scroll_area, _L("Print"), true); + m_mode_print = new SendModeSwitchButton(scroll_area, _CTX("Print", "Verb"), true); m_mode_send = new SendModeSwitchButton(scroll_area,_L("Save to printer"), false); m_sizer_mode_switch->Add(m_mode_print, 0, wxALIGN_CENTER, 0); m_sizer_mode_switch->Add(0, 0, 0, wxLEFT, FromDIP(8)); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 8c73ae7d3a..b96e2c8779 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -6694,7 +6694,7 @@ bool Tab::validate_filament_temperature_pairs() RichMessageDialog dialog(parent(), msg_text, _L("Temperature Safety Check"), wxYES | wxNO | wxICON_WARNING); dialog.SetButtonLabel(wxID_YES, _L("Continue"), true); - dialog.SetButtonLabel(wxID_NO, _L("Back")); + dialog.SetButtonLabel(wxID_NO, _CTX("Back", "Navigation")); dialog.ShowCheckBox(_L("Don't warn again for this preset")); const int answer = dialog.ShowModal(); // Session-only suppression (does not modify/save filament preset data). diff --git a/src/slic3r/GUI/WebViewDialog.cpp b/src/slic3r/GUI/WebViewDialog.cpp index 20ed70e799..d10eaedef5 100644 --- a/src/slic3r/GUI/WebViewDialog.cpp +++ b/src/slic3r/GUI/WebViewDialog.cpp @@ -47,7 +47,7 @@ WebViewPanel::WebViewPanel(wxWindow *parent) // Create the button bSizer_toolbar = new wxBoxSizer(wxHORIZONTAL); - m_button_back = new wxButton(this, wxID_ANY, _L("Back"), wxDefaultPosition, wxDefaultSize, 0); + m_button_back = new wxButton(this, wxID_ANY, _CTX("Back", "Navigation"), wxDefaultPosition, wxDefaultSize, 0); m_button_back->Enable(false); bSizer_toolbar->Add(m_button_back, 0, wxALL, 5); diff --git a/src/slic3r/Utils/CalibUtils.cpp b/src/slic3r/Utils/CalibUtils.cpp index 29d302c776..8e7de91318 100644 --- a/src/slic3r/Utils/CalibUtils.cpp +++ b/src/slic3r/Utils/CalibUtils.cpp @@ -7,6 +7,7 @@ #include "../GUI/PartPlate.hpp" #include "libslic3r/CutUtils.hpp" #include "libslic3r/ClipperUtils.hpp" +#include "libslic3r/Utils.hpp" #include "libslic3r/Model.hpp" #include "slic3r/GUI/Jobs/BoostThreadWorker.hpp" @@ -27,11 +28,19 @@ const double MIN_PA_K_VALUE = 0.0; const double MAX_PA_K_VALUE = 2.0; std::unique_ptr CalibUtils::print_worker; -wxString wxstr_temp_dir = fs::path(fs::temp_directory_path() / "calib").wstring(); -static const std::string temp_dir = wxstr_temp_dir.utf8_string(); -static const std::string temp_gcode_path = temp_dir + "/temp.gcode"; -static const std::string path = temp_dir + "/test.3mf"; -static const std::string config_3mf_path = temp_dir + "/test_config.3mf"; + +// Built lazily so temporary_dir() is read on first use, after set_temporary_dir() +// has run at startup (it isolates the temp root per user to avoid cross-user collisions). +static const std::string& calib_temp_dir() +{ + static const std::string dir = temporary_dir() + "/calib"; + return dir; +} +static std::string calib_temp_file(const std::string& name) { return calib_temp_dir() + "/" + name; } + +static const std::string gcode_filename = "temp.gcode"; +static const std::string model_filename = "test.3mf"; +static const std::string config_filename = "test_config.3mf"; static std::string MachineBedTypeString[7] = { "auto", @@ -1599,7 +1608,7 @@ bool CalibUtils::process_and_store_3mf(Model *model, const DynamicPrintConfig &f part_plate->update_slice_result_valid_state(true); gcode_result->reset(); - fff_print->export_gcode(temp_gcode_path, gcode_result, nullptr); + fff_print->export_gcode(calib_temp_file(gcode_filename), gcode_result, nullptr); std::vector thumbnails; PlateDataPtrs plate_data_list; @@ -1618,7 +1627,7 @@ bool CalibUtils::process_and_store_3mf(Model *model, const DynamicPrintConfig &f } for (auto plate_data : plate_data_list) { - plate_data->gcode_file = temp_gcode_path; + plate_data->gcode_file = calib_temp_file(gcode_filename); plate_data->is_sliced_valid = true; plate_data->printer_model_id = obj_->printer_type; FilamentInfo& filament_info = plate_data->slice_filaments_info.front(); @@ -1681,7 +1690,7 @@ bool CalibUtils::process_and_store_3mf(Model *model, const DynamicPrintConfig &f } StoreParams store_params; - store_params.path = path.c_str(); + store_params.path = calib_temp_file(model_filename); store_params.model = model; store_params.plate_data_list = plate_data_list; store_params.config = &new_print_config; @@ -1695,7 +1704,7 @@ bool CalibUtils::process_and_store_3mf(Model *model, const DynamicPrintConfig &f bool success = Slic3r::store_bbs_3mf(store_params); store_params.strategy = SaveStrategy::Silence | SaveStrategy::SplitModel | SaveStrategy::WithSliceInfo | SaveStrategy::SkipAuxiliary; - store_params.path = config_3mf_path.c_str(); + store_params.path = calib_temp_file(config_filename); success = Slic3r::store_bbs_3mf(store_params); release_PlateData_list(plate_data_list); @@ -1784,9 +1793,9 @@ void CalibUtils::send_to_print(const CalibInfo &calib_info, wxString &error_mess PrintPrepareData job_data; job_data.is_from_plater = false; job_data.plate_idx = 0; - job_data._3mf_config_path = config_3mf_path; - job_data._3mf_path = path; - job_data._temp_path = temp_dir; + job_data._3mf_config_path = calib_temp_file(config_filename); + job_data._3mf_path = calib_temp_file(model_filename); + job_data._temp_path = calib_temp_dir(); PlateListData plate_data; plate_data.is_valid = true; @@ -1889,9 +1898,9 @@ void CalibUtils::send_to_print(const std::vector &calib_infos, wxStri PrintPrepareData job_data; job_data.is_from_plater = false; job_data.plate_idx = 0; - job_data._3mf_config_path = config_3mf_path; - job_data._3mf_path = path; - job_data._temp_path = temp_dir; + job_data._3mf_config_path = calib_temp_file(config_filename); + job_data._3mf_path = calib_temp_file(model_filename); + job_data._temp_path = calib_temp_dir(); PlateListData plate_data; plate_data.is_valid = true; diff --git a/tests/fff_print/test_support_material.cpp b/tests/fff_print/test_support_material.cpp index 24c0e9cdc5..f854939c8c 100644 --- a/tests/fff_print/test_support_material.cpp +++ b/tests/fff_print/test_support_material.cpp @@ -93,3 +93,14 @@ SCENARIO("Support layer Z honors contact distance", "[SupportMaterial]") } } } + +// extrude_support once held a `static` lambda capturing `this`, so a second export in the +// same process dereferenced a returned stack frame (ASan: stack-use-after-return). +TEST_CASE("Support G-code emission survives a second slice in the same process", "[SupportMaterial][Regression]") +{ + const std::string first = slice({ TestMesh::overhang }, { { "enable_support", 1 } }); + REQUIRE(! layers_with_role(first, "support").empty()); + + const std::string second = slice({ TestMesh::overhang }, { { "enable_support", 1 } }); + REQUIRE(! layers_with_role(second, "support").empty()); +} diff --git a/tests/libslic3r/test_arachne_walls.cpp b/tests/libslic3r/test_arachne_walls.cpp index 2d8ee3bc03..4ca18400af 100644 --- a/tests/libslic3r/test_arachne_walls.cpp +++ b/tests/libslic3r/test_arachne_walls.cpp @@ -18,6 +18,7 @@ #include #include "libslic3r/Arachne/WallToolPaths.hpp" +#include "libslic3r/Arachne/SkeletalTrapezoidation.hpp" #include "libslic3r/Arachne/utils/ExtrusionLine.hpp" #include "libslic3r/Arachne/BeadingStrategy/BeadingStrategyFactory.hpp" #include "libslic3r/Arachne/BeadingStrategy/BeadingStrategy.hpp" @@ -265,3 +266,46 @@ TEST_CASE("Arachne widening keeps two beads in transition band (#14376)", "[Arac for (const coord_t w : beading.bead_widths) CHECK(w <= inner_width); } + +namespace { +// Exposes the protected static interpolate() for a focused unit test. +struct InterpolateProbe : SkeletalTrapezoidation { + using SkeletalTrapezoidation::interpolate; +}; +} // anonymous namespace + +// interpolate() indexes the merged beading with an index derived from `left`. The merged beading +// follows the thicker of left/right, so when the thicker side has fewer insets the index runs past +// its end. +TEST_CASE("Beading interpolation tolerates a thicker side with fewer insets", "[Arachne][Regression]") { + using Beading = BeadingStrategy::Beading; + + // Thicker side (right) has fewer insets, so the merged beading holds only 2 toolpath locations. + const coord_t w = scaled(0.42); + Beading left; + left.total_thickness = scaled(1.0); + left.bead_widths = { w, w, w, w }; + left.toolpath_locations = { scaled(0.1), scaled(0.3), scaled(0.5), scaled(0.7) }; + left.left_over = 0; + + Beading right; + right.total_thickness = scaled(2.0); + right.bead_widths = { w, w }; + right.toolpath_locations = { scaled(0.1), scaled(0.3) }; + right.left_over = 0; + + // Just past left's location [2] (0.5), so the derived index is 2, past the end of the 2-inset merged beading. + const coord_t switching_radius = scaled(0.6); + + Beading result; + REQUIRE_NOTHROW(result = InterpolateProbe::interpolate(left, 0.5, right, switching_radius)); + + // With the guard the adjustment is skipped, so the result is the plain interpolation. + const Beading expected = InterpolateProbe::interpolate(left, 0.5, right); + REQUIRE(result.toolpath_locations.size() == expected.toolpath_locations.size()); + REQUIRE(result.bead_widths.size() == expected.bead_widths.size()); + for (size_t i = 0; i < expected.toolpath_locations.size(); ++i) { + CHECK(result.toolpath_locations[i] == expected.toolpath_locations[i]); + CHECK(result.bead_widths[i] == expected.bead_widths[i]); + } +} diff --git a/tests/libslic3r/test_placeholder_parser.cpp b/tests/libslic3r/test_placeholder_parser.cpp index ec72b72769..14716b0b5e 100644 --- a/tests/libslic3r/test_placeholder_parser.cpp +++ b/tests/libslic3r/test_placeholder_parser.cpp @@ -52,6 +52,11 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") { SECTION("math: round(-13.4)") { REQUIRE(parser.process("{round(-13.4)}") == "-13"); } SECTION("math: round(13.6)") { REQUIRE(parser.process("{round(13.6)}") == "14"); } SECTION("math: round(-13.6)") { REQUIRE(parser.process("{round(-13.6)}") == "-14"); } + SECTION("math: round(13.5)") { REQUIRE(parser.process("{round(13.5)}") == "14"); } + SECTION("math: floor(13.9)") { REQUIRE(parser.process("{floor(13.9)}") == "13"); } + SECTION("math: floor(-13.1)") { REQUIRE(parser.process("{floor(-13.1)}") == "-14"); } + SECTION("math: ceil(13.1)") { REQUIRE(parser.process("{ceil(13.1)}") == "14"); } + SECTION("math: ceil(-13.9)") { REQUIRE(parser.process("{ceil(-13.9)}") == "-13"); } SECTION("math: digits(5, 15)") { REQUIRE(parser.process("{digits(5, 15)}") == " 5"); } SECTION("math: digits(5., 15)") { REQUIRE(parser.process("{digits(5., 15)}") == " 5"); } SECTION("math: zdigits(5, 15)") { REQUIRE(parser.process("{zdigits(5, 15)}") == "000000000000005"); } @@ -65,6 +70,8 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") { SECTION("math: interpolate_table(13.84375892476, (0, 0), (20, 20))") { REQUIRE(std::stod(parser.process("{interpolate_table(13.84375892476, (0, 0), (20, 20))}")) == Catch::Approx(13.84375892476)); } SECTION("math: interpolate_table(13, (0, 0), (20, 20), (30, 20))") { REQUIRE(std::stod(parser.process("{interpolate_table(13, (0, 0), (20, 20), (30, 20))}")) == Catch::Approx(13.)); } SECTION("math: interpolate_table(25, (0, 0), (20, 20), (30, 20))") { REQUIRE(std::stod(parser.process("{interpolate_table(25, (0, 0), (20, 20), (30, 20))}")) == Catch::Approx(20.)); } + // Only the grammar's built-in functions are callable; any other name is an undefined variable and throws. + SECTION("math: a non-built-in function name throws") { REQUIRE_THROWS(parser.process("{sqrt(16)}")); } // regex_replace(subject, /pattern/, replacement): the string-transform primitive. SECTION("regex_replace: strips a file extension") { REQUIRE(parser.process("{regex_replace(\"part.stl\", /\\.[^.]*$/, \"\")}") == "part"); }